Skip to content

Make Dial return net.Conn interface, not custom struct. #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 3, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 25 additions & 41 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down Expand Up @@ -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
Expand All @@ -123,24 +120,24 @@ 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.
c.ch <- nil
}()
}

// 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"
Expand All @@ -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 {
Expand All @@ -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() {
Expand Down Expand Up @@ -196,14 +193,13 @@ 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())
}

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 {
Expand Down Expand Up @@ -237,42 +233,30 @@ 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
func (c *conn) Write(b []byte) (n int, err error) {
// []byte is converted to an Uint8Array by GopherJS, which fullfils the
// ArrayBufferView definition.
err = c.Send(b)
if err != nil {
return
}
n = len(b)
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
return 0, err
}
n = len(s)
return
return len(b), nil
}

// 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?
Expand All @@ -285,20 +269,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
}