TUN-8685: Bump coredns dependency
Some checks are pending
Check / check (1.22.x, macos-latest) (push) Waiting to run
Check / check (1.22.x, ubuntu-latest) (push) Waiting to run
Check / check (1.22.x, windows-latest) (push) Waiting to run
Semgrep config / semgrep/ci (push) Waiting to run

Closes TUN-8685
This commit is contained in:
Devin Carr
2024-10-17 13:09:39 -07:00
parent abb3466c31
commit d608a64cc5
127 changed files with 4201 additions and 1747 deletions

View File

@@ -1,25 +0,0 @@
sudo: required
language: go
services:
- docker
os:
- linux
- windows
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.x
install:
- go get github.com/gobwas/pool
- go get github.com/gobwas/httphead
script:
- if [ "$TRAVIS_OS_NAME" = "windows" ]; then go test ./...; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test autobahn; fi

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2018 Sergey Kamardin <gobwas@gmail.com>
Copyright (c) 2017-2021 Sergey Kamardin <gobwas@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

11
vendor/github.com/gobwas/ws/Makefile generated vendored
View File

@@ -13,15 +13,22 @@ bin/gocovmerge:
.PHONY: autobahn
autobahn: clean bin/reporter
./autobahn/script/test.sh --build
./autobahn/script/test.sh --build --follow-logs
bin/reporter $(PWD)/autobahn/report/index.json
.PHONY: autobahn/report
autobahn/report: bin/reporter
./bin/reporter -http localhost:5555 ./autobahn/report/index.json
test:
go test -coverprofile=ws.coverage .
go test -coverprofile=wsutil.coverage ./wsutil
go test -coverprofile=wsfalte.coverage ./wsflate
# No statemenets to cover in ./tests (there are only tests).
go test ./tests
cover: bin/gocovmerge test autobahn
bin/gocovmerge ws.coverage wsutil.coverage autobahn/report/server.coverage > total.coverage
bin/gocovmerge ws.coverage wsutil.coverage wsflate.coverage autobahn/report/server.coverage > total.coverage
benchcmp: BENCH_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
benchcmp: BENCH_OLD:=$(shell mktemp -t old.XXXX)

187
vendor/github.com/gobwas/ws/README.md generated vendored
View File

@@ -1,7 +1,7 @@
# ws
[![GoDoc][godoc-image]][godoc-url]
[![Travis][travis-image]][travis-url]
[![CI][ci-badge]][ci-url]
> [RFC6455][rfc-url] WebSocket implementation in Go.
@@ -351,10 +351,191 @@ func main() {
}
```
# Compression
There is a `ws/wsflate` package to support [Permessage-Deflate Compression
Extension][rfc-pmce].
It provides minimalistic I/O wrappers to be used in conjunction with any
deflate implementation (for example, the standard library's
[compress/flate][compress/flate]).
It is also compatible with `wsutil`'s reader and writer by providing
`wsflate.MessageState` type, which implements `wsutil.SendExtension` and
`wsutil.RecvExtension` interfaces.
```go
package main
import (
"bytes"
"log"
"net"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsflate"
)
func main() {
ln, err := net.Listen("tcp", "localhost:8080")
if err != nil {
// handle error
}
e := wsflate.Extension{
// We are using default parameters here since we use
// wsflate.{Compress,Decompress}Frame helpers below in the code.
// This assumes that we use standard compress/flate package as flate
// implementation.
Parameters: wsflate.DefaultParameters,
}
u := ws.Upgrader{
Negotiate: e.Negotiate,
}
for {
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
// Reset extension after previous upgrades.
e.Reset()
_, err = u.Upgrade(conn)
if err != nil {
log.Printf("upgrade error: %s", err)
continue
}
if _, ok := e.Accepted(); !ok {
log.Printf("didn't negotiate compression for %s", conn.RemoteAddr())
conn.Close()
continue
}
go func() {
defer conn.Close()
for {
frame, err := ws.ReadFrame(conn)
if err != nil {
// Handle error.
return
}
frame = ws.UnmaskFrameInPlace(frame)
if wsflate.IsCompressed(frame.Header) {
// Note that even after successful negotiation of
// compression extension, both sides are able to send
// non-compressed messages.
frame, err = wsflate.DecompressFrame(frame)
if err != nil {
// Handle error.
return
}
}
// Do something with frame...
ack := ws.NewTextFrame([]byte("this is an acknowledgement"))
// Compress response unconditionally.
ack, err = wsflate.CompressFrame(ack)
if err != nil {
// Handle error.
return
}
if err = ws.WriteFrame(conn, ack); err != nil {
// Handle error.
return
}
}
}()
}
}
```
You can use compression with `wsutil` package this way:
```go
// Upgrade somehow and negotiate compression to get the conn...
// Initialize flate reader. We are using nil as a source io.Reader because
// we will Reset() it in the message i/o loop below.
fr := wsflate.NewReader(nil, func(r io.Reader) wsflate.Decompressor {
return flate.NewReader(r)
})
// Initialize flate writer. We are using nil as a destination io.Writer
// because we will Reset() it in the message i/o loop below.
fw := wsflate.NewWriter(nil, func(w io.Writer) wsflate.Compressor {
f, _ := flate.NewWriter(w, 9)
return f
})
// Declare compression message state variable.
//
// It has two goals:
// - Allow users to check whether received message is compressed or not.
// - Help wsutil.Reader and wsutil.Writer to set/unset appropriate
// WebSocket header bits while writing next frame to the wire (it
// implements wsutil.RecvExtension and wsutil.SendExtension).
var msg wsflate.MessageState
// Initialize WebSocket reader as previously.
// Please note the use of Reader.Extensions field as well as
// of ws.StateExtended flag.
rd := &wsutil.Reader{
Source: conn,
State: ws.StateServerSide | ws.StateExtended,
Extensions: []wsutil.RecvExtension{
&msg,
},
}
// Initialize WebSocket writer with ws.StateExtended flag as well.
wr := wsutil.NewWriter(conn, ws.StateServerSide|ws.StateExtended, 0)
// Use the message state as wsutil.SendExtension.
wr.SetExtensions(&msg)
for {
h, err := rd.NextFrame()
if err != nil {
// handle error.
}
if h.OpCode.IsControl() {
// handle control frame.
}
if !msg.IsCompressed() {
// handle uncompressed frame (skipped for the sake of example
// simplicity).
}
// Reset the writer to echo same op code.
wr.Reset(h.OpCode)
// Reset both flate reader and writer to start the new round of i/o.
fr.Reset(rd)
fw.Reset(wr)
// Copy whole message from reader to writer decompressing it and
// compressing again.
if _, err := io.Copy(fw, fr); err != nil {
// handle error.
}
// Flush any remaining buffers from flate writer to WebSocket writer.
if err := fw.Close(); err != nil {
// handle error.
}
// Flush the whole WebSocket message to the wire.
if err := wr.Flush(); err != nil {
// handle error.
}
}
```
[rfc-url]: https://tools.ietf.org/html/rfc6455
[rfc-pmce]: https://tools.ietf.org/html/rfc7692#section-7
[godoc-image]: https://godoc.org/github.com/gobwas/ws?status.svg
[godoc-url]: https://godoc.org/github.com/gobwas/ws
[travis-image]: https://travis-ci.org/gobwas/ws.svg?branch=master
[travis-url]: https://travis-ci.org/gobwas/ws
[compress/flate]: https://golang.org/pkg/compress/flate/
[ci-badge]: https://github.com/gobwas/ws/workflows/CI/badge.svg
[ci-url]: https://github.com/gobwas/ws/actions?query=workflow%3ACI

View File

@@ -36,7 +36,7 @@ func Cipher(payload []byte, mask [4]byte, offset int) {
}
// NOTE: we use here binary.LittleEndian regardless of what is real
// endianess on machine is. To do so, we have to use binary.LittleEndian in
// endianness on machine is. To do so, we have to use binary.LittleEndian in
// the masking loop below as well.
var (
m = binary.LittleEndian.Uint32(mask[:])

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
@@ -145,7 +146,7 @@ type Dialer struct {
func (d Dialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *bufio.Reader, hs Handshake, err error) {
u, err := url.ParseRequestURI(urlstr)
if err != nil {
return
return nil, nil, hs, err
}
// Prepare context to dial with. Initially it is the same as original, but
@@ -163,7 +164,7 @@ func (d Dialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *buf
}
}
if conn, err = d.dial(dialctx, u); err != nil {
return
return conn, nil, hs, err
}
defer func() {
if err != nil {
@@ -189,7 +190,7 @@ func (d Dialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *buf
br, hs, err = d.Upgrade(conn, u)
return
return conn, br, hs, err
}
var (
@@ -204,7 +205,7 @@ func tlsDefaultConfig() *tls.Config {
return &tlsEmptyConfig
}
func hostport(host string, defaultPort string) (hostname, addr string) {
func hostport(host, defaultPort string) (hostname, addr string) {
var (
colon = strings.LastIndexByte(host, ':')
bracket = strings.IndexByte(host, ']')
@@ -228,7 +229,7 @@ func (d Dialer) dial(ctx context.Context, u *url.URL) (conn net.Conn, err error)
hostname, addr := hostport(u.Host, ":443")
conn, err = dial(ctx, "tcp", addr)
if err != nil {
return
return nil, err
}
tlsClient := d.TLSClient
if tlsClient == nil {
@@ -241,7 +242,7 @@ func (d Dialer) dial(ctx context.Context, u *url.URL) (conn net.Conn, err error)
if wrap := d.WrapConn; wrap != nil {
conn = wrap(conn)
}
return
return conn, err
}
func (d Dialer) tlsClient(conn net.Conn, hostname string) net.Conn {
@@ -310,29 +311,29 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
initNonce(nonce)
httpWriteUpgradeRequest(bw, u, nonce, d.Protocols, d.Extensions, d.Header)
if err = bw.Flush(); err != nil {
return
if err := bw.Flush(); err != nil {
return br, hs, err
}
// Read HTTP status line like "HTTP/1.1 101 Switching Protocols".
sl, err := readLine(br)
if err != nil {
return
return br, hs, err
}
// Begin validation of the response.
// See https://tools.ietf.org/html/rfc6455#section-4.2.2
// Parse request line data like HTTP version, uri and method.
resp, err := httpParseResponseLine(sl)
if err != nil {
return
return br, hs, err
}
// Even if RFC says "1.1 or higher" without mentioning the part of the
// version, we apply it only to minor part.
if resp.major != 1 || resp.minor < 1 {
err = ErrHandshakeBadProtocol
return
return br, hs, err
}
if resp.status != 101 {
if resp.status != http.StatusSwitchingProtocols {
err = StatusError(resp.status)
if onStatusError := d.OnStatusError; onStatusError != nil {
// Invoke callback with multireader of status-line bytes br.
@@ -344,7 +345,7 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
),
)
}
return
return br, hs, err
}
// If response status is 101 then we expect all technical headers to be
// valid. If not, then we stop processing response without giving user
@@ -355,7 +356,7 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
line, e := readLine(br)
if e != nil {
err = e
return
return br, hs, err
}
if len(line) == 0 {
// Blank line, no more lines to read.
@@ -365,7 +366,7 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
k, v, ok := httpParseHeaderLine(line)
if !ok {
err = ErrMalformedResponse
return
return br, hs, err
}
switch btsToString(k) {
@@ -373,7 +374,7 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
headerSeen |= headerSeenUpgrade
if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) {
err = ErrHandshakeBadUpgrade
return
return br, hs, err
}
case headerConnectionCanonical:
@@ -384,14 +385,14 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
// multiple token. But in response it must contains exactly one.
if !bytes.Equal(v, specHeaderValueConnection) && !bytes.EqualFold(v, specHeaderValueConnection) {
err = ErrHandshakeBadConnection
return
return br, hs, err
}
case headerSecAcceptCanonical:
headerSeen |= headerSeenSecAccept
if !checkAcceptFromNonce(v, nonce) {
err = ErrHandshakeBadSecAccept
return
return br, hs, err
}
case headerSecProtocolCanonical:
@@ -409,20 +410,20 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
// Server echoed subprotocol that is not present in client
// requested protocols.
err = ErrHandshakeBadSubProtocol
return
return br, hs, err
}
case headerSecExtensionsCanonical:
hs.Extensions, err = matchSelectedExtensions(v, d.Extensions, hs.Extensions)
if err != nil {
return
return br, hs, err
}
default:
if onHeader := d.OnHeader; onHeader != nil {
if e := onHeader(k, v); e != nil {
err = e
return
return br, hs, err
}
}
}
@@ -439,7 +440,7 @@ func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Ha
panic("unknown headers state")
}
}
return
return br, hs, err
}
// PutReader returns bufio.Reader instance to the inner reuse pool.
@@ -474,10 +475,19 @@ func matchSelectedExtensions(selected []byte, wanted, received []httphead.Option
index = -1
match := func() (ok bool) {
for _, want := range wanted {
if option.Equal(want) {
// A server accepts one or more extensions by including a
// |Sec-WebSocket-Extensions| header field containing one or more
// extensions that were requested by the client.
//
// The interpretation of any extension parameters, and what
// constitutes a valid response by a server to a requested set of
// parameters by a client, will be defined by each such extension.
if bytes.Equal(option.Name, want.Name) {
// Check parsed extension to be present in client
// requested extensions. We move matched extension
// from client list to avoid allocation.
// from client list to avoid allocation of httphead.Option.Name,
// httphead.Option.Parameters have to be copied from the header
want.Parameters, _ = option.Parameters.Copy(make([]byte, option.Parameters.Size()))
received = append(received, want)
return true
}

View File

@@ -1,3 +1,4 @@
//go:build go1.8
// +build go1.8
package ws

80
vendor/github.com/gobwas/ws/doc.go generated vendored
View File

@@ -11,70 +11,70 @@ Upgrade to WebSocket (or WebSocket handshake) can be done in two ways.
The first way is to use `net/http` server:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
})
The second and much more efficient way is so-called "zero-copy upgrade". It
avoids redundant allocations and copying of not used headers or other request
data. User decides by himself which data should be copied.
ln, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
ln, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
conn, err := ln.Accept()
if err != nil {
// handle error
}
conn, err := ln.Accept()
if err != nil {
// handle error
}
handshake, err := ws.Upgrade(conn)
if err != nil {
// handle error
}
handshake, err := ws.Upgrade(conn)
if err != nil {
// handle error
}
For customization details see `ws.Upgrader` documentation.
After WebSocket handshake you can work with connection in multiple ways.
That is, `ws` does not force the only one way of how to work with WebSocket:
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
buf := make([]byte, header.Length)
_, err := io.ReadFull(conn, buf)
if err != nil {
// handle err
}
buf := make([]byte, header.Length)
_, err := io.ReadFull(conn, buf)
if err != nil {
// handle err
}
resp := ws.NewBinaryFrame([]byte("hello, world!"))
if err := ws.WriteFrame(conn, frame); err != nil {
// handle err
}
resp := ws.NewBinaryFrame([]byte("hello, world!"))
if err := ws.WriteFrame(conn, frame); err != nil {
// handle err
}
As you can see, it stream friendly:
const N = 42
const N = 42
ws.WriteHeader(ws.Header{
Fin: true,
Length: N,
OpCode: ws.OpBinary,
})
ws.WriteHeader(ws.Header{
Fin: true,
Length: N,
OpCode: ws.OpBinary,
})
io.CopyN(conn, rand.Reader, N)
io.CopyN(conn, rand.Reader, N)
Or:
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
io.CopyN(ioutil.Discard, conn, header.Length)
io.CopyN(ioutil.Discard, conn, header.Length)
For more info see the documentation.
*/

View File

@@ -2,12 +2,12 @@ package ws
// RejectOption represents an option used to control the way connection is
// rejected.
type RejectOption func(*rejectConnectionError)
type RejectOption func(*ConnectionRejectedError)
// RejectionReason returns an option that makes connection to be rejected with
// given reason.
func RejectionReason(reason string) RejectOption {
return func(err *rejectConnectionError) {
return func(err *ConnectionRejectedError) {
err.reason = reason
}
}
@@ -15,7 +15,7 @@ func RejectionReason(reason string) RejectOption {
// RejectionStatus returns an option that makes connection to be rejected with
// given HTTP status code.
func RejectionStatus(code int) RejectOption {
return func(err *rejectConnectionError) {
return func(err *ConnectionRejectedError) {
err.code = code
}
}
@@ -23,32 +23,37 @@ func RejectionStatus(code int) RejectOption {
// RejectionHeader returns an option that makes connection to be rejected with
// given HTTP headers.
func RejectionHeader(h HandshakeHeader) RejectOption {
return func(err *rejectConnectionError) {
return func(err *ConnectionRejectedError) {
err.header = h
}
}
// RejectConnectionError constructs an error that could be used to control the way
// handshake is rejected by Upgrader.
// RejectConnectionError constructs an error that could be used to control the
// way handshake is rejected by Upgrader.
func RejectConnectionError(options ...RejectOption) error {
err := new(rejectConnectionError)
err := new(ConnectionRejectedError)
for _, opt := range options {
opt(err)
}
return err
}
// rejectConnectionError represents a rejection of upgrade error.
// ConnectionRejectedError represents a rejection of connection during
// WebSocket handshake error.
//
// It can be returned by Upgrader's On* hooks to control the way WebSocket
// handshake is rejected.
type rejectConnectionError struct {
// It can be returned by Upgrader's On* hooks to indicate that WebSocket
// handshake should be rejected.
type ConnectionRejectedError struct {
reason string
code int
header HandshakeHeader
}
// Error implements error interface.
func (r *rejectConnectionError) Error() string {
func (r *ConnectionRejectedError) Error() string {
return r.reason
}
func (r *ConnectionRejectedError) StatusCode() int {
return r.code
}

63
vendor/github.com/gobwas/ws/frame.go generated vendored
View File

@@ -206,6 +206,28 @@ func (h Header) Rsv2() bool { return h.Rsv&bit6 != 0 }
// Rsv3 reports whether the header has third rsv bit set.
func (h Header) Rsv3() bool { return h.Rsv&bit7 != 0 }
// Rsv creates rsv byte representation from bits.
func Rsv(r1, r2, r3 bool) (rsv byte) {
if r1 {
rsv |= bit5
}
if r2 {
rsv |= bit6
}
if r3 {
rsv |= bit7
}
return rsv
}
// RsvBits returns rsv bits from bytes representation.
func RsvBits(rsv byte) (r1, r2, r3 bool) {
r1 = rsv&bit5 != 0
r2 = rsv&bit6 != 0
r3 = rsv&bit7 != 0
return r1, r2, r3
}
// Frame represents websocket frame.
// See https://tools.ietf.org/html/rfc6455#section-5.2
type Frame struct {
@@ -319,6 +341,29 @@ func MaskFrameInPlace(f Frame) Frame {
return MaskFrameInPlaceWith(f, NewMask())
}
var zeroMask [4]byte
// UnmaskFrame unmasks frame and returns frame with unmasked payload and Mask
// header's field cleared.
// Note that it copies f payload.
func UnmaskFrame(f Frame) Frame {
p := make([]byte, len(f.Payload))
copy(p, f.Payload)
f.Payload = p
return UnmaskFrameInPlace(f)
}
// UnmaskFrameInPlace unmasks frame and returns frame with unmasked payload and
// Mask header's field cleared.
// Note that it applies xor cipher to f.Payload without copying, that is, it
// modifies f.Payload inplace.
func UnmaskFrameInPlace(f Frame) Frame {
Cipher(f.Payload, f.Header.Mask, 0)
f.Header.Masked = false
f.Header.Mask = zeroMask
return f
}
// MaskFrameInPlaceWith masks frame with given mask and returns frame
// with masked payload and Mask header's field set.
// Note that it applies xor cipher to f.Payload without copying, that is, it
@@ -333,7 +378,7 @@ func MaskFrameInPlaceWith(f Frame, m [4]byte) Frame {
// NewMask creates new random mask.
func NewMask() (ret [4]byte) {
binary.BigEndian.PutUint32(ret[:], rand.Uint32())
return
return ret
}
// CompileFrame returns byte representation of given frame.
@@ -343,7 +388,7 @@ func CompileFrame(f Frame) (bts []byte, err error) {
buf := bytes.NewBuffer(make([]byte, 0, 16))
err = WriteFrame(buf, f)
bts = buf.Bytes()
return
return bts, err
}
// MustCompileFrame is like CompileFrame but panics if frame can not be
@@ -356,20 +401,6 @@ func MustCompileFrame(f Frame) []byte {
return bts
}
// Rsv creates rsv byte representation.
func Rsv(r1, r2, r3 bool) (rsv byte) {
if r1 {
rsv |= bit5
}
if r2 {
rsv |= bit6
}
if r3 {
rsv |= bit7
}
return rsv
}
func makeCloseFrame(code StatusCode) Frame {
return NewCloseFrame(NewCloseFrameBody(code, ""))
}

87
vendor/github.com/gobwas/ws/http.go generated vendored
View File

@@ -5,7 +5,6 @@ import (
"bytes"
"io"
"net/http"
"net/textproto"
"net/url"
"strconv"
@@ -38,7 +37,8 @@ var (
textTailErrUpgradeRequired = errorText(ErrHandshakeUpgradeRequired)
)
var (
const (
// Every new header must be added to TestHeaderNames test.
headerHost = "Host"
headerUpgrade = "Upgrade"
headerConnection = "Connection"
@@ -48,14 +48,14 @@ var (
headerSecKey = "Sec-WebSocket-Key"
headerSecAccept = "Sec-WebSocket-Accept"
headerHostCanonical = textproto.CanonicalMIMEHeaderKey(headerHost)
headerUpgradeCanonical = textproto.CanonicalMIMEHeaderKey(headerUpgrade)
headerConnectionCanonical = textproto.CanonicalMIMEHeaderKey(headerConnection)
headerSecVersionCanonical = textproto.CanonicalMIMEHeaderKey(headerSecVersion)
headerSecProtocolCanonical = textproto.CanonicalMIMEHeaderKey(headerSecProtocol)
headerSecExtensionsCanonical = textproto.CanonicalMIMEHeaderKey(headerSecExtensions)
headerSecKeyCanonical = textproto.CanonicalMIMEHeaderKey(headerSecKey)
headerSecAcceptCanonical = textproto.CanonicalMIMEHeaderKey(headerSecAccept)
headerHostCanonical = headerHost
headerUpgradeCanonical = headerUpgrade
headerConnectionCanonical = headerConnection
headerSecVersionCanonical = "Sec-Websocket-Version"
headerSecProtocolCanonical = "Sec-Websocket-Protocol"
headerSecExtensionsCanonical = "Sec-Websocket-Extensions"
headerSecKeyCanonical = "Sec-Websocket-Key"
headerSecAcceptCanonical = "Sec-Websocket-Accept"
)
var (
@@ -91,10 +91,8 @@ func httpParseRequestLine(line []byte) (req httpRequestLine, err error) {
req.major, req.minor, ok = httpParseVersion(proto)
if !ok {
err = ErrMalformedRequest
return
}
return
return req, err
}
func httpParseResponseLine(line []byte) (resp httpResponseLine, err error) {
@@ -128,25 +126,25 @@ func httpParseVersion(bts []byte) (major, minor int, ok bool) {
case bytes.Equal(bts, httpVersion1_1):
return 1, 1, true
case len(bts) < 8:
return
return 0, 0, false
case !bytes.Equal(bts[:5], httpVersionPrefix):
return
return 0, 0, false
}
bts = bts[5:]
dot := bytes.IndexByte(bts, '.')
if dot == -1 {
return
return 0, 0, false
}
var err error
major, err = asciiToInt(bts[:dot])
if err != nil {
return
return major, 0, false
}
minor, err = asciiToInt(bts[dot+1:])
if err != nil {
return
return major, minor, false
}
return major, minor, true
@@ -157,7 +155,7 @@ func httpParseVersion(bts []byte) (major, minor int, ok bool) {
func httpParseHeaderLine(line []byte) (k, v []byte, ok bool) {
colon := bytes.IndexByte(line, ':')
if colon == -1 {
return
return nil, nil, false
}
k = btrim(line[:colon])
@@ -198,8 +196,9 @@ func strSelectProtocol(h string, check func(string) bool) (ret string, ok bool)
}
return true
})
return
return ret, ok
}
func btsSelectProtocol(h []byte, check func([]byte) bool) (ret string, ok bool) {
var selected []byte
ok = httphead.ScanTokens(h, func(v []byte) bool {
@@ -212,21 +211,57 @@ func btsSelectProtocol(h []byte, check func([]byte) bool) (ret string, ok bool)
if ok && selected != nil {
return string(selected), true
}
return
}
func strSelectExtensions(h string, selected []httphead.Option, check func(httphead.Option) bool) ([]httphead.Option, bool) {
return btsSelectExtensions(strToBytes(h), selected, check)
return ret, ok
}
func btsSelectExtensions(h []byte, selected []httphead.Option, check func(httphead.Option) bool) ([]httphead.Option, bool) {
s := httphead.OptionSelector{
Flags: httphead.SelectUnique | httphead.SelectCopy,
Flags: httphead.SelectCopy,
Check: check,
}
return s.Select(h, selected)
}
func negotiateMaybe(in httphead.Option, dest []httphead.Option, f func(httphead.Option) (httphead.Option, error)) ([]httphead.Option, error) {
if in.Size() == 0 {
return dest, nil
}
opt, err := f(in)
if err != nil {
return nil, err
}
if opt.Size() > 0 {
dest = append(dest, opt)
}
return dest, nil
}
func negotiateExtensions(
h []byte, dest []httphead.Option,
f func(httphead.Option) (httphead.Option, error),
) (_ []httphead.Option, err error) {
index := -1
var current httphead.Option
ok := httphead.ScanOptions(h, func(i int, name, attr, val []byte) httphead.Control {
if i != index {
dest, err = negotiateMaybe(current, dest, f)
if err != nil {
return httphead.ControlBreak
}
index = i
current = httphead.Option{Name: name}
}
if attr != nil {
current.Parameters.Set(attr, val)
}
return httphead.ControlContinue
})
if !ok {
return nil, ErrMalformedRequest
}
return negotiateMaybe(current, dest, f)
}
func httpWriteHeader(bw *bufio.Writer, key, value string) {
httpWriteHeaderKey(bw, key)
bw.WriteString(value)

View File

@@ -65,8 +65,6 @@ func initAcceptFromNonce(accept, nonce []byte) {
sum := sha1.Sum(p)
base64.StdEncoding.Encode(accept, sum[:])
return
}
func writeAccept(bw *bufio.Writer, nonce []byte) (int, error) {

24
vendor/github.com/gobwas/ws/read.go generated vendored
View File

@@ -24,7 +24,7 @@ func ReadHeader(r io.Reader) (h Header, err error) {
// Prepare to hold first 2 bytes to choose size of next read.
_, err = io.ReadFull(r, bts)
if err != nil {
return
return h, err
}
h.Fin = bts[0]&bit0 != 0
@@ -51,11 +51,11 @@ func ReadHeader(r io.Reader) (h Header, err error) {
default:
err = ErrHeaderLengthUnexpected
return
return h, err
}
if extra == 0 {
return
return h, err
}
// Increase len of bts to extra bytes need to read.
@@ -63,7 +63,7 @@ func ReadHeader(r io.Reader) (h Header, err error) {
bts = bts[:extra]
_, err = io.ReadFull(r, bts)
if err != nil {
return
return h, err
}
switch {
@@ -74,7 +74,7 @@ func ReadHeader(r io.Reader) (h Header, err error) {
case length == 127:
if bts[0]&0x80 != 0 {
err = ErrHeaderLengthMSB
return
return h, err
}
h.Length = int64(binary.BigEndian.Uint64(bts[:8]))
bts = bts[8:]
@@ -84,7 +84,7 @@ func ReadHeader(r io.Reader) (h Header, err error) {
copy(h.Mask[:], bts)
}
return
return h, nil
}
// ReadFrame reads a frame from r.
@@ -95,7 +95,7 @@ func ReadHeader(r io.Reader) (h Header, err error) {
func ReadFrame(r io.Reader) (f Frame, err error) {
f.Header, err = ReadHeader(r)
if err != nil {
return
return f, err
}
if f.Header.Length > 0 {
@@ -105,7 +105,7 @@ func ReadFrame(r io.Reader) (f Frame, err error) {
_, err = io.ReadFull(r, f.Payload)
}
return
return f, err
}
// MustReadFrame is like ReadFrame but panics if frame can not be read.
@@ -128,20 +128,20 @@ func ParseCloseFrameData(payload []byte) (code StatusCode, reason string) {
// In other words, we ignoring this rule [RFC6455:7.1.5]:
// If this Close control frame contains no status code, _The WebSocket
// Connection Close Code_ is considered to be 1005.
return
return code, reason
}
code = StatusCode(binary.BigEndian.Uint16(payload))
reason = string(payload[2:])
return
return code, reason
}
// ParseCloseFrameDataUnsafe is like ParseCloseFrameData except the thing
// that it does not copies payload bytes into reason, but prepares unsafe cast.
func ParseCloseFrameDataUnsafe(payload []byte) (code StatusCode, reason string) {
if len(payload) < 2 {
return
return code, reason
}
code = StatusCode(binary.BigEndian.Uint16(payload))
reason = btsToString(payload[2:])
return
return code, reason
}

View File

@@ -24,11 +24,11 @@ const (
var (
ErrHandshakeBadProtocol = RejectConnectionError(
RejectionStatus(http.StatusHTTPVersionNotSupported),
RejectionReason(fmt.Sprintf("handshake error: bad HTTP protocol version")),
RejectionReason("handshake error: bad HTTP protocol version"),
)
ErrHandshakeBadMethod = RejectConnectionError(
RejectionStatus(http.StatusMethodNotAllowed),
RejectionReason(fmt.Sprintf("handshake error: bad HTTP request method")),
RejectionReason("handshake error: bad HTTP request method"),
)
ErrHandshakeBadHost = RejectConnectionError(
RejectionStatus(http.StatusBadRequest),
@@ -129,7 +129,22 @@ type HTTPUpgrader struct {
// Extension is the select function that is used to select extensions from
// list requested by client. If this field is set, then the all matched
// extensions are sent to a client as negotiated.
//
// Deprecated: use Negotiate instead.
Extension func(httphead.Option) bool
// Negotiate is the callback that is used to negotiate extensions from
// the client's offer. If this field is set, then the returned non-zero
// extensions are sent to the client as accepted extensions in the
// response.
//
// The argument is only valid until the Negotiate callback returns.
//
// If returned error is non-nil then connection is rejected and response is
// sent with appropriate HTTP error code and body set to error message.
//
// RejectConnectionError could be used to get more control on response.
Negotiate func(httphead.Option) (httphead.Option, error)
}
// Upgrade upgrades http connection to the websocket connection.
@@ -148,7 +163,7 @@ func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.
}
if err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
return conn, rw, hs, err
}
// See https://tools.ietf.org/html/rfc6455#section-4.1
@@ -200,11 +215,20 @@ func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.
}
}
}
if check := u.Extension; err == nil && check != nil {
if f := u.Negotiate; err == nil && f != nil {
for _, h := range r.Header[headerSecExtensionsCanonical] {
hs.Extensions, err = negotiateExtensions(strToBytes(h), hs.Extensions, f)
if err != nil {
break
}
}
}
// DEPRECATED path.
if check := u.Extension; err == nil && check != nil && u.Negotiate == nil {
xs := r.Header[headerSecExtensionsCanonical]
for i := 0; i < len(xs) && err == nil; i++ {
var ok bool
hs.Extensions, ok = strSelectExtensions(xs[i], hs.Extensions, check)
hs.Extensions, ok = btsSelectExtensions(strToBytes(xs[i]), hs.Extensions, check)
if !ok {
err = ErrMalformedRequest
}
@@ -227,7 +251,7 @@ func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.
err = rw.Writer.Flush()
} else {
var code int
if rej, ok := err.(*rejectConnectionError); ok {
if rej, ok := err.(*ConnectionRejectedError); ok {
code = rej.code
header[1] = rej.header
}
@@ -236,9 +260,9 @@ func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.
}
httpWriteResponseError(rw.Writer, err, code, header.WriteTo)
// Do not store Flush() error to not override already existing one.
rw.Writer.Flush()
_ = rw.Writer.Flush()
}
return
return conn, rw, hs, err
}
// Upgrader contains options for upgrading connection to websocket.
@@ -271,6 +295,9 @@ type Upgrader struct {
// from list requested by client. If this field is set, then the all matched
// extensions are sent to a client as negotiated.
//
// Note that Extension may be called multiple times and implementations
// must track uniqueness of accepted extensions manually.
//
// The argument is only valid until the callback returns.
//
// According to the RFC6455 order of extensions passed by a client is
@@ -283,13 +310,38 @@ type Upgrader struct {
// fields listed by the client in its request represent a preference of the
// header fields it wishes to use, with the first options listed being most
// preferable."
//
// Deprecated: use Negotiate instead.
Extension func(httphead.Option) bool
// ExtensionCustom allow user to parse Sec-WebSocket-Extensions header manually.
// ExtensionCustom allow user to parse Sec-WebSocket-Extensions header
// manually.
//
// If ExtensionCustom() decides to accept received extension, it must
// append appropriate option to the given slice of httphead.Option.
// It returns results of append() to the given slice and a flag that
// reports whether given header value is wellformed or not.
//
// Note that ExtensionCustom may be called multiple times and
// implementations must track uniqueness of accepted extensions manually.
//
// Note that returned options should be valid until Upgrade returns.
// If ExtensionCustom is set, it used instead of Extension function.
ExtensionCustom func([]byte, []httphead.Option) ([]httphead.Option, bool)
// Negotiate is the callback that is used to negotiate extensions from
// the client's offer. If this field is set, then the returned non-zero
// extensions are sent to the client as accepted extensions in the
// response.
//
// The argument is only valid until the Negotiate callback returns.
//
// If returned error is non-nil then connection is rejected and response is
// sent with appropriate HTTP error code and body set to error message.
//
// RejectConnectionError could be used to get more control on response.
Negotiate func(httphead.Option) (httphead.Option, error)
// Header is an optional HandshakeHeader instance that could be used to
// write additional headers to the handshake response.
//
@@ -399,12 +451,12 @@ func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
// Read HTTP request line like "GET /ws HTTP/1.1".
rl, err := readLine(br)
if err != nil {
return
return hs, err
}
// Parse request line data like HTTP version, uri and method.
req, err := httpParseRequestLine(rl)
if err != nil {
return
return hs, err
}
// Prepare stack-based handshake header list.
@@ -497,7 +549,7 @@ func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
if len(v) != nonceSize {
err = ErrHandshakeBadSecKey
} else {
copy(nonce[:], v)
copy(nonce, v)
}
case headerSecProtocolCanonical:
@@ -514,7 +566,11 @@ func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
}
case headerSecExtensionsCanonical:
if custom, check := u.ExtensionCustom, u.Extension; custom != nil || check != nil {
if f := u.Negotiate; err == nil && f != nil {
hs.Extensions, err = negotiateExtensions(v, hs.Extensions, f)
}
// DEPRECATED path.
if custom, check := u.ExtensionCustom, u.Extension; u.Negotiate == nil && (custom != nil || check != nil) {
var ok bool
if custom != nil {
hs.Extensions, ok = custom(v, hs.Extensions)
@@ -574,7 +630,7 @@ func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
}
if err != nil {
var code int
if rej, ok := err.(*rejectConnectionError); ok {
if rej, ok := err.(*ConnectionRejectedError); ok {
code = rej.code
header[1] = rej.header
}
@@ -583,14 +639,14 @@ func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
}
httpWriteResponseError(bw, err, code, header.WriteTo)
// Do not store Flush() error to not override already existing one.
bw.Flush()
return
_ = bw.Flush()
return hs, err
}
httpWriteResponseUpgrade(bw, nonce, hs, header.WriteTo)
err = bw.Flush()
return
return hs, err
}
type handshakeHeader [2]HandshakeHeader

View File

19
vendor/github.com/gobwas/ws/util.go generated vendored
View File

@@ -4,8 +4,6 @@ import (
"bufio"
"bytes"
"fmt"
"reflect"
"unsafe"
"github.com/gobwas/httphead"
)
@@ -41,19 +39,6 @@ func SelectEqual(v string) func(string) bool {
}
}
func strToBytes(str string) (bts []byte) {
s := (*reflect.StringHeader)(unsafe.Pointer(&str))
b := (*reflect.SliceHeader)(unsafe.Pointer(&bts))
b.Data = s.Data
b.Len = s.Len
b.Cap = s.Len
return
}
func btsToString(bts []byte) (str string) {
return *(*string)(unsafe.Pointer(&bts))
}
// asciiToInt converts bytes to int.
func asciiToInt(bts []byte) (ret int, err error) {
// ASCII numbers all start with the high-order bits 0011.
@@ -73,7 +58,7 @@ func asciiToInt(bts []byte) (ret int, err error) {
}
// pow for integers implementation.
// See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3
// See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3.
func pow(a, b int) int {
p := 1
for b > 0 {
@@ -116,7 +101,7 @@ func btsHasToken(header, token []byte) (has bool) {
has = bytes.EqualFold(v, token)
return !has
})
return
return has
}
const (

12
vendor/github.com/gobwas/ws/util_purego.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
//go:build purego
// +build purego
package ws
func strToBytes(str string) (bts []byte) {
return []byte(str)
}
func btsToString(bts []byte) (str string) {
return string(bts)
}

22
vendor/github.com/gobwas/ws/util_unsafe.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
//go:build !purego
// +build !purego
package ws
import (
"reflect"
"unsafe"
)
func strToBytes(str string) (bts []byte) {
s := (*reflect.StringHeader)(unsafe.Pointer(&str))
b := (*reflect.SliceHeader)(unsafe.Pointer(&bts))
b.Data = s.Data
b.Len = s.Len
b.Cap = s.Len
return bts
}
func btsToString(bts []byte) (str string) {
return *(*string)(unsafe.Pointer(&bts))
}

View File

@@ -34,7 +34,7 @@ func (c *CipherReader) Read(p []byte) (n int, err error) {
n, err = c.r.Read(p)
ws.Cipher(p[:n], c.mask, c.pos)
c.pos += n
return
return n, err
}
// CipherWriter implements io.Writer that applies xor-cipher to the bytes
@@ -68,5 +68,5 @@ func (c *CipherWriter) Write(p []byte) (n int, err error) {
n, err = c.w.Write(cp)
c.pos += n
return
return n, err
}

View File

@@ -113,6 +113,7 @@ type rwConn struct {
func (rwc rwConn) Read(p []byte) (int, error) {
return rwc.r.Read(p)
}
func (rwc rwConn) Write(p []byte) (int, error) {
return rwc.w.Write(p)
}

31
vendor/github.com/gobwas/ws/wsutil/extenstion.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package wsutil
import "github.com/gobwas/ws"
// RecvExtension is an interface for clearing fragment header RSV bits.
type RecvExtension interface {
UnsetBits(ws.Header) (ws.Header, error)
}
// RecvExtensionFunc is an adapter to allow the use of ordinary functions as
// RecvExtension.
type RecvExtensionFunc func(ws.Header) (ws.Header, error)
// BitsRecv implements RecvExtension.
func (fn RecvExtensionFunc) UnsetBits(h ws.Header) (ws.Header, error) {
return fn(h)
}
// SendExtension is an interface for setting fragment header RSV bits.
type SendExtension interface {
SetBits(ws.Header) (ws.Header, error)
}
// SendExtensionFunc is an adapter to allow the use of ordinary functions as
// SendExtension.
type SendExtensionFunc func(ws.Header) (ws.Header, error)
// BitsSend implements SendExtension.
func (fn SendExtensionFunc) SetBits(h ws.Header) (ws.Header, error) {
return fn(h)
}

View File

@@ -199,7 +199,7 @@ func (c ControlHandler) HandleClose(h ws.Header) error {
if err != nil {
return err
}
if err = w.Flush(); err != nil {
if err := w.Flush(); err != nil {
return err
}
return ClosedError{

View File

@@ -64,14 +64,14 @@ func ReadMessage(r io.Reader, s ws.State, m []Message) ([]Message, error) {
// ReadClientMessage reads next message from r, considering that caller
// represents server side.
// It is a shortcut for ReadMessage(r, ws.StateServerSide, m)
// It is a shortcut for ReadMessage(r, ws.StateServerSide, m).
func ReadClientMessage(r io.Reader, m []Message) ([]Message, error) {
return ReadMessage(r, ws.StateServerSide, m)
}
// ReadServerMessage reads next message from r, considering that caller
// represents client side.
// It is a shortcut for ReadMessage(r, ws.StateClientSide, m)
// It is a shortcut for ReadMessage(r, ws.StateClientSide, m).
func ReadServerMessage(r io.Reader, m []Message) ([]Message, error) {
return ReadMessage(r, ws.StateClientSide, m)
}
@@ -113,7 +113,7 @@ func ReadClientText(rw io.ReadWriter) ([]byte, error) {
// It discards received text messages.
//
// Note this may handle and write control frames into the writer part of a given
// io.ReadWriter.
// io.ReadWriter.
func ReadClientBinary(rw io.ReadWriter) ([]byte, error) {
p, _, err := readData(rw, ws.StateServerSide, ws.OpBinary)
return p, err
@@ -133,7 +133,7 @@ func ReadServerData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
// It discards received binary messages.
//
// Note this may handle and write control frames into the writer part of a given
// io.ReadWriter.
// io.ReadWriter.
func ReadServerText(rw io.ReadWriter) ([]byte, error) {
p, _, err := readData(rw, ws.StateClientSide, ws.OpText)
return p, err

View File

@@ -12,6 +12,10 @@ import (
// preceding NextFrame() call.
var ErrNoFrameAdvance = errors.New("no frame advance")
// ErrFrameTooLarge indicates that a message of length higher than
// MaxFrameSize was being read.
var ErrFrameTooLarge = errors.New("frame too large")
// FrameHandlerFunc handles parsed frame header and its body represented by
// io.Reader.
//
@@ -37,7 +41,17 @@ type Reader struct {
// bytes are not valid UTF-8 sequence, ErrInvalidUTF8 returned.
CheckUTF8 bool
// TODO(gobwas): add max frame size limit here.
// Extensions is a list of negotiated extensions for reader Source.
// It is used to meet the specs and clear appropriate bits in fragment
// header RSV segment.
Extensions []RecvExtension
// MaxFrameSize controls the maximum frame size in bytes
// that can be read. A message exceeding that size will return
// a ErrFrameTooLarge to the application.
//
// Not setting this field means there is no limit.
MaxFrameSize int64
OnContinuation FrameHandlerFunc
OnIntermediate FrameHandlerFunc
@@ -97,12 +111,13 @@ func (r *Reader) Read(p []byte) (n int, err error) {
n, err = r.frame.Read(p)
if err != nil && err != io.EOF {
return
return n, err
}
if err == nil && r.raw.N != 0 {
return
return n, nil
}
// EOF condition (either err is io.EOF or r.raw.N is zero).
switch {
case r.raw.N != 0:
err = io.ErrUnexpectedEOF
@@ -112,6 +127,8 @@ func (r *Reader) Read(p []byte) (n int, err error) {
r.resetFragment()
case r.CheckUTF8 && !r.utf8.Valid():
// NOTE: check utf8 only when full message received, since partial
// reads may be invalid.
n = r.utf8.Accepted()
err = ErrInvalidUTF8
@@ -120,7 +137,7 @@ func (r *Reader) Read(p []byte) (n int, err error) {
err = io.EOF
}
return
return n, err
}
// Discard discards current message unread bytes.
@@ -166,14 +183,29 @@ func (r *Reader) NextFrame() (hdr ws.Header, err error) {
return hdr, err
}
if n := r.MaxFrameSize; n > 0 && hdr.Length > n {
return hdr, ErrFrameTooLarge
}
// Save raw reader to use it on discarding frame without ciphering and
// other streaming checks.
r.raw = io.LimitedReader{r.Source, hdr.Length}
r.raw = io.LimitedReader{
R: r.Source,
N: hdr.Length,
}
frame := io.Reader(&r.raw)
if hdr.Masked {
frame = NewCipherReader(frame, hdr.Mask)
}
for _, x := range r.Extensions {
hdr, err = x.UnsetBits(hdr)
if err != nil {
return hdr, err
}
}
if r.fragmented() {
if hdr.OpCode.IsControl() {
if cb := r.OnIntermediate; cb != nil {
@@ -183,7 +215,7 @@ func (r *Reader) NextFrame() (hdr ws.Header, err error) {
// Ensure that src is empty.
_, err = io.Copy(ioutil.Discard, &r.raw)
}
return
return hdr, err
}
} else {
r.opCode = hdr.OpCode
@@ -208,7 +240,7 @@ func (r *Reader) NextFrame() (hdr ws.Header, err error) {
r.State = r.State.Set(ws.StateFragmented)
}
return
return hdr, err
}
func (r *Reader) fragmented() bool {

View File

@@ -65,7 +65,7 @@ func (u *UTF8Reader) Read(p []byte) (n int, err error) {
u.state, u.codep = s, c
u.accepted = accepted
return
return n, err
}
// Valid checks current reader state. It returns true if all read bytes are

View File

@@ -84,38 +84,6 @@ func (c *ControlWriter) Flush() error {
return c.w.Flush()
}
// Writer contains logic of buffering output data into a WebSocket fragments.
// It is much the same as bufio.Writer, except the thing that it works with
// WebSocket frames, not the raw data.
//
// Writer writes frames with specified OpCode.
// It uses ws.State to decide whether the output frames must be masked.
//
// Note that it does not check control frame size or other RFC rules.
// That is, it must be used with special care to write control frames without
// violation of RFC. You could use ControlWriter that wraps Writer and contains
// some guards for writing control frames.
//
// If an error occurs writing to a Writer, no more data will be accepted and
// all subsequent writes will return the error.
// After all data has been written, the client should call the Flush() method
// to guarantee all data has been forwarded to the underlying io.Writer.
type Writer struct {
dest io.Writer
n int // Buffered bytes counter.
raw []byte // Raw representation of buffer, including reserved header bytes.
buf []byte // Writeable part of buffer, without reserved header bytes.
op ws.OpCode
state ws.State
dirty bool
fragmented bool
err error
}
var writers = pool.New(128, 65536)
// GetWriter tries to reuse Writer getting it from the pool.
@@ -145,6 +113,58 @@ func PutWriter(w *Writer) {
writers.Put(w, w.Size())
}
// Writer contains logic of buffering output data into a WebSocket fragments.
// It is much the same as bufio.Writer, except the thing that it works with
// WebSocket frames, not the raw data.
//
// Writer writes frames with specified OpCode.
// It uses ws.State to decide whether the output frames must be masked.
//
// Note that it does not check control frame size or other RFC rules.
// That is, it must be used with special care to write control frames without
// violation of RFC. You could use ControlWriter that wraps Writer and contains
// some guards for writing control frames.
//
// If an error occurs writing to a Writer, no more data will be accepted and
// all subsequent writes will return the error.
//
// After all data has been written, the client should call the Flush() method
// to guarantee all data has been forwarded to the underlying io.Writer.
type Writer struct {
// dest specifies a destination of buffer flushes.
dest io.Writer
// op specifies the WebSocket operation code used in flushed frames.
op ws.OpCode
// state specifies the state of the Writer.
state ws.State
// extensions is a list of negotiated extensions for writer Dest.
// It is used to meet the specs and set appropriate bits in fragment
// header RSV segment.
extensions []SendExtension
// noFlush reports whether buffer must grow instead of being flushed.
noFlush bool
// Raw representation of the buffer, including reserved header bytes.
raw []byte
// Writeable part of buffer, without reserved header bytes.
// Resetting this to nil will not result in reallocation if raw is not nil.
// And vice versa: if buf is not nil, then Writer is assumed as ready and
// initialized.
buf []byte
// Buffered bytes counter.
n int
dirty bool
fseq int
err error
}
// NewWriter returns a new Writer whose buffer has the DefaultWriteBuffer size.
func NewWriter(dest io.Writer, state ws.State, op ws.OpCode) *Writer {
return NewWriterBufferSize(dest, state, op, 0)
@@ -186,57 +206,63 @@ func NewWriterBufferSize(dest io.Writer, state ws.State, op ws.OpCode, n int) *W
//
// It panics if len(buf) is too small to fit header and payload data.
func NewWriterBuffer(dest io.Writer, state ws.State, op ws.OpCode, buf []byte) *Writer {
offset := reserve(state, len(buf))
if len(buf) <= offset {
panic("buffer too small")
}
return &Writer{
w := &Writer{
dest: dest,
raw: buf,
buf: buf[offset:],
state: state,
op: op,
raw: buf,
}
w.initBuf()
return w
}
func reserve(state ws.State, n int) (offset int) {
var mask int
if state.ClientSide() {
mask = 4
}
switch {
case n <= int(len7)+mask+2:
return mask + 2
case n <= int(len16)+mask+4:
return mask + 4
default:
return mask + 10
func (w *Writer) initBuf() {
offset := reserve(w.state, len(w.raw))
if len(w.raw) <= offset {
panic("wsutil: writer buffer is too small")
}
w.buf = w.raw[offset:]
}
// headerSize returns number of bytes needed to encode header of a frame with
// given state and length.
func headerSize(s ws.State, n int) int {
return ws.HeaderSize(ws.Header{
Length: int64(n),
Masked: s.ClientSide(),
})
}
// Reset discards any buffered data, clears error, and resets w to have given
// state and write frames with given OpCode to dest.
// Reset resets Writer as it was created by New() methods.
// Note that Reset does reset extensions and other options was set after
// Writer initialization.
func (w *Writer) Reset(dest io.Writer, state ws.State, op ws.OpCode) {
w.n = 0
w.dirty = false
w.fragmented = false
w.dest = dest
w.state = state
w.op = op
w.initBuf()
w.n = 0
w.dirty = false
w.fseq = 0
w.extensions = w.extensions[:0]
w.noFlush = false
}
// Size returns the size of the underlying buffer in bytes.
// ResetOp is an quick version of Reset().
// ResetOp does reset unwritten fragments and does not reset results of
// SetExtensions() or DisableFlush() methods.
func (w *Writer) ResetOp(op ws.OpCode) {
w.op = op
w.n = 0
w.dirty = false
w.fseq = 0
}
// SetExtensions adds xs as extensions to be used during writes.
func (w *Writer) SetExtensions(xs ...SendExtension) {
w.extensions = xs
}
// DisableFlush denies Writer to write fragments.
func (w *Writer) DisableFlush() {
w.noFlush = true
}
// Size returns the size of the underlying buffer in bytes (not including
// WebSocket header bytes).
func (w *Writer) Size() int {
return len(w.buf)
}
@@ -263,6 +289,10 @@ func (w *Writer) Write(p []byte) (n int, err error) {
var nn int
for len(p) > w.Available() && w.err == nil {
if w.noFlush {
w.Grow(len(p))
continue
}
if w.Buffered() == 0 {
// Large write, empty buffer. Write directly from p to avoid copy.
// Trade off here is that we make additional Write() to underlying
@@ -295,6 +325,55 @@ func (w *Writer) Write(p []byte) (n int, err error) {
return n, w.err
}
func ceilPowerOfTwo(n int) int {
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
n |= n >> 32
n++
return n
}
// Grow grows Writer's internal buffer capacity to guarantee space for another
// n bytes of _payload_ -- that is, frame header is not included in n.
func (w *Writer) Grow(n int) {
// NOTE: we must respect the possibility of header reserved bytes grow.
var (
size = len(w.raw)
prevOffset = len(w.raw) - len(w.buf)
nextOffset = len(w.raw) - len(w.buf)
buffered = w.Buffered()
)
for cap := size - nextOffset - buffered; cap < n; {
// This loop runs twice only at split cases, when reservation of raw
// buffer space for the header shrinks capacity of new buffer such that
// it still less than n.
//
// Loop is safe here because:
// - (offset + buffered + n) is greater than size, otherwise (cap < n)
// would be false:
// size = offset + buffered + freeSpace (cap)
// size' = offset + buffered + wantSpace (n)
// Since (cap < n) is true in the loop condition, size' is guaranteed
// to be greater => no infinite loop.
size = ceilPowerOfTwo(nextOffset + buffered + n)
nextOffset = reserve(w.state, size)
cap = size - nextOffset - buffered
}
if size < len(w.raw) {
panic("wsutil: buffer grow leads to its reduce")
}
if size == len(w.raw) {
return
}
p := make([]byte, size)
copy(p[nextOffset-prevOffset:], w.raw[:prevOffset+buffered])
w.raw = p
w.buf = w.raw[nextOffset:]
}
// WriteThrough writes data bypassing the buffer.
// Note that Writer's buffer must be empty before calling WriteThrough().
func (w *Writer) WriteThrough(p []byte) (n int, err error) {
@@ -305,13 +384,37 @@ func (w *Writer) WriteThrough(p []byte) (n int, err error) {
return 0, ErrNotEmpty
}
w.err = writeFrame(w.dest, w.state, w.opCode(), false, p)
var frame ws.Frame
frame.Header = ws.Header{
OpCode: w.opCode(),
Fin: false,
Length: int64(len(p)),
}
for _, x := range w.extensions {
frame.Header, err = x.SetBits(frame.Header)
if err != nil {
return 0, err
}
}
if w.state.ClientSide() {
// Should copy bytes to prevent corruption of caller data.
payload := pbytes.GetLen(len(p))
defer pbytes.Put(payload)
copy(payload, p)
frame.Payload = payload
frame = ws.MaskFrameInPlace(frame)
} else {
frame.Payload = p
}
w.err = ws.WriteFrame(w.dest, frame)
if w.err == nil {
n = len(p)
}
w.dirty = true
w.fragmented = true
w.fseq++
return n, w.err
}
@@ -321,7 +424,11 @@ func (w *Writer) ReadFrom(src io.Reader) (n int64, err error) {
var nn int
for err == nil {
if w.Available() == 0 {
err = w.FlushFragment()
if w.noFlush {
w.Grow(w.Buffered()) // Twice bigger.
} else {
err = w.FlushFragment()
}
continue
}
@@ -367,7 +474,7 @@ func (w *Writer) Flush() error {
w.err = w.flushFragment(true)
w.n = 0
w.dirty = false
w.fragmented = false
w.fseq = 0
return w.err
}
@@ -381,35 +488,49 @@ func (w *Writer) FlushFragment() error {
w.err = w.flushFragment(false)
w.n = 0
w.fragmented = true
w.fseq++
return w.err
}
func (w *Writer) flushFragment(fin bool) error {
frame := ws.NewFrame(w.opCode(), fin, w.buf[:w.n])
func (w *Writer) flushFragment(fin bool) (err error) {
var (
payload = w.buf[:w.n]
header = ws.Header{
OpCode: w.opCode(),
Fin: fin,
Length: int64(len(payload)),
}
)
for _, ext := range w.extensions {
header, err = ext.SetBits(header)
if err != nil {
return err
}
}
if w.state.ClientSide() {
frame = ws.MaskFrameInPlace(frame)
header.Masked = true
header.Mask = ws.NewMask()
ws.Cipher(payload, header.Mask, 0)
}
// Write header to the header segment of the raw buffer.
head := len(w.raw) - len(w.buf)
offset := head - ws.HeaderSize(frame.Header)
var (
offset = len(w.raw) - len(w.buf)
skip = offset - ws.HeaderSize(header)
)
buf := bytesWriter{
buf: w.raw[offset:head],
buf: w.raw[skip:offset],
}
if err := ws.WriteHeader(&buf, frame.Header); err != nil {
if err := ws.WriteHeader(&buf, header); err != nil {
// Must never be reached.
panic("dump header error: " + err.Error())
}
_, err := w.dest.Write(w.raw[offset : head+w.n])
_, err = w.dest.Write(w.raw[skip : offset+w.n])
return err
}
func (w *Writer) opCode() ws.OpCode {
if w.fragmented {
if w.fseq > 0 {
return ws.OpContinuation
}
return w.op
@@ -448,3 +569,31 @@ func writeFrame(w io.Writer, s ws.State, op ws.OpCode, fin bool, p []byte) error
return ws.WriteFrame(w, frame)
}
// reserve calculates number of bytes need to be reserved for frame header.
//
// Note that instead of ws.HeaderSize() it does calculation based on the buffer
// size, not the payload size.
func reserve(state ws.State, n int) (offset int) {
var mask int
if state.ClientSide() {
mask = 4
}
switch {
case n <= int(len7)+mask+2:
return mask + 2
case n <= int(len16)+mask+4:
return mask + 4
default:
return mask + 10
}
}
// headerSize returns number of bytes needed to encode header of a frame with
// given state and length.
func headerSize(s ws.State, n int) int {
return ws.HeaderSize(ws.Header{
Length: int64(n),
Masked: s.ClientSide(),
})
}

View File

@@ -3,54 +3,54 @@ Package wsutil provides utilities for working with WebSocket protocol.
Overview:
// Read masked text message from peer and check utf8 encoding.
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
// Read masked text message from peer and check utf8 encoding.
header, err := ws.ReadHeader(conn)
if err != nil {
// handle err
}
// Prepare to read payload.
r := io.LimitReader(conn, header.Length)
r = wsutil.NewCipherReader(r, header.Mask)
r = wsutil.NewUTF8Reader(r)
// Prepare to read payload.
r := io.LimitReader(conn, header.Length)
r = wsutil.NewCipherReader(r, header.Mask)
r = wsutil.NewUTF8Reader(r)
payload, err := ioutil.ReadAll(r)
if err != nil {
// handle err
}
payload, err := ioutil.ReadAll(r)
if err != nil {
// handle err
}
You could get the same behavior using just `wsutil.Reader`:
r := wsutil.Reader{
Source: conn,
CheckUTF8: true,
}
r := wsutil.Reader{
Source: conn,
CheckUTF8: true,
}
payload, err := ioutil.ReadAll(r)
if err != nil {
// handle err
}
payload, err := ioutil.ReadAll(r)
if err != nil {
// handle err
}
Or even simplest:
payload, err := wsutil.ReadClientText(conn)
if err != nil {
// handle err
}
payload, err := wsutil.ReadClientText(conn)
if err != nil {
// handle err
}
Package is also exports tools for buffered writing:
// Create buffered writer, that will buffer output bytes and send them as
// 128-length fragments (with exception on large writes, see the doc).
writer := wsutil.NewWriterSize(conn, ws.StateServerSide, ws.OpText, 128)
// Create buffered writer, that will buffer output bytes and send them as
// 128-length fragments (with exception on large writes, see the doc).
writer := wsutil.NewWriterSize(conn, ws.StateServerSide, ws.OpText, 128)
_, err := io.CopyN(writer, rand.Reader, 100)
if err == nil {
err = writer.Flush()
}
if err != nil {
// handle error
}
_, err := io.CopyN(writer, rand.Reader, 100)
if err == nil {
err = writer.Flush()
}
if err != nil {
// handle error
}
For more utils and helpers see the documentation.
*/