Skip to content

Commit 5220da3

Browse files
committed
finish updating websocket/read/send logic
1 parent 234071f commit 5220da3

File tree

7 files changed

+160
-85
lines changed

7 files changed

+160
-85
lines changed

Gopkg.lock

-15
This file was deleted.

Gopkg.toml

-34
This file was deleted.

chrome_target.go

+39-30
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,14 @@ THE SOFTWARE.
2525
package gcd
2626

2727
import (
28-
"io"
28+
"context"
2929
"log"
30-
"net"
3130
"sync"
3231
"sync/atomic"
3332
"time"
3433

3534
"github.com/wirepair/gcd/gcdapi"
3635
"github.com/wirepair/gcd/gcdmessage"
37-
"golang.org/x/net/websocket"
3836
)
3937

4038
// TargetInfo defines the 'tab' or target for this chrome instance,
@@ -57,13 +55,14 @@ type TargetInfo struct {
5755
// Events are handled by mapping the method name to a function which takes a target and byte output.
5856
// For now, callers will need to unmarshall the types themselves.
5957
type ChromeTarget struct {
58+
ctx context.Context
6059
sendId int64 // An Id which is atomically incremented per request.
6160
// must be at top because of alignement and atomic usage
6261
replyLock sync.RWMutex // lock for dispatching responses
6362
replyDispatcher map[int64]chan *gcdmessage.Message // Replies to synch methods using a non-buffered channel
6463
eventLock sync.RWMutex // lock for dispatching events
6564
eventDispatcher map[string]func(*ChromeTarget, []byte) // calls the function when events match the subscribed method
66-
conn *websocket.Conn // the connection to the chrome debugger service for this tab/process
65+
conn *wsConn // the connection to the chrome debugger service for this tab/process
6766

6867
// Chrome Debugger Domains
6968
Accessibility *gcdapi.Accessibility
@@ -122,8 +121,8 @@ type ChromeTarget struct {
122121
}
123122

124123
// openChromeTarget creates a new Chrome Target by connecting to the service given the URL taken from initial connection.
125-
func openChromeTarget(addr string, target *TargetInfo) (*ChromeTarget, error) {
126-
conn, err := wsConnection(addr, target.WebSocketDebuggerUrl)
124+
func openChromeTarget(ctx context.Context, addr string, target *TargetInfo) (*ChromeTarget, error) {
125+
conn, err := wsConnection(ctx, target.WebSocketDebuggerUrl)
127126
if err != nil {
128127
return nil, err
129128
}
@@ -135,6 +134,7 @@ func openChromeTarget(addr string, target *TargetInfo) (*ChromeTarget, error) {
135134
doneCh := make(chan struct{})
136135
chromeTarget := &ChromeTarget{conn: conn, Target: target, sendCh: sendCh, replyDispatcher: replier, eventDispatcher: eventer, doneCh: doneCh, sendId: 0}
137136
chromeTarget.apiTimeout = 120 * time.Second // default 120 seconds to wait for chrome to respond to us
137+
chromeTarget.ctx = ctx
138138
chromeTarget.Init()
139139
chromeTarget.listen()
140140
return chromeTarget, nil
@@ -262,9 +262,10 @@ func (c *ChromeTarget) listenWrite() {
262262
c.replyLock.Unlock()
263263

264264
c.debugf("%d sending to chrome. %s\n", msg.Id, msg.Data)
265-
266-
err := websocket.Message.Send(c.conn, string(msg.Data))
265+
log.Printf("%d sending to chrome. %s\n", msg.Id, msg.Data)
266+
err := c.conn.Write(c.ctx, msg.Data)
267267
if err != nil {
268+
log.Printf("error sending message: %s\n", err)
268269
c.debugf("error sending message: %s\n", err)
269270
return
270271
}
@@ -277,25 +278,42 @@ func (c *ChromeTarget) listenWrite() {
277278

278279
// Listens for responses coming in from the Chrome Debugger Service.
279280
func (c *ChromeTarget) listenRead() {
281+
readCh := make(chan []byte, 1)
282+
writeClosed := make(chan struct{})
283+
go func() {
284+
for {
285+
var msg []byte
286+
err := c.conn.Read(c.ctx, &msg)
287+
if err != nil {
288+
c.debugf("error in ws read: %s\n", err)
289+
close(writeClosed)
290+
return
291+
} else {
292+
select {
293+
case <-c.ctx.Done():
294+
return
295+
case readCh <- msg:
296+
}
297+
}
298+
}
299+
}()
300+
280301
for {
281302
select {
303+
case <-writeClosed:
304+
return
282305
// receive done from listenWrite
283306
case <-c.doneCh:
284307
return
285-
// read data from websocket connection
286-
default:
287-
var msg string
288-
err := websocket.Message.Receive(c.conn, &msg)
289-
if err == io.EOF {
290-
c.debugf("error io.EOF in websocket read")
291-
return
292-
} else if err != nil {
293-
c.debugf("error in ws read: %s\n", err)
294-
} else {
295-
go c.dispatchResponse([]byte(msg))
308+
case <-c.ctx.Done():
309+
return
310+
case msg := <-readCh:
311+
if len(msg) != 0 {
312+
c.dispatchResponse(msg)
296313
}
297314
}
298315
}
316+
299317
}
300318

301319
type responseHeader struct {
@@ -374,20 +392,11 @@ func (c *ChromeTarget) checkTargetDisconnected(method string) {
374392
}
375393

376394
// Connects to the tab/process for sending/recv'ing debug events
377-
func wsConnection(addr, url string) (*websocket.Conn, error) {
378-
conn, err := net.Dial("tcp", addr)
395+
func wsConnection(ctx context.Context, url string) (*wsConn, error) {
396+
client, err := newWsConnDial(ctx, url)
379397
if err != nil {
380398
return nil, err
381399
}
382-
383-
config, errConfig := websocket.NewConfig(url, "http://localhost")
384-
if errConfig != nil {
385-
return nil, errConfig
386-
}
387-
client, errWS := websocket.NewClient(config, conn)
388-
if errWS != nil {
389-
return nil, errWS
390-
}
391400
return client, nil
392401
}
393402

gcd.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ THE SOFTWARE.
2525
package gcd
2626

2727
import (
28+
"context"
2829
"errors"
2930
"fmt"
3031
"io/ioutil"
@@ -83,6 +84,7 @@ type Gcd struct {
8384
flags []string
8485
env []string
8586
chomeApiVersion string
87+
ctx context.Context
8688
}
8789

8890
// Give it a friendly name.
@@ -94,6 +96,7 @@ func NewChromeDebugger() *Gcd {
9496
c.terminatedHandler = nil
9597
c.flags = make([]string, 0)
9698
c.env = make([]string, 0)
99+
c.ctx = context.Background()
97100
return c
98101
}
99102

@@ -261,7 +264,7 @@ func (c *Gcd) GetNewTargets(knownIds map[string]struct{}) ([]*ChromeTarget, erro
261264
chromeTargets := make([]*ChromeTarget, 0)
262265
for _, v := range connectableTargets {
263266
if _, ok := knownIds[v.Id]; !ok {
264-
target, err := openChromeTarget(c.addr, v)
267+
target, err := openChromeTarget(c.ctx, c.addr, v)
265268
if err != nil {
266269
return nil, err
267270
}
@@ -316,7 +319,7 @@ func (c *Gcd) NewTab() (*ChromeTarget, error) {
316319
if err != nil {
317320
return nil, &GcdDecodingErr{Message: err.Error()}
318321
}
319-
return openChromeTarget(c.addr, tabTarget)
322+
return openChromeTarget(c.ctx, c.addr, tabTarget)
320323
}
321324

322325
// GetFirstTab returns the first tab created, to be called when
@@ -328,7 +331,7 @@ func (c *Gcd) GetFirstTab() (*ChromeTarget, error) {
328331
}
329332
for _, tabTarget := range connectableTargets {
330333
if tabTarget.Type == "page" {
331-
return openChromeTarget(c.addr, tabTarget)
334+
return openChromeTarget(c.ctx, c.addr, tabTarget)
332335
}
333336
}
334337
return nil, ErrNoTabAvailable

go.mod

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ module github.com/wirepair/gcd
33
go 1.14
44

55
require (
6+
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
7+
github.com/gobwas/pool v0.2.0 // indirect
8+
github.com/gobwas/ws v1.0.3
69
github.com/json-iterator/go v1.1.9
710
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
811
github.com/modern-go/reflect2 v1.0.1 // indirect
9-
golang.org/x/net v0.0.0-20181207154023-610586996380
12+
github.com/stretchr/testify v1.4.0 // indirect
13+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
14+
gopkg.in/yaml.v2 v2.2.8 // indirect
1015
)

go.sum

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
23
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
5+
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
6+
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
7+
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
8+
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
9+
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
310
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
411
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
512
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -11,8 +18,16 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
1118
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
1219
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
1320
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
21+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1422
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1523
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1624
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
17-
golang.org/x/net v0.0.0-20181207154023-610586996380 h1:zPQexyRtNYBc7bcHmehl1dH6TB3qn8zytv8cBGLDNY0=
18-
golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
25+
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
26+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
27+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
28+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
29+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
30+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
31+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
32+
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
33+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

wsconn.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package gcd
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"io"
8+
"net"
9+
"net/url"
10+
"time"
11+
12+
"github.com/gobwas/ws"
13+
"github.com/gobwas/ws/wsutil"
14+
)
15+
16+
const writeSize = 15000000 // 15mb
17+
18+
// adapted from https://github.com/chromedp/chromedp/blob/8e0a16689423d48d8907c62a543c7ea468059228/conn.go
19+
type wsConn struct {
20+
conn net.Conn
21+
writer wsutil.Writer
22+
}
23+
24+
func newWsConnDial(ctx context.Context, url string) (*wsConn, error) {
25+
wconn := &wsConn{}
26+
conn, br, _, err := ws.Dial(ctx, url)
27+
if err != nil {
28+
return nil, err
29+
}
30+
if br != nil {
31+
panic("br should be nil")
32+
}
33+
wconn.conn = conn
34+
wconn.writer = *wsutil.NewWriterBufferSize(conn, ws.StateClientSide, ws.OpText, 1<<20)
35+
return wconn, nil
36+
}
37+
38+
func formatURL(toFormat string) string {
39+
u, err := url.Parse(toFormat)
40+
if err != nil {
41+
return ""
42+
}
43+
host, port, err := net.SplitHostPort(u.Host)
44+
if err != nil {
45+
return ""
46+
}
47+
addr, err := net.ResolveIPAddr("ip", host)
48+
if err != nil {
49+
return ""
50+
}
51+
u.Host = net.JoinHostPort(addr.IP.String(), port)
52+
return u.String()
53+
}
54+
55+
func (c *wsConn) Read(ctx context.Context, msg *[]byte) error {
56+
// get websocket reader
57+
c.conn.SetReadDeadline(time.Now().Add(10 * time.Second))
58+
reader := wsutil.NewReader(c.conn, ws.StateClientSide)
59+
h, err := reader.NextFrame()
60+
if err != nil {
61+
return err
62+
}
63+
64+
if h.OpCode == ws.OpClose {
65+
return io.EOF
66+
}
67+
68+
if h.OpCode != ws.OpText {
69+
return fmt.Errorf("InvalidWebsocketMessage")
70+
}
71+
72+
var b bytes.Buffer
73+
if _, err := b.ReadFrom(reader); err != nil {
74+
return err
75+
}
76+
77+
*msg = b.Bytes()
78+
return nil
79+
}
80+
81+
// Write writes a message.
82+
func (c *wsConn) Write(_ context.Context, msg []byte) error {
83+
c.writer.Reset(c.conn, ws.StateClientSide, ws.OpText)
84+
if _, err := c.writer.Write(msg); err != nil {
85+
return err
86+
}
87+
return c.writer.Flush()
88+
}
89+
90+
func (c *wsConn) Close() error {
91+
return c.Close()
92+
}

0 commit comments

Comments
 (0)