Skip to content

Commit 968ab19

Browse files
authored
refactor(transport): handle transport error, no more log.Fatal (#395)
1 parent 3319f69 commit 968ab19

14 files changed

+301
-143
lines changed

browser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (b *browserImpl) NewContext(options ...BrowserNewContextOptions) (BrowserCo
7979
}
8080
channel, err := b.channel.Send("newContext", options, overrides)
8181
if err != nil {
82-
return nil, fmt.Errorf("could not send message: %w", err)
82+
return nil, err
8383
}
8484
context := fromChannel(channel).(*browserContextImpl)
8585
context.browser = b
@@ -108,7 +108,7 @@ func (b *browserImpl) NewPage(options ...BrowserNewPageOptions) (Page, error) {
108108
func (b *browserImpl) NewBrowserCDPSession() (CDPSession, error) {
109109
channel, err := b.channel.Send("newBrowserCDPSession")
110110
if err != nil {
111-
return nil, fmt.Errorf("could not send message: %w", err)
111+
return nil, err
112112
}
113113

114114
cdpSession := fromChannel(channel).(*cdpSessionImpl)

browser_context.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (b *browserContextImpl) NewCDPSession(page interface{}) (CDPSession, error)
7979

8080
channel, err := b.channel.Send("newCDPSession", params)
8181
if err != nil {
82-
return nil, fmt.Errorf("could not send message: %w", err)
82+
return nil, err
8383
}
8484

8585
cdpSession := fromChannel(channel).(*cdpSessionImpl)
@@ -93,7 +93,7 @@ func (b *browserContextImpl) NewPage() (Page, error) {
9393
}
9494
channel, err := b.channel.Send("newPage")
9595
if err != nil {
96-
return nil, fmt.Errorf("could not send message: %w", err)
96+
return nil, err
9797
}
9898
return fromChannel(channel).(*pageImpl), nil
9999
}
@@ -103,7 +103,7 @@ func (b *browserContextImpl) Cookies(urls ...string) ([]Cookie, error) {
103103
"urls": urls,
104104
})
105105
if err != nil {
106-
return nil, fmt.Errorf("could not send message: %w", err)
106+
return nil, err
107107
}
108108
cookies := make([]Cookie, len(result.([]interface{})))
109109
for i, item := range result.([]interface{}) {
@@ -369,6 +369,9 @@ func (b *browserContextImpl) Close(options ...BrowserContextCloseOptions) error
369369
_, err = b.channel.Send("close", map[string]interface{}{
370370
"reason": b.closeReason,
371371
})
372+
if err != nil {
373+
return err
374+
}
372375
<-b.closed
373376
return err
374377
}

browser_type.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (b *browserTypeImpl) Launch(options ...BrowserTypeLaunchOptions) (Browser,
2525
}
2626
channel, err := b.channel.Send("launch", options, overrides)
2727
if err != nil {
28-
return nil, fmt.Errorf("could not send message: %w", err)
28+
return nil, err
2929
}
3030
browser := fromChannel(channel).(*browserImpl)
3131
b.didLaunchBrowser(browser)
@@ -81,7 +81,7 @@ func (b *browserTypeImpl) LaunchPersistentContext(userDataDir string, options ..
8181
}
8282
channel, err := b.channel.Send("launchPersistentContext", options, overrides)
8383
if err != nil {
84-
return nil, fmt.Errorf("could not send message: %w", err)
84+
return nil, err
8585
}
8686
context := fromChannel(channel).(*browserContextImpl)
8787
b.didCreateContext(context, option, tracesDir)
@@ -97,9 +97,15 @@ func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConne
9797
return nil, err
9898
}
9999
jsonPipe := fromChannel(pipe.(map[string]interface{})["pipe"]).(*jsonPipe)
100-
connection := newConnection(jsonPipe.Close, localUtils)
101-
connection.isRemote = true
102-
var browser *browserImpl
100+
connection := newConnection(jsonPipe, localUtils)
101+
102+
playwright, err := connection.Start()
103+
if err != nil {
104+
return nil, err
105+
}
106+
playwright.setSelectors(b.playwright.Selectors)
107+
browser := fromChannel(playwright.initializer["preLaunchedBrowser"]).(*browserImpl)
108+
browser.shouldCloseConnectionOnClose = true
103109
pipeClosed := func() {
104110
for _, context := range browser.Contexts() {
105111
pages := context.Pages()
@@ -112,18 +118,7 @@ func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConne
112118
connection.cleanup()
113119
}
114120
jsonPipe.On("closed", pipeClosed)
115-
connection.onmessage = func(message map[string]interface{}) error {
116-
if err := jsonPipe.Send(message); err != nil {
117-
pipeClosed()
118-
return err
119-
}
120-
return nil
121-
}
122-
jsonPipe.On("message", connection.Dispatch)
123-
playwright := connection.Start()
124-
playwright.setSelectors(b.playwright.Selectors)
125-
browser = fromChannel(playwright.initializer["preLaunchedBrowser"]).(*browserImpl)
126-
browser.shouldCloseConnectionOnClose = true
121+
127122
b.didLaunchBrowser(browser)
128123
return browser, nil
129124
}

connection.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package playwright
33
import (
44
"errors"
55
"fmt"
6-
"log"
76
"reflect"
87
"regexp"
98
"strconv"
@@ -26,6 +25,7 @@ type result struct {
2625
}
2726

2827
type connection struct {
28+
transport transport
2929
apiZone sync.Map
3030
objects map[string]*channelOwner
3131
lastID int
@@ -34,30 +34,39 @@ type connection struct {
3434
callbacks sync.Map
3535
afterClose func()
3636
onClose func() error
37-
onmessage func(map[string]interface{}) error
3837
isRemote bool
3938
localUtils *localUtilsImpl
4039
tracingCount atomic.Int32
4140
abort chan struct{}
42-
closedError atomic.Value
41+
abortOnce sync.Once
42+
closedError *safeValue[error]
4343
}
4444

45-
func (c *connection) Start() *Playwright {
46-
playwright := make(chan *Playwright, 1)
45+
func (c *connection) Start() (*Playwright, error) {
4746
go func() {
48-
pw, err := c.rootObject.initialize()
49-
if err != nil {
50-
log.Fatal(err)
51-
return
47+
for {
48+
msg, err := c.transport.Poll()
49+
if err != nil {
50+
_ = c.transport.Close()
51+
c.cleanup(err)
52+
return
53+
}
54+
c.Dispatch(msg)
5255
}
53-
playwright <- pw
5456
}()
55-
return <-playwright
57+
58+
c.onClose = func() error {
59+
if err := c.transport.Close(); err != nil {
60+
return err
61+
}
62+
return nil
63+
}
64+
65+
return c.rootObject.initialize()
5666
}
5767

5868
func (c *connection) Stop() error {
59-
err := c.onClose()
60-
if err != nil {
69+
if err := c.onClose(); err != nil {
6170
return err
6271
}
6372
c.cleanup()
@@ -66,22 +75,24 @@ func (c *connection) Stop() error {
6675

6776
func (c *connection) cleanup(cause ...error) {
6877
if len(cause) > 0 {
69-
c.closedError.Store(fmt.Errorf("%w: %w", ErrTargetClosed, cause[0]))
78+
c.closedError.Set(fmt.Errorf("%w: %w", ErrTargetClosed, cause[0]))
7079
} else {
71-
c.closedError.Store(ErrTargetClosed)
80+
c.closedError.Set(ErrTargetClosed)
7281
}
7382
if c.afterClose != nil {
7483
c.afterClose()
7584
}
76-
select {
77-
case <-c.abort:
78-
default:
79-
close(c.abort)
80-
}
85+
c.abortOnce.Do(func() {
86+
select {
87+
case <-c.abort:
88+
default:
89+
close(c.abort)
90+
}
91+
})
8192
}
8293

8394
func (c *connection) Dispatch(msg *message) {
84-
if c.closedError.Load() != nil {
95+
if c.closedError.Get() != nil {
8596
return
8697
}
8798
method := msg.Method
@@ -208,8 +219,8 @@ func (c *connection) replaceGuidsWithChannels(payload interface{}) interface{} {
208219
}
209220

210221
func (c *connection) sendMessageToServer(object *channelOwner, method string, params interface{}, noReply bool) (*protocolCallback, error) {
211-
if e := c.closedError.Load(); e != nil {
212-
return nil, e.(error)
222+
if err := c.closedError.Get(); err != nil {
223+
return nil, err
213224
}
214225
if object.wasCollected {
215226
return nil, errors.New("The object has been collected to prevent unbounded heap growth.")
@@ -243,7 +254,7 @@ func (c *connection) sendMessageToServer(object *channelOwner, method string, pa
243254
c.LocalUtils().AddStackToTracingNoReply(id, stack)
244255
}
245256

246-
if err := c.onmessage(message); err != nil {
257+
if err := c.transport.Send(message); err != nil {
247258
return nil, fmt.Errorf("could not send message: %w", err)
248259
}
249260

@@ -317,15 +328,17 @@ func serializeCallLocation(caller stack.Call) map[string]interface{} {
317328
}
318329
}
319330

320-
func newConnection(onClose func() error, localUtils ...*localUtilsImpl) *connection {
331+
func newConnection(transport transport, localUtils ...*localUtilsImpl) *connection {
321332
connection := &connection{
322-
abort: make(chan struct{}, 1),
323-
objects: make(map[string]*channelOwner),
324-
onClose: onClose,
325-
isRemote: false,
333+
abort: make(chan struct{}, 1),
334+
objects: make(map[string]*channelOwner),
335+
transport: transport,
336+
isRemote: false,
337+
closedError: &safeValue[error]{},
326338
}
327339
if len(localUtils) > 0 {
328340
connection.localUtils = localUtils[0]
341+
connection.isRemote = true
329342
}
330343
connection.rootObject = newRootChannelOwner(connection)
331344
return connection

element_handle.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func (e *elementHandleImpl) Screenshot(options ...ElementHandleScreenshotOptions
261261
}
262262
data, err := e.channel.Send("screenshot", options, overrides)
263263
if err != nil {
264-
return nil, fmt.Errorf("could not send message :%w", err)
264+
return nil, err
265265
}
266266
image, err := base64.StdEncoding.DecodeString(data.(string))
267267
if err != nil {

fetch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func (r *apiRequestImpl) NewContext(options ...APIRequestNewContextOptions) (API
3737

3838
channel, err := r.channel.Send("newRequest", options, overrides)
3939
if err != nil {
40-
return nil, fmt.Errorf("could not send message: %w", err)
40+
return nil, err
4141
}
4242
return fromChannel(channel).(*apiRequestContextImpl), nil
4343
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/go-stack/stack v1.8.1
99
github.com/gorilla/websocket v1.5.1
1010
github.com/h2non/filetype v1.1.3
11+
github.com/mitchellh/go-ps v1.0.0
1112
github.com/stretchr/testify v1.8.4
1213
github.com/tidwall/gjson v1.17.0
1314
go.uber.org/multierr v1.11.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
2020
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
2121
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
2222
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
23+
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
24+
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
2325
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
2426
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2527
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

helpers.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,20 @@ func prepareRecordHarOptions(option recordHarInputOptions) recordHarOptions {
578578
}
579579
return out
580580
}
581+
582+
type safeValue[T any] struct {
583+
sync.Mutex
584+
v T
585+
}
586+
587+
func (s *safeValue[T]) Set(v T) {
588+
s.Lock()
589+
defer s.Unlock()
590+
s.v = v
591+
}
592+
593+
func (s *safeValue[T]) Get() T {
594+
s.Lock()
595+
defer s.Unlock()
596+
return s.v
597+
}

jsonPipe.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package playwright
22

33
import (
44
"encoding/json"
5-
"log"
5+
"errors"
6+
"fmt"
67
)
78

89
type jsonPipe struct {
910
channelOwner
11+
msgChan chan *message
1012
}
1113

1214
func (j *jsonPipe) Send(message map[string]interface{}) error {
@@ -19,23 +21,43 @@ func (j *jsonPipe) Close() error {
1921
_, err := j.channel.Send("close")
2022
return err
2123
}
24+
25+
func (j *jsonPipe) Poll() (*message, error) {
26+
msg := <-j.msgChan
27+
if msg == nil {
28+
return nil, errors.New("jsonPipe closed")
29+
}
30+
return msg, nil
31+
}
32+
2233
func newJsonPipe(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *jsonPipe {
23-
j := &jsonPipe{}
34+
j := &jsonPipe{
35+
msgChan: make(chan *message, 2),
36+
}
2437
j.createChannelOwner(j, parent, objectType, guid, initializer)
2538
j.channel.On("message", func(ev map[string]interface{}) {
39+
var msg message
2640
m, err := json.Marshal(ev["message"])
27-
if err != nil {
28-
log.Fatal(err)
41+
if err == nil {
42+
err = json.Unmarshal(m, &msg)
2943
}
30-
var msg message
31-
err = json.Unmarshal(m, &msg)
3244
if err != nil {
33-
log.Fatal(err)
45+
msg = message{
46+
Error: &struct {
47+
Error Error "json:\"error\""
48+
}{
49+
Error: Error{
50+
Name: "Error",
51+
Message: fmt.Sprintf("jsonPipe: could not decode message: %s", err.Error()),
52+
},
53+
},
54+
}
3455
}
35-
j.Emit("message", &msg)
56+
j.msgChan <- &msg
3657
})
37-
j.channel.On("closed", func() {
58+
j.channel.Once("closed", func() {
3859
j.Emit("closed")
60+
close(j.msgChan)
3961
})
4062
return j
4163
}

0 commit comments

Comments
 (0)