From 85f0e7a3749c865b0bc2ebe818c2b9bf3162eb7b Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Thu, 31 Mar 2016 21:48:18 -0700 Subject: [PATCH 1/2] Make Dial return net.Conn interface, not custom struct. This simplifies the documentation and package slightly. Remove WriteString method. It's a wrapper around Send method, and is not helpful in the context of a net.Conn, which deals with pure streams of bytes. To send text frames, either use a WebSocket and Send, or use type assertion to access the Send method from the returned net.Conn implementation (it embeds a WebSocket). Resolves #11. --- conn.go | 54 ++++++++++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/conn.go b/conn.go index 2678e6d..107cbf2 100644 --- a/conn.go +++ b/conn.go @@ -63,12 +63,12 @@ var errDeadlineReached = &deadlineErr{} // Dial opens a new WebSocket connection. It will block until the connection is // established or fails to connect. -func Dial(url string) (*Conn, error) { +func Dial(url string) (net.Conn, error) { ws, err := New(url) if err != nil { return nil, err } - conn := &Conn{ + conn := &conn{ WebSocket: ws, ch: make(chan *messageEvent, 1), } @@ -105,11 +105,8 @@ func Dial(url string) (*Conn, error) { return conn, nil } -// Conn is a high-level wrapper around WebSocket. It is intended to satisfy the -// net.Conn interface. -// -// To create a Conn, use Dial. Instantiating Conn without Dial will not work. -type Conn struct { +// conn is a high-level wrapper around WebSocket. It implements net.Conn interface. +type conn struct { *WebSocket ch chan *messageEvent @@ -123,13 +120,13 @@ type messageEvent struct { Data *js.Object `js:"data"` } -func (c *Conn) onMessage(event *js.Object) { +func (c *conn) onMessage(event *js.Object) { go func() { c.ch <- &messageEvent{Object: event} }() } -func (c *Conn) onClose(event *js.Object) { +func (c *conn) onClose(event *js.Object) { go func() { // We queue nil to the end so that any messages received prior to // closing get handled first. @@ -137,10 +134,10 @@ func (c *Conn) onClose(event *js.Object) { }() } -// initialize adds all of the event handlers necessary for a Conn to function. +// initialize adds all of the event handlers necessary for a conn to function. // It should never be called more than once and is already called if Dial was -// used to create the Conn. -func (c *Conn) initialize() { +// used to create the conn. +func (c *conn) initialize() { // We need this so that received binary data is in ArrayBufferView format so // that it can easily be read. c.BinaryType = "arraybuffer" @@ -151,7 +148,7 @@ func (c *Conn) initialize() { // handleFrame handles a single frame received from the channel. This is a // convenience funciton to dedupe code for the multiple deadline cases. -func (c *Conn) handleFrame(message *messageEvent, ok bool) (*messageEvent, error) { +func (c *conn) handleFrame(message *messageEvent, ok bool) (*messageEvent, error) { if !ok { // The channel has been closed return nil, io.EOF } else if message == nil { @@ -165,7 +162,7 @@ func (c *Conn) handleFrame(message *messageEvent, ok bool) (*messageEvent, error // receiveFrame receives one full frame from the WebSocket. It blocks until the // frame is received. -func (c *Conn) receiveFrame(observeDeadline bool) (*messageEvent, error) { +func (c *conn) receiveFrame(observeDeadline bool) (*messageEvent, error) { var deadlineChan <-chan time.Time // Receiving on a nil channel always blocks indefinitely if observeDeadline && !c.readDeadline.IsZero() { @@ -203,7 +200,7 @@ func getFrameData(obj *js.Object) []byte { return []byte(obj.String()) } -func (c *Conn) Read(b []byte) (n int, err error) { +func (c *conn) Read(b []byte) (n int, err error) { if c.readBuf != nil { n, err = c.readBuf.Read(b) if err == io.EOF { @@ -237,7 +234,7 @@ func (c *Conn) Read(b []byte) (n int, err error) { } // Write writes the contents of b to the WebSocket using a binary opcode. -func (c *Conn) Write(b []byte) (n int, err error) { +func (c *conn) Write(b []byte) (n int, err error) { // []byte is converted to an (U)Int8Array by GopherJS, which fullfils the // ArrayBufferView definition. err = c.Send(b) @@ -248,31 +245,20 @@ func (c *Conn) Write(b []byte) (n int, err error) { return } -// WriteString writes the contents of s to the WebSocket using a text frame -// opcode. -func (c *Conn) WriteString(s string) (n int, err error) { - err = c.Send(s) - if err != nil { - return - } - n = len(s) - return -} - // LocalAddr would typically return the local network address, but due to // limitations in the JavaScript API, it is unable to. Calling this method will // cause a panic. -func (c *Conn) LocalAddr() net.Addr { - // BUG(nightexcessive): Conn.LocalAddr() panics because the underlying +func (c *conn) LocalAddr() net.Addr { + // BUG(nightexcessive): conn.LocalAddr() panics because the underlying // JavaScript API has no way of figuring out the local address. // TODO(nightexcessive): Find a more graceful way to handle this - panic("we are unable to implement websocket.Conn.LocalAddr() due to limitations in the underlying JavaScript API") + panic("we are unable to implement websocket.conn.LocalAddr() due to limitations in the underlying JavaScript API") } // RemoteAddr returns the remote network address, based on // websocket.WebSocket.URL. -func (c *Conn) RemoteAddr() net.Addr { +func (c *conn) RemoteAddr() net.Addr { wsURL, err := url.Parse(c.URL) if err != nil { // TODO(nightexcessive): Should we be panicking for this? @@ -285,20 +271,20 @@ func (c *Conn) RemoteAddr() net.Addr { // It is equivalent to calling both SetReadDeadline and SetWriteDeadline. // // A zero value for t means that I/O operations will not time out. -func (c *Conn) SetDeadline(t time.Time) error { +func (c *conn) SetDeadline(t time.Time) error { c.readDeadline = t return nil } // SetReadDeadline sets the deadline for future Read calls. A zero value for t // means Read will not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { +func (c *conn) SetReadDeadline(t time.Time) error { c.readDeadline = t return nil } // SetWriteDeadline sets the deadline for future Write calls. Because our writes // do not block, this function is a no-op. -func (c *Conn) SetWriteDeadline(t time.Time) error { +func (c *conn) SetWriteDeadline(t time.Time) error { return nil } From 546cb2554a84c7fbe4bf18ef8552f446320ed961 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Thu, 31 Mar 2016 21:48:50 -0700 Subject: [PATCH 2/2] Minor style improvements, internal doc fixes. Use less naked returns. They're harder to read. --- conn.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/conn.go b/conn.go index 107cbf2..1af4474 100644 --- a/conn.go +++ b/conn.go @@ -193,10 +193,9 @@ func (c *conn) receiveFrame(observeDeadline bool) (*messageEvent, error) { func getFrameData(obj *js.Object) []byte { // Check if it's an array buffer. If so, convert it to a Go byte slice. if constructor := obj.Get("constructor"); constructor == js.Global.Get("ArrayBuffer") { - int8Array := js.Global.Get("Uint8Array").New(obj) - return int8Array.Interface().([]byte) + uint8Array := js.Global.Get("Uint8Array").New(obj) + return uint8Array.Interface().([]byte) } - return []byte(obj.String()) } @@ -235,14 +234,13 @@ func (c *conn) Read(b []byte) (n int, err error) { // Write writes the contents of b to the WebSocket using a binary opcode. func (c *conn) Write(b []byte) (n int, err error) { - // []byte is converted to an (U)Int8Array by GopherJS, which fullfils the + // []byte is converted to an Uint8Array by GopherJS, which fullfils the // ArrayBufferView definition. err = c.Send(b) if err != nil { - return + return 0, err } - n = len(b) - return + return len(b), nil } // LocalAddr would typically return the local network address, but due to