Skip to content

Commit 273ecad

Browse files
committed
Initial commit
0 parents  commit 273ecad

20 files changed

+2066
-0
lines changed

.gitignore

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
10+
# Architecture specific extensions/prefixes
11+
*.[568vq]
12+
[568vq].out
13+
14+
*.cgo1.go
15+
*.cgo2.c
16+
_cgo_defun.c
17+
_cgo_gotypes.go
18+
_cgo_export.*
19+
20+
_testmain.go
21+
22+
*.exe

LICENSE

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Copyright (c) 2013, Gorilla web toolkit
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification,
5+
are permitted provided that the following conditions are met:
6+
7+
Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
Redistributions in binary form must reproduce the above copyright notice, this
11+
list of conditions and the following disclaimer in the documentation and/or
12+
other materials provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# WebSocket
2+
3+
This project is a [Go](http://golang.org/) implementation of the
4+
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
5+
6+
The project passes the server tests in the [Autobahn WebSockets Test
7+
Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
8+
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
9+
10+
## Documentation
11+
12+
* [Reference](http://godoc.org/github.com/gorilla/websocket)
13+
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
14+
15+
## Features
16+
17+
- Send and receive ping, pong and close control messages.
18+
- Limit size of received messages.
19+
- Stream messages.
20+
- Specify IO buffer sizes.
21+
- Application has full control over origin checks and sub-protocol negotiation.
22+
23+
## Installation
24+
25+
go get github.com/gorilla/websocket
26+

client.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2013 Gary Burd. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package websocket
6+
7+
import (
8+
"errors"
9+
"net"
10+
"net/http"
11+
"net/url"
12+
"strings"
13+
)
14+
15+
// ErrBadHandshake is returned when the server response to opening handshake is
16+
// invalid.
17+
var ErrBadHandshake = errors.New("websocket: bad handshake")
18+
19+
// NewClient creates a new client connection using the given net connection.
20+
// The URL u specifies the host and request URI. Use requestHeader to specify
21+
// the origin (Origin), subprotocols (Set-WebSocket-Protocol) and cookies
22+
// (Cookie). Use the response.Header to get the selected subprotocol
23+
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
24+
//
25+
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
26+
// non-nil *http.Response so that callers can handle redirects, authentication,
27+
// etc.
28+
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
29+
challengeKey, err := generateChallengeKey()
30+
if err != nil {
31+
return nil, nil, err
32+
}
33+
acceptKey := computeAcceptKey(challengeKey)
34+
35+
c = newConn(netConn, false, readBufSize, writeBufSize)
36+
p := c.writeBuf[:0]
37+
p = append(p, "GET "...)
38+
p = append(p, u.RequestURI()...)
39+
p = append(p, " HTTP/1.1\r\nHost: "...)
40+
p = append(p, u.Host...)
41+
p = append(p, "\r\nUpgrade: websocket\r\nConnection: upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "...)
42+
p = append(p, challengeKey...)
43+
p = append(p, "\r\n"...)
44+
for k, vs := range requestHeader {
45+
for _, v := range vs {
46+
p = append(p, k...)
47+
p = append(p, ": "...)
48+
p = append(p, v...)
49+
p = append(p, "\r\n"...)
50+
}
51+
}
52+
p = append(p, "\r\n"...)
53+
54+
if _, err := netConn.Write(p); err != nil {
55+
return nil, nil, err
56+
}
57+
58+
resp, err := http.ReadResponse(c.br, &http.Request{Method: "GET", URL: u})
59+
if err != nil {
60+
return nil, nil, err
61+
}
62+
if resp.StatusCode != 101 ||
63+
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
64+
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
65+
resp.Header.Get("Sec-Websocket-Accept") != acceptKey {
66+
return nil, resp, ErrBadHandshake
67+
}
68+
return c, resp, nil
69+
}

client_server_test.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2013 Gary Burd. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package websocket_test
6+
7+
import (
8+
"io"
9+
"io/ioutil"
10+
"net"
11+
"net/http"
12+
"net/http/httptest"
13+
"net/url"
14+
"testing"
15+
"time"
16+
17+
"github.com/gorilla/websocket"
18+
)
19+
20+
type wsHandler struct {
21+
*testing.T
22+
}
23+
24+
func (t wsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
25+
if r.Method != "GET" {
26+
http.Error(w, "Method not allowed", 405)
27+
t.Logf("bad method: %s", r.Method)
28+
return
29+
}
30+
if r.Header.Get("Origin") != "http://"+r.Host {
31+
http.Error(w, "Origin not allowed", 403)
32+
t.Logf("bad origin: %s", r.Header.Get("Origin"))
33+
return
34+
}
35+
ws, err := websocket.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}}, 1024, 1024)
36+
if _, ok := err.(websocket.HandshakeError); ok {
37+
t.Logf("bad handshake: %v", err)
38+
http.Error(w, "Not a websocket handshake", 400)
39+
return
40+
} else if err != nil {
41+
t.Logf("upgrade error: %v", err)
42+
return
43+
}
44+
defer ws.Close()
45+
for {
46+
op, r, err := ws.NextReader()
47+
if err != nil {
48+
if err != io.EOF {
49+
t.Logf("NextReader: %v", err)
50+
}
51+
return
52+
}
53+
if op == websocket.PongMessage {
54+
continue
55+
}
56+
w, err := ws.NextWriter(op)
57+
if err != nil {
58+
t.Logf("NextWriter: %v", err)
59+
return
60+
}
61+
if _, err = io.Copy(w, r); err != nil {
62+
t.Logf("Copy: %v", err)
63+
return
64+
}
65+
if err := w.Close(); err != nil {
66+
t.Logf("Close: %v", err)
67+
return
68+
}
69+
}
70+
}
71+
72+
func TestClientServer(t *testing.T) {
73+
s := httptest.NewServer(wsHandler{t})
74+
defer s.Close()
75+
u, _ := url.Parse(s.URL)
76+
c, err := net.Dial("tcp", u.Host)
77+
if err != nil {
78+
t.Fatalf("Dial: %v", err)
79+
}
80+
ws, resp, err := websocket.NewClient(c, u, http.Header{"Origin": {s.URL}}, 1024, 1024)
81+
if err != nil {
82+
t.Fatalf("NewClient: %v", err)
83+
}
84+
defer ws.Close()
85+
86+
var sessionID string
87+
for _, c := range resp.Cookies() {
88+
if c.Name == "sessionID" {
89+
sessionID = c.Value
90+
}
91+
}
92+
if sessionID != "1234" {
93+
t.Error("Set-Cookie not received from the server.")
94+
}
95+
96+
w, _ := ws.NextWriter(websocket.TextMessage)
97+
io.WriteString(w, "HELLO")
98+
w.Close()
99+
ws.SetReadDeadline(time.Now().Add(1 * time.Second))
100+
op, r, err := ws.NextReader()
101+
if err != nil {
102+
t.Fatalf("NextReader: %v", err)
103+
}
104+
if op != websocket.TextMessage {
105+
t.Fatalf("op=%d, want %d", op, websocket.TextMessage)
106+
}
107+
b, err := ioutil.ReadAll(r)
108+
if err != nil {
109+
t.Fatalf("ReadAll: %v", err)
110+
}
111+
if string(b) != "HELLO" {
112+
t.Fatalf("message=%s, want %s", b, "HELLO")
113+
}
114+
}

0 commit comments

Comments
 (0)