From 4f4cd5e63573da4d6edcc7d4213afaca67c19f88 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Mon, 23 Nov 2015 15:53:48 -0800 Subject: upgrading libs --- .../src/github.com/gorilla/websocket/README.md | 2 + .../src/github.com/gorilla/websocket/client.go | 246 +++++++++++++-------- .../gorilla/websocket/client_server_test.go | 93 +++++++- .../github.com/gorilla/websocket/client_test.go | 1 + .../src/github.com/gorilla/websocket/conn.go | 48 ++-- .../src/github.com/gorilla/websocket/conn_test.go | 44 +++- .../src/github.com/gorilla/websocket/doc.go | 13 +- .../gorilla/websocket/examples/autobahn/server.go | 2 +- .../gorilla/websocket/examples/chat/README.md | 1 + .../gorilla/websocket/examples/chat/conn.go | 8 +- .../gorilla/websocket/examples/command/README.md | 19 ++ .../gorilla/websocket/examples/command/home.html | 96 ++++++++ .../gorilla/websocket/examples/command/main.go | 188 ++++++++++++++++ .../gorilla/websocket/examples/echo/README.md | 17 ++ .../gorilla/websocket/examples/echo/client.go | 55 +++++ .../gorilla/websocket/examples/echo/server.go | 132 +++++++++++ .../gorilla/websocket/examples/filewatch/main.go | 2 +- .../src/github.com/gorilla/websocket/json.go | 4 +- .../src/github.com/gorilla/websocket/json_test.go | 4 +- .../src/github.com/gorilla/websocket/server.go | 3 + 20 files changed, 834 insertions(+), 144 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/README.md create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/home.html create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/main.go create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/README.md create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/client.go create mode 100644 Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/server.go (limited to 'Godeps/_workspace/src/github.com/gorilla/websocket') diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/README.md b/Godeps/_workspace/src/github.com/gorilla/websocket/README.md index 9ad75a0f5..9d71959ea 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/README.md +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/README.md @@ -7,6 +7,8 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the * [API Reference](http://godoc.org/github.com/gorilla/websocket) * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) +* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) +* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) ### Status diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/client.go b/Godeps/_workspace/src/github.com/gorilla/websocket/client.go index 5bc27e193..3bf9b2e84 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/client.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/client.go @@ -5,6 +5,7 @@ package websocket import ( + "bufio" "bytes" "crypto/tls" "errors" @@ -30,50 +31,17 @@ var ErrBadHandshake = errors.New("websocket: bad handshake") // If the WebSocket handshake fails, ErrBadHandshake is returned along with a // non-nil *http.Response so that callers can handle redirects, authentication, // etc. +// +// Deprecated: Use Dialer instead. func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { - challengeKey, err := generateChallengeKey() - if err != nil { - return nil, nil, err + d := Dialer{ + ReadBufferSize: readBufSize, + WriteBufferSize: writeBufSize, + NetDial: func(net, addr string) (net.Conn, error) { + return netConn, nil + }, } - acceptKey := computeAcceptKey(challengeKey) - - c = newConn(netConn, false, readBufSize, writeBufSize) - p := c.writeBuf[:0] - p = append(p, "GET "...) - p = append(p, u.RequestURI()...) - p = append(p, " HTTP/1.1\r\nHost: "...) - p = append(p, u.Host...) - // "Upgrade" is capitalized for servers that do not use case insensitive - // comparisons on header tokens. - p = append(p, "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "...) - p = append(p, challengeKey...) - p = append(p, "\r\n"...) - for k, vs := range requestHeader { - for _, v := range vs { - p = append(p, k...) - p = append(p, ": "...) - p = append(p, v...) - p = append(p, "\r\n"...) - } - } - p = append(p, "\r\n"...) - - if _, err := netConn.Write(p); err != nil { - return nil, nil, err - } - - resp, err := http.ReadResponse(c.br, &http.Request{Method: "GET", URL: u}) - if err != nil { - return nil, nil, err - } - if resp.StatusCode != 101 || - !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || - !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || - resp.Header.Get("Sec-Websocket-Accept") != acceptKey { - return nil, resp, ErrBadHandshake - } - c.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") - return c, resp, nil + return d.Dial(u.String(), requestHeader) } // A Dialer contains options for connecting to WebSocket server. @@ -82,6 +50,12 @@ type Dialer struct { // NetDial is nil, net.Dial is used. NetDial func(network, addr string) (net.Conn, error) + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + // TLSClientConfig specifies the TLS configuration to use with tls.Client. // If nil, the default configuration is used. TLSClientConfig *tls.Config @@ -99,17 +73,15 @@ type Dialer struct { var errMalformedURL = errors.New("malformed ws or wss URL") -// parseURL parses the URL. The url.Parse function is not used here because -// url.Parse mangles the path. +// parseURL parses the URL. +// +// This function is a replacement for the standard library url.Parse function. +// In Go 1.4 and earlier, url.Parse loses information from the path. func parseURL(s string) (*url.URL, error) { // From the RFC: // // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] - // - // We don't use the net/url parser here because the dialer interface does - // not provide a way for applications to work around percent deocding in - // the net/url parser. var u url.URL switch { @@ -130,6 +102,12 @@ func parseURL(s string) (*url.URL, error) { u.Opaque = s[i:] } + if strings.Contains(u.Host, "@") { + // Don't bother parsing user information because user information is + // not allowed in websocket URIs. + return nil, errMalformedURL + } + return &u, nil } @@ -139,9 +117,12 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { hostNoPort = hostNoPort[:i] } else { - if u.Scheme == "wss" { + switch u.Scheme { + case "wss": + hostPort += ":443" + case "https": hostPort += ":443" - } else { + default: hostPort += ":80" } } @@ -149,7 +130,9 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { } // DefaultDialer is a dialer with all fields set to the default zero values. -var DefaultDialer *Dialer +var DefaultDialer = &Dialer{ + Proxy: http.ProxyFromEnvironment, +} // Dial creates a new client connection. Use requestHeader to specify the // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). @@ -161,15 +144,91 @@ var DefaultDialer *Dialer // etcetera. The response body may not contain the entire response and does not // need to be closed by the application. func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + + if d == nil { + d = &Dialer{ + Proxy: http.ProxyFromEnvironment, + } + } + + challengeKey, err := generateChallengeKey() + if err != nil { + return nil, nil, err + } + u, err := parseURL(urlStr) if err != nil { return nil, nil, err } + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + default: + return nil, nil, errMalformedURL + } + + if u.User != nil { + // User name and password are not allowed in websocket URIs. + return nil, nil, errMalformedURL + } + + req := &http.Request{ + Method: "GET", + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + + // Set the request headers using the capitalization for names and values in + // RFC examples. Although the capitalization shouldn't matter, there are + // servers that depend on it. The Header.Set method is not used because the + // method canonicalizes the header names. + req.Header["Upgrade"] = []string{"websocket"} + req.Header["Connection"] = []string{"Upgrade"} + req.Header["Sec-WebSocket-Key"] = []string{challengeKey} + req.Header["Sec-WebSocket-Version"] = []string{"13"} + if len(d.Subprotocols) > 0 { + req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} + } + for k, vs := range requestHeader { + switch { + case k == "Host": + if len(vs) > 0 { + req.Host = vs[0] + } + case k == "Upgrade" || + k == "Connection" || + k == "Sec-Websocket-Key" || + k == "Sec-Websocket-Version" || + (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): + return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) + default: + req.Header[k] = vs + } + } + hostPort, hostNoPort := hostPortNoPort(u) - if d == nil { - d = &Dialer{} + var proxyURL *url.URL + // Check wether the proxy method has been configured + if d.Proxy != nil { + proxyURL, err = d.Proxy(req) + } + if err != nil { + return nil, nil, err + } + + var targetHostPort string + if proxyURL != nil { + targetHostPort, _ = hostPortNoPort(proxyURL) + } else { + targetHostPort = hostPort } var deadline time.Time @@ -183,7 +242,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re netDial = netDialer.Dial } - netConn, err := netDial("tcp", hostPort) + netConn, err := netDial("tcp", targetHostPort) if err != nil { return nil, nil, err } @@ -198,7 +257,31 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re return nil, nil, err } - if u.Scheme == "wss" { + if proxyURL != nil { + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: hostPort}, + Host: hostPort, + Header: make(http.Header), + } + + connectReq.Write(netConn) + + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(netConn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + return nil, nil, err + } + if resp.StatusCode != 200 { + f := strings.SplitN(resp.Status, " ", 2) + return nil, nil, errors.New(f[1]) + } + } + + if u.Scheme == "https" { cfg := d.TLSClientConfig if cfg == nil { cfg = &tls.Config{ServerName: hostNoPort} @@ -219,45 +302,32 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re } } - if len(d.Subprotocols) > 0 { - h := http.Header{} - for k, v := range requestHeader { - h[k] = v - } - h.Set("Sec-Websocket-Protocol", strings.Join(d.Subprotocols, ", ")) - requestHeader = h - } - - if len(requestHeader["Host"]) > 0 { - // This can be used to supply a Host: header which is different from - // the dial address. - u.Host = requestHeader.Get("Host") + conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) - // Drop "Host" header - h := http.Header{} - for k, v := range requestHeader { - if k == "Host" { - continue - } - h[k] = v - } - requestHeader = h + if err := req.Write(netConn); err != nil { + return nil, nil, err } - conn, resp, err := NewClient(netConn, u, requestHeader, d.ReadBufferSize, d.WriteBufferSize) - + resp, err := http.ReadResponse(conn.br, req) if err != nil { - if err == ErrBadHandshake { - // Before closing the network connection on return from this - // function, slurp up some of the response to aid application - // debugging. - buf := make([]byte, 1024) - n, _ := io.ReadFull(resp.Body, buf) - resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) - } - return nil, resp, err + return nil, nil, err + } + if resp.StatusCode != 101 || + !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || + !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || + resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { + // Before closing the network connection on return from this + // function, slurp up some of the response to aid application + // debugging. + buf := make([]byte, 1024) + n, _ := io.ReadFull(resp.Body, buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) + return nil, resp, ErrBadHandshake } + resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") + netConn.SetDeadline(time.Time{}) netConn = nil // to avoid close in defer. return conn, resp, nil diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/client_server_test.go b/Godeps/_workspace/src/github.com/gorilla/websocket/client_server_test.go index 749ef2050..05a7888fe 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/client_server_test.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/client_server_test.go @@ -56,11 +56,6 @@ func newTLSServer(t *testing.T) *cstServer { } func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - t.Logf("method %s not allowed", r.Method) - http.Error(w, "method not allowed", 405) - return - } subprotos := Subprotocols(r) if !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) { t.Logf("subprotols=%v, want %v", subprotos, cstDialer.Subprotocols) @@ -123,6 +118,45 @@ func sendRecv(t *testing.T, ws *Conn) { } } +func TestProxyDial(t *testing.T) { + + s := newServer(t) + defer s.Close() + + surl, _ := url.Parse(s.URL) + + cstDialer.Proxy = http.ProxyURL(surl) + + connect := false + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == "CONNECT" { + connect = true + w.WriteHeader(200) + return + } + + if !connect { + t.Log("connect not recieved") + http.Error(w, "connect not recieved", 405) + return + } + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) + + cstDialer.Proxy = http.ProxyFromEnvironment +} + func TestDial(t *testing.T) { s := newServer(t) defer s.Close() @@ -229,6 +263,45 @@ func TestDialBadOrigin(t *testing.T) { } } +func TestDialBadHeader(t *testing.T) { + s := newServer(t) + defer s.Close() + + for _, k := range []string{"Upgrade", + "Connection", + "Sec-Websocket-Key", + "Sec-Websocket-Version", + "Sec-Websocket-Protocol"} { + h := http.Header{} + h.Set(k, "bad") + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) + if err == nil { + ws.Close() + t.Errorf("Dial with header %s returned nil", k) + } + } +} + +func TestBadMethod(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ws, err := cstUpgrader.Upgrade(w, r, nil) + if err == nil { + t.Errorf("handshake succeeded, expect fail") + ws.Close() + } + })) + defer s.Close() + + resp, err := http.PostForm(s.URL, url.Values{}) + if err != nil { + t.Fatalf("PostForm returned error %v", err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusMethodNotAllowed { + t.Errorf("Status = %d, want %d", resp.StatusCode, http.StatusMethodNotAllowed) + } +} + func TestHandshake(t *testing.T) { s := newServer(t) defer s.Close() @@ -289,8 +362,8 @@ func TestRespOnBadHandshake(t *testing.T) { } } -// If the Host header is specified in `Dial()`, the server must receive it as -// the `Host:` header. +// TestHostHeader confirms that the host header provided in the call to Dial is +// sent to the server. func TestHostHeader(t *testing.T) { s := newServer(t) defer s.Close() @@ -305,16 +378,12 @@ func TestHostHeader(t *testing.T) { origHandler.ServeHTTP(w, r) }) - ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}}) + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}}) if err != nil { t.Fatalf("Dial: %v", err) } defer ws.Close() - if resp.StatusCode != http.StatusSwitchingProtocols { - t.Fatalf("resp.StatusCode = %v, want http.StatusSwitchingProtocols", resp.StatusCode) - } - if gotHost := <-specifiedHost; gotHost != "testhost" { t.Fatalf("gotHost = %q, want \"testhost\"", gotHost) } diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/client_test.go b/Godeps/_workspace/src/github.com/gorilla/websocket/client_test.go index d2f2ebd79..07a9cb453 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/client_test.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/client_test.go @@ -20,6 +20,7 @@ var parseURLTests = []struct { {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}}, {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}}, {"ss://example.com/a/b", nil}, + {"ws://webmaster@example.com/", nil}, } func TestParseURL(t *testing.T) { diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/conn.go b/Godeps/_workspace/src/github.com/gorilla/websocket/conn.go index e719f1ce6..e8b6b3e04 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/conn.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/conn.go @@ -88,19 +88,23 @@ 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 +// CloseError represents close frame. +type CloseError struct { + + // Code is defined in RFC 6455, section 11.7. + Code int + + // Text is the optional text payload. + Text string } -func (e *closeError) Error() string { - return "websocket: close " + strconv.Itoa(e.code) + " " + e.text +func (e *CloseError) Error() string { + return "websocket: close " + strconv.Itoa(e.Code) + " " + e.Text } var ( - errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true} - errUnexpectedEOF = &closeError{code: CloseAbnormalClosure, text: io.ErrUnexpectedEOF.Error()} + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: 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") @@ -296,7 +300,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er if n != 0 && n != len(buf) { c.conn.Close() } - return err + return hideTempErr(err) } // NextWriter returns a writer for the next message to send. The writer's @@ -673,12 +677,7 @@ func (c *Conn) advanceFrame() (int, error) { closeCode = int(binary.BigEndian.Uint16(payload)) closeText = string(payload[2:]) } - switch closeCode { - case CloseNormalClosure, CloseGoingAway: - return noFrame, io.EOF - default: - return noFrame, &closeError{code: closeCode, text: closeText} - } + return noFrame, &CloseError{Code: closeCode, Text: closeText} } return frameType, nil @@ -790,20 +789,27 @@ func (c *Conn) SetReadLimit(limit int64) { } // SetPingHandler sets the handler for ping messages received from the peer. -// The default ping handler sends a pong to the peer. -func (c *Conn) SetPingHandler(h func(string) error) { +// The appData argument to h is the PING frame application data. The default +// ping handler sends a pong to the peer. +func (c *Conn) SetPingHandler(h func(appData string) error) { if h == nil { h = func(message string) error { - c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) - return nil + err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) + if err == ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err } } c.handlePing = h } // SetPongHandler sets the handler for pong messages received from the peer. -// The default pong handler does nothing. -func (c *Conn) SetPongHandler(h func(string) error) { +// The appData argument to h is the PONG frame application data. The default +// pong handler does nothing. +func (c *Conn) SetPongHandler(h func(appData string) error) { if h == nil { h = func(string) error { return nil } } diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/conn_test.go b/Godeps/_workspace/src/github.com/gorilla/websocket/conn_test.go index 1f1197e71..02f2d4b50 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/conn_test.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/conn_test.go @@ -5,11 +5,13 @@ package websocket import ( + "bufio" "bytes" "fmt" "io" "io/ioutil" "net" + "reflect" "testing" "testing/iotest" "time" @@ -146,13 +148,15 @@ func TestControl(t *testing.T) { func TestCloseBeforeFinalFrame(t *testing.T) { const bufSize = 512 + expectedErr := &CloseError{Code: CloseNormalClosure, Text: "hello"} + var b1, b2 bytes.Buffer wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) w, _ := wc.NextWriter(BinaryMessage) w.Write(make([]byte, bufSize+bufSize/2)) - wc.WriteControl(CloseMessage, FormatCloseMessage(CloseNormalClosure, ""), time.Now().Add(10*time.Second)) + wc.WriteControl(CloseMessage, FormatCloseMessage(expectedErr.Code, expectedErr.Text), time.Now().Add(10*time.Second)) w.Close() op, r, err := rc.NextReader() @@ -160,12 +164,12 @@ func TestCloseBeforeFinalFrame(t *testing.T) { t.Fatalf("NextReader() returned %d, %v", op, err) } _, err = io.Copy(ioutil.Discard, r) - if err != errUnexpectedEOF { - t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("io.Copy() returned %v, want %v", err, expectedErr) } _, _, err = rc.NextReader() - if err != io.EOF { - t.Fatalf("NextReader() returned %v, want %v", err, io.EOF) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("NextReader() returned %v, want %v", err, expectedErr) } } @@ -236,3 +240,33 @@ func TestUnderlyingConn(t *testing.T) { t.Fatalf("Underlying conn is not what it should be.") } } + +func TestBufioReadBytes(t *testing.T) { + + // Test calling bufio.ReadBytes for value longer than read buffer size. + + m := make([]byte, 512) + m[len(m)-1] = '\n' + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, len(m)+64, len(m)+64) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, len(m)-64, len(m)-64) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(m) + w.Close() + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + + br := bufio.NewReader(r) + p, err := br.ReadBytes('\n') + if err != nil { + t.Fatalf("ReadBytes() returned %v", err) + } + if len(p) != len(m) { + t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m)) + } +} diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/doc.go b/Godeps/_workspace/src/github.com/gorilla/websocket/doc.go index 0d2bd912b..72286279c 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/doc.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/doc.go @@ -24,7 +24,7 @@ // ... Use conn to send and receive messages. // } // -// Call the connection WriteMessage and ReadMessages methods to send and +// Call the connection's WriteMessage and ReadMessage methods to send and // receive messages as a slice of bytes. This snippet of code shows how to echo // messages using these methods: // @@ -97,10 +97,13 @@ // // Concurrency // -// 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. +// Connections support one concurrent reader and one concurrent writer. +// +// Applications are responsible for ensuring that no more than one goroutine +// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, +// WriteJSON) concurrently and that no more than one goroutine calls the read +// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, +// SetPingHandler) concurrently. // // The Close and WriteControl methods can be called concurrently with all other // methods. diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/server.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/server.go index d96ac84db..d0b1c3158 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/server.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/server.go @@ -8,7 +8,7 @@ package main import ( "errors" "flag" - "github.com/gorilla/websocket" + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" "io" "log" "net/http" diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/README.md b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/README.md index 08fc3e65c..5df3cf1a3 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/README.md +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/README.md @@ -17,3 +17,4 @@ using the following commands. $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` $ go run *.go +To use the chat example, open http://localhost:8080/ in your browser. diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/conn.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/conn.go index 7cc0496c3..675d64113 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/conn.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/conn.go @@ -5,7 +5,7 @@ package main import ( - "github.com/gorilla/websocket" + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" "log" "net/http" "time" @@ -88,12 +88,8 @@ func (c *connection) writePump() { } } -// serverWs handles websocket requests from the peer. +// serveWs handles websocket requests from the peer. func serveWs(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - http.Error(w, "Method not allowed", 405) - return - } ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/README.md b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/README.md new file mode 100644 index 000000000..c30d3979a --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/README.md @@ -0,0 +1,19 @@ +# Command example + +This example connects a websocket connection to stdin and stdout of a command. +Received messages are written to stdin followed by a `\n`. Each line read from +from standard out is sent as a message to the client. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command` + $ go run main.go + # Open http://localhost:8080/ . + +Try the following commands. + + # Echo sent messages to the output area. + $ go run main.go cat + + # Run a shell.Try sending "ls" and "cat main.go". + $ go run main.go sh + diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/home.html b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/home.html new file mode 100644 index 000000000..72fd02b2a --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/home.html @@ -0,0 +1,96 @@ + + + +Command Example + + + + + +
+
+ + +
+ + diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/main.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/main.go new file mode 100644 index 000000000..3531b368c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/command/main.go @@ -0,0 +1,188 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "flag" + "io" + "log" + "net/http" + "os" + "os/exec" + "text/template" + "time" + + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" +) + +var ( + addr = flag.String("addr", "127.0.0.1:8080", "http service address") + cmdPath string + homeTempl = template.Must(template.ParseFiles("home.html")) +) + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Maximum message size allowed from peer. + maxMessageSize = 8192 + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 +) + +func pumpStdin(ws *websocket.Conn, w io.Writer) { + defer ws.Close() + ws.SetReadLimit(maxMessageSize) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := ws.ReadMessage() + if err != nil { + break + } + message = append(message, '\n') + if _, err := w.Write(message); err != nil { + break + } + } +} + +func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { + defer func() { + ws.Close() + close(done) + }() + s := bufio.NewScanner(r) + for s.Scan() { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { + break + } + } + if s.Err() != nil { + log.Println("scan:", s.Err()) + } +} + +func ping(ws *websocket.Conn, done chan struct{}) { + ticker := time.NewTicker(pingPeriod) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { + log.Println("ping:", err) + } + case <-done: + return + } + } +} + +func internalError(ws *websocket.Conn, msg string, err error) { + log.Println(msg, err) + ws.WriteMessage(websocket.TextMessage, []byte("Internal server error.")) +} + +var upgrader = websocket.Upgrader{} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade:", err) + return + } + + defer ws.Close() + + outr, outw, err := os.Pipe() + if err != nil { + internalError(ws, "stdout:", err) + return + } + defer outr.Close() + defer outw.Close() + + inr, inw, err := os.Pipe() + if err != nil { + internalError(ws, "stdin:", err) + return + } + defer inr.Close() + defer inw.Close() + + proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ + Files: []*os.File{inr, outw, outw}, + }) + if err != nil { + internalError(ws, "start:", err) + return + } + + inr.Close() + outw.Close() + + stdoutDone := make(chan struct{}) + go pumpStdout(ws, outr, stdoutDone) + go ping(ws, stdoutDone) + + pumpStdin(ws, inw) + + // Some commands will exit when stdin is closed. + inw.Close() + + // Other commands need a bonk on the head. + if err := proc.Signal(os.Interrupt); err != nil { + log.Println("inter:", err) + } + + select { + case <-stdoutDone: + case <-time.After(time.Second): + // A bigger bonk on the head. + if err := proc.Signal(os.Kill); err != nil { + log.Println("term:", err) + } + <-stdoutDone + } + + if _, err := proc.Wait(); err != nil { + log.Println("wait:", err) + } +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + homeTempl.Execute(w, r.Host) +} + +func main() { + flag.Parse() + if len(flag.Args()) < 1 { + log.Fatal("must specify at least one argument") + } + var err error + cmdPath, err = exec.LookPath(flag.Args()[0]) + if err != nil { + log.Fatal(err) + } + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + log.Fatal(http.ListenAndServe(*addr, nil)) +} diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/README.md b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/README.md new file mode 100644 index 000000000..6ad79ed76 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/README.md @@ -0,0 +1,17 @@ +# Client and server example + +This example shows a simple client and server. + +The server echoes messages sent to it. The client sends a message every second +and prints all messages received. + +To run the example, start the server: + + $ go run server.go + +Next, start the client: + + $ go run client.go + +The server includes a simple web client. To use the client, open +http://127.0.0.1:8080 in the browser and follow the instructions on the page. diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/client.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/client.go new file mode 100644 index 000000000..998d54462 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/client.go @@ -0,0 +1,55 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "log" + "net/url" + "time" + + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +func main() { + flag.Parse() + log.SetFlags(0) + + u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"} + log.Printf("connecting to %s", u.String()) + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + log.Fatal("dial:", err) + } + defer c.Close() + + go func() { + defer c.Close() + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", message) + } + }() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for t := range ticker.C { + err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) + if err != nil { + log.Println("write:", err) + break + } + } +} diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/server.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/server.go new file mode 100644 index 000000000..87f14ceb5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/echo/server.go @@ -0,0 +1,132 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "html/template" + "log" + "net/http" + + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +var upgrader = websocket.Upgrader{} // use default options + +func echo(w http.ResponseWriter, r *http.Request) { + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Print("upgrade:", err) + return + } + defer c.Close() + for { + mt, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", message) + err = c.WriteMessage(mt, message) + if err != nil { + log.Println("write:", err) + break + } + } +} + +func home(w http.ResponseWriter, r *http.Request) { + homeTemplate.Execute(w, "ws://"+r.Host+"/echo") +} + +func main() { + flag.Parse() + log.SetFlags(0) + http.HandleFunc("/echo", echo) + http.HandleFunc("/", home) + log.Fatal(http.ListenAndServe(*addr, nil)) +} + +var homeTemplate = template.Must(template.New("").Parse(` + + + + + + + +
+

Click "Open" to create a connection to the server, +"Send" to send a message to the server and "Close" to close the connection. +You can change the message and send multiple times. +

+

+ + +

+ +

+
+
+
+ + +`)) diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/main.go b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/main.go index a2c7b85fa..04244bc0f 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/main.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/main.go @@ -14,7 +14,7 @@ import ( "text/template" "time" - "github.com/gorilla/websocket" + "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/websocket" ) const ( diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/json.go b/Godeps/_workspace/src/github.com/gorilla/websocket/json.go index 18e62f225..4f0e36875 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/json.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/json.go @@ -48,9 +48,7 @@ func (c *Conn) ReadJSON(v interface{}) error { } err = json.NewDecoder(r).Decode(v) if err == io.EOF { - // Decode returns io.EOF when the message is empty or all whitespace. - // Convert to io.ErrUnexpectedEOF so that application can distinguish - // between an error reading the JSON value and the connection closing. + // One value is expected in the message. err = io.ErrUnexpectedEOF } return err diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/json_test.go b/Godeps/_workspace/src/github.com/gorilla/websocket/json_test.go index 1b7a5ec8b..61100e481 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/json_test.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/json_test.go @@ -38,7 +38,7 @@ func TestJSON(t *testing.T) { } } -func TestPartialJsonRead(t *testing.T) { +func TestPartialJSONRead(t *testing.T) { var buf bytes.Buffer c := fakeNetConn{&buf, &buf} wc := newConn(c, true, 1024, 1024) @@ -87,7 +87,7 @@ func TestPartialJsonRead(t *testing.T) { } err = rc.ReadJSON(&v) - if err != io.EOF { + if _, ok := err.(*CloseError); !ok { t.Error("final", err) } } diff --git a/Godeps/_workspace/src/github.com/gorilla/websocket/server.go b/Godeps/_workspace/src/github.com/gorilla/websocket/server.go index e56a00493..3a9805f02 100644 --- a/Godeps/_workspace/src/github.com/gorilla/websocket/server.go +++ b/Godeps/_workspace/src/github.com/gorilla/websocket/server.go @@ -93,6 +93,9 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header // request. Use the responseHeader to specify cookies (Set-Cookie) and the // application negotiated subprotocol (Sec-Websocket-Protocol). func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { + if r.Method != "GET" { + return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET") + } if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" { return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13") } -- cgit v1.2.3-1-g7c22