diff --git a/README.md b/README.md index 9ca3ca8..9ad75a0 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn - - + + @@ -50,10 +50,10 @@ Notes: 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 2. The application can get the type of a received data message by implementing - a [Codec marshal](http://godoc.org/code.google.com/p/go.net/websocket#Codec.Marshal) + a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) function. 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. Read returns when the input buffer is full or a frame boundary is - encountered, Each call to Write sends a single frame message. The Gorilla + encountered. Each call to Write sends a single frame message. The Gorilla io.Reader and io.WriteCloser operate on a single WebSocket message. diff --git a/client.go b/client.go index 3b5cac4..c25d24f 100644 --- a/client.go +++ b/client.go @@ -215,16 +215,6 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re } } - readBufferSize := d.ReadBufferSize - if readBufferSize == 0 { - readBufferSize = 4096 - } - - writeBufferSize := d.WriteBufferSize - if writeBufferSize == 0 { - writeBufferSize = 4096 - } - if len(d.Subprotocols) > 0 { h := http.Header{} for k, v := range requestHeader { @@ -234,7 +224,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re requestHeader = h } - conn, resp, err := NewClient(netConn, u, requestHeader, readBufferSize, writeBufferSize) + conn, resp, err := NewClient(netConn, u, requestHeader, d.ReadBufferSize, d.WriteBufferSize) if err != nil { return nil, resp, err } diff --git a/conn.go b/conn.go index 2701142..86c35e5 100644 --- a/conn.go +++ b/conn.go @@ -16,6 +16,20 @@ import ( "time" ) +const ( + maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask + maxControlFramePayloadSize = 125 + finalBit = 1 << 7 + maskBit = 1 << 7 + writeWait = time.Second + + defaultReadBufferSize = 4096 + defaultWriteBufferSize = 4096 + + continuationFrame = 0 + noFrame = -1 +) + // Close codes defined in RFC 6455, section 11.7. const ( CloseNormalClosure = 1000 @@ -55,49 +69,46 @@ const ( PongMessage = 10 ) -var ( - continuationFrame = 0 - noFrame = -1 -) +// ErrCloseSent is returned when the application writes a message to the +// connection after sending a close message. +var ErrCloseSent = errors.New("websocket: close sent") -var ( - // ErrCloseSent is returned when the application writes a message to the - // connection after sending a close message. - ErrCloseSent = errors.New("websocket: close sent") +// ErrReadLimit is returned when reading a message that is larger than the +// read limit set for the connection. +var ErrReadLimit = errors.New("websocket: read limit exceeded") - // ErrReadLimit is returned when reading a message that is larger than the - // read limit set for the connection. - ErrReadLimit = errors.New("websocket: read limit exceeded") -) - -type websocketError struct { +// netError satisfies the net Error interface. +type netError struct { msg string temporary bool timeout bool } -func (e *websocketError) Error() string { return e.msg } -func (e *websocketError) Temporary() bool { return e.temporary } -func (e *websocketError) Timeout() bool { return e.timeout } +func (e *netError) Error() string { return e.msg } +func (e *netError) Temporary() bool { return e.temporary } +func (e *netError) Timeout() bool { return e.timeout } + +// closeError represents close frame. +type closeError struct { + code int + text string +} + +func (e *closeError) Error() string { + return "websocket: close " + strconv.Itoa(e.code) + " " + e.text +} var ( - errWriteTimeout = &websocketError{msg: "websocket: write timeout", timeout: true} + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true} + errUnexpectedEOF = &closeError{code: CloseAbnormalClosure, text: io.ErrUnexpectedEOF.Error()} errBadWriteOpCode = errors.New("websocket: bad write message type") errWriteClosed = errors.New("websocket: write closed") errInvalidControlFrame = errors.New("websocket: invalid control frame") ) -const ( - maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask - maxControlFramePayloadSize = 125 - finalBit = 1 << 7 - maskBit = 1 << 7 - writeWait = time.Second -) - func hideTempErr(err error) error { if e, ok := err.(net.Error); ok && e.Temporary() { - err = struct{ error }{err} + err = &netError{msg: e.Error(), timeout: e.Timeout()} } return err } @@ -155,17 +166,24 @@ type Conn struct { handlePing func(string) error } -func newConn(conn net.Conn, isServer bool, readBufSize, writeBufSize int) *Conn { +func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { mu := make(chan bool, 1) mu <- true + if readBufferSize == 0 { + readBufferSize = defaultReadBufferSize + } + if writeBufferSize == 0 { + writeBufferSize = defaultWriteBufferSize + } + c := &Conn{ isServer: isServer, - br: bufio.NewReaderSize(conn, readBufSize), + br: bufio.NewReaderSize(conn, readBufferSize), conn: conn, mu: mu, readFinal: true, - writeBuf: make([]byte, writeBufSize+maxFrameHeaderSize), + writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize), writeFrameType: noFrame, writePos: maxFrameHeaderSize, } @@ -508,7 +526,7 @@ func (c *Conn) WriteMessage(messageType int, data []byte) error { // SetWriteDeadline sets the write deadline on the underlying network // connection. After a write has timed out, the websocket state is corrupt and // all future writes will return an error. A zero value for t means writes will -// not time out +// not time out. func (c *Conn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t return nil @@ -527,7 +545,7 @@ func (c *Conn) readFull(p []byte) (err error) { if n == len(p) { err = nil } else if err == io.EOF { - err = io.ErrUnexpectedEOF + err = errUnexpectedEOF } return } @@ -649,17 +667,17 @@ func (c *Conn) advanceFrame() (int, error) { } case CloseMessage: c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait)) - if len(payload) < 2 { - return noFrame, io.EOF + closeCode := CloseNoStatusReceived + closeText := "" + if len(payload) >= 2 { + closeCode = int(binary.BigEndian.Uint16(payload)) + closeText = string(payload[2:]) } - closeCode := binary.BigEndian.Uint16(payload) switch closeCode { case CloseNormalClosure, CloseGoingAway: return noFrame, io.EOF default: - return noFrame, errors.New("websocket: close " + - strconv.Itoa(int(closeCode)) + " " + - string(payload[2:])) + return noFrame, &closeError{code: closeCode, text: closeText} } } @@ -739,7 +757,7 @@ func (r messageReader) Read(b []byte) (int, error) { err := r.c.readErr if err == io.EOF && r.seq == r.c.readSeq { - err = io.ErrUnexpectedEOF + err = errUnexpectedEOF } return 0, err } diff --git a/conn_test.go b/conn_test.go index 632725a..1f1197e 100644 --- a/conn_test.go +++ b/conn_test.go @@ -152,7 +152,7 @@ func TestCloseBeforeFinalFrame(t *testing.T) { w, _ := wc.NextWriter(BinaryMessage) w.Write(make([]byte, bufSize+bufSize/2)) - wc.WriteControl(CloseMessage, []byte{}, time.Now().Add(10*time.Second)) + wc.WriteControl(CloseMessage, FormatCloseMessage(CloseNormalClosure, ""), time.Now().Add(10*time.Second)) w.Close() op, r, err := rc.NextReader() @@ -160,8 +160,8 @@ func TestCloseBeforeFinalFrame(t *testing.T) { t.Fatalf("NextReader() returned %d, %v", op, err) } _, err = io.Copy(ioutil.Discard, r) - if err != io.ErrUnexpectedEOF { - t.Fatalf("io.Copy() returned %v, want %v", err, io.ErrUnexpectedEOF) + if err != errUnexpectedEOF { + t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) } _, _, err = rc.NextReader() if err != io.EOF { @@ -184,12 +184,12 @@ func TestEOFBeforeFinalFrame(t *testing.T) { t.Fatalf("NextReader() returned %d, %v", op, err) } _, err = io.Copy(ioutil.Discard, r) - if err != io.ErrUnexpectedEOF { - t.Fatalf("io.Copy() returned %v, want %v", err, io.ErrUnexpectedEOF) + if err != errUnexpectedEOF { + t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) } _, _, err = rc.NextReader() - if err != io.ErrUnexpectedEOF { - t.Fatalf("NextReader() returned %v, want %v", err, io.ErrUnexpectedEOF) + if err != errUnexpectedEOF { + t.Fatalf("NextReader() returned %v, want %v", err, errUnexpectedEOF) } } diff --git a/doc.go b/doc.go index efde3dc..0d2bd91 100644 --- a/doc.go +++ b/doc.go @@ -97,10 +97,13 @@ // // Concurrency // -// A Conn supports a single concurrent caller to the write methods (NextWriter, -// SetWriteDeadline, WriteMessage) and a single concurrent caller to the read -// methods (NextReader, SetReadDeadline, ReadMessage). The Close and -// WriteControl methods can be called concurrently with all other methods. +// Connections do not support concurrent calls to the write methods +// (NextWriter, SetWriteDeadline, WriteMessage) or concurrent calls to the read +// methods methods (NextReader, SetReadDeadline, ReadMessage). Connections do +// support a concurrent reader and writer. +// +// The Close and WriteControl methods can be called concurrently with all other +// methods. // // Read is Required // @@ -117,4 +120,29 @@ // } // } // } +// +// Origin Considerations +// +// Web browsers allow Javascript applications to open a WebSocket connection to +// any host. It's up to the server to enforce an origin policy using the Origin +// request header sent by the browser. +// +// The Upgrader calls the function specified in the CheckOrigin field to check +// the origin. If the CheckOrigin function returns false, then the Upgrade +// method fails the WebSocket handshake with HTTP status 403. +// +// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail +// the handshake if the Origin request header is present and not equal to the +// Host request header. +// +// An application can allow connections from any origin by specifying a +// function that always returns true: +// +// var upgrader = websocket.Upgrader{ +// CheckOrigin: func(r *http.Request) bool { return true }, +// } +// +// The deprecated Upgrade function does not enforce an origin policy. It's the +// application's responsibility to check the Origin header before calling +// Upgrade. package websocket diff --git a/server.go b/server.go index aae9b68..01e8899 100644 --- a/server.go +++ b/server.go @@ -21,11 +21,6 @@ type HandshakeError struct { func (e HandshakeError) Error() string { return e.message } -const ( - defaultReadBufferSize = 4096 - defaultWriteBufferSize = 4096 -) - // Upgrader specifies parameters for upgrading an HTTP connection to a // WebSocket connection. type Upgrader struct { @@ -147,15 +142,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade return nil, errors.New("websocket: client sent data before handshake is complete") } - readBufSize := u.ReadBufferSize - if readBufSize == 0 { - readBufSize = defaultReadBufferSize - } - writeBufSize := u.WriteBufferSize - if writeBufSize == 0 { - writeBufSize = defaultWriteBufferSize - } - c := newConn(netConn, true, readBufSize, writeBufSize) + c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) c.subprotocol = subprotocol p := c.writeBuf[:0]
gorillago.netgithub.com/gorillagolang.org/x/net
RFC 6455 Features