You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
3.4 KiB
182 lines
3.4 KiB
3 years ago
|
// +build !js
|
||
|
|
||
|
package websocket
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/klauspost/compress/flate"
|
||
|
)
|
||
|
|
||
|
func (m CompressionMode) opts() *compressionOptions {
|
||
|
return &compressionOptions{
|
||
|
clientNoContextTakeover: m == CompressionNoContextTakeover,
|
||
|
serverNoContextTakeover: m == CompressionNoContextTakeover,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type compressionOptions struct {
|
||
|
clientNoContextTakeover bool
|
||
|
serverNoContextTakeover bool
|
||
|
}
|
||
|
|
||
|
func (copts *compressionOptions) setHeader(h http.Header) {
|
||
|
s := "permessage-deflate"
|
||
|
if copts.clientNoContextTakeover {
|
||
|
s += "; client_no_context_takeover"
|
||
|
}
|
||
|
if copts.serverNoContextTakeover {
|
||
|
s += "; server_no_context_takeover"
|
||
|
}
|
||
|
h.Set("Sec-WebSocket-Extensions", s)
|
||
|
}
|
||
|
|
||
|
// These bytes are required to get flate.Reader to return.
|
||
|
// They are removed when sending to avoid the overhead as
|
||
|
// WebSocket framing tell's when the message has ended but then
|
||
|
// we need to add them back otherwise flate.Reader keeps
|
||
|
// trying to return more bytes.
|
||
|
const deflateMessageTail = "\x00\x00\xff\xff"
|
||
|
|
||
|
type trimLastFourBytesWriter struct {
|
||
|
w io.Writer
|
||
|
tail []byte
|
||
|
}
|
||
|
|
||
|
func (tw *trimLastFourBytesWriter) reset() {
|
||
|
if tw != nil && tw.tail != nil {
|
||
|
tw.tail = tw.tail[:0]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (tw *trimLastFourBytesWriter) Write(p []byte) (int, error) {
|
||
|
if tw.tail == nil {
|
||
|
tw.tail = make([]byte, 0, 4)
|
||
|
}
|
||
|
|
||
|
extra := len(tw.tail) + len(p) - 4
|
||
|
|
||
|
if extra <= 0 {
|
||
|
tw.tail = append(tw.tail, p...)
|
||
|
return len(p), nil
|
||
|
}
|
||
|
|
||
|
// Now we need to write as many extra bytes as we can from the previous tail.
|
||
|
if extra > len(tw.tail) {
|
||
|
extra = len(tw.tail)
|
||
|
}
|
||
|
if extra > 0 {
|
||
|
_, err := tw.w.Write(tw.tail[:extra])
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
// Shift remaining bytes in tail over.
|
||
|
n := copy(tw.tail, tw.tail[extra:])
|
||
|
tw.tail = tw.tail[:n]
|
||
|
}
|
||
|
|
||
|
// If p is less than or equal to 4 bytes,
|
||
|
// all of it is is part of the tail.
|
||
|
if len(p) <= 4 {
|
||
|
tw.tail = append(tw.tail, p...)
|
||
|
return len(p), nil
|
||
|
}
|
||
|
|
||
|
// Otherwise, only the last 4 bytes are.
|
||
|
tw.tail = append(tw.tail, p[len(p)-4:]...)
|
||
|
|
||
|
p = p[:len(p)-4]
|
||
|
n, err := tw.w.Write(p)
|
||
|
return n + 4, err
|
||
|
}
|
||
|
|
||
|
var flateReaderPool sync.Pool
|
||
|
|
||
|
func getFlateReader(r io.Reader, dict []byte) io.Reader {
|
||
|
fr, ok := flateReaderPool.Get().(io.Reader)
|
||
|
if !ok {
|
||
|
return flate.NewReaderDict(r, dict)
|
||
|
}
|
||
|
fr.(flate.Resetter).Reset(r, dict)
|
||
|
return fr
|
||
|
}
|
||
|
|
||
|
func putFlateReader(fr io.Reader) {
|
||
|
flateReaderPool.Put(fr)
|
||
|
}
|
||
|
|
||
|
type slidingWindow struct {
|
||
|
buf []byte
|
||
|
}
|
||
|
|
||
|
var swPoolMu sync.RWMutex
|
||
|
var swPool = map[int]*sync.Pool{}
|
||
|
|
||
|
func slidingWindowPool(n int) *sync.Pool {
|
||
|
swPoolMu.RLock()
|
||
|
p, ok := swPool[n]
|
||
|
swPoolMu.RUnlock()
|
||
|
if ok {
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
p = &sync.Pool{}
|
||
|
|
||
|
swPoolMu.Lock()
|
||
|
swPool[n] = p
|
||
|
swPoolMu.Unlock()
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (sw *slidingWindow) init(n int) {
|
||
|
if sw.buf != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if n == 0 {
|
||
|
n = 32768
|
||
|
}
|
||
|
|
||
|
p := slidingWindowPool(n)
|
||
|
buf, ok := p.Get().([]byte)
|
||
|
if ok {
|
||
|
sw.buf = buf[:0]
|
||
|
} else {
|
||
|
sw.buf = make([]byte, 0, n)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (sw *slidingWindow) close() {
|
||
|
if sw.buf == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
swPoolMu.Lock()
|
||
|
swPool[cap(sw.buf)].Put(sw.buf)
|
||
|
swPoolMu.Unlock()
|
||
|
sw.buf = nil
|
||
|
}
|
||
|
|
||
|
func (sw *slidingWindow) write(p []byte) {
|
||
|
if len(p) >= cap(sw.buf) {
|
||
|
sw.buf = sw.buf[:cap(sw.buf)]
|
||
|
p = p[len(p)-cap(sw.buf):]
|
||
|
copy(sw.buf, p)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
left := cap(sw.buf) - len(sw.buf)
|
||
|
if left < len(p) {
|
||
|
// We need to shift spaceNeeded bytes from the end to make room for p at the end.
|
||
|
spaceNeeded := len(p) - left
|
||
|
copy(sw.buf, sw.buf[spaceNeeded:])
|
||
|
sw.buf = sw.buf[:len(sw.buf)-spaceNeeded]
|
||
|
}
|
||
|
|
||
|
sw.buf = append(sw.buf, p...)
|
||
|
}
|