Upgrading server dependancies (#4566)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0b296dd8c2
Коммит
0135904f7d
3
vendor/github.com/gorilla/websocket/.gitignore
сгенерированный
поставляемый
3
vendor/github.com/gorilla/websocket/.gitignore
сгенерированный
поставляемый
@@ -20,3 +20,6 @@ _cgo_export.*
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
1
vendor/github.com/gorilla/websocket/.travis.yml
сгенерированный
поставляемый
1
vendor/github.com/gorilla/websocket/.travis.yml
сгенерированный
поставляемый
@@ -6,6 +6,7 @@ matrix:
|
||||
- go: 1.4
|
||||
- go: 1.5
|
||||
- go: 1.6
|
||||
- go: 1.7
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
5
vendor/github.com/gorilla/websocket/README.md
сгенерированный
поставляемый
5
vendor/github.com/gorilla/websocket/README.md
сгенерированный
поставляемый
@@ -3,6 +3,9 @@
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
[](https://travis-ci.org/gorilla/websocket)
|
||||
[](https://godoc.org/github.com/gorilla/websocket)
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
||||
@@ -43,7 +46,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
||||
<tr><td colspan="3">Other Features</tr></td>
|
||||
<tr><td>Limit size of received message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.SetReadLimit">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=5082">No</a></td></tr>
|
||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
||||
</table>
|
||||
|
||||
19
vendor/github.com/gorilla/websocket/bench_test.go
сгенерированный
поставляемый
19
vendor/github.com/gorilla/websocket/bench_test.go
сгенерированный
поставляемый
@@ -1,19 +0,0 @@
|
||||
// Copyright 2014 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 websocket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMaskBytes(b *testing.B) {
|
||||
var key [4]byte
|
||||
data := make([]byte, 1024)
|
||||
pos := 0
|
||||
for i := 0; i < b.N; i++ {
|
||||
pos = maskBytes(key, pos, data)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
}
|
||||
47
vendor/github.com/gorilla/websocket/client.go
сгенерированный
поставляемый
47
vendor/github.com/gorilla/websocket/client.go
сгенерированный
поставляемый
@@ -23,6 +23,8 @@ import (
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
@@ -70,6 +72,17 @@ type Dialer struct {
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
@@ -83,7 +96,6 @@ func parseURL(s string) (*url.URL, error) {
|
||||
//
|
||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||
|
||||
var u url.URL
|
||||
switch {
|
||||
case strings.HasPrefix(s, "ws://"):
|
||||
@@ -193,6 +205,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
Host: u.Host,
|
||||
}
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -214,6 +233,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
default:
|
||||
@@ -221,6 +241,10 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
|
||||
var proxyURL *url.URL
|
||||
@@ -324,6 +348,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||
@@ -337,6 +368,20 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(req.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
|
||||
77
vendor/github.com/gorilla/websocket/client_server_test.go
сгенерированный
поставляемый
77
vendor/github.com/gorilla/websocket/client_server_test.go
сгенерированный
поставляемый
@@ -10,8 +10,8 @@ import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -21,9 +21,10 @@ import (
|
||||
)
|
||||
|
||||
var cstUpgrader = Upgrader{
|
||||
Subprotocols: []string{"p0", "p1"},
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
Subprotocols: []string{"p0", "p1"},
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
EnableCompression: true,
|
||||
Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
|
||||
http.Error(w, reason.Error(), status)
|
||||
},
|
||||
@@ -228,6 +229,54 @@ func TestDial(t *testing.T) {
|
||||
sendRecv(t, ws)
|
||||
}
|
||||
|
||||
func TestDialCookieJar(t *testing.T) {
|
||||
s := newServer(t)
|
||||
defer s.Close()
|
||||
|
||||
jar, _ := cookiejar.New(nil)
|
||||
d := cstDialer
|
||||
d.Jar = jar
|
||||
|
||||
u, _ := parseURL(s.URL)
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
}
|
||||
|
||||
cookies := []*http.Cookie{&http.Cookie{Name: "gorilla", Value: "ws", Path: "/"}}
|
||||
d.Jar.SetCookies(u, cookies)
|
||||
|
||||
ws, _, err := d.Dial(s.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial: %v", err)
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
var gorilla string
|
||||
var sessionID string
|
||||
for _, c := range d.Jar.Cookies(u) {
|
||||
if c.Name == "gorilla" {
|
||||
gorilla = c.Value
|
||||
}
|
||||
|
||||
if c.Name == "sessionID" {
|
||||
sessionID = c.Value
|
||||
}
|
||||
}
|
||||
if gorilla != "ws" {
|
||||
t.Error("Cookie not present in jar.")
|
||||
}
|
||||
|
||||
if sessionID != "1234" {
|
||||
t.Error("Set-Cookie not received from the server.")
|
||||
}
|
||||
|
||||
sendRecv(t, ws)
|
||||
}
|
||||
|
||||
func TestDialTLS(t *testing.T) {
|
||||
s := newTLSServer(t)
|
||||
defer s.Close()
|
||||
@@ -243,11 +292,9 @@ func TestDialTLS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(s.URL)
|
||||
d := cstDialer
|
||||
d.NetDial = func(network, addr string) (net.Conn, error) { return net.Dial(network, u.Host) }
|
||||
d.TLSClientConfig = &tls.Config{RootCAs: certs}
|
||||
ws, _, err := d.Dial("wss://example.com"+cstRequestURI, nil)
|
||||
ws, _, err := d.Dial(s.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial: %v", err)
|
||||
}
|
||||
@@ -267,7 +314,7 @@ func xTestDialTLSBadCert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func xTestDialTLSNoVerify(t *testing.T) {
|
||||
func TestDialTLSNoVerify(t *testing.T) {
|
||||
s := newTLSServer(t)
|
||||
defer s.Close()
|
||||
|
||||
@@ -449,3 +496,17 @@ func TestHostHeader(t *testing.T) {
|
||||
|
||||
sendRecv(t, ws)
|
||||
}
|
||||
|
||||
func TestDialCompression(t *testing.T) {
|
||||
s := newServer(t)
|
||||
defer s.Close()
|
||||
|
||||
dialer := cstDialer
|
||||
dialer.EnableCompression = true
|
||||
ws, _, err := dialer.Dial(s.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial: %v", err)
|
||||
}
|
||||
defer ws.Close()
|
||||
sendRecv(t, ws)
|
||||
}
|
||||
|
||||
85
vendor/github.com/gorilla/websocket/compression.go
сгенерированный
поставляемый
Обычный файл
85
vendor/github.com/gorilla/websocket/compression.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,85 @@
|
||||
// Copyright 2016 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 websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func decompressNoContextTakeover(r io.Reader) io.Reader {
|
||||
const tail =
|
||||
// Add four bytes as specified in RFC
|
||||
"\x00\x00\xff\xff" +
|
||||
// Add final block to squelch unexpected EOF error from flate reader.
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
return flate.NewReader(io.MultiReader(r, strings.NewReader(tail)))
|
||||
}
|
||||
|
||||
func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) {
|
||||
tw := &truncWriter{w: w}
|
||||
fw, err := flate.NewWriter(tw, 3)
|
||||
return &flateWrapper{fw: fw, tw: tw}, err
|
||||
}
|
||||
|
||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||
// stream to another io.Writer.
|
||||
type truncWriter struct {
|
||||
w io.WriteCloser
|
||||
n int
|
||||
p [4]byte
|
||||
}
|
||||
|
||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||
n := 0
|
||||
|
||||
// fill buffer first for simplicity.
|
||||
if w.n < len(w.p) {
|
||||
n = copy(w.p[w.n:], p)
|
||||
p = p[n:]
|
||||
w.n += n
|
||||
if len(p) == 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
m := len(p)
|
||||
if m > len(w.p) {
|
||||
m = len(w.p)
|
||||
}
|
||||
|
||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
copy(w.p[:], w.p[m:])
|
||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||
nn, err := w.w.Write(p[:len(p)-m])
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
type flateWrapper struct {
|
||||
fw *flate.Writer
|
||||
tw *truncWriter
|
||||
}
|
||||
|
||||
func (w *flateWrapper) Write(p []byte) (int, error) {
|
||||
return w.fw.Write(p)
|
||||
}
|
||||
|
||||
func (w *flateWrapper) Close() error {
|
||||
err1 := w.fw.Flush()
|
||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||
}
|
||||
err2 := w.tw.w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
31
vendor/github.com/gorilla/websocket/compression_test.go
сгенерированный
поставляемый
Обычный файл
31
vendor/github.com/gorilla/websocket/compression_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type nopCloser struct{ io.Writer }
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func TestTruncWriter(t *testing.T) {
|
||||
const data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz987654321"
|
||||
for n := 1; n <= 10; n++ {
|
||||
var b bytes.Buffer
|
||||
w := &truncWriter{w: nopCloser{&b}}
|
||||
p := []byte(data)
|
||||
for len(p) > 0 {
|
||||
m := len(p)
|
||||
if m > n {
|
||||
m = n
|
||||
}
|
||||
w.Write(p[:m])
|
||||
p = p[m:]
|
||||
}
|
||||
if b.String() != data[:len(data)-len(w.p)] {
|
||||
t.Errorf("%d: %q", n, b.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
489
vendor/github.com/gorilla/websocket/conn.go
сгенерированный
поставляемый
489
vendor/github.com/gorilla/websocket/conn.go
сгенерированный
поставляемый
@@ -10,19 +10,27 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// Frame header byte 0 bits from Section 5.2 of RFC 6455
|
||||
finalBit = 1 << 7
|
||||
rsv1Bit = 1 << 6
|
||||
rsv2Bit = 1 << 5
|
||||
rsv3Bit = 1 << 4
|
||||
|
||||
// Frame header byte 1 bits from Section 5.2 of RFC 6455
|
||||
maskBit = 1 << 7
|
||||
|
||||
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
||||
maxControlFramePayloadSize = 125
|
||||
finalBit = 1 << 7
|
||||
maskBit = 1 << 7
|
||||
writeWait = time.Second
|
||||
|
||||
writeWait = time.Second
|
||||
|
||||
defaultReadBufferSize = 4096
|
||||
defaultWriteBufferSize = 4096
|
||||
@@ -210,51 +218,41 @@ func isValidReceivedCloseCode(code int) bool {
|
||||
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
||||
}
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
}
|
||||
|
||||
// Conn represents a WebSocket connection.
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
isServer bool
|
||||
subprotocol string
|
||||
|
||||
// Write fields
|
||||
mu chan bool // used as mutex to protect write to conn and closeSent
|
||||
closeSent bool // true if close message was sent
|
||||
mu chan bool // used as mutex to protect write to conn
|
||||
writeBuf []byte // frame is constructed in this buffer.
|
||||
writeDeadline time.Time
|
||||
writer io.WriteCloser // the current writer returned to the application
|
||||
isWriting bool // for best-effort concurrent write detection
|
||||
|
||||
// Message writer fields.
|
||||
writeErr error
|
||||
writeBuf []byte // frame is constructed in this buffer.
|
||||
writePos int // end of data in writeBuf.
|
||||
writeFrameType int // type of the current frame.
|
||||
writeSeq int // incremented to invalidate message writers.
|
||||
writeDeadline time.Time
|
||||
isWriting bool // for best-effort concurrent write detection
|
||||
writeErrMu sync.Mutex
|
||||
writeErr error
|
||||
|
||||
enableWriteCompression bool
|
||||
newCompressionWriter func(io.WriteCloser) (io.WriteCloser, error)
|
||||
|
||||
// Read fields
|
||||
readErr error
|
||||
br *bufio.Reader
|
||||
readRemaining int64 // bytes remaining in current frame.
|
||||
readFinal bool // true the current message has more frames.
|
||||
readSeq int // incremented to invalidate message readers.
|
||||
readLength int64 // Message size.
|
||||
readLimit int64 // Maximum message size.
|
||||
readMaskPos int
|
||||
readMaskKey [4]byte
|
||||
handlePong func(string) error
|
||||
handlePing func(string) error
|
||||
handleClose func(int, string) error
|
||||
readErrCount int
|
||||
messageReader *messageReader // the current low-level reader
|
||||
|
||||
readDecompress bool // whether last read frame had RSV1 set
|
||||
newDecompressionReader func(io.Reader) io.Reader
|
||||
}
|
||||
|
||||
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
||||
@@ -264,20 +262,23 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int)
|
||||
if readBufferSize == 0 {
|
||||
readBufferSize = defaultReadBufferSize
|
||||
}
|
||||
if readBufferSize < maxControlFramePayloadSize {
|
||||
readBufferSize = maxControlFramePayloadSize
|
||||
}
|
||||
if writeBufferSize == 0 {
|
||||
writeBufferSize = defaultWriteBufferSize
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
isServer: isServer,
|
||||
br: bufio.NewReaderSize(conn, readBufferSize),
|
||||
conn: conn,
|
||||
mu: mu,
|
||||
readFinal: true,
|
||||
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
|
||||
writeFrameType: noFrame,
|
||||
writePos: maxFrameHeaderSize,
|
||||
isServer: isServer,
|
||||
br: bufio.NewReaderSize(conn, readBufferSize),
|
||||
conn: conn,
|
||||
mu: mu,
|
||||
readFinal: true,
|
||||
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
|
||||
enableWriteCompression: true,
|
||||
}
|
||||
c.SetCloseHandler(nil)
|
||||
c.SetPingHandler(nil)
|
||||
c.SetPongHandler(nil)
|
||||
return c
|
||||
@@ -305,29 +306,40 @@ func (c *Conn) RemoteAddr() net.Addr {
|
||||
|
||||
// Write methods
|
||||
|
||||
func (c *Conn) writeFatal(err error) error {
|
||||
err = hideTempErr(err)
|
||||
c.writeErrMu.Lock()
|
||||
if c.writeErr == nil {
|
||||
c.writeErr = err
|
||||
}
|
||||
c.writeErrMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
||||
<-c.mu
|
||||
defer func() { c.mu <- true }()
|
||||
|
||||
if c.closeSent {
|
||||
return ErrCloseSent
|
||||
} else if frameType == CloseMessage {
|
||||
c.closeSent = true
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
for _, buf := range bufs {
|
||||
if len(buf) > 0 {
|
||||
n, err := c.conn.Write(buf)
|
||||
if n != len(buf) {
|
||||
// Close on partial write.
|
||||
c.conn.Close()
|
||||
}
|
||||
_, err := c.conn.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if frameType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -376,60 +388,104 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
}
|
||||
defer func() { c.mu <- true }()
|
||||
|
||||
if c.closeSent {
|
||||
return ErrCloseSent
|
||||
} else if messageType == CloseMessage {
|
||||
c.closeSent = true
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
n, err := c.conn.Write(buf)
|
||||
if n != 0 && n != len(buf) {
|
||||
c.conn.Close()
|
||||
_, err = c.conn.Write(buf)
|
||||
if err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
return hideTempErr(err)
|
||||
if messageType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NextWriter returns a writer for the next message to send. The writer's
|
||||
// Close method flushes the complete message to the network.
|
||||
// NextWriter returns a writer for the next message to send. The writer's Close
|
||||
// method flushes the complete message to the network.
|
||||
//
|
||||
// There can be at most one open writer on a connection. NextWriter closes the
|
||||
// previous writer if the application has not already done so.
|
||||
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
||||
if c.writeErr != nil {
|
||||
return nil, c.writeErr
|
||||
}
|
||||
|
||||
if c.writeFrameType != noFrame {
|
||||
if err := c.flushFrame(true, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Close previous writer if not already closed by the application. It's
|
||||
// probably better to return an error in this situation, but we cannot
|
||||
// change this without breaking existing applications.
|
||||
if c.writer != nil {
|
||||
c.writer.Close()
|
||||
c.writer = nil
|
||||
}
|
||||
|
||||
if !isControl(messageType) && !isData(messageType) {
|
||||
return nil, errBadWriteOpCode
|
||||
}
|
||||
|
||||
c.writeFrameType = messageType
|
||||
return messageWriter{c, c.writeSeq}, nil
|
||||
}
|
||||
|
||||
func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
length := c.writePos - maxFrameHeaderSize + len(extra)
|
||||
|
||||
// Check for invalid control frames.
|
||||
if isControl(c.writeFrameType) &&
|
||||
(!final || length > maxControlFramePayloadSize) {
|
||||
c.writeSeq++
|
||||
c.writeFrameType = noFrame
|
||||
c.writePos = maxFrameHeaderSize
|
||||
return errInvalidControlFrame
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b0 := byte(c.writeFrameType)
|
||||
mw := &messageWriter{
|
||||
c: c,
|
||||
frameType: messageType,
|
||||
pos: maxFrameHeaderSize,
|
||||
}
|
||||
c.writer = mw
|
||||
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
|
||||
w, err := c.newCompressionWriter(c.writer)
|
||||
if err != nil {
|
||||
c.writer = nil
|
||||
return nil, err
|
||||
}
|
||||
mw.compress = true
|
||||
c.writer = w
|
||||
}
|
||||
return c.writer, nil
|
||||
}
|
||||
|
||||
type messageWriter struct {
|
||||
c *Conn
|
||||
compress bool // whether next call to flushFrame should set RSV1
|
||||
pos int // end of data in writeBuf.
|
||||
frameType int // type of the current frame.
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *messageWriter) fatal(err error) error {
|
||||
if w.err != nil {
|
||||
w.err = err
|
||||
w.c.writer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// flushFrame writes buffered data and extra as a frame to the network. The
|
||||
// final argument indicates that this is the last frame in the message.
|
||||
func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||
c := w.c
|
||||
length := w.pos - maxFrameHeaderSize + len(extra)
|
||||
|
||||
// Check for invalid control frames.
|
||||
if isControl(w.frameType) &&
|
||||
(!final || length > maxControlFramePayloadSize) {
|
||||
return w.fatal(errInvalidControlFrame)
|
||||
}
|
||||
|
||||
b0 := byte(w.frameType)
|
||||
if final {
|
||||
b0 |= finalBit
|
||||
}
|
||||
if w.compress {
|
||||
b0 |= rsv1Bit
|
||||
}
|
||||
w.compress = false
|
||||
|
||||
b1 := byte(0)
|
||||
if !c.isServer {
|
||||
b1 |= maskBit
|
||||
@@ -461,10 +517,9 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
if !c.isServer {
|
||||
key := newMaskKey()
|
||||
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
|
||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])
|
||||
if len(extra) > 0 {
|
||||
c.writeErr = errors.New("websocket: internal error, extra used in client mode")
|
||||
return c.writeErr
|
||||
return c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,46 +532,35 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
}
|
||||
c.isWriting = true
|
||||
|
||||
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
||||
err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
|
||||
|
||||
if !c.isWriting {
|
||||
panic("concurrent write to websocket connection")
|
||||
}
|
||||
c.isWriting = false
|
||||
|
||||
// Setup for next frame.
|
||||
c.writePos = maxFrameHeaderSize
|
||||
c.writeFrameType = continuationFrame
|
||||
if err != nil {
|
||||
return w.fatal(err)
|
||||
}
|
||||
|
||||
if final {
|
||||
c.writeSeq++
|
||||
c.writeFrameType = noFrame
|
||||
c.writer = nil
|
||||
return nil
|
||||
}
|
||||
return c.writeErr
|
||||
}
|
||||
|
||||
type messageWriter struct {
|
||||
c *Conn
|
||||
seq int
|
||||
}
|
||||
|
||||
func (w messageWriter) err() error {
|
||||
c := w.c
|
||||
if c.writeSeq != w.seq {
|
||||
return errWriteClosed
|
||||
}
|
||||
if c.writeErr != nil {
|
||||
return c.writeErr
|
||||
}
|
||||
// Setup for next frame.
|
||||
w.pos = maxFrameHeaderSize
|
||||
w.frameType = continuationFrame
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w messageWriter) ncopy(max int) (int, error) {
|
||||
n := len(w.c.writeBuf) - w.c.writePos
|
||||
func (w *messageWriter) ncopy(max int) (int, error) {
|
||||
n := len(w.c.writeBuf) - w.pos
|
||||
if n <= 0 {
|
||||
if err := w.c.flushFrame(false, nil); err != nil {
|
||||
if err := w.flushFrame(false, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = len(w.c.writeBuf) - w.c.writePos
|
||||
n = len(w.c.writeBuf) - w.pos
|
||||
}
|
||||
if n > max {
|
||||
n = max
|
||||
@@ -524,14 +568,14 @@ func (w messageWriter) ncopy(max int) (int, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w messageWriter) write(final bool, p []byte) (int, error) {
|
||||
if err := w.err(); err != nil {
|
||||
return 0, err
|
||||
func (w *messageWriter) Write(p []byte) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
||||
// Don't buffer large messages.
|
||||
err := w.c.flushFrame(final, p)
|
||||
err := w.flushFrame(false, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -544,20 +588,16 @@ func (w messageWriter) write(final bool, p []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
||||
w.c.writePos += n
|
||||
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||
w.pos += n
|
||||
p = p[n:]
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (w messageWriter) Write(p []byte) (int, error) {
|
||||
return w.write(false, p)
|
||||
}
|
||||
|
||||
func (w messageWriter) WriteString(p string) (int, error) {
|
||||
if err := w.err(); err != nil {
|
||||
return 0, err
|
||||
func (w *messageWriter) WriteString(p string) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
nn := len(p)
|
||||
@@ -566,27 +606,27 @@ func (w messageWriter) WriteString(p string) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
||||
w.c.writePos += n
|
||||
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||
w.pos += n
|
||||
p = p[n:]
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||
if err := w.err(); err != nil {
|
||||
return 0, err
|
||||
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for {
|
||||
if w.c.writePos == len(w.c.writeBuf) {
|
||||
err = w.c.flushFrame(false, nil)
|
||||
if w.pos == len(w.c.writeBuf) {
|
||||
err = w.flushFrame(false, nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
var n int
|
||||
n, err = r.Read(w.c.writeBuf[w.c.writePos:])
|
||||
w.c.writePos += n
|
||||
n, err = r.Read(w.c.writeBuf[w.pos:])
|
||||
w.pos += n
|
||||
nn += int64(n)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -598,30 +638,36 @@ func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||
return nn, err
|
||||
}
|
||||
|
||||
func (w messageWriter) Close() error {
|
||||
if err := w.err(); err != nil {
|
||||
func (w *messageWriter) Close() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if err := w.flushFrame(true, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return w.c.flushFrame(true, nil)
|
||||
w.err = errWriteClosed
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMessage is a helper method for getting a writer using NextWriter,
|
||||
// writing the message and closing the writer.
|
||||
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
||||
wr, err := c.NextWriter(messageType)
|
||||
w, err := c.NextWriter(messageType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := wr.(messageWriter)
|
||||
if _, err := w.write(true, data); err != nil {
|
||||
if mw, ok := w.(*messageWriter); ok && c.isServer {
|
||||
// Optimize write as a single frame.
|
||||
n := copy(c.writeBuf[mw.pos:], data)
|
||||
mw.pos += n
|
||||
data = data[n:]
|
||||
err = mw.flushFrame(true, data)
|
||||
return err
|
||||
}
|
||||
if c.writeSeq == w.seq {
|
||||
if err := c.flushFrame(true, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline on the underlying network
|
||||
@@ -635,22 +681,6 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
|
||||
// Read methods
|
||||
|
||||
// readFull is like io.ReadFull except that io.EOF is never returned.
|
||||
func (c *Conn) readFull(p []byte) (err error) {
|
||||
var n int
|
||||
for n < len(p) && err == nil {
|
||||
var nn int
|
||||
nn, err = c.br.Read(p[n:])
|
||||
n += nn
|
||||
}
|
||||
if n == len(p) {
|
||||
err = nil
|
||||
} else if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
// 1. Skip remainder of previous frame.
|
||||
@@ -663,19 +693,24 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
// 2. Read and parse first two bytes of frame header.
|
||||
|
||||
var b [8]byte
|
||||
if err := c.readFull(b[:2]); err != nil {
|
||||
p, err := c.read(2)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
|
||||
final := b[0]&finalBit != 0
|
||||
frameType := int(b[0] & 0xf)
|
||||
reserved := int((b[0] >> 4) & 0x7)
|
||||
mask := b[1]&maskBit != 0
|
||||
c.readRemaining = int64(b[1] & 0x7f)
|
||||
final := p[0]&finalBit != 0
|
||||
frameType := int(p[0] & 0xf)
|
||||
mask := p[1]&maskBit != 0
|
||||
c.readRemaining = int64(p[1] & 0x7f)
|
||||
|
||||
if reserved != 0 {
|
||||
return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
|
||||
c.readDecompress = false
|
||||
if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {
|
||||
c.readDecompress = true
|
||||
p[0] &^= rsv1Bit
|
||||
}
|
||||
|
||||
if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
|
||||
return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16))
|
||||
}
|
||||
|
||||
switch frameType {
|
||||
@@ -704,15 +739,17 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
switch c.readRemaining {
|
||||
case 126:
|
||||
if err := c.readFull(b[:2]); err != nil {
|
||||
p, err := c.read(2)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
c.readRemaining = int64(binary.BigEndian.Uint16(b[:2]))
|
||||
c.readRemaining = int64(binary.BigEndian.Uint16(p))
|
||||
case 127:
|
||||
if err := c.readFull(b[:8]); err != nil {
|
||||
p, err := c.read(8)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
c.readRemaining = int64(binary.BigEndian.Uint64(b[:8]))
|
||||
c.readRemaining = int64(binary.BigEndian.Uint64(p))
|
||||
}
|
||||
|
||||
// 4. Handle frame masking.
|
||||
@@ -723,9 +760,11 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
if mask {
|
||||
c.readMaskPos = 0
|
||||
if err := c.readFull(c.readMaskKey[:]); err != nil {
|
||||
p, err := c.read(len(c.readMaskKey))
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
copy(c.readMaskKey[:], p)
|
||||
}
|
||||
|
||||
// 5. For text and binary messages, enforce read limit and return.
|
||||
@@ -745,9 +784,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
var payload []byte
|
||||
if c.readRemaining > 0 {
|
||||
payload = make([]byte, c.readRemaining)
|
||||
payload, err = c.read(int(c.readRemaining))
|
||||
c.readRemaining = 0
|
||||
if err := c.readFull(payload); err != nil {
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
if c.isServer {
|
||||
@@ -767,11 +806,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
return noFrame, err
|
||||
}
|
||||
case CloseMessage:
|
||||
echoMessage := []byte{}
|
||||
closeCode := CloseNoStatusReceived
|
||||
closeText := ""
|
||||
if len(payload) >= 2 {
|
||||
echoMessage = payload[:2]
|
||||
closeCode = int(binary.BigEndian.Uint16(payload))
|
||||
if !isValidReceivedCloseCode(closeCode) {
|
||||
return noFrame, c.handleProtocolError("invalid close code")
|
||||
@@ -781,7 +818,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
|
||||
}
|
||||
}
|
||||
c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
||||
if err := c.handleClose(closeCode, closeText); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
||||
}
|
||||
|
||||
@@ -805,7 +844,7 @@ func (c *Conn) handleProtocolError(message string) error {
|
||||
// this method return the same error.
|
||||
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
|
||||
c.readSeq++
|
||||
c.messageReader = nil
|
||||
c.readLength = 0
|
||||
|
||||
for c.readErr == nil {
|
||||
@@ -815,7 +854,12 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
break
|
||||
}
|
||||
if frameType == TextMessage || frameType == BinaryMessage {
|
||||
return frameType, messageReader{c, c.readSeq}, nil
|
||||
c.messageReader = &messageReader{c}
|
||||
var r io.Reader = c.messageReader
|
||||
if c.readDecompress {
|
||||
r = c.newDecompressionReader(r)
|
||||
}
|
||||
return frameType, r, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,51 +874,48 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
return noFrame, nil, c.readErr
|
||||
}
|
||||
|
||||
type messageReader struct {
|
||||
c *Conn
|
||||
seq int
|
||||
}
|
||||
type messageReader struct{ c *Conn }
|
||||
|
||||
func (r messageReader) Read(b []byte) (int, error) {
|
||||
|
||||
if r.seq != r.c.readSeq {
|
||||
func (r *messageReader) Read(b []byte) (int, error) {
|
||||
c := r.c
|
||||
if c.messageReader != r {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
for r.c.readErr == nil {
|
||||
for c.readErr == nil {
|
||||
|
||||
if r.c.readRemaining > 0 {
|
||||
if int64(len(b)) > r.c.readRemaining {
|
||||
b = b[:r.c.readRemaining]
|
||||
if c.readRemaining > 0 {
|
||||
if int64(len(b)) > c.readRemaining {
|
||||
b = b[:c.readRemaining]
|
||||
}
|
||||
n, err := r.c.br.Read(b)
|
||||
r.c.readErr = hideTempErr(err)
|
||||
if r.c.isServer {
|
||||
r.c.readMaskPos = maskBytes(r.c.readMaskKey, r.c.readMaskPos, b[:n])
|
||||
n, err := c.br.Read(b)
|
||||
c.readErr = hideTempErr(err)
|
||||
if c.isServer {
|
||||
c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
|
||||
}
|
||||
r.c.readRemaining -= int64(n)
|
||||
if r.c.readRemaining > 0 && r.c.readErr == io.EOF {
|
||||
r.c.readErr = errUnexpectedEOF
|
||||
c.readRemaining -= int64(n)
|
||||
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||
c.readErr = errUnexpectedEOF
|
||||
}
|
||||
return n, r.c.readErr
|
||||
return n, c.readErr
|
||||
}
|
||||
|
||||
if r.c.readFinal {
|
||||
r.c.readSeq++
|
||||
if c.readFinal {
|
||||
c.messageReader = nil
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
frameType, err := r.c.advanceFrame()
|
||||
frameType, err := c.advanceFrame()
|
||||
switch {
|
||||
case err != nil:
|
||||
r.c.readErr = hideTempErr(err)
|
||||
c.readErr = hideTempErr(err)
|
||||
case frameType == TextMessage || frameType == BinaryMessage:
|
||||
r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||
c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||
}
|
||||
}
|
||||
|
||||
err := r.c.readErr
|
||||
if err == io.EOF && r.seq == r.c.readSeq {
|
||||
err := c.readErr
|
||||
if err == io.EOF && c.messageReader == r {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
return 0, err
|
||||
@@ -907,6 +948,34 @@ func (c *Conn) SetReadLimit(limit int64) {
|
||||
c.readLimit = limit
|
||||
}
|
||||
|
||||
// CloseHandler returns the current close handler
|
||||
func (c *Conn) CloseHandler() func(code int, text string) error {
|
||||
return c.handleClose
|
||||
}
|
||||
|
||||
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||
// if the close message is empty. The default close handler sends a close frame
|
||||
// back to the peer.
|
||||
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||
if h == nil {
|
||||
h = func(code int, text string) error {
|
||||
message := []byte{}
|
||||
if code != CloseNoStatusReceived {
|
||||
message = FormatCloseMessage(code, "")
|
||||
}
|
||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
c.handleClose = h
|
||||
}
|
||||
|
||||
// PingHandler returns the current ping handler
|
||||
func (c *Conn) PingHandler() func(appData string) error {
|
||||
return c.handlePing
|
||||
}
|
||||
|
||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||
// The appData argument to h is the PING frame application data. The default
|
||||
// ping handler sends a pong to the peer.
|
||||
@@ -925,6 +994,11 @@ func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
c.handlePing = h
|
||||
}
|
||||
|
||||
// PongHandler returns the current pong handler
|
||||
func (c *Conn) PongHandler() func(appData string) error {
|
||||
return c.handlePong
|
||||
}
|
||||
|
||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||
// The appData argument to h is the PONG frame application data. The default
|
||||
// pong handler does nothing.
|
||||
@@ -941,6 +1015,13 @@ func (c *Conn) UnderlyingConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
// EnableWriteCompression enables and disables write compression of
|
||||
// subsequent text and binary messages. This function is a noop if
|
||||
// compression was not negotiated with the peer.
|
||||
func (c *Conn) EnableWriteCompression(enable bool) {
|
||||
c.enableWriteCompression = enable
|
||||
}
|
||||
|
||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||
buf := make([]byte, 2+len(text))
|
||||
|
||||
18
vendor/github.com/gorilla/websocket/conn_read.go
сгенерированный
поставляемый
Обычный файл
18
vendor/github.com/gorilla/websocket/conn_read.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,18 @@
|
||||
// Copyright 2016 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 go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
c.br.Discard(len(p))
|
||||
return p, err
|
||||
}
|
||||
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
сгенерированный
поставляемый
Обычный файл
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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 !go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
if len(p) > 0 {
|
||||
// advance over the bytes just read
|
||||
io.ReadFull(c.br, p)
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
169
vendor/github.com/gorilla/websocket/conn_test.go
сгенерированный
поставляемый
169
vendor/github.com/gorilla/websocket/conn_test.go
сгенерированный
поставляемый
@@ -26,12 +26,27 @@ type fakeNetConn struct {
|
||||
}
|
||||
|
||||
func (c fakeNetConn) Close() error { return nil }
|
||||
func (c fakeNetConn) LocalAddr() net.Addr { return nil }
|
||||
func (c fakeNetConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c fakeNetConn) LocalAddr() net.Addr { return localAddr }
|
||||
func (c fakeNetConn) RemoteAddr() net.Addr { return remoteAddr }
|
||||
func (c fakeNetConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
type fakeAddr int
|
||||
|
||||
var (
|
||||
localAddr = fakeAddr(1)
|
||||
remoteAddr = fakeAddr(2)
|
||||
)
|
||||
|
||||
func (a fakeAddr) Network() string {
|
||||
return "net"
|
||||
}
|
||||
|
||||
func (a fakeAddr) String() string {
|
||||
return "str"
|
||||
}
|
||||
|
||||
func TestFraming(t *testing.T) {
|
||||
frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537}
|
||||
var readChunkers = []struct {
|
||||
@@ -42,66 +57,78 @@ func TestFraming(t *testing.T) {
|
||||
{"one", iotest.OneByteReader},
|
||||
{"asis", func(r io.Reader) io.Reader { return r }},
|
||||
}
|
||||
|
||||
writeBuf := make([]byte, 65537)
|
||||
for i := range writeBuf {
|
||||
writeBuf[i] = byte(i)
|
||||
}
|
||||
var writers = []struct {
|
||||
name string
|
||||
f func(w io.Writer, n int) (int, error)
|
||||
}{
|
||||
{"iocopy", func(w io.Writer, n int) (int, error) {
|
||||
nn, err := io.Copy(w, bytes.NewReader(writeBuf[:n]))
|
||||
return int(nn), err
|
||||
}},
|
||||
{"write", func(w io.Writer, n int) (int, error) {
|
||||
return w.Write(writeBuf[:n])
|
||||
}},
|
||||
{"string", func(w io.Writer, n int) (int, error) {
|
||||
return io.WriteString(w, string(writeBuf[:n]))
|
||||
}},
|
||||
}
|
||||
|
||||
for _, isServer := range []bool{true, false} {
|
||||
for _, chunker := range readChunkers {
|
||||
for _, compress := range []bool{false, true} {
|
||||
for _, isServer := range []bool{true, false} {
|
||||
for _, chunker := range readChunkers {
|
||||
|
||||
var connBuf bytes.Buffer
|
||||
wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024)
|
||||
rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024)
|
||||
var connBuf bytes.Buffer
|
||||
wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024)
|
||||
rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024)
|
||||
if compress {
|
||||
wc.newCompressionWriter = compressNoContextTakeover
|
||||
rc.newDecompressionReader = decompressNoContextTakeover
|
||||
}
|
||||
for _, n := range frameSizes {
|
||||
for _, writer := range writers {
|
||||
name := fmt.Sprintf("z:%v, s:%v, r:%s, n:%d w:%s", compress, isServer, chunker.name, n, writer.name)
|
||||
|
||||
for _, n := range frameSizes {
|
||||
for _, iocopy := range []bool{true, false} {
|
||||
name := fmt.Sprintf("s:%v, r:%s, n:%d c:%v", isServer, chunker.name, n, iocopy)
|
||||
w, err := wc.NextWriter(TextMessage)
|
||||
if err != nil {
|
||||
t.Errorf("%s: wc.NextWriter() returned %v", name, err)
|
||||
continue
|
||||
}
|
||||
nn, err := writer.f(w, n)
|
||||
if err != nil || nn != n {
|
||||
t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err)
|
||||
continue
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
t.Errorf("%s: w.Close() returned %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
w, err := wc.NextWriter(TextMessage)
|
||||
if err != nil {
|
||||
t.Errorf("%s: wc.NextWriter() returned %v", name, err)
|
||||
continue
|
||||
}
|
||||
var nn int
|
||||
if iocopy {
|
||||
var n64 int64
|
||||
n64, err = io.Copy(w, bytes.NewReader(writeBuf[:n]))
|
||||
nn = int(n64)
|
||||
} else {
|
||||
nn, err = w.Write(writeBuf[:n])
|
||||
}
|
||||
if err != nil || nn != n {
|
||||
t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err)
|
||||
continue
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
t.Errorf("%s: w.Close() returned %v", name, err)
|
||||
continue
|
||||
}
|
||||
opCode, r, err := rc.NextReader()
|
||||
if err != nil || opCode != TextMessage {
|
||||
t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err)
|
||||
continue
|
||||
}
|
||||
rbuf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("%s: ReadFull() returned rbuf, %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
opCode, r, err := rc.NextReader()
|
||||
if err != nil || opCode != TextMessage {
|
||||
t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err)
|
||||
continue
|
||||
}
|
||||
rbuf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("%s: ReadFull() returned rbuf, %v", name, err)
|
||||
continue
|
||||
}
|
||||
if len(rbuf) != n {
|
||||
t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(rbuf) != n {
|
||||
t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n)
|
||||
continue
|
||||
}
|
||||
|
||||
for i, b := range rbuf {
|
||||
if byte(i) != b {
|
||||
t.Errorf("%s: bad byte at offset %d", name, i)
|
||||
break
|
||||
for i, b := range rbuf {
|
||||
if byte(i) != b {
|
||||
t.Errorf("%s: bad byte at offset %d", name, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +173,7 @@ func TestControl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseBeforeFinalFrame(t *testing.T) {
|
||||
func TestCloseFrameBeforeFinalMessageFrame(t *testing.T) {
|
||||
const bufSize = 512
|
||||
|
||||
expectedErr := &CloseError{Code: CloseNormalClosure, Text: "hello"}
|
||||
@@ -233,6 +260,32 @@ func TestEOFBeforeFinalFrame(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteAfterMessageWriterClose(t *testing.T) {
|
||||
wc := newConn(fakeNetConn{Reader: nil, Writer: &bytes.Buffer{}}, false, 1024, 1024)
|
||||
w, _ := wc.NextWriter(BinaryMessage)
|
||||
io.WriteString(w, "hello")
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatalf("unxpected error closing message writer, %v", err)
|
||||
}
|
||||
|
||||
if _, err := io.WriteString(w, "world"); err == nil {
|
||||
t.Fatalf("no error writing after close")
|
||||
}
|
||||
|
||||
w, _ = wc.NextWriter(BinaryMessage)
|
||||
io.WriteString(w, "hello")
|
||||
|
||||
// close w by getting next writer
|
||||
_, err := wc.NextWriter(BinaryMessage)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting next writer, %v", err)
|
||||
}
|
||||
|
||||
if _, err := io.WriteString(w, "world"); err == nil {
|
||||
t.Fatalf("no error writing after close")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadLimit(t *testing.T) {
|
||||
|
||||
const readLimit = 512
|
||||
@@ -267,6 +320,16 @@ func TestReadLimit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddrs(t *testing.T) {
|
||||
c := newConn(&fakeNetConn{}, true, 1024, 1024)
|
||||
if c.LocalAddr() != localAddr {
|
||||
t.Errorf("LocalAddr = %v, want %v", c.LocalAddr(), localAddr)
|
||||
}
|
||||
if c.RemoteAddr() != remoteAddr {
|
||||
t.Errorf("RemoteAddr = %v, want %v", c.RemoteAddr(), remoteAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnderlyingConn(t *testing.T) {
|
||||
var b1, b2 bytes.Buffer
|
||||
fc := fakeNetConn{Reader: &b1, Writer: &b2}
|
||||
|
||||
21
vendor/github.com/gorilla/websocket/doc.go
сгенерированный
поставляемый
21
vendor/github.com/gorilla/websocket/doc.go
сгенерированный
поставляемый
@@ -149,4 +149,25 @@
|
||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||
// application's responsibility to check the Origin header before calling
|
||||
// Upgrade.
|
||||
//
|
||||
// Compression [Experimental]
|
||||
//
|
||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||
// by this package in a limited capacity. Setting the EnableCompression option
|
||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||
// support. If compression was successfully negotiated with the connection's
|
||||
// peer, any message received in compressed form will be automatically
|
||||
// decompressed. All Read methods will return uncompressed bytes.
|
||||
//
|
||||
// Per message compression of messages written to a connection can be enabled
|
||||
// or disabled by calling the corresponding Conn method:
|
||||
//
|
||||
// conn.EnableWriteCompression(true)
|
||||
//
|
||||
// Currently this package does not support compression with "context takeover".
|
||||
// This means that messages must be compressed and decompressed in isolation,
|
||||
// without retaining sliding window or dictionary state across messages. For
|
||||
// more details refer to RFC 7692.
|
||||
//
|
||||
// Use of compression is experimental and may result in decreased performance.
|
||||
package websocket
|
||||
|
||||
8
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
сгенерированный
поставляемый
8
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
сгенерированный
поставляемый
@@ -8,17 +8,19 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"github.com/gorilla/websocket"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
EnableCompression: true,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
|
||||
86
vendor/github.com/gorilla/websocket/examples/chat/README.md
сгенерированный
поставляемый
86
vendor/github.com/gorilla/websocket/examples/chat/README.md
сгенерированный
поставляемый
@@ -1,8 +1,8 @@
|
||||
# Chat Example
|
||||
|
||||
This application shows how to use use the
|
||||
[websocket](https://github.com/gorilla/websocket) package and
|
||||
[jQuery](http://jquery.com) to implement a simple web chat application.
|
||||
[websocket](https://github.com/gorilla/websocket) package to implement a simple
|
||||
web chat application.
|
||||
|
||||
## Running the example
|
||||
|
||||
@@ -18,3 +18,85 @@ using the following commands.
|
||||
$ go run *.go
|
||||
|
||||
To use the chat example, open http://localhost:8080/ in your browser.
|
||||
|
||||
## Server
|
||||
|
||||
The server application defines two types, `Client` and `Hub`. The server
|
||||
creates an instance of the `Client` type for each websocket connection. A
|
||||
`Client` acts as an intermediary between the websocket connection and a single
|
||||
instance of the `Hub` type. The `Hub` maintains a set of registered clients and
|
||||
broadcasts messages to the clients.
|
||||
|
||||
The application runs one goroutine for the `Hub` and two goroutines for each
|
||||
`Client`. The goroutines communicate with each other using channels. The `Hub`
|
||||
has channels for registering clients, unregistering clients and broadcasting
|
||||
messages. A `Client` has a buffered channel of outbound messages. One of the
|
||||
client's goroutines reads messages from this channel and writes the messages to
|
||||
the websocket. The other client goroutine reads messages from the websocket and
|
||||
sends them to the hub.
|
||||
|
||||
### Hub
|
||||
|
||||
The code for the `Hub` type is in
|
||||
[hub.go](https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go).
|
||||
The application's `main` function starts the hub's `run` method as a goroutine.
|
||||
Clients send requests to the hub using the `register`, `unregister` and
|
||||
`broadcast` channels.
|
||||
|
||||
The hub registers clients by adding the client pointer as a key in the
|
||||
`clients` map. The map value is always true.
|
||||
|
||||
The unregister code is a little more complicated. In addition to deleting the
|
||||
client pointer from the `clients` map, the hub closes the clients's `send`
|
||||
channel to signal the client that no more messages will be sent to the client.
|
||||
|
||||
The hub handles messages by looping over the registered clients and sending the
|
||||
message to the client's `send` channel. If the client's `send` buffer is full,
|
||||
then the hub assumes that the client is dead or stuck. In this case, the hub
|
||||
unregisters the client and closes the websocket.
|
||||
|
||||
### Client
|
||||
|
||||
The code for the `Client` type is in [client.go](https://github.com/gorilla/websocket/blob/master/examples/chat/client.go).
|
||||
|
||||
The `serveWs` function is registered by the application's `main` function as
|
||||
an HTTP handler. The handler upgrades the HTTP connection to the WebSocket
|
||||
protocol, creates a client, registers the client with the hub and schedules the
|
||||
client to be unregistered using a defer statement.
|
||||
|
||||
Next, the HTTP handler starts the client's `writePump` method as a goroutine.
|
||||
This method transfers messages from the client's send channel to the websocket
|
||||
connection. The writer method exits when the channel is closed by the hub or
|
||||
there's an error writing to the websocket connection.
|
||||
|
||||
Finally, the HTTP handler calls the client's `readPump` method. This method
|
||||
transfers inbound messages from the websocket to the hub.
|
||||
|
||||
WebSocket connections [support one concurrent reader and one concurrent
|
||||
writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The
|
||||
application ensures that these concurrency requirements are met by executing
|
||||
all reads from the `readPump` goroutine and all writes from the `writePump`
|
||||
goroutine.
|
||||
|
||||
To improve efficiency under high load, the `writePump` function coalesces
|
||||
pending chat messages in the `send` channel to a single WebSocket message. This
|
||||
reduces the number of system calls and the amount of data sent over the
|
||||
network.
|
||||
|
||||
## Frontend
|
||||
|
||||
The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html).
|
||||
|
||||
On document load, the script checks for websocket functionality in the browser.
|
||||
If websocket functionality is available, then the script opens a connection to
|
||||
the server and registers a callback to handle messages from the server. The
|
||||
callback appends the message to the chat log using the appendLog function.
|
||||
|
||||
To allow the user to manually scroll through the chat log without interruption
|
||||
from new messages, the `appendLog` function checks the scroll position before
|
||||
adding new content. If the chat log is scrolled to the bottom, then the
|
||||
function scrolls new content into view after adding the content. Otherwise, the
|
||||
scroll position is not changed.
|
||||
|
||||
The form handler writes the user input to the websocket and clears the input
|
||||
field.
|
||||
|
||||
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
сгенерированный
поставляемый
Обычный файл
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,134 @@
|
||||
// Copyright 2013 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 (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to write a message to the peer.
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
// 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
|
||||
|
||||
// Maximum message size allowed from peer.
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
var (
|
||||
newline = []byte{'\n'}
|
||||
space = []byte{' '}
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
// Client is a middleman between the websocket connection and the hub.
|
||||
type Client struct {
|
||||
hub *Hub
|
||||
|
||||
// The websocket connection.
|
||||
conn *websocket.Conn
|
||||
|
||||
// Buffered channel of outbound messages.
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
// readPump pumps messages from the websocket connection to the hub.
|
||||
//
|
||||
// The application runs readPump in a per-connection goroutine. The application
|
||||
// ensures that there is at most one reader on a connection by executing all
|
||||
// reads from this goroutine.
|
||||
func (c *Client) readPump() {
|
||||
defer func() {
|
||||
c.hub.unregister <- c
|
||||
c.conn.Close()
|
||||
}()
|
||||
c.conn.SetReadLimit(maxMessageSize)
|
||||
c.conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Printf("error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
|
||||
c.hub.broadcast <- message
|
||||
}
|
||||
}
|
||||
|
||||
// writePump pumps messages from the hub to the websocket connection.
|
||||
//
|
||||
// A goroutine running writePump is started for each connection. The
|
||||
// application ensures that there is at most one writer to a connection by
|
||||
// executing all writes from this goroutine.
|
||||
func (c *Client) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.conn.Close()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.send:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if !ok {
|
||||
// The hub closed the channel.
|
||||
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
w, err := c.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.Write(message)
|
||||
|
||||
// Add queued chat messages to the current websocket message.
|
||||
n := len(c.send)
|
||||
for i := 0; i < n; i++ {
|
||||
w.Write(newline)
|
||||
w.Write(<-c.send)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serveWs handles websocket requests from the peer.
|
||||
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
||||
client.hub.register <- client
|
||||
go client.writePump()
|
||||
client.readPump()
|
||||
}
|
||||
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
сгенерированный
поставляемый
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
сгенерированный
поставляемый
@@ -1,105 +0,0 @@
|
||||
// Copyright 2013 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 (
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to write a message to the peer.
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
// 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
|
||||
|
||||
// Maximum message size allowed from peer.
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
// connection is an middleman between the websocket connection and the hub.
|
||||
type connection struct {
|
||||
// The websocket connection.
|
||||
ws *websocket.Conn
|
||||
|
||||
// Buffered channel of outbound messages.
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
// readPump pumps messages from the websocket connection to the hub.
|
||||
func (c *connection) readPump() {
|
||||
defer func() {
|
||||
h.unregister <- c
|
||||
c.ws.Close()
|
||||
}()
|
||||
c.ws.SetReadLimit(maxMessageSize)
|
||||
c.ws.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.ws.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Printf("error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
h.broadcast <- message
|
||||
}
|
||||
}
|
||||
|
||||
// write writes a message with the given message type and payload.
|
||||
func (c *connection) write(mt int, payload []byte) error {
|
||||
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
return c.ws.WriteMessage(mt, payload)
|
||||
}
|
||||
|
||||
// writePump pumps messages from the hub to the websocket connection.
|
||||
func (c *connection) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.ws.Close()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.send:
|
||||
if !ok {
|
||||
c.write(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
if err := c.write(websocket.TextMessage, message); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serveWs handles websocket requests from the peer.
|
||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
c := &connection{send: make(chan []byte, 256), ws: ws}
|
||||
h.register <- c
|
||||
go c.writePump()
|
||||
c.readPump()
|
||||
}
|
||||
54
vendor/github.com/gorilla/websocket/examples/chat/home.html
сгенерированный
поставляемый
54
vendor/github.com/gorilla/websocket/examples/chat/home.html
сгенерированный
поставляемый
@@ -2,47 +2,53 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Chat Example</title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
window.onload = function () {
|
||||
var conn;
|
||||
var msg = $("#msg");
|
||||
var log = $("#log");
|
||||
var msg = document.getElementById("msg");
|
||||
var log = document.getElementById("log");
|
||||
|
||||
function appendLog(msg) {
|
||||
var d = log[0]
|
||||
var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
|
||||
msg.appendTo(log)
|
||||
function appendLog(item) {
|
||||
var doScroll = log.scrollTop === log.scrollHeight - log.clientHeight;
|
||||
log.appendChild(item);
|
||||
if (doScroll) {
|
||||
d.scrollTop = d.scrollHeight - d.clientHeight;
|
||||
log.scrollTop = log.scrollHeight - log.clientHeight;
|
||||
}
|
||||
}
|
||||
|
||||
$("#form").submit(function() {
|
||||
document.getElementById("form").onsubmit = function () {
|
||||
if (!conn) {
|
||||
return false;
|
||||
}
|
||||
if (!msg.val()) {
|
||||
if (!msg.value) {
|
||||
return false;
|
||||
}
|
||||
conn.send(msg.val());
|
||||
msg.val("");
|
||||
return false
|
||||
});
|
||||
conn.send(msg.value);
|
||||
msg.value = "";
|
||||
return false;
|
||||
};
|
||||
|
||||
if (window["WebSocket"]) {
|
||||
conn = new WebSocket("ws://{{$}}/ws");
|
||||
conn.onclose = function(evt) {
|
||||
appendLog($("<div><b>Connection closed.</b></div>"))
|
||||
}
|
||||
conn.onmessage = function(evt) {
|
||||
appendLog($("<div/>").text(evt.data))
|
||||
}
|
||||
conn.onclose = function (evt) {
|
||||
var item = document.createElement("div");
|
||||
item.innerHTML = "<b>Connection closed.</b>";
|
||||
appendLog(item);
|
||||
};
|
||||
conn.onmessage = function (evt) {
|
||||
var messages = evt.data.split('\n');
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
var item = document.createElement("div");
|
||||
item.innerText = messages[i];
|
||||
appendLog(item);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
|
||||
var item = document.createElement("div");
|
||||
item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
|
||||
appendLog(item);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style type="text/css">
|
||||
html {
|
||||
|
||||
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
сгенерированный
поставляемый
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
сгенерированный
поставляемый
@@ -4,46 +4,48 @@
|
||||
|
||||
package main
|
||||
|
||||
// hub maintains the set of active connections and broadcasts messages to the
|
||||
// connections.
|
||||
type hub struct {
|
||||
// Registered connections.
|
||||
connections map[*connection]bool
|
||||
// hub maintains the set of active clients and broadcasts messages to the
|
||||
// clients.
|
||||
type Hub struct {
|
||||
// Registered clients.
|
||||
clients map[*Client]bool
|
||||
|
||||
// Inbound messages from the connections.
|
||||
// Inbound messages from the clients.
|
||||
broadcast chan []byte
|
||||
|
||||
// Register requests from the connections.
|
||||
register chan *connection
|
||||
// Register requests from the clients.
|
||||
register chan *Client
|
||||
|
||||
// Unregister requests from connections.
|
||||
unregister chan *connection
|
||||
// Unregister requests from clients.
|
||||
unregister chan *Client
|
||||
}
|
||||
|
||||
var h = hub{
|
||||
broadcast: make(chan []byte),
|
||||
register: make(chan *connection),
|
||||
unregister: make(chan *connection),
|
||||
connections: make(map[*connection]bool),
|
||||
func newHub() *Hub {
|
||||
return &Hub{
|
||||
broadcast: make(chan []byte),
|
||||
register: make(chan *Client),
|
||||
unregister: make(chan *Client),
|
||||
clients: make(map[*Client]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hub) run() {
|
||||
func (h *Hub) run() {
|
||||
for {
|
||||
select {
|
||||
case c := <-h.register:
|
||||
h.connections[c] = true
|
||||
case c := <-h.unregister:
|
||||
if _, ok := h.connections[c]; ok {
|
||||
delete(h.connections, c)
|
||||
close(c.send)
|
||||
case client := <-h.register:
|
||||
h.clients[client] = true
|
||||
case client := <-h.unregister:
|
||||
if _, ok := h.clients[client]; ok {
|
||||
delete(h.clients, client)
|
||||
close(client.send)
|
||||
}
|
||||
case m := <-h.broadcast:
|
||||
for c := range h.connections {
|
||||
case message := <-h.broadcast:
|
||||
for client := range h.clients {
|
||||
select {
|
||||
case c.send <- m:
|
||||
case client.send <- message:
|
||||
default:
|
||||
close(c.send)
|
||||
delete(h.connections, c)
|
||||
close(client.send)
|
||||
delete(h.clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
vendor/github.com/gorilla/websocket/examples/chat/main.go
сгенерированный
поставляемый
12
vendor/github.com/gorilla/websocket/examples/chat/main.go
сгенерированный
поставляемый
@@ -12,9 +12,10 @@ import (
|
||||
)
|
||||
|
||||
var addr = flag.String("addr", ":8080", "http service address")
|
||||
var homeTempl = template.Must(template.ParseFiles("home.html"))
|
||||
var homeTemplate = template.Must(template.ParseFiles("home.html"))
|
||||
|
||||
func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(r.URL)
|
||||
if r.URL.Path != "/" {
|
||||
http.Error(w, "Not found", 404)
|
||||
return
|
||||
@@ -24,14 +25,17 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
homeTempl.Execute(w, r.Host)
|
||||
homeTemplate.Execute(w, r.Host)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
go h.run()
|
||||
hub := newHub()
|
||||
go hub.run()
|
||||
http.HandleFunc("/", serveHome)
|
||||
http.HandleFunc("/ws", serveWs)
|
||||
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveWs(hub, w, r)
|
||||
})
|
||||
err := http.ListenAndServe(*addr, nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
|
||||
12
vendor/github.com/gorilla/websocket/examples/command/main.go
сгенерированный
поставляемый
12
vendor/github.com/gorilla/websocket/examples/command/main.go
сгенерированный
поставляемый
@@ -36,6 +36,9 @@ const (
|
||||
|
||||
// Send pings to peer with this period. Must be less than pongWait.
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
// Time to wait before force close on connection.
|
||||
closeGracePeriod = 10 * time.Second
|
||||
)
|
||||
|
||||
func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
||||
@@ -57,19 +60,24 @@ func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
||||
|
||||
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 {
|
||||
ws.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.Err() != nil {
|
||||
log.Println("scan:", s.Err())
|
||||
}
|
||||
close(done)
|
||||
|
||||
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
time.Sleep(closeGracePeriod)
|
||||
ws.Close()
|
||||
}
|
||||
|
||||
func ping(ws *websocket.Conn, done chan struct{}) {
|
||||
|
||||
61
vendor/github.com/gorilla/websocket/mask.go
сгенерированный
поставляемый
Обычный файл
61
vendor/github.com/gorilla/websocket/mask.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,61 @@
|
||||
// Copyright 2016 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 websocket
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
}
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
|
||||
// Create aligned word size key.
|
||||
var k [wordSize]byte
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
// Mask one byte at a time for remaining bytes.
|
||||
b = b[n:]
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
|
||||
return pos & 3
|
||||
}
|
||||
73
vendor/github.com/gorilla/websocket/mask_test.go
сгенерированный
поставляемый
Обычный файл
73
vendor/github.com/gorilla/websocket/mask_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,73 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Require 1.7 for sub-bencmarks
|
||||
// +build go1.7
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func maskBytesByByte(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
func notzero(b []byte) int {
|
||||
for i := range b {
|
||||
if b[i] != 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func TestMaskBytes(t *testing.T) {
|
||||
key := [4]byte{1, 2, 3, 4}
|
||||
for size := 1; size <= 1024; size++ {
|
||||
for align := 0; align < wordSize; align++ {
|
||||
for pos := 0; pos < 4; pos++ {
|
||||
b := make([]byte, size+align)[align:]
|
||||
maskBytes(key, pos, b)
|
||||
maskBytesByByte(key, pos, b)
|
||||
if i := notzero(b); i >= 0 {
|
||||
t.Errorf("size:%d, align:%d, pos:%d, offset:%d", size, align, pos, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMaskBytes(b *testing.B) {
|
||||
for _, size := range []int{2, 4, 8, 16, 32, 512, 1024} {
|
||||
b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) {
|
||||
for _, align := range []int{wordSize / 2} {
|
||||
b.Run(fmt.Sprintf("align-%d", align), func(b *testing.B) {
|
||||
for _, fn := range []struct {
|
||||
name string
|
||||
fn func(key [4]byte, pos int, b []byte) int
|
||||
}{
|
||||
{"byte", maskBytesByByte},
|
||||
{"word", maskBytes},
|
||||
} {
|
||||
b.Run(fn.name, func(b *testing.B) {
|
||||
key := newMaskKey()
|
||||
data := make([]byte, size+align)[align:]
|
||||
for i := 0; i < b.N; i++ {
|
||||
fn.fn(key, 0, data)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
34
vendor/github.com/gorilla/websocket/server.go
сгенерированный
поставляемый
34
vendor/github.com/gorilla/websocket/server.go
сгенерированный
поставляемый
@@ -46,6 +46,12 @@ type Upgrader struct {
|
||||
// CheckOrigin is nil, the host in the Origin header must not be set or
|
||||
// must match the host of the request.
|
||||
CheckOrigin func(r *http.Request) bool
|
||||
|
||||
// EnableCompression specify if the server should attempt to negotiate per
|
||||
// message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
}
|
||||
|
||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||
@@ -53,6 +59,7 @@ func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status in
|
||||
if u.Error != nil {
|
||||
u.Error(w, r, status, err)
|
||||
} else {
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
return nil, err
|
||||
@@ -99,7 +106,12 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
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" {
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific Sec-Websocket-Extensions headers are unsupported")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
|
||||
}
|
||||
|
||||
@@ -126,6 +138,18 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
// Negotiate PMCE
|
||||
var compress bool
|
||||
if u.EnableCompression {
|
||||
for _, ext := range parseExtensions(r.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
compress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
netConn net.Conn
|
||||
br *bufio.Reader
|
||||
@@ -151,6 +175,11 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
|
||||
c.subprotocol = subprotocol
|
||||
|
||||
if compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
c.newDecompressionReader = decompressNoContextTakeover
|
||||
}
|
||||
|
||||
p := c.writeBuf[:0]
|
||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||
p = append(p, computeAcceptKey(challengeKey)...)
|
||||
@@ -160,6 +189,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
p = append(p, c.subprotocol...)
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
if compress {
|
||||
p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||
}
|
||||
for k, vs := range responseHeader {
|
||||
if k == "Sec-Websocket-Protocol" {
|
||||
continue
|
||||
|
||||
196
vendor/github.com/gorilla/websocket/util.go
сгенерированный
поставляемый
196
vendor/github.com/gorilla/websocket/util.go
сгенерированный
поставляемый
@@ -13,19 +13,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains token.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
for _, v := range header[name] {
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if strings.EqualFold(value, strings.TrimSpace(s)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func computeAcceptKey(challengeKey string) string {
|
||||
@@ -42,3 +29,186 @@ func generateChallengeKey() (string, error) {
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(p), nil
|
||||
}
|
||||
|
||||
// Octet types from RFC 2616.
|
||||
var octetTypes [256]byte
|
||||
|
||||
const (
|
||||
isTokenOctet = 1 << iota
|
||||
isSpaceOctet
|
||||
)
|
||||
|
||||
func init() {
|
||||
// From RFC 2616
|
||||
//
|
||||
// OCTET = <any 8-bit sequence of data>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
// CR = <US-ASCII CR, carriage return (13)>
|
||||
// LF = <US-ASCII LF, linefeed (10)>
|
||||
// SP = <US-ASCII SP, space (32)>
|
||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
// <"> = <US-ASCII double-quote mark (34)>
|
||||
// CRLF = CR LF
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||
// token = 1*<any CHAR except CTLs or separators>
|
||||
// qdtext = <any TEXT except <">>
|
||||
|
||||
for c := 0; c < 256; c++ {
|
||||
var t byte
|
||||
isCtl := c <= 31 || c == 127
|
||||
isChar := 0 <= c && c <= 127
|
||||
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||
t |= isSpaceOctet
|
||||
}
|
||||
if isChar && !isCtl && !isSeparator {
|
||||
t |= isTokenOctet
|
||||
}
|
||||
octetTypes[c] = t
|
||||
}
|
||||
}
|
||||
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isSpaceOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
func nextToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isTokenOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return nextToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + 1; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j += 1
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains token.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if strings.EqualFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensiosn parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
|
||||
// From RFC 6455:
|
||||
//
|
||||
// Sec-WebSocket-Extensions = extension-list
|
||||
// extension-list = 1#extension
|
||||
// extension = extension-token *( ";" extension-param )
|
||||
// extension-token = registered-token
|
||||
// registered-token = token
|
||||
// extension-param = token [ "=" (token | quoted-string) ]
|
||||
// ;When using the quoted-string syntax variant, the value
|
||||
// ;after quoted-string unescaping MUST conform to the
|
||||
// ;'token' ABNF.
|
||||
|
||||
var result []map[string]string
|
||||
headers:
|
||||
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
ext := map[string]string{"": t}
|
||||
for {
|
||||
s = skipSpace(s)
|
||||
if !strings.HasPrefix(s, ";") {
|
||||
break
|
||||
}
|
||||
var k string
|
||||
k, s = nextToken(skipSpace(s[1:]))
|
||||
if k == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
var v string
|
||||
if strings.HasPrefix(s, "=") {
|
||||
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||
s = skipSpace(s)
|
||||
}
|
||||
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||
continue headers
|
||||
}
|
||||
ext[k] = v
|
||||
}
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
result = append(result, ext)
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
40
vendor/github.com/gorilla/websocket/util_test.go
сгенерированный
поставляемый
40
vendor/github.com/gorilla/websocket/util_test.go
сгенерированный
поставляемый
@@ -6,6 +6,7 @@ package websocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -32,3 +33,42 @@ func TestTokenListContainsValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parseExtensionTests = []struct {
|
||||
value string
|
||||
extensions []map[string]string
|
||||
}{
|
||||
{`foo`, []map[string]string{map[string]string{"": "foo"}}},
|
||||
{`foo, bar; baz=2`, []map[string]string{
|
||||
map[string]string{"": "foo"},
|
||||
map[string]string{"": "bar", "baz": "2"}}},
|
||||
{`foo; bar="b,a;z"`, []map[string]string{
|
||||
map[string]string{"": "foo", "bar": "b,a;z"}}},
|
||||
{`foo , bar; baz = 2`, []map[string]string{
|
||||
map[string]string{"": "foo"},
|
||||
map[string]string{"": "bar", "baz": "2"}}},
|
||||
{`foo, bar; baz=2 junk`, []map[string]string{
|
||||
map[string]string{"": "foo"}}},
|
||||
{`foo junk, bar; baz=2 junk`, nil},
|
||||
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
|
||||
map[string]string{"": "mux", "max-channels": "4", "flow-control": ""},
|
||||
map[string]string{"": "deflate-stream"}}},
|
||||
{`permessage-foo; x="10"`, []map[string]string{
|
||||
map[string]string{"": "permessage-foo", "x": "10"}}},
|
||||
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
|
||||
map[string]string{"": "permessage-foo", "use_y": ""},
|
||||
map[string]string{"": "permessage-foo"}}},
|
||||
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
|
||||
map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
|
||||
map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}},
|
||||
}
|
||||
|
||||
func TestParseExtensions(t *testing.T) {
|
||||
for _, tt := range parseExtensionTests {
|
||||
h := http.Header{http.CanonicalHeaderKey("Sec-WebSocket-Extensions"): {tt.value}}
|
||||
extensions := parseExtensions(h)
|
||||
if !reflect.DeepEqual(extensions, tt.extensions) {
|
||||
t.Errorf("parseExtensions(%q)\n = %v,\nwant %v", tt.value, extensions, tt.extensions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user