Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rfc7692 context-takeover implemention #342

Open
wants to merge 70 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
8a8329b
modify: header option
Jan 17, 2018
053a620
add: validation
Jan 18, 2018
785fa70
add server header option
Jan 21, 2018
9177fe8
mod: comment out
Jan 21, 2018
a2b4877
Merge branch 'feature/add_extension_header' into feature/impl_decompr…
Jan 21, 2018
e0da4e3
mod: .gitignore
Jan 21, 2018
36c4397
impl: decompressContextTakeover
Jan 24, 2018
a366cdf
impl: compressContextTakeover
Jan 24, 2018
f7abc95
mod: method args name
Jan 24, 2018
8ddcb40
Merge pull request #4 from smith-30/feature/impl_compressContextTakeover
smith-30 Jan 24, 2018
eec63bf
mod: split dict to rx and tx
Jan 24, 2018
ad97036
Merge pull request #6 from smith-30/feature/split_dict
smith-30 Jan 24, 2018
dfe35b7
add: context-takeover option to client
Jan 24, 2018
538a96d
Merge pull request #7 from smith-30/feature/client_context_takeover
smith-30 Jan 24, 2018
cecdf7c
mod: remove mutex.
Jan 24, 2018
bf99740
Merge pull request #8 from smith-30/feature/remove_dict_lock
smith-30 Jan 24, 2018
80a8097
mod: add dict process
Jan 25, 2018
67b9939
Merge pull request #10 from smith-30/feature/mod_add_dict
smith-30 Jan 25, 2018
70abf72
mod: .gitignore to ignore .test file
Jan 25, 2018
e873eb1
mod: test and write compression
Jan 26, 2018
d0e8769
Merge pull request #11 from smith-30/feature/add_test
smith-30 Jan 26, 2018
e2dd00d
mod: dict strategy
Jan 29, 2018
a9475f2
mod: remove judge dict nil
Jan 29, 2018
f2a68e2
add: call compress method test
Jan 30, 2018
2472e6d
upgrade: use pool for writerdict
Jan 30, 2018
b2e3d3e
Merge pull request #12 from smith-30/feature/upgrade_writer
smith-30 Jan 30, 2018
fededdd
mod: detach compressContextTakeover method and add debug comment.
Jan 31, 2018
62df16a
mod: compressContextTakeover method.
Jan 31, 2018
ee46f85
add: comment
Jan 31, 2018
fb7d67a
mod: fmt variables of conn.go
Feb 1, 2018
f8b4a0f
mod: flate.writer for context-takeover
Feb 1, 2018
b822eaa
Merge pull request #13 from smith-30/feature/upgrade_writer
smith-30 Feb 1, 2018
3452ab8
mod: client & server setting for context-takeover
Feb 1, 2018
ee5a233
Merge pull request #14 from smith-30/feature/upgrade_writer
smith-30 Feb 1, 2018
b9e2343
mod: set compressionLevel to conn, if EnabeleCompression
Feb 1, 2018
aca4275
Merge branch 'feature/upgrade_writer' into feature/context-takeover
Feb 1, 2018
e7575e2
upgrade: compressContextTakeover reader
Feb 1, 2018
83a1338
mod: NextReader if compress
Feb 1, 2018
7c6832c
mod: NextWriter if compress
Feb 1, 2018
8e146c3
mod: remove unnecessary field
Feb 1, 2018
c830889
upgrade: TestFraming
Feb 2, 2018
e79bb70
upgrade: add context-takeover test to client_server_test
Feb 2, 2018
4131adc
upgrade: remove readBench, it read blank data
Feb 2, 2018
1bf0779
Merge branch 'master' into feature/context-takeover
Feb 2, 2018
df2a0cc
mod: comment
Feb 7, 2018
6282a7b
mod: doc.go
Feb 7, 2018
e9a52af
mod: unnecessary format change
Feb 9, 2018
f68770a
mod: unnecessary format change
Feb 9, 2018
7fa60f8
mod: gitignore
Mar 5, 2018
7ce5144
Merge branch 'master' of github.com:gorilla/websocket into upstream
Mar 5, 2018
d0111e6
Simplify echo example client (#349)
Feb 19, 2018
5241e53
Use latest patch releases of Go
AlekSi Feb 15, 2018
3ab5e92
Travis config: add Go 1.10.x, revert 1.4.x to 1.4
garyburd Feb 19, 2018
c13439f
Modify http status code to variable
KimMachineGun Feb 26, 2018
416a1d5
add newline and remove extra space
carterjones Mar 4, 2018
af46d4f
mod: modification of review
Mar 5, 2018
1727e34
resolve: conflict
Mar 5, 2018
360a199
mod: doc.go
Mar 5, 2018
658a2fb
temp: autobahn/server.go
Apr 1, 2018
811803b
add: comment
Apr 6, 2018
6caf089
mod: examples/autobahn/server.go to pass build go 1.4, 1.5
Apr 6, 2018
cd973fd
mod: pointed out part
Apr 20, 2018
b27407e
mod: no need to change line feed
Apr 20, 2018
e82d2a9
mod: fmt
Apr 23, 2018
6f489db
merge: v1.4.0
Sep 11, 2018
89ee8d4
mod: test
Sep 11, 2018
a8d45c1
merge: master
Feb 2, 2019
62bb07b
add: comment
Feb 3, 2019
985aed2
mod: doc
Feb 3, 2019
4f8acdc
Merge branch 'main' into feature/context-takeover
coreydaley Aug 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package websocket

import (
"bytes"
"compress/flate"
"crypto/tls"
"errors"
"io"
Expand Down Expand Up @@ -72,16 +73,19 @@ 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 specify if the server should attempt to negotiate per
// message compression (RFC 7692).
EnableCompression bool

// Jar specifies the cookie jar.
// If Jar is nil, cookies are not sent in requests and ignored
// in responses.
Jar http.CookieJar

// AllowClientContextTakeover specifies whether the server will negotiate client context
// takeover for per message compression. Context takeover improves compression at the
// the cost of using more memory.
AllowClientContextTakeover bool
}

var errMalformedURL = errors.New("malformed ws or wss URL")
Expand Down Expand Up @@ -200,8 +204,11 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
}
}

if d.EnableCompression {
req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
switch {
case d.EnableCompression && d.AllowClientContextTakeover:
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_max_window_bits=15; client_max_window_bits=15")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the significance of the bit size that's been chosen here?

case d.EnableCompression:
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
}

var deadline time.Time
Expand Down Expand Up @@ -311,13 +318,24 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
if ext[""] != "permessage-deflate" {
continue
}
_, snct := ext["server_no_context_takeover"]
_, cnct := ext["client_no_context_takeover"]
if !snct || !cnct {
return nil, resp, errInvalidCompression

_, cmwb := ext["client_max_window_bits"]
_, smwb := ext["server_max_window_bits"]

switch {
case cmwb && smwb:
var wf contextTakeoverWriterFactory
conn.newCompressionWriter = wf.newCompressionWriter

var rf contextTakeoverReaderFactory
fr := flate.NewReader(nil)
rf.fr = fr
conn.newDecompressionReader = rf.newDeCompressionReader
default:
conn.newCompressionWriter = compressNoContextTakeover
conn.newDecompressionReader = decompressNoContextTakeover
}
conn.newCompressionWriter = compressNoContextTakeover
conn.newDecompressionReader = decompressNoContextTakeover

break
}

Expand Down
109 changes: 89 additions & 20 deletions client_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ var cstDialer = Dialer{
HandshakeTimeout: 30 * time.Second,
}

type cstHandler struct{ *testing.T }
type cstHandlerConfig struct {
contextTakeover bool
}

type cstHandler struct {
*testing.T
cstHandlerConfig
}

type cstServer struct {
*httptest.Server
Expand All @@ -53,17 +60,17 @@ const (
cstRequestURI = cstPath + "?" + cstRawQuery
)

func newServer(t *testing.T) *cstServer {
func newServer(t *testing.T, c cstHandlerConfig) *cstServer {
var s cstServer
s.Server = httptest.NewServer(cstHandler{t})
s.Server = httptest.NewServer(cstHandler{t, c})
s.Server.URL += cstRequestURI
s.URL = makeWsProto(s.Server.URL)
return &s
}

func newTLSServer(t *testing.T) *cstServer {
func newTLSServer(t *testing.T, c cstHandlerConfig) *cstServer {
var s cstServer
s.Server = httptest.NewTLSServer(cstHandler{t})
s.Server = httptest.NewTLSServer(cstHandler{t, c})
s.Server.URL += cstRequestURI
s.URL = makeWsProto(s.Server.URL)
return &s
Expand All @@ -86,6 +93,9 @@ func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, "bad protocol", http.StatusBadRequest)
return
}
if t.contextTakeover {
cstUpgrader.AllowServerContextTakeover = true
}
ws, err := cstUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}})
if err != nil {
t.Logf("Upgrade: %v", err)
Expand Down Expand Up @@ -116,6 +126,28 @@ func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.Logf("Close: %v", err)
return
}

// for multipleSendRecv when context takeover.
if t.contextTakeover {
op, rd, err := ws.NextReader()
if err != nil {
t.Logf("NextReader: %v", err)
return
}
wr, err := ws.NextWriter(op)
if err != nil {
t.Logf("NextWriter: %v", err)
return
}
if _, err = io.Copy(wr, rd); err != nil {
t.Logf("NextWriter: %v", err)
return
}
if err := wr.Close(); err != nil {
t.Logf("Close: %v", err)
return
}
}
}

func makeWsProto(s string) string {
Expand All @@ -142,9 +174,30 @@ func sendRecv(t *testing.T, ws *Conn) {
}
}

func multipleSendRecv(t *testing.T, ws *Conn) {
for _, message := range []string{"Hello World", "Can you read message?"} {
if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatalf("SetWriteDeadline: %v", err)
}
if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil {
t.Fatalf("WriteMessage: %v", err)
}
if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatalf("SetReadDeadline: %v", err)
}
_, p, err := ws.ReadMessage()
if err != nil {
t.Fatalf("ReadMessage: %v", err)
}
if string(p) != message {
t.Fatalf("message=%s, want %s", p, message)
}
}
}

func TestProxyDial(t *testing.T) {

s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

surl, _ := url.Parse(s.Server.URL)
Expand Down Expand Up @@ -181,7 +234,7 @@ func TestProxyDial(t *testing.T) {
}

func TestProxyAuthorizationDial(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

surl, _ := url.Parse(s.Server.URL)
Expand Down Expand Up @@ -221,7 +274,7 @@ func TestProxyAuthorizationDial(t *testing.T) {
}

func TestDial(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

ws, _, err := cstDialer.Dial(s.URL, nil)
Expand All @@ -233,7 +286,7 @@ func TestDial(t *testing.T) {
}

func TestDialCookieJar(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

jar, _ := cookiejar.New(nil)
Expand Down Expand Up @@ -281,7 +334,7 @@ func TestDialCookieJar(t *testing.T) {
}

func TestDialTLS(t *testing.T) {
s := newTLSServer(t)
s := newTLSServer(t, cstHandlerConfig{})
defer s.Close()

certs := x509.NewCertPool()
Expand All @@ -307,7 +360,7 @@ func TestDialTLS(t *testing.T) {

func xTestDialTLSBadCert(t *testing.T) {
// This test is deactivated because of noisy logging from the net/http package.
s := newTLSServer(t)
s := newTLSServer(t, cstHandlerConfig{})
defer s.Close()

ws, _, err := cstDialer.Dial(s.URL, nil)
Expand All @@ -318,7 +371,7 @@ func xTestDialTLSBadCert(t *testing.T) {
}

func TestDialTLSNoVerify(t *testing.T) {
s := newTLSServer(t)
s := newTLSServer(t, cstHandlerConfig{})
defer s.Close()

d := cstDialer
Expand All @@ -332,7 +385,7 @@ func TestDialTLSNoVerify(t *testing.T) {
}

func TestDialTimeout(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

d := cstDialer
Expand All @@ -345,7 +398,7 @@ func TestDialTimeout(t *testing.T) {
}

func TestDialBadScheme(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

ws, _, err := cstDialer.Dial(s.Server.URL, nil)
Expand All @@ -356,7 +409,7 @@ func TestDialBadScheme(t *testing.T) {
}

func TestDialBadOrigin(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}})
Expand All @@ -373,7 +426,7 @@ func TestDialBadOrigin(t *testing.T) {
}

func TestDialBadHeader(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

for _, k := range []string{"Upgrade",
Expand Down Expand Up @@ -420,7 +473,7 @@ func TestBadMethod(t *testing.T) {
}

func TestHandshake(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {s.URL}})
Expand Down Expand Up @@ -482,7 +535,7 @@ func TestRespOnBadHandshake(t *testing.T) {
// TestHostHeader confirms that the host header provided in the call to Dial is
// sent to the server.
func TestHostHeader(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

specifiedHost := make(chan string, 1)
Expand All @@ -509,7 +562,7 @@ func TestHostHeader(t *testing.T) {
}

func TestDialCompression(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

dialer := cstDialer
Expand All @@ -522,8 +575,24 @@ func TestDialCompression(t *testing.T) {
sendRecv(t, ws)
}

func TestDialCompressionOfContextTakeover(t *testing.T) {
s := newServer(t, cstHandlerConfig{true})
defer s.Close()

dialer := cstDialer
dialer.EnableCompression = true
dialer.AllowClientContextTakeover = true
ws, _, err := dialer.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()

multipleSendRecv(t, ws)
}

func TestSocksProxyDial(t *testing.T) {
s := newServer(t)
s := newServer(t, cstHandlerConfig{})
defer s.Close()

proxyListener, err := net.Listen("tcp", "127.0.0.1:0")
Expand Down
Loading