From 83bcf9a62044611ded135970c66d47e37fc29c72 Mon Sep 17 00:00:00 2001 From: moriya Date: Sat, 22 Apr 2023 14:14:06 +0900 Subject: [PATCH 1/4] use gobwas/ws instead of gorilla/websocket --- go.mod | 6 +- go.sum | 12 +- vendor/github.com/gobwas/httphead/LICENSE | 21 + vendor/github.com/gobwas/httphead/README.md | 63 + vendor/github.com/gobwas/httphead/cookie.go | 200 +++ vendor/github.com/gobwas/httphead/head.go | 275 ++++ vendor/github.com/gobwas/httphead/httphead.go | 331 +++++ vendor/github.com/gobwas/httphead/lexer.go | 360 +++++ vendor/github.com/gobwas/httphead/octet.go | 83 ++ vendor/github.com/gobwas/httphead/option.go | 193 +++ vendor/github.com/gobwas/httphead/writer.go | 101 ++ vendor/github.com/gobwas/pool/LICENSE | 21 + vendor/github.com/gobwas/pool/README.md | 107 ++ vendor/github.com/gobwas/pool/generic.go | 87 ++ .../gobwas/pool/internal/pmath/pmath.go | 65 + vendor/github.com/gobwas/pool/option.go | 43 + .../github.com/gobwas/pool/pbufio/pbufio.go | 106 ++ .../gobwas/pool/pbufio/pbufio_go110.go | 13 + .../gobwas/pool/pbufio/pbufio_go19.go | 27 + .../github.com/gobwas/pool/pbytes/pbytes.go | 24 + vendor/github.com/gobwas/pool/pbytes/pool.go | 59 + .../gobwas/pool/pbytes/pool_sanitize.go | 121 ++ vendor/github.com/gobwas/pool/pool.go | 25 + vendor/github.com/gobwas/ws/.gitignore | 5 + vendor/github.com/gobwas/ws/LICENSE | 21 + vendor/github.com/gobwas/ws/Makefile | 54 + vendor/github.com/gobwas/ws/README.md | 541 ++++++++ vendor/github.com/gobwas/ws/check.go | 145 ++ vendor/github.com/gobwas/ws/cipher.go | 61 + vendor/github.com/gobwas/ws/dialer.go | 566 ++++++++ .../github.com/gobwas/ws/dialer_tls_go17.go | 35 + .../github.com/gobwas/ws/dialer_tls_go18.go | 10 + vendor/github.com/gobwas/ws/doc.go | 81 ++ vendor/github.com/gobwas/ws/errors.go | 59 + vendor/github.com/gobwas/ws/frame.go | 420 ++++++ vendor/github.com/gobwas/ws/http.go | 503 +++++++ vendor/github.com/gobwas/ws/nonce.go | 78 ++ vendor/github.com/gobwas/ws/read.go | 147 ++ vendor/github.com/gobwas/ws/server.go | 663 +++++++++ vendor/github.com/gobwas/ws/util.go | 199 +++ vendor/github.com/gobwas/ws/util_purego.go | 11 + vendor/github.com/gobwas/ws/util_unsafe.go | 21 + vendor/github.com/gobwas/ws/write.go | 104 ++ vendor/github.com/gobwas/ws/wsutil/cipher.go | 72 + vendor/github.com/gobwas/ws/wsutil/dialer.go | 147 ++ .../github.com/gobwas/ws/wsutil/extenstion.go | 31 + vendor/github.com/gobwas/ws/wsutil/handler.go | 219 +++ vendor/github.com/gobwas/ws/wsutil/helper.go | 279 ++++ vendor/github.com/gobwas/ws/wsutil/reader.go | 289 ++++ .../github.com/gobwas/ws/wsutil/upgrader.go | 68 + vendor/github.com/gobwas/ws/wsutil/utf8.go | 140 ++ vendor/github.com/gobwas/ws/wsutil/writer.go | 599 ++++++++ vendor/github.com/gobwas/ws/wsutil/wsutil.go | 57 + .../github.com/gorilla/websocket/.gitignore | 25 - vendor/github.com/gorilla/websocket/AUTHORS | 9 - vendor/github.com/gorilla/websocket/LICENSE | 22 - vendor/github.com/gorilla/websocket/README.md | 64 - vendor/github.com/gorilla/websocket/client.go | 395 ------ .../gorilla/websocket/client_clone.go | 16 - .../gorilla/websocket/client_clone_legacy.go | 38 - .../gorilla/websocket/compression.go | 148 -- vendor/github.com/gorilla/websocket/conn.go | 1201 ----------------- .../gorilla/websocket/conn_write.go | 15 - .../gorilla/websocket/conn_write_legacy.go | 18 - vendor/github.com/gorilla/websocket/doc.go | 227 ---- vendor/github.com/gorilla/websocket/join.go | 42 - vendor/github.com/gorilla/websocket/json.go | 60 - vendor/github.com/gorilla/websocket/mask.go | 54 - .../github.com/gorilla/websocket/mask_safe.go | 15 - .../github.com/gorilla/websocket/prepared.go | 102 -- vendor/github.com/gorilla/websocket/proxy.go | 77 -- vendor/github.com/gorilla/websocket/server.go | 363 ----- vendor/github.com/gorilla/websocket/trace.go | 19 - .../github.com/gorilla/websocket/trace_17.go | 12 - vendor/github.com/gorilla/websocket/util.go | 283 ---- .../gorilla/websocket/x_net_proxy.go | 473 ------- vendor/golang.org/x/sys/execabs/execabs.go | 2 +- .../golang.org/x/sys/execabs/execabs_go118.go | 6 + .../golang.org/x/sys/execabs/execabs_go119.go | 4 + vendor/golang.org/x/sys/unix/ioctl.go | 17 +- vendor/golang.org/x/sys/unix/ioctl_zos.go | 8 +- vendor/golang.org/x/sys/unix/ptrace_darwin.go | 6 + vendor/golang.org/x/sys/unix/ptrace_ios.go | 6 + vendor/golang.org/x/sys/unix/syscall_aix.go | 5 +- vendor/golang.org/x/sys/unix/syscall_bsd.go | 3 +- .../golang.org/x/sys/unix/syscall_darwin.go | 12 +- .../x/sys/unix/syscall_darwin_amd64.go | 1 + .../x/sys/unix/syscall_darwin_arm64.go | 1 + .../x/sys/unix/syscall_dragonfly.go | 1 + .../golang.org/x/sys/unix/syscall_freebsd.go | 43 +- .../x/sys/unix/syscall_freebsd_386.go | 17 +- .../x/sys/unix/syscall_freebsd_amd64.go | 17 +- .../x/sys/unix/syscall_freebsd_arm.go | 15 +- .../x/sys/unix/syscall_freebsd_arm64.go | 15 +- .../x/sys/unix/syscall_freebsd_riscv64.go | 15 +- vendor/golang.org/x/sys/unix/syscall_hurd.go | 8 + vendor/golang.org/x/sys/unix/syscall_linux.go | 36 +- .../golang.org/x/sys/unix/syscall_netbsd.go | 5 +- .../golang.org/x/sys/unix/syscall_openbsd.go | 1 + .../golang.org/x/sys/unix/syscall_solaris.go | 21 +- .../x/sys/unix/syscall_zos_s390x.go | 4 +- vendor/golang.org/x/sys/unix/zerrors_linux.go | 10 +- .../x/sys/unix/zptrace_armnn_linux.go | 8 +- .../x/sys/unix/zptrace_linux_arm64.go | 4 +- .../x/sys/unix/zptrace_mipsnn_linux.go | 8 +- .../x/sys/unix/zptrace_mipsnnle_linux.go | 8 +- .../x/sys/unix/zptrace_x86_linux.go | 8 +- .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 10 + .../x/sys/unix/zsyscall_aix_ppc64.go | 10 + .../x/sys/unix/zsyscall_aix_ppc64_gc.go | 7 + .../x/sys/unix/zsyscall_aix_ppc64_gccgo.go | 8 + .../x/sys/unix/zsyscall_darwin_amd64.go | 16 + .../x/sys/unix/zsyscall_darwin_arm64.go | 16 + .../x/sys/unix/zsyscall_dragonfly_amd64.go | 10 + .../x/sys/unix/zsyscall_freebsd_386.go | 20 + .../x/sys/unix/zsyscall_freebsd_amd64.go | 20 + .../x/sys/unix/zsyscall_freebsd_arm.go | 20 + .../x/sys/unix/zsyscall_freebsd_arm64.go | 20 + .../x/sys/unix/zsyscall_freebsd_riscv64.go | 20 + .../golang.org/x/sys/unix/zsyscall_linux.go | 10 + .../x/sys/unix/zsyscall_netbsd_386.go | 10 + .../x/sys/unix/zsyscall_netbsd_amd64.go | 10 + .../x/sys/unix/zsyscall_netbsd_arm.go | 10 + .../x/sys/unix/zsyscall_netbsd_arm64.go | 10 + .../x/sys/unix/zsyscall_openbsd_386.go | 8 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 8 + .../x/sys/unix/zsyscall_openbsd_arm.go | 8 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 8 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 8 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 8 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 8 + .../x/sys/unix/zsyscall_solaris_amd64.go | 11 + .../x/sys/unix/zsyscall_zos_s390x.go | 10 + .../x/sys/unix/ztypes_freebsd_386.go | 2 +- .../x/sys/unix/ztypes_freebsd_amd64.go | 2 +- .../x/sys/unix/ztypes_freebsd_arm.go | 2 +- .../x/sys/unix/ztypes_freebsd_arm64.go | 2 +- .../x/sys/unix/ztypes_freebsd_riscv64.go | 2 +- vendor/golang.org/x/sys/unix/ztypes_linux.go | 140 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 2 +- .../x/sys/unix/ztypes_linux_amd64.go | 2 +- .../golang.org/x/sys/unix/ztypes_linux_arm.go | 2 +- .../x/sys/unix/ztypes_linux_arm64.go | 2 +- .../x/sys/unix/ztypes_linux_loong64.go | 2 +- .../x/sys/unix/ztypes_linux_mips.go | 2 +- .../x/sys/unix/ztypes_linux_mips64.go | 2 +- .../x/sys/unix/ztypes_linux_mips64le.go | 2 +- .../x/sys/unix/ztypes_linux_mipsle.go | 2 +- .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 2 +- .../x/sys/unix/ztypes_linux_ppc64.go | 2 +- .../x/sys/unix/ztypes_linux_ppc64le.go | 2 +- .../x/sys/unix/ztypes_linux_riscv64.go | 2 +- .../x/sys/unix/ztypes_linux_s390x.go | 2 +- .../x/sys/unix/ztypes_linux_sparc64.go | 2 +- .../x/sys/windows/syscall_windows.go | 6 +- .../golang.org/x/sys/windows/types_windows.go | 85 ++ .../x/sys/windows/zsyscall_windows.go | 27 + vendor/modules.txt | 18 +- websocket/connection.go | 83 +- websocket/connection_test.go | 118 +- 160 files changed, 8811 insertions(+), 3971 deletions(-) create mode 100644 vendor/github.com/gobwas/httphead/LICENSE create mode 100644 vendor/github.com/gobwas/httphead/README.md create mode 100644 vendor/github.com/gobwas/httphead/cookie.go create mode 100644 vendor/github.com/gobwas/httphead/head.go create mode 100644 vendor/github.com/gobwas/httphead/httphead.go create mode 100644 vendor/github.com/gobwas/httphead/lexer.go create mode 100644 vendor/github.com/gobwas/httphead/octet.go create mode 100644 vendor/github.com/gobwas/httphead/option.go create mode 100644 vendor/github.com/gobwas/httphead/writer.go create mode 100644 vendor/github.com/gobwas/pool/LICENSE create mode 100644 vendor/github.com/gobwas/pool/README.md create mode 100644 vendor/github.com/gobwas/pool/generic.go create mode 100644 vendor/github.com/gobwas/pool/internal/pmath/pmath.go create mode 100644 vendor/github.com/gobwas/pool/option.go create mode 100644 vendor/github.com/gobwas/pool/pbufio/pbufio.go create mode 100644 vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go create mode 100644 vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go create mode 100644 vendor/github.com/gobwas/pool/pbytes/pbytes.go create mode 100644 vendor/github.com/gobwas/pool/pbytes/pool.go create mode 100644 vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go create mode 100644 vendor/github.com/gobwas/pool/pool.go create mode 100644 vendor/github.com/gobwas/ws/.gitignore create mode 100644 vendor/github.com/gobwas/ws/LICENSE create mode 100644 vendor/github.com/gobwas/ws/Makefile create mode 100644 vendor/github.com/gobwas/ws/README.md create mode 100644 vendor/github.com/gobwas/ws/check.go create mode 100644 vendor/github.com/gobwas/ws/cipher.go create mode 100644 vendor/github.com/gobwas/ws/dialer.go create mode 100644 vendor/github.com/gobwas/ws/dialer_tls_go17.go create mode 100644 vendor/github.com/gobwas/ws/dialer_tls_go18.go create mode 100644 vendor/github.com/gobwas/ws/doc.go create mode 100644 vendor/github.com/gobwas/ws/errors.go create mode 100644 vendor/github.com/gobwas/ws/frame.go create mode 100644 vendor/github.com/gobwas/ws/http.go create mode 100644 vendor/github.com/gobwas/ws/nonce.go create mode 100644 vendor/github.com/gobwas/ws/read.go create mode 100644 vendor/github.com/gobwas/ws/server.go create mode 100644 vendor/github.com/gobwas/ws/util.go create mode 100644 vendor/github.com/gobwas/ws/util_purego.go create mode 100644 vendor/github.com/gobwas/ws/util_unsafe.go create mode 100644 vendor/github.com/gobwas/ws/write.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/cipher.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/dialer.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/extenstion.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/handler.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/helper.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/reader.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/upgrader.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/utf8.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/writer.go create mode 100644 vendor/github.com/gobwas/ws/wsutil/wsutil.go delete mode 100644 vendor/github.com/gorilla/websocket/.gitignore delete mode 100644 vendor/github.com/gorilla/websocket/AUTHORS delete mode 100644 vendor/github.com/gorilla/websocket/LICENSE delete mode 100644 vendor/github.com/gorilla/websocket/README.md delete mode 100644 vendor/github.com/gorilla/websocket/client.go delete mode 100644 vendor/github.com/gorilla/websocket/client_clone.go delete mode 100644 vendor/github.com/gorilla/websocket/client_clone_legacy.go delete mode 100644 vendor/github.com/gorilla/websocket/compression.go delete mode 100644 vendor/github.com/gorilla/websocket/conn.go delete mode 100644 vendor/github.com/gorilla/websocket/conn_write.go delete mode 100644 vendor/github.com/gorilla/websocket/conn_write_legacy.go delete mode 100644 vendor/github.com/gorilla/websocket/doc.go delete mode 100644 vendor/github.com/gorilla/websocket/join.go delete mode 100644 vendor/github.com/gorilla/websocket/json.go delete mode 100644 vendor/github.com/gorilla/websocket/mask.go delete mode 100644 vendor/github.com/gorilla/websocket/mask_safe.go delete mode 100644 vendor/github.com/gorilla/websocket/prepared.go delete mode 100644 vendor/github.com/gorilla/websocket/proxy.go delete mode 100644 vendor/github.com/gorilla/websocket/server.go delete mode 100644 vendor/github.com/gorilla/websocket/trace.go delete mode 100644 vendor/github.com/gorilla/websocket/trace_17.go delete mode 100644 vendor/github.com/gorilla/websocket/util.go delete mode 100644 vendor/github.com/gorilla/websocket/x_net_proxy.go diff --git a/go.mod b/go.mod index e8946386f4..a81a900c9a 100644 --- a/go.mod +++ b/go.mod @@ -14,13 +14,13 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/evanphx/json-patch/v5 v5.6.0 github.com/gobuffalo/flect v0.2.4 + github.com/gobwas/ws v1.2.0 github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.8 github.com/google/go-github/v27 v27.0.6 github.com/google/gofuzz v1.2.0 github.com/google/mako v0.0.0-20190821191249-122f8dcef9e3 github.com/google/uuid v1.3.0 - github.com/gorilla/websocket v1.4.2 github.com/hashicorp/golang-lru v0.5.4 github.com/kelseyhightower/envconfig v1.4.0 github.com/openzipkin/zipkin-go v0.3.0 @@ -72,6 +72,8 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.15 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -96,7 +98,7 @@ require ( go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 3188663f9e..7add89ed4d 100644 --- a/go.sum +++ b/go.sum @@ -173,6 +173,12 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.4 h1:BSYA8+T60cdyq+vynaSUjqSVI9mDEg9ZfQUXKmfjo4I= github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I= +github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -294,8 +300,6 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -673,8 +677,8 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/vendor/github.com/gobwas/httphead/LICENSE b/vendor/github.com/gobwas/httphead/LICENSE new file mode 100644 index 0000000000..274431766f --- /dev/null +++ b/vendor/github.com/gobwas/httphead/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gobwas/httphead/README.md b/vendor/github.com/gobwas/httphead/README.md new file mode 100644 index 0000000000..67a97fdbe9 --- /dev/null +++ b/vendor/github.com/gobwas/httphead/README.md @@ -0,0 +1,63 @@ +# httphead.[go](https://golang.org) + +[![GoDoc][godoc-image]][godoc-url] + +> Tiny HTTP header value parsing library in go. + +## Overview + +This library contains low-level functions for scanning HTTP RFC2616 compatible header value grammars. + +## Install + +```shell + go get github.com/gobwas/httphead +``` + +## Example + +The example below shows how multiple-choise HTTP header value could be parsed with this library: + +```go + options, ok := httphead.ParseOptions([]byte(`foo;bar=1,baz`), nil) + fmt.Println(options, ok) + // Output: [{foo map[bar:1]} {baz map[]}] true +``` + +The low-level example below shows how to optimize keys skipping and selection +of some key: + +```go + // The right part of full header line like: + // X-My-Header: key;foo=bar;baz,key;baz + header := []byte(`foo;a=0,foo;a=1,foo;a=2,foo;a=3`) + + // We want to search key "foo" with an "a" parameter that equal to "2". + var ( + foo = []byte(`foo`) + a = []byte(`a`) + v = []byte(`2`) + ) + var found bool + httphead.ScanOptions(header, func(i int, key, param, value []byte) Control { + if !bytes.Equal(key, foo) { + return ControlSkip + } + if !bytes.Equal(param, a) { + if bytes.Equal(value, v) { + // Found it! + found = true + return ControlBreak + } + return ControlSkip + } + return ControlContinue + }) +``` + +For more usage examples please see [docs][godoc-url] or package tests. + +[godoc-image]: https://godoc.org/github.com/gobwas/httphead?status.svg +[godoc-url]: https://godoc.org/github.com/gobwas/httphead +[travis-image]: https://travis-ci.org/gobwas/httphead.svg?branch=master +[travis-url]: https://travis-ci.org/gobwas/httphead diff --git a/vendor/github.com/gobwas/httphead/cookie.go b/vendor/github.com/gobwas/httphead/cookie.go new file mode 100644 index 0000000000..05c9a1fb6a --- /dev/null +++ b/vendor/github.com/gobwas/httphead/cookie.go @@ -0,0 +1,200 @@ +package httphead + +import ( + "bytes" +) + +// ScanCookie scans cookie pairs from data using DefaultCookieScanner.Scan() +// method. +func ScanCookie(data []byte, it func(key, value []byte) bool) bool { + return DefaultCookieScanner.Scan(data, it) +} + +// DefaultCookieScanner is a CookieScanner which is used by ScanCookie(). +// Note that it is intended to have the same behavior as http.Request.Cookies() +// has. +var DefaultCookieScanner = CookieScanner{} + +// CookieScanner contains options for scanning cookie pairs. +// See https://tools.ietf.org/html/rfc6265#section-4.1.1 +type CookieScanner struct { + // DisableNameValidation disables name validation of a cookie. If false, + // only RFC2616 "tokens" are accepted. + DisableNameValidation bool + + // DisableValueValidation disables value validation of a cookie. If false, + // only RFC6265 "cookie-octet" characters are accepted. + // + // Note that Strict option also affects validation of a value. + // + // If Strict is false, then scanner begins to allow space and comma + // characters inside the value for better compatibility with non standard + // cookies implementations. + DisableValueValidation bool + + // BreakOnPairError sets scanner to immediately return after first pair syntax + // validation error. + // If false, scanner will try to skip invalid pair bytes and go ahead. + BreakOnPairError bool + + // Strict enables strict RFC6265 mode scanning. It affects name and value + // validation, as also some other rules. + // If false, it is intended to bring the same behavior as + // http.Request.Cookies(). + Strict bool +} + +// Scan maps data to name and value pairs. Usually data represents value of the +// Cookie header. +func (c CookieScanner) Scan(data []byte, it func(name, value []byte) bool) bool { + lexer := &Scanner{data: data} + + const ( + statePair = iota + stateBefore + ) + + state := statePair + + for lexer.Buffered() > 0 { + switch state { + case stateBefore: + // Pairs separated by ";" and space, according to the RFC6265: + // cookie-pair *( ";" SP cookie-pair ) + // + // Cookie pairs MUST be separated by (";" SP). So our only option + // here is to fail as syntax error. + a, b := lexer.Peek2() + if a != ';' { + return false + } + + state = statePair + + advance := 1 + if b == ' ' { + advance++ + } else if c.Strict { + return false + } + + lexer.Advance(advance) + + case statePair: + if !lexer.FetchUntil(';') { + return false + } + + var value []byte + name := lexer.Bytes() + if i := bytes.IndexByte(name, '='); i != -1 { + value = name[i+1:] + name = name[:i] + } else if c.Strict { + if !c.BreakOnPairError { + goto nextPair + } + return false + } + + if !c.Strict { + trimLeft(name) + } + if !c.DisableNameValidation && !ValidCookieName(name) { + if !c.BreakOnPairError { + goto nextPair + } + return false + } + + if !c.Strict { + value = trimRight(value) + } + value = stripQuotes(value) + if !c.DisableValueValidation && !ValidCookieValue(value, c.Strict) { + if !c.BreakOnPairError { + goto nextPair + } + return false + } + + if !it(name, value) { + return true + } + + nextPair: + state = stateBefore + } + } + + return true +} + +// ValidCookieValue reports whether given value is a valid RFC6265 +// "cookie-octet" bytes. +// +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E +// ; US-ASCII characters excluding CTLs, +// ; whitespace DQUOTE, comma, semicolon, +// ; and backslash +// +// Note that the false strict parameter disables errors on space 0x20 and comma +// 0x2c. This could be useful to bring some compatibility with non-compliant +// clients/servers in the real world. +// It acts the same as standard library cookie parser if strict is false. +func ValidCookieValue(value []byte, strict bool) bool { + if len(value) == 0 { + return true + } + for _, c := range value { + switch c { + case '"', ';', '\\': + return false + case ',', ' ': + if strict { + return false + } + default: + if c <= 0x20 { + return false + } + if c >= 0x7f { + return false + } + } + } + return true +} + +// ValidCookieName reports wheter given bytes is a valid RFC2616 "token" bytes. +func ValidCookieName(name []byte) bool { + for _, c := range name { + if !OctetTypes[c].IsToken() { + return false + } + } + return true +} + +func stripQuotes(bts []byte) []byte { + if last := len(bts) - 1; last > 0 && bts[0] == '"' && bts[last] == '"' { + return bts[1:last] + } + return bts +} + +func trimLeft(p []byte) []byte { + var i int + for i < len(p) && OctetTypes[p[i]].IsSpace() { + i++ + } + return p[i:] +} + +func trimRight(p []byte) []byte { + j := len(p) + for j > 0 && OctetTypes[p[j-1]].IsSpace() { + j-- + } + return p[:j] +} diff --git a/vendor/github.com/gobwas/httphead/head.go b/vendor/github.com/gobwas/httphead/head.go new file mode 100644 index 0000000000..a50e907dd1 --- /dev/null +++ b/vendor/github.com/gobwas/httphead/head.go @@ -0,0 +1,275 @@ +package httphead + +import ( + "bufio" + "bytes" +) + +// Version contains protocol major and minor version. +type Version struct { + Major int + Minor int +} + +// RequestLine contains parameters parsed from the first request line. +type RequestLine struct { + Method []byte + URI []byte + Version Version +} + +// ResponseLine contains parameters parsed from the first response line. +type ResponseLine struct { + Version Version + Status int + Reason []byte +} + +// SplitRequestLine splits given slice of bytes into three chunks without +// parsing. +func SplitRequestLine(line []byte) (method, uri, version []byte) { + return split3(line, ' ') +} + +// ParseRequestLine parses http request line like "GET / HTTP/1.0". +func ParseRequestLine(line []byte) (r RequestLine, ok bool) { + var i int + for i = 0; i < len(line); i++ { + c := line[i] + if !OctetTypes[c].IsToken() { + if i > 0 && c == ' ' { + break + } + return + } + } + if i == len(line) { + return + } + + var proto []byte + r.Method = line[:i] + r.URI, proto = split2(line[i+1:], ' ') + if len(r.URI) == 0 { + return + } + if major, minor, ok := ParseVersion(proto); ok { + r.Version.Major = major + r.Version.Minor = minor + return r, true + } + + return r, false +} + +// SplitResponseLine splits given slice of bytes into three chunks without +// parsing. +func SplitResponseLine(line []byte) (version, status, reason []byte) { + return split3(line, ' ') +} + +// ParseResponseLine parses first response line into ResponseLine struct. +func ParseResponseLine(line []byte) (r ResponseLine, ok bool) { + var ( + proto []byte + status []byte + ) + proto, status, r.Reason = split3(line, ' ') + if major, minor, ok := ParseVersion(proto); ok { + r.Version.Major = major + r.Version.Minor = minor + } else { + return r, false + } + if n, ok := IntFromASCII(status); ok { + r.Status = n + } else { + return r, false + } + // TODO(gobwas): parse here r.Reason fot TEXT rule: + // TEXT = + return r, true +} + +var ( + httpVersion10 = []byte("HTTP/1.0") + httpVersion11 = []byte("HTTP/1.1") + httpVersionPrefix = []byte("HTTP/") +) + +// ParseVersion parses major and minor version of HTTP protocol. +// It returns parsed values and true if parse is ok. +func ParseVersion(bts []byte) (major, minor int, ok bool) { + switch { + case bytes.Equal(bts, httpVersion11): + return 1, 1, true + case bytes.Equal(bts, httpVersion10): + return 1, 0, true + case len(bts) < 8: + return + case !bytes.Equal(bts[:5], httpVersionPrefix): + return + } + + bts = bts[5:] + + dot := bytes.IndexByte(bts, '.') + if dot == -1 { + return + } + major, ok = IntFromASCII(bts[:dot]) + if !ok { + return + } + minor, ok = IntFromASCII(bts[dot+1:]) + if !ok { + return + } + + return major, minor, true +} + +// ReadLine reads line from br. It reads until '\n' and returns bytes without +// '\n' or '\r\n' at the end. +// It returns err if and only if line does not end in '\n'. Note that read +// bytes returned in any case of error. +// +// It is much like the textproto/Reader.ReadLine() except the thing that it +// returns raw bytes, instead of string. That is, it avoids copying bytes read +// from br. +// +// textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be +// safe with future I/O operations on br. +// +// We could control I/O operations on br and do not need to make additional +// copy for safety. +func ReadLine(br *bufio.Reader) ([]byte, error) { + var line []byte + for { + bts, err := br.ReadSlice('\n') + if err == bufio.ErrBufferFull { + // Copy bytes because next read will discard them. + line = append(line, bts...) + continue + } + // Avoid copy of single read. + if line == nil { + line = bts + } else { + line = append(line, bts...) + } + if err != nil { + return line, err + } + // Size of line is at least 1. + // In other case bufio.ReadSlice() returns error. + n := len(line) + // Cut '\n' or '\r\n'. + if n > 1 && line[n-2] == '\r' { + line = line[:n-2] + } else { + line = line[:n-1] + } + return line, nil + } +} + +// ParseHeaderLine parses HTTP header as key-value pair. It returns parsed +// values and true if parse is ok. +func ParseHeaderLine(line []byte) (k, v []byte, ok bool) { + colon := bytes.IndexByte(line, ':') + if colon == -1 { + return + } + k = trim(line[:colon]) + for _, c := range k { + if !OctetTypes[c].IsToken() { + return nil, nil, false + } + } + v = trim(line[colon+1:]) + return k, v, true +} + +// IntFromASCII converts ascii encoded decimal numeric value from HTTP entities +// to an integer. +func IntFromASCII(bts []byte) (ret int, ok bool) { + // ASCII numbers all start with the high-order bits 0011. + // If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those + // bits and interpret them directly as an integer. + var n int + if n = len(bts); n < 1 { + return 0, false + } + for i := 0; i < n; i++ { + if bts[i]&0xf0 != 0x30 { + return 0, false + } + ret += int(bts[i]&0xf) * pow(10, n-i-1) + } + return ret, true +} + +const ( + toLower = 'a' - 'A' // for use with OR. + toUpper = ^byte(toLower) // for use with AND. +) + +// CanonicalizeHeaderKey is like standard textproto/CanonicalMIMEHeaderKey, +// except that it operates with slice of bytes and modifies it inplace without +// copying. +func CanonicalizeHeaderKey(k []byte) { + upper := true + for i, c := range k { + if upper && 'a' <= c && c <= 'z' { + k[i] &= toUpper + } else if !upper && 'A' <= c && c <= 'Z' { + k[i] |= toLower + } + upper = c == '-' + } +} + +// pow for integers implementation. +// 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 { + if b&1 != 0 { + p *= a + } + b >>= 1 + a *= a + } + return p +} + +func split3(p []byte, sep byte) (p1, p2, p3 []byte) { + a := bytes.IndexByte(p, sep) + b := bytes.IndexByte(p[a+1:], sep) + if a == -1 || b == -1 { + return p, nil, nil + } + b += a + 1 + return p[:a], p[a+1 : b], p[b+1:] +} + +func split2(p []byte, sep byte) (p1, p2 []byte) { + i := bytes.IndexByte(p, sep) + if i == -1 { + return p, nil + } + return p[:i], p[i+1:] +} + +func trim(p []byte) []byte { + var i, j int + for i = 0; i < len(p) && (p[i] == ' ' || p[i] == '\t'); { + i++ + } + for j = len(p); j > i && (p[j-1] == ' ' || p[j-1] == '\t'); { + j-- + } + return p[i:j] +} diff --git a/vendor/github.com/gobwas/httphead/httphead.go b/vendor/github.com/gobwas/httphead/httphead.go new file mode 100644 index 0000000000..2387e8033c --- /dev/null +++ b/vendor/github.com/gobwas/httphead/httphead.go @@ -0,0 +1,331 @@ +// Package httphead contains utils for parsing HTTP and HTTP-grammar compatible +// text protocols headers. +// +// That is, this package first aim is to bring ability to easily parse +// constructions, described here https://tools.ietf.org/html/rfc2616#section-2 +package httphead + +import ( + "bytes" + "strings" +) + +// ScanTokens parses data in this form: +// +// list = 1#token +// +// It returns false if data is malformed. +func ScanTokens(data []byte, it func([]byte) bool) bool { + lexer := &Scanner{data: data} + + var ok bool + for lexer.Next() { + switch lexer.Type() { + case ItemToken: + ok = true + if !it(lexer.Bytes()) { + return true + } + case ItemSeparator: + if !isComma(lexer.Bytes()) { + return false + } + default: + return false + } + } + + return ok && !lexer.err +} + +// ParseOptions parses all header options and appends it to given slice of +// Option. It returns flag of successful (wellformed input) parsing. +// +// Note that appended options are all consist of subslices of data. That is, +// mutation of data will mutate appended options. +func ParseOptions(data []byte, options []Option) ([]Option, bool) { + var i int + index := -1 + return options, ScanOptions(data, func(idx int, name, attr, val []byte) Control { + if idx != index { + index = idx + i = len(options) + options = append(options, Option{Name: name}) + } + if attr != nil { + options[i].Parameters.Set(attr, val) + } + return ControlContinue + }) +} + +// SelectFlag encodes way of options selection. +type SelectFlag byte + +// String represetns flag as string. +func (f SelectFlag) String() string { + var flags [2]string + var n int + if f&SelectCopy != 0 { + flags[n] = "copy" + n++ + } + if f&SelectUnique != 0 { + flags[n] = "unique" + n++ + } + return "[" + strings.Join(flags[:n], "|") + "]" +} + +const ( + // SelectCopy causes selector to copy selected option before appending it + // to resulting slice. + // If SelectCopy flag is not passed to selector, then appended options will + // contain sub-slices of the initial data. + SelectCopy SelectFlag = 1 << iota + + // SelectUnique causes selector to append only not yet existing option to + // resulting slice. Unique is checked by comparing option names. + SelectUnique +) + +// OptionSelector contains configuration for selecting Options from header value. +type OptionSelector struct { + // Check is a filter function that applied to every Option that possibly + // could be selected. + // If Check is nil all options will be selected. + Check func(Option) bool + + // Flags contains flags for options selection. + Flags SelectFlag + + // Alloc used to allocate slice of bytes when selector is configured with + // SelectCopy flag. It will be called with number of bytes needed for copy + // of single Option. + // If Alloc is nil make is used. + Alloc func(n int) []byte +} + +// Select parses header data and appends it to given slice of Option. +// It also returns flag of successful (wellformed input) parsing. +func (s OptionSelector) Select(data []byte, options []Option) ([]Option, bool) { + var current Option + var has bool + index := -1 + + alloc := s.Alloc + if alloc == nil { + alloc = defaultAlloc + } + check := s.Check + if check == nil { + check = defaultCheck + } + + ok := ScanOptions(data, func(idx int, name, attr, val []byte) Control { + if idx != index { + if has && check(current) { + if s.Flags&SelectCopy != 0 { + current = current.Copy(alloc(current.Size())) + } + options = append(options, current) + has = false + } + if s.Flags&SelectUnique != 0 { + for i := len(options) - 1; i >= 0; i-- { + if bytes.Equal(options[i].Name, name) { + return ControlSkip + } + } + } + index = idx + current = Option{Name: name} + has = true + } + if attr != nil { + current.Parameters.Set(attr, val) + } + + return ControlContinue + }) + if has && check(current) { + if s.Flags&SelectCopy != 0 { + current = current.Copy(alloc(current.Size())) + } + options = append(options, current) + } + + return options, ok +} + +func defaultAlloc(n int) []byte { return make([]byte, n) } +func defaultCheck(Option) bool { return true } + +// Control represents operation that scanner should perform. +type Control byte + +const ( + // ControlContinue causes scanner to continue scan tokens. + ControlContinue Control = iota + // ControlBreak causes scanner to stop scan tokens. + ControlBreak + // ControlSkip causes scanner to skip current entity. + ControlSkip +) + +// ScanOptions parses data in this form: +// +// values = 1#value +// value = token *( ";" param ) +// param = token [ "=" (token | quoted-string) ] +// +// It calls given callback with the index of the option, option itself and its +// parameter (attribute and its value, both could be nil). Index is useful when +// header contains multiple choises for the same named option. +// +// Given callback should return one of the defined Control* values. +// ControlSkip means that passed key is not in caller's interest. That is, all +// parameters of that key will be skipped. +// ControlBreak means that no more keys and parameters should be parsed. That +// is, it must break parsing immediately. +// ControlContinue means that caller want to receive next parameter and its +// value or the next key. +// +// It returns false if data is malformed. +func ScanOptions(data []byte, it func(index int, option, attribute, value []byte) Control) bool { + lexer := &Scanner{data: data} + + var ok bool + var state int + const ( + stateKey = iota + stateParamBeforeName + stateParamName + stateParamBeforeValue + stateParamValue + ) + + var ( + index int + key, param, value []byte + mustCall bool + ) + for lexer.Next() { + var ( + call bool + growIndex int + ) + + t := lexer.Type() + v := lexer.Bytes() + + switch t { + case ItemToken: + switch state { + case stateKey, stateParamBeforeName: + key = v + state = stateParamBeforeName + mustCall = true + case stateParamName: + param = v + state = stateParamBeforeValue + mustCall = true + case stateParamValue: + value = v + state = stateParamBeforeName + call = true + default: + return false + } + + case ItemString: + if state != stateParamValue { + return false + } + value = v + state = stateParamBeforeName + call = true + + case ItemSeparator: + switch { + case isComma(v) && state == stateKey: + // Nothing to do. + + case isComma(v) && state == stateParamBeforeName: + state = stateKey + // Make call only if we have not called this key yet. + call = mustCall + if !call { + // If we have already called callback with the key + // that just ended. + index++ + } else { + // Else grow the index after calling callback. + growIndex = 1 + } + + case isComma(v) && state == stateParamBeforeValue: + state = stateKey + growIndex = 1 + call = true + + case isSemicolon(v) && state == stateParamBeforeName: + state = stateParamName + + case isSemicolon(v) && state == stateParamBeforeValue: + state = stateParamName + call = true + + case isEquality(v) && state == stateParamBeforeValue: + state = stateParamValue + + default: + return false + } + + default: + return false + } + + if call { + switch it(index, key, param, value) { + case ControlBreak: + // User want to stop to parsing parameters. + return true + + case ControlSkip: + // User want to skip current param. + state = stateKey + lexer.SkipEscaped(',') + + case ControlContinue: + // User is interested in rest of parameters. + // Nothing to do. + + default: + panic("unexpected control value") + } + ok = true + param = nil + value = nil + mustCall = false + index += growIndex + } + } + if mustCall { + ok = true + it(index, key, param, value) + } + + return ok && !lexer.err +} + +func isComma(b []byte) bool { + return len(b) == 1 && b[0] == ',' +} +func isSemicolon(b []byte) bool { + return len(b) == 1 && b[0] == ';' +} +func isEquality(b []byte) bool { + return len(b) == 1 && b[0] == '=' +} diff --git a/vendor/github.com/gobwas/httphead/lexer.go b/vendor/github.com/gobwas/httphead/lexer.go new file mode 100644 index 0000000000..729855ed0d --- /dev/null +++ b/vendor/github.com/gobwas/httphead/lexer.go @@ -0,0 +1,360 @@ +package httphead + +import ( + "bytes" +) + +// ItemType encodes type of the lexing token. +type ItemType int + +const ( + // ItemUndef reports that token is undefined. + ItemUndef ItemType = iota + // ItemToken reports that token is RFC2616 token. + ItemToken + // ItemSeparator reports that token is RFC2616 separator. + ItemSeparator + // ItemString reports that token is RFC2616 quouted string. + ItemString + // ItemComment reports that token is RFC2616 comment. + ItemComment + // ItemOctet reports that token is octet slice. + ItemOctet +) + +// Scanner represents header tokens scanner. +// See https://tools.ietf.org/html/rfc2616#section-2 +type Scanner struct { + data []byte + pos int + + itemType ItemType + itemBytes []byte + + err bool +} + +// NewScanner creates new RFC2616 data scanner. +func NewScanner(data []byte) *Scanner { + return &Scanner{data: data} +} + +// Next scans for next token. It returns true on successful scanning, and false +// on error or EOF. +func (l *Scanner) Next() bool { + c, ok := l.nextChar() + if !ok { + return false + } + switch c { + case '"': // quoted-string; + return l.fetchQuotedString() + + case '(': // comment; + return l.fetchComment() + + case '\\', ')': // unexpected chars; + l.err = true + return false + + default: + return l.fetchToken() + } +} + +// FetchUntil fetches ItemOctet from current scanner position to first +// occurence of the c or to the end of the underlying data. +func (l *Scanner) FetchUntil(c byte) bool { + l.resetItem() + if l.pos == len(l.data) { + return false + } + return l.fetchOctet(c) +} + +// Peek reads byte at current position without advancing it. On end of data it +// returns 0. +func (l *Scanner) Peek() byte { + if l.pos == len(l.data) { + return 0 + } + return l.data[l.pos] +} + +// Peek2 reads two first bytes at current position without advancing it. +// If there not enough data it returs 0. +func (l *Scanner) Peek2() (a, b byte) { + if l.pos == len(l.data) { + return 0, 0 + } + if l.pos+1 == len(l.data) { + return l.data[l.pos], 0 + } + return l.data[l.pos], l.data[l.pos+1] +} + +// Buffered reporst how many bytes there are left to scan. +func (l *Scanner) Buffered() int { + return len(l.data) - l.pos +} + +// Advance moves current position index at n bytes. It returns true on +// successful move. +func (l *Scanner) Advance(n int) bool { + l.pos += n + if l.pos > len(l.data) { + l.pos = len(l.data) + return false + } + return true +} + +// Skip skips all bytes until first occurence of c. +func (l *Scanner) Skip(c byte) { + if l.err { + return + } + // Reset scanner state. + l.resetItem() + + if i := bytes.IndexByte(l.data[l.pos:], c); i == -1 { + // Reached the end of data. + l.pos = len(l.data) + } else { + l.pos += i + 1 + } +} + +// SkipEscaped skips all bytes until first occurence of non-escaped c. +func (l *Scanner) SkipEscaped(c byte) { + if l.err { + return + } + // Reset scanner state. + l.resetItem() + + if i := ScanUntil(l.data[l.pos:], c); i == -1 { + // Reached the end of data. + l.pos = len(l.data) + } else { + l.pos += i + 1 + } +} + +// Type reports current token type. +func (l *Scanner) Type() ItemType { + return l.itemType +} + +// Bytes returns current token bytes. +func (l *Scanner) Bytes() []byte { + return l.itemBytes +} + +func (l *Scanner) nextChar() (byte, bool) { + // Reset scanner state. + l.resetItem() + + if l.err { + return 0, false + } + l.pos += SkipSpace(l.data[l.pos:]) + if l.pos == len(l.data) { + return 0, false + } + return l.data[l.pos], true +} + +func (l *Scanner) resetItem() { + l.itemType = ItemUndef + l.itemBytes = nil +} + +func (l *Scanner) fetchOctet(c byte) bool { + i := l.pos + if j := bytes.IndexByte(l.data[l.pos:], c); j == -1 { + // Reached the end of data. + l.pos = len(l.data) + } else { + l.pos += j + } + + l.itemType = ItemOctet + l.itemBytes = l.data[i:l.pos] + + return true +} + +func (l *Scanner) fetchToken() bool { + n, t := ScanToken(l.data[l.pos:]) + if n == -1 { + l.err = true + return false + } + + l.itemType = t + l.itemBytes = l.data[l.pos : l.pos+n] + l.pos += n + + return true +} + +func (l *Scanner) fetchQuotedString() (ok bool) { + l.pos++ + + n := ScanUntil(l.data[l.pos:], '"') + if n == -1 { + l.err = true + return false + } + + l.itemType = ItemString + l.itemBytes = RemoveByte(l.data[l.pos:l.pos+n], '\\') + l.pos += n + 1 + + return true +} + +func (l *Scanner) fetchComment() (ok bool) { + l.pos++ + + n := ScanPairGreedy(l.data[l.pos:], '(', ')') + if n == -1 { + l.err = true + return false + } + + l.itemType = ItemComment + l.itemBytes = RemoveByte(l.data[l.pos:l.pos+n], '\\') + l.pos += n + 1 + + return true +} + +// ScanUntil scans for first non-escaped character c in given data. +// It returns index of matched c and -1 if c is not found. +func ScanUntil(data []byte, c byte) (n int) { + for { + i := bytes.IndexByte(data[n:], c) + if i == -1 { + return -1 + } + n += i + if n == 0 || data[n-1] != '\\' { + break + } + n++ + } + return +} + +// ScanPairGreedy scans for complete pair of opening and closing chars in greedy manner. +// Note that first opening byte must not be present in data. +func ScanPairGreedy(data []byte, open, close byte) (n int) { + var m int + opened := 1 + for { + i := bytes.IndexByte(data[n:], close) + if i == -1 { + return -1 + } + n += i + // If found index is not escaped then it is the end. + if n == 0 || data[n-1] != '\\' { + opened-- + } + + for m < i { + j := bytes.IndexByte(data[m:i], open) + if j == -1 { + break + } + m += j + 1 + opened++ + } + + if opened == 0 { + break + } + + n++ + m = n + } + return +} + +// RemoveByte returns data without c. If c is not present in data it returns +// the same slice. If not, it copies data without c. +func RemoveByte(data []byte, c byte) []byte { + j := bytes.IndexByte(data, c) + if j == -1 { + return data + } + + n := len(data) - 1 + + // If character is present, than allocate slice with n-1 capacity. That is, + // resulting bytes could be at most n-1 length. + result := make([]byte, n) + k := copy(result, data[:j]) + + for i := j + 1; i < n; { + j = bytes.IndexByte(data[i:], c) + if j != -1 { + k += copy(result[k:], data[i:i+j]) + i = i + j + 1 + } else { + k += copy(result[k:], data[i:]) + break + } + } + + return result[:k] +} + +// SkipSpace skips spaces and lws-sequences from p. +// It returns number ob bytes skipped. +func SkipSpace(p []byte) (n int) { + for len(p) > 0 { + switch { + case len(p) >= 3 && + p[0] == '\r' && + p[1] == '\n' && + OctetTypes[p[2]].IsSpace(): + p = p[3:] + n += 3 + case OctetTypes[p[0]].IsSpace(): + p = p[1:] + n++ + default: + return + } + } + return +} + +// ScanToken scan for next token in p. It returns length of the token and its +// type. It do not trim p. +func ScanToken(p []byte) (n int, t ItemType) { + if len(p) == 0 { + return 0, ItemUndef + } + + c := p[0] + switch { + case OctetTypes[c].IsSeparator(): + return 1, ItemSeparator + + case OctetTypes[c].IsToken(): + for n = 1; n < len(p); n++ { + c := p[n] + if !OctetTypes[c].IsToken() { + break + } + } + return n, ItemToken + + default: + return -1, ItemUndef + } +} diff --git a/vendor/github.com/gobwas/httphead/octet.go b/vendor/github.com/gobwas/httphead/octet.go new file mode 100644 index 0000000000..2a04cdd090 --- /dev/null +++ b/vendor/github.com/gobwas/httphead/octet.go @@ -0,0 +1,83 @@ +package httphead + +// OctetType desribes character type. +// +// From the "Basic Rules" chapter of RFC2616 +// See https://tools.ietf.org/html/rfc2616#section-2.2 +// +// OCTET = +// CHAR = +// UPALPHA = +// LOALPHA = +// ALPHA = UPALPHA | LOALPHA +// DIGIT = +// CTL = +// CR = +// LF = +// SP = +// HT = +// <"> = +// CRLF = CR LF +// LWS = [CRLF] 1*( SP | HT ) +// +// Many HTTP/1.1 header field values consist of words separated by LWS +// or special characters. These special characters MUST be in a quoted +// string to be used within a parameter value (as defined in section +// 3.6). +// +// token = 1* +// separators = "(" | ")" | "<" | ">" | "@" +// | "," | ";" | ":" | "\" | <"> +// | "/" | "[" | "]" | "?" | "=" +// | "{" | "}" | SP | HT +type OctetType byte + +// IsChar reports whether octet is CHAR. +func (t OctetType) IsChar() bool { return t&octetChar != 0 } + +// IsControl reports whether octet is CTL. +func (t OctetType) IsControl() bool { return t&octetControl != 0 } + +// IsSeparator reports whether octet is separator. +func (t OctetType) IsSeparator() bool { return t&octetSeparator != 0 } + +// IsSpace reports whether octet is space (SP or HT). +func (t OctetType) IsSpace() bool { return t&octetSpace != 0 } + +// IsToken reports whether octet is token. +func (t OctetType) IsToken() bool { return t&octetToken != 0 } + +const ( + octetChar OctetType = 1 << iota + octetControl + octetSpace + octetSeparator + octetToken +) + +// OctetTypes is a table of octets. +var OctetTypes [256]OctetType + +func init() { + for c := 32; c < 256; c++ { + var t OctetType + if c <= 127 { + t |= octetChar + } + if 0 <= c && c <= 31 || c == 127 { + t |= octetControl + } + switch c { + case '(', ')', '<', '>', '@', ',', ';', ':', '"', '/', '[', ']', '?', '=', '{', '}', '\\': + t |= octetSeparator + case ' ', '\t': + t |= octetSpace | octetSeparator + } + + if t.IsChar() && !t.IsControl() && !t.IsSeparator() && !t.IsSpace() { + t |= octetToken + } + + OctetTypes[c] = t + } +} diff --git a/vendor/github.com/gobwas/httphead/option.go b/vendor/github.com/gobwas/httphead/option.go new file mode 100644 index 0000000000..0a18c7c7c6 --- /dev/null +++ b/vendor/github.com/gobwas/httphead/option.go @@ -0,0 +1,193 @@ +package httphead + +import ( + "bytes" + "sort" +) + +// Option represents a header option. +type Option struct { + Name []byte + Parameters Parameters +} + +// Size returns number of bytes need to be allocated for use in opt.Copy. +func (opt Option) Size() int { + return len(opt.Name) + opt.Parameters.bytes +} + +// Copy copies all underlying []byte slices into p and returns new Option. +// Note that p must be at least of opt.Size() length. +func (opt Option) Copy(p []byte) Option { + n := copy(p, opt.Name) + opt.Name = p[:n] + opt.Parameters, p = opt.Parameters.Copy(p[n:]) + return opt +} + +// Clone is a shorthand for making slice of opt.Size() sequenced with Copy() +// call. +func (opt Option) Clone() Option { + return opt.Copy(make([]byte, opt.Size())) +} + +// String represents option as a string. +func (opt Option) String() string { + return "{" + string(opt.Name) + " " + opt.Parameters.String() + "}" +} + +// NewOption creates named option with given parameters. +func NewOption(name string, params map[string]string) Option { + p := Parameters{} + for k, v := range params { + p.Set([]byte(k), []byte(v)) + } + return Option{ + Name: []byte(name), + Parameters: p, + } +} + +// Equal reports whether option is equal to b. +func (opt Option) Equal(b Option) bool { + if bytes.Equal(opt.Name, b.Name) { + return opt.Parameters.Equal(b.Parameters) + } + return false +} + +// Parameters represents option's parameters. +type Parameters struct { + pos int + bytes int + arr [8]pair + dyn []pair +} + +// Equal reports whether a equal to b. +func (p Parameters) Equal(b Parameters) bool { + switch { + case p.dyn == nil && b.dyn == nil: + case p.dyn != nil && b.dyn != nil: + default: + return false + } + + ad, bd := p.data(), b.data() + if len(ad) != len(bd) { + return false + } + + sort.Sort(pairs(ad)) + sort.Sort(pairs(bd)) + + for i := 0; i < len(ad); i++ { + av, bv := ad[i], bd[i] + if !bytes.Equal(av.key, bv.key) || !bytes.Equal(av.value, bv.value) { + return false + } + } + return true +} + +// Size returns number of bytes that needed to copy p. +func (p *Parameters) Size() int { + return p.bytes +} + +// Copy copies all underlying []byte slices into dst and returns new +// Parameters. +// Note that dst must be at least of p.Size() length. +func (p *Parameters) Copy(dst []byte) (Parameters, []byte) { + ret := Parameters{ + pos: p.pos, + bytes: p.bytes, + } + if p.dyn != nil { + ret.dyn = make([]pair, len(p.dyn)) + for i, v := range p.dyn { + ret.dyn[i], dst = v.copy(dst) + } + } else { + for i, p := range p.arr { + ret.arr[i], dst = p.copy(dst) + } + } + return ret, dst +} + +// Get returns value by key and flag about existence such value. +func (p *Parameters) Get(key string) (value []byte, ok bool) { + for _, v := range p.data() { + if string(v.key) == key { + return v.value, true + } + } + return nil, false +} + +// Set sets value by key. +func (p *Parameters) Set(key, value []byte) { + p.bytes += len(key) + len(value) + + if p.pos < len(p.arr) { + p.arr[p.pos] = pair{key, value} + p.pos++ + return + } + + if p.dyn == nil { + p.dyn = make([]pair, len(p.arr), len(p.arr)+1) + copy(p.dyn, p.arr[:]) + } + p.dyn = append(p.dyn, pair{key, value}) +} + +// ForEach iterates over parameters key-value pairs and calls cb for each one. +func (p *Parameters) ForEach(cb func(k, v []byte) bool) { + for _, v := range p.data() { + if !cb(v.key, v.value) { + break + } + } +} + +// String represents parameters as a string. +func (p *Parameters) String() (ret string) { + ret = "[" + for i, v := range p.data() { + if i > 0 { + ret += " " + } + ret += string(v.key) + ":" + string(v.value) + } + return ret + "]" +} + +func (p *Parameters) data() []pair { + if p.dyn != nil { + return p.dyn + } + return p.arr[:p.pos] +} + +type pair struct { + key, value []byte +} + +func (p pair) copy(dst []byte) (pair, []byte) { + n := copy(dst, p.key) + p.key = dst[:n] + m := n + copy(dst[n:], p.value) + p.value = dst[n:m] + + dst = dst[m:] + + return p, dst +} + +type pairs []pair + +func (p pairs) Len() int { return len(p) } +func (p pairs) Less(a, b int) bool { return bytes.Compare(p[a].key, p[b].key) == -1 } +func (p pairs) Swap(a, b int) { p[a], p[b] = p[b], p[a] } diff --git a/vendor/github.com/gobwas/httphead/writer.go b/vendor/github.com/gobwas/httphead/writer.go new file mode 100644 index 0000000000..e5df3ddf40 --- /dev/null +++ b/vendor/github.com/gobwas/httphead/writer.go @@ -0,0 +1,101 @@ +package httphead + +import "io" + +var ( + comma = []byte{','} + equality = []byte{'='} + semicolon = []byte{';'} + quote = []byte{'"'} + escape = []byte{'\\'} +) + +// WriteOptions write options list to the dest. +// It uses the same form as {Scan,Parse}Options functions: +// values = 1#value +// value = token *( ";" param ) +// param = token [ "=" (token | quoted-string) ] +// +// It wraps valuse into the quoted-string sequence if it contains any +// non-token characters. +func WriteOptions(dest io.Writer, options []Option) (n int, err error) { + w := writer{w: dest} + for i, opt := range options { + if i > 0 { + w.write(comma) + } + + writeTokenSanitized(&w, opt.Name) + + for _, p := range opt.Parameters.data() { + w.write(semicolon) + writeTokenSanitized(&w, p.key) + if len(p.value) != 0 { + w.write(equality) + writeTokenSanitized(&w, p.value) + } + } + } + return w.result() +} + +// writeTokenSanitized writes token as is or as quouted string if it contains +// non-token characters. +// +// Note that is is not expects LWS sequnces be in s, cause LWS is used only as +// header field continuation: +// "A CRLF is allowed in the definition of TEXT only as part of a header field +// continuation. It is expected that the folding LWS will be replaced with a +// single SP before interpretation of the TEXT value." +// See https://tools.ietf.org/html/rfc2616#section-2 +// +// That is we sanitizing s for writing, so there could not be any header field +// continuation. +// That is any CRLF will be escaped as any other control characters not allowd in TEXT. +func writeTokenSanitized(bw *writer, bts []byte) { + var qt bool + var pos int + for i := 0; i < len(bts); i++ { + c := bts[i] + if !OctetTypes[c].IsToken() && !qt { + qt = true + bw.write(quote) + } + if OctetTypes[c].IsControl() || c == '"' { + if !qt { + qt = true + bw.write(quote) + } + bw.write(bts[pos:i]) + bw.write(escape) + bw.write(bts[i : i+1]) + pos = i + 1 + } + } + if !qt { + bw.write(bts) + } else { + bw.write(bts[pos:]) + bw.write(quote) + } +} + +type writer struct { + w io.Writer + n int + err error +} + +func (w *writer) write(p []byte) { + if w.err != nil { + return + } + var n int + n, w.err = w.w.Write(p) + w.n += n + return +} + +func (w *writer) result() (int, error) { + return w.n, w.err +} diff --git a/vendor/github.com/gobwas/pool/LICENSE b/vendor/github.com/gobwas/pool/LICENSE new file mode 100644 index 0000000000..c41ffde6f7 --- /dev/null +++ b/vendor/github.com/gobwas/pool/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gobwas/pool/README.md b/vendor/github.com/gobwas/pool/README.md new file mode 100644 index 0000000000..45685581da --- /dev/null +++ b/vendor/github.com/gobwas/pool/README.md @@ -0,0 +1,107 @@ +# pool + +[![GoDoc][godoc-image]][godoc-url] + +> Tiny memory reuse helpers for Go. + +## generic + +Without use of subpackages, `pool` allows to reuse any struct distinguishable +by size in generic way: + +```go +package main + +import "github.com/gobwas/pool" + +func main() { + x, n := pool.Get(100) // Returns object with size 128 or nil. + if x == nil { + // Create x somehow with knowledge that n is 128. + } + defer pool.Put(x, n) + + // Work with x. +} +``` + +Pool allows you to pass specific options for constructing custom pool: + +```go +package main + +import "github.com/gobwas/pool" + +func main() { + p := pool.Custom( + pool.WithLogSizeMapping(), // Will ceil size n passed to Get(n) to nearest power of two. + pool.WithLogSizeRange(64, 512), // Will reuse objects in logarithmic range [64, 512]. + pool.WithSize(65536), // Will reuse object with size 65536. + ) + x, n := p.Get(1000) // Returns nil and 1000 because mapped size 1000 => 1024 is not reusing by the pool. + defer pool.Put(x, n) // Will not reuse x. + + // Work with x. +} +``` + +Note that there are few non-generic pooling implementations inside subpackages. + +## pbytes + +Subpackage `pbytes` is intended for `[]byte` reuse. + +```go +package main + +import "github.com/gobwas/pool/pbytes" + +func main() { + bts := pbytes.GetCap(100) // Returns make([]byte, 0, 128). + defer pbytes.Put(bts) + + // Work with bts. +} +``` + +You can also create your own range for pooling: + +```go +package main + +import "github.com/gobwas/pool/pbytes" + +func main() { + // Reuse only slices whose capacity is 128, 256, 512 or 1024. + pool := pbytes.New(128, 1024) + + bts := pool.GetCap(100) // Returns make([]byte, 0, 128). + defer pool.Put(bts) + + // Work with bts. +} +``` + +## pbufio + +Subpackage `pbufio` is intended for `*bufio.{Reader, Writer}` reuse. + +```go +package main + +import "github.com/gobwas/pool/pbufio" + +func main() { + bw := pbufio.GetWriter(os.Stdout, 100) // Returns bufio.NewWriterSize(128). + defer pbufio.PutWriter(bw) + + // Work with bw. +} +``` + +Like with `pbytes`, you can also create pool with custom reuse bounds. + + + +[godoc-image]: https://godoc.org/github.com/gobwas/pool?status.svg +[godoc-url]: https://godoc.org/github.com/gobwas/pool diff --git a/vendor/github.com/gobwas/pool/generic.go b/vendor/github.com/gobwas/pool/generic.go new file mode 100644 index 0000000000..d40b362458 --- /dev/null +++ b/vendor/github.com/gobwas/pool/generic.go @@ -0,0 +1,87 @@ +package pool + +import ( + "sync" + + "github.com/gobwas/pool/internal/pmath" +) + +var DefaultPool = New(128, 65536) + +// Get pulls object whose generic size is at least of given size. It also +// returns a real size of x for further pass to Put(). It returns -1 as real +// size for nil x. Size >-1 does not mean that x is non-nil, so checks must be +// done. +// +// Note that size could be ceiled to the next power of two. +// +// Get is a wrapper around DefaultPool.Get(). +func Get(size int) (interface{}, int) { return DefaultPool.Get(size) } + +// Put takes x and its size for future reuse. +// Put is a wrapper around DefaultPool.Put(). +func Put(x interface{}, size int) { DefaultPool.Put(x, size) } + +// Pool contains logic of reusing objects distinguishable by size in generic +// way. +type Pool struct { + pool map[int]*sync.Pool + size func(int) int +} + +// New creates new Pool that reuses objects which size is in logarithmic range +// [min, max]. +// +// Note that it is a shortcut for Custom() constructor with Options provided by +// WithLogSizeMapping() and WithLogSizeRange(min, max) calls. +func New(min, max int) *Pool { + return Custom( + WithLogSizeMapping(), + WithLogSizeRange(min, max), + ) +} + +// Custom creates new Pool with given options. +func Custom(opts ...Option) *Pool { + p := &Pool{ + pool: make(map[int]*sync.Pool), + size: pmath.Identity, + } + + c := (*poolConfig)(p) + for _, opt := range opts { + opt(c) + } + + return p +} + +// Get pulls object whose generic size is at least of given size. +// It also returns a real size of x for further pass to Put() even if x is nil. +// Note that size could be ceiled to the next power of two. +func (p *Pool) Get(size int) (interface{}, int) { + n := p.size(size) + if pool := p.pool[n]; pool != nil { + return pool.Get(), n + } + return nil, size +} + +// Put takes x and its size for future reuse. +func (p *Pool) Put(x interface{}, size int) { + if pool := p.pool[size]; pool != nil { + pool.Put(x) + } +} + +type poolConfig Pool + +// AddSize adds size n to the map. +func (p *poolConfig) AddSize(n int) { + p.pool[n] = new(sync.Pool) +} + +// SetSizeMapping sets up incoming size mapping function. +func (p *poolConfig) SetSizeMapping(size func(int) int) { + p.size = size +} diff --git a/vendor/github.com/gobwas/pool/internal/pmath/pmath.go b/vendor/github.com/gobwas/pool/internal/pmath/pmath.go new file mode 100644 index 0000000000..df152ed12a --- /dev/null +++ b/vendor/github.com/gobwas/pool/internal/pmath/pmath.go @@ -0,0 +1,65 @@ +package pmath + +const ( + bitsize = 32 << (^uint(0) >> 63) + maxint = int(1<<(bitsize-1) - 1) + maxintHeadBit = 1 << (bitsize - 2) +) + +// LogarithmicRange iterates from ceiled to power of two min to max, +// calling cb on each iteration. +func LogarithmicRange(min, max int, cb func(int)) { + if min == 0 { + min = 1 + } + for n := CeilToPowerOfTwo(min); n <= max; n <<= 1 { + cb(n) + } +} + +// IsPowerOfTwo reports whether given integer is a power of two. +func IsPowerOfTwo(n int) bool { + return n&(n-1) == 0 +} + +// Identity is identity. +func Identity(n int) int { + return n +} + +// CeilToPowerOfTwo returns the least power of two integer value greater than +// or equal to n. +func CeilToPowerOfTwo(n int) int { + if n&maxintHeadBit != 0 && n > maxintHeadBit { + panic("argument is too large") + } + if n <= 2 { + return n + } + n-- + n = fillBits(n) + n++ + return n +} + +// FloorToPowerOfTwo returns the greatest power of two integer value less than +// or equal to n. +func FloorToPowerOfTwo(n int) int { + if n <= 2 { + return n + } + n = fillBits(n) + n >>= 1 + n++ + return n +} + +func fillBits(n int) int { + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n |= n >> 32 + return n +} diff --git a/vendor/github.com/gobwas/pool/option.go b/vendor/github.com/gobwas/pool/option.go new file mode 100644 index 0000000000..d6e42b7005 --- /dev/null +++ b/vendor/github.com/gobwas/pool/option.go @@ -0,0 +1,43 @@ +package pool + +import "github.com/gobwas/pool/internal/pmath" + +// Option configures pool. +type Option func(Config) + +// Config describes generic pool configuration. +type Config interface { + AddSize(n int) + SetSizeMapping(func(int) int) +} + +// WithSizeLogRange returns an Option that will add logarithmic range of +// pooling sizes containing [min, max] values. +func WithLogSizeRange(min, max int) Option { + return func(c Config) { + pmath.LogarithmicRange(min, max, func(n int) { + c.AddSize(n) + }) + } +} + +// WithSize returns an Option that will add given pooling size to the pool. +func WithSize(n int) Option { + return func(c Config) { + c.AddSize(n) + } +} + +func WithSizeMapping(sz func(int) int) Option { + return func(c Config) { + c.SetSizeMapping(sz) + } +} + +func WithLogSizeMapping() Option { + return WithSizeMapping(pmath.CeilToPowerOfTwo) +} + +func WithIdentitySizeMapping() Option { + return WithSizeMapping(pmath.Identity) +} diff --git a/vendor/github.com/gobwas/pool/pbufio/pbufio.go b/vendor/github.com/gobwas/pool/pbufio/pbufio.go new file mode 100644 index 0000000000..d526bd80da --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbufio/pbufio.go @@ -0,0 +1,106 @@ +// Package pbufio contains tools for pooling bufio.Reader and bufio.Writers. +package pbufio + +import ( + "bufio" + "io" + + "github.com/gobwas/pool" +) + +var ( + DefaultWriterPool = NewWriterPool(256, 65536) + DefaultReaderPool = NewReaderPool(256, 65536) +) + +// GetWriter returns bufio.Writer whose buffer has at least size bytes. +// Note that size could be ceiled to the next power of two. +// GetWriter is a wrapper around DefaultWriterPool.Get(). +func GetWriter(w io.Writer, size int) *bufio.Writer { return DefaultWriterPool.Get(w, size) } + +// PutWriter takes bufio.Writer for future reuse. +// It does not reuse bufio.Writer which underlying buffer size is not power of +// PutWriter is a wrapper around DefaultWriterPool.Put(). +func PutWriter(bw *bufio.Writer) { DefaultWriterPool.Put(bw) } + +// GetReader returns bufio.Reader whose buffer has at least size bytes. It returns +// its capacity for further pass to Put(). +// Note that size could be ceiled to the next power of two. +// GetReader is a wrapper around DefaultReaderPool.Get(). +func GetReader(w io.Reader, size int) *bufio.Reader { return DefaultReaderPool.Get(w, size) } + +// PutReader takes bufio.Reader and its size for future reuse. +// It does not reuse bufio.Reader if size is not power of two or is out of pool +// min/max range. +// PutReader is a wrapper around DefaultReaderPool.Put(). +func PutReader(bw *bufio.Reader) { DefaultReaderPool.Put(bw) } + +// WriterPool contains logic of *bufio.Writer reuse with various size. +type WriterPool struct { + pool *pool.Pool +} + +// NewWriterPool creates new WriterPool that reuses writers which size is in +// logarithmic range [min, max]. +func NewWriterPool(min, max int) *WriterPool { + return &WriterPool{pool.New(min, max)} +} + +// CustomWriterPool creates new WriterPool with given options. +func CustomWriterPool(opts ...pool.Option) *WriterPool { + return &WriterPool{pool.Custom(opts...)} +} + +// Get returns bufio.Writer whose buffer has at least size bytes. +func (wp *WriterPool) Get(w io.Writer, size int) *bufio.Writer { + v, n := wp.pool.Get(size) + if v != nil { + bw := v.(*bufio.Writer) + bw.Reset(w) + return bw + } + return bufio.NewWriterSize(w, n) +} + +// Put takes ownership of bufio.Writer for further reuse. +func (wp *WriterPool) Put(bw *bufio.Writer) { + // Should reset even if we do Reset() inside Get(). + // This is done to prevent locking underlying io.Writer from GC. + bw.Reset(nil) + wp.pool.Put(bw, writerSize(bw)) +} + +// ReaderPool contains logic of *bufio.Reader reuse with various size. +type ReaderPool struct { + pool *pool.Pool +} + +// NewReaderPool creates new ReaderPool that reuses writers which size is in +// logarithmic range [min, max]. +func NewReaderPool(min, max int) *ReaderPool { + return &ReaderPool{pool.New(min, max)} +} + +// CustomReaderPool creates new ReaderPool with given options. +func CustomReaderPool(opts ...pool.Option) *ReaderPool { + return &ReaderPool{pool.Custom(opts...)} +} + +// Get returns bufio.Reader whose buffer has at least size bytes. +func (rp *ReaderPool) Get(r io.Reader, size int) *bufio.Reader { + v, n := rp.pool.Get(size) + if v != nil { + br := v.(*bufio.Reader) + br.Reset(r) + return br + } + return bufio.NewReaderSize(r, n) +} + +// Put takes ownership of bufio.Reader for further reuse. +func (rp *ReaderPool) Put(br *bufio.Reader) { + // Should reset even if we do Reset() inside Get(). + // This is done to prevent locking underlying io.Reader from GC. + br.Reset(nil) + rp.pool.Put(br, readerSize(br)) +} diff --git a/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go b/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go new file mode 100644 index 0000000000..c736ae56e1 --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go @@ -0,0 +1,13 @@ +// +build go1.10 + +package pbufio + +import "bufio" + +func writerSize(bw *bufio.Writer) int { + return bw.Size() +} + +func readerSize(br *bufio.Reader) int { + return br.Size() +} diff --git a/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go b/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go new file mode 100644 index 0000000000..e71dd447d2 --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go @@ -0,0 +1,27 @@ +// +build !go1.10 + +package pbufio + +import "bufio" + +func writerSize(bw *bufio.Writer) int { + return bw.Available() + bw.Buffered() +} + +// readerSize returns buffer size of the given buffered reader. +// NOTE: current workaround implementation resets underlying io.Reader. +func readerSize(br *bufio.Reader) int { + br.Reset(sizeReader) + br.ReadByte() + n := br.Buffered() + 1 + br.Reset(nil) + return n +} + +var sizeReader optimisticReader + +type optimisticReader struct{} + +func (optimisticReader) Read(p []byte) (int, error) { + return len(p), nil +} diff --git a/vendor/github.com/gobwas/pool/pbytes/pbytes.go b/vendor/github.com/gobwas/pool/pbytes/pbytes.go new file mode 100644 index 0000000000..919705b109 --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbytes/pbytes.go @@ -0,0 +1,24 @@ +// Package pbytes contains tools for pooling byte pool. +// Note that by default it reuse slices with capacity from 128 to 65536 bytes. +package pbytes + +// DefaultPool is used by pacakge level functions. +var DefaultPool = New(128, 65536) + +// Get returns probably reused slice of bytes with at least capacity of c and +// exactly len of n. +// Get is a wrapper around DefaultPool.Get(). +func Get(n, c int) []byte { return DefaultPool.Get(n, c) } + +// GetCap returns probably reused slice of bytes with at least capacity of n. +// GetCap is a wrapper around DefaultPool.GetCap(). +func GetCap(c int) []byte { return DefaultPool.GetCap(c) } + +// GetLen returns probably reused slice of bytes with at least capacity of n +// and exactly len of n. +// GetLen is a wrapper around DefaultPool.GetLen(). +func GetLen(n int) []byte { return DefaultPool.GetLen(n) } + +// Put returns given slice to reuse pool. +// Put is a wrapper around DefaultPool.Put(). +func Put(p []byte) { DefaultPool.Put(p) } diff --git a/vendor/github.com/gobwas/pool/pbytes/pool.go b/vendor/github.com/gobwas/pool/pbytes/pool.go new file mode 100644 index 0000000000..1dde225f3e --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbytes/pool.go @@ -0,0 +1,59 @@ +// +build !pool_sanitize + +package pbytes + +import "github.com/gobwas/pool" + +// Pool contains logic of reusing byte slices of various size. +type Pool struct { + pool *pool.Pool +} + +// New creates new Pool that reuses slices which size is in logarithmic range +// [min, max]. +// +// Note that it is a shortcut for Custom() constructor with Options provided by +// pool.WithLogSizeMapping() and pool.WithLogSizeRange(min, max) calls. +func New(min, max int) *Pool { + return &Pool{pool.New(min, max)} +} + +// New creates new Pool with given options. +func Custom(opts ...pool.Option) *Pool { + return &Pool{pool.Custom(opts...)} +} + +// Get returns probably reused slice of bytes with at least capacity of c and +// exactly len of n. +func (p *Pool) Get(n, c int) []byte { + if n > c { + panic("requested length is greater than capacity") + } + + v, x := p.pool.Get(c) + if v != nil { + bts := v.([]byte) + bts = bts[:n] + return bts + } + + return make([]byte, n, x) +} + +// Put returns given slice to reuse pool. +// It does not reuse bytes whose size is not power of two or is out of pool +// min/max range. +func (p *Pool) Put(bts []byte) { + p.pool.Put(bts, cap(bts)) +} + +// GetCap returns probably reused slice of bytes with at least capacity of n. +func (p *Pool) GetCap(c int) []byte { + return p.Get(0, c) +} + +// GetLen returns probably reused slice of bytes with at least capacity of n +// and exactly len of n. +func (p *Pool) GetLen(n int) []byte { + return p.Get(n, n) +} diff --git a/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go b/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go new file mode 100644 index 0000000000..fae9af49b1 --- /dev/null +++ b/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go @@ -0,0 +1,121 @@ +// +build pool_sanitize + +package pbytes + +import ( + "reflect" + "runtime" + "sync/atomic" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +const magic = uint64(0x777742) + +type guard struct { + magic uint64 + size int + owners int32 +} + +const guardSize = int(unsafe.Sizeof(guard{})) + +type Pool struct { + min, max int +} + +func New(min, max int) *Pool { + return &Pool{min, max} +} + +// Get returns probably reused slice of bytes with at least capacity of c and +// exactly len of n. +func (p *Pool) Get(n, c int) []byte { + if n > c { + panic("requested length is greater than capacity") + } + + pageSize := syscall.Getpagesize() + pages := (c+guardSize)/pageSize + 1 + size := pages * pageSize + + bts := alloc(size) + + g := (*guard)(unsafe.Pointer(&bts[0])) + *g = guard{ + magic: magic, + size: size, + owners: 1, + } + + return bts[guardSize : guardSize+n] +} + +func (p *Pool) GetCap(c int) []byte { return p.Get(0, c) } +func (p *Pool) GetLen(n int) []byte { return Get(n, n) } + +// Put returns given slice to reuse pool. +func (p *Pool) Put(bts []byte) { + hdr := *(*reflect.SliceHeader)(unsafe.Pointer(&bts)) + ptr := hdr.Data - uintptr(guardSize) + + g := (*guard)(unsafe.Pointer(ptr)) + if g.magic != magic { + panic("unknown slice returned to the pool") + } + if n := atomic.AddInt32(&g.owners, -1); n < 0 { + panic("multiple Put() detected") + } + + // Disable read and write on bytes memory pages. This will cause panic on + // incorrect access to returned slice. + mprotect(ptr, false, false, g.size) + + runtime.SetFinalizer(&bts, func(b *[]byte) { + mprotect(ptr, true, true, g.size) + free(*(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: ptr, + Len: g.size, + Cap: g.size, + }))) + }) +} + +func alloc(n int) []byte { + b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE|unix.PROT_EXEC, unix.MAP_SHARED|unix.MAP_ANONYMOUS) + if err != nil { + panic(err.Error()) + } + return b +} + +func free(b []byte) { + if err := unix.Munmap(b); err != nil { + panic(err.Error()) + } +} + +func mprotect(ptr uintptr, r, w bool, size int) { + // Need to avoid "EINVAL addr is not a valid pointer, + // or not a multiple of PAGESIZE." + start := ptr & ^(uintptr(syscall.Getpagesize() - 1)) + + prot := uintptr(syscall.PROT_EXEC) + switch { + case r && w: + prot |= syscall.PROT_READ | syscall.PROT_WRITE + case r: + prot |= syscall.PROT_READ + case w: + prot |= syscall.PROT_WRITE + } + + _, _, err := syscall.Syscall(syscall.SYS_MPROTECT, + start, uintptr(size), prot, + ) + if err != 0 { + panic(err.Error()) + } +} diff --git a/vendor/github.com/gobwas/pool/pool.go b/vendor/github.com/gobwas/pool/pool.go new file mode 100644 index 0000000000..1fe9e602fc --- /dev/null +++ b/vendor/github.com/gobwas/pool/pool.go @@ -0,0 +1,25 @@ +// Package pool contains helpers for pooling structures distinguishable by +// size. +// +// Quick example: +// +// import "github.com/gobwas/pool" +// +// func main() { +// // Reuse objects in logarithmic range from 0 to 64 (0,1,2,4,6,8,16,32,64). +// p := pool.New(0, 64) +// +// buf, n := p.Get(10) // Returns buffer with 16 capacity. +// if buf == nil { +// buf = bytes.NewBuffer(make([]byte, n)) +// } +// defer p.Put(buf, n) +// +// // Work with buf. +// } +// +// There are non-generic implementations for pooling: +// - pool/pbytes for []byte reuse; +// - pool/pbufio for *bufio.Reader and *bufio.Writer reuse; +// +package pool diff --git a/vendor/github.com/gobwas/ws/.gitignore b/vendor/github.com/gobwas/ws/.gitignore new file mode 100644 index 0000000000..e3e2b1080d --- /dev/null +++ b/vendor/github.com/gobwas/ws/.gitignore @@ -0,0 +1,5 @@ +bin/ +reports/ +cpu.out +mem.out +ws.test diff --git a/vendor/github.com/gobwas/ws/LICENSE b/vendor/github.com/gobwas/ws/LICENSE new file mode 100644 index 0000000000..ca6dfd9e16 --- /dev/null +++ b/vendor/github.com/gobwas/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2021 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gobwas/ws/Makefile b/vendor/github.com/gobwas/ws/Makefile new file mode 100644 index 0000000000..8f727393ac --- /dev/null +++ b/vendor/github.com/gobwas/ws/Makefile @@ -0,0 +1,54 @@ +BENCH ?=. +BENCH_BASE?=master + +clean: + rm -f bin/reporter + rm -fr autobahn/report/* + +bin/reporter: + go build -o bin/reporter ./autobahn + +bin/gocovmerge: + go build -o bin/gocovmerge github.com/wadey/gocovmerge + +.PHONY: autobahn +autobahn: clean bin/reporter + ./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 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) +benchcmp: BENCH_NEW:=$(shell mktemp -t new.XXXX) +benchcmp: + if [ ! -z "$(shell git status -s)" ]; then\ + echo "could not compare with $(BENCH_BASE) – found unstaged changes";\ + exit 1;\ + fi;\ + if [ "$(BENCH_BRANCH)" == "$(BENCH_BASE)" ]; then\ + echo "comparing the same branches";\ + exit 1;\ + fi;\ + echo "benchmarking $(BENCH_BRANCH)...";\ + go test -run=none -bench=$(BENCH) -benchmem > $(BENCH_NEW);\ + echo "benchmarking $(BENCH_BASE)...";\ + git checkout -q $(BENCH_BASE);\ + go test -run=none -bench=$(BENCH) -benchmem > $(BENCH_OLD);\ + git checkout -q $(BENCH_BRANCH);\ + echo "\nresults:";\ + echo "========\n";\ + benchcmp $(BENCH_OLD) $(BENCH_NEW);\ + diff --git a/vendor/github.com/gobwas/ws/README.md b/vendor/github.com/gobwas/ws/README.md new file mode 100644 index 0000000000..0bd0f6b0ae --- /dev/null +++ b/vendor/github.com/gobwas/ws/README.md @@ -0,0 +1,541 @@ +# ws + +[![GoDoc][godoc-image]][godoc-url] +[![CI][ci-badge]][ci-url] + +> [RFC6455][rfc-url] WebSocket implementation in Go. + +# Features + +- Zero-copy upgrade +- No intermediate allocations during I/O +- Low-level API which allows to build your own logic of packet handling and + buffers reuse +- High-level wrappers and helpers around API in `wsutil` package, which allow + to start fast without digging the protocol internals + +# Documentation + +[GoDoc][godoc-url]. + +# Why + +Existing WebSocket implementations do not allow users to reuse I/O buffers +between connections in clear way. This library aims to export efficient +low-level interface for working with the protocol without forcing only one way +it could be used. + +By the way, if you want get the higher-level tools, you can use `wsutil` +package. + +# Status + +Library is tagged as `v1*` so its API must not be broken during some +improvements or refactoring. + +This implementation of RFC6455 passes [Autobahn Test +Suite](https://github.com/crossbario/autobahn-testsuite) and currently has +about 78% coverage. + +# Examples + +Example applications using `ws` are developed in separate repository +[ws-examples](https://github.com/gobwas/ws-examples). + +# Usage + +The higher-level example of WebSocket echo server: + +```go +package main + +import ( + "net/http" + + "github.com/gobwas/ws" + "github.com/gobwas/ws/wsutil" +) + +func main() { + http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + conn, _, _, err := ws.UpgradeHTTP(r, w) + if err != nil { + // handle error + } + go func() { + defer conn.Close() + + for { + msg, op, err := wsutil.ReadClientData(conn) + if err != nil { + // handle error + } + err = wsutil.WriteServerMessage(conn, op, msg) + if err != nil { + // handle error + } + } + }() + })) +} +``` + +Lower-level, but still high-level example: + + +```go +import ( + "net/http" + "io" + + "github.com/gobwas/ws" + "github.com/gobwas/ws/wsutil" +) + +func main() { + http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + conn, _, _, err := ws.UpgradeHTTP(r, w) + if err != nil { + // handle error + } + go func() { + defer conn.Close() + + var ( + state = ws.StateServerSide + reader = wsutil.NewReader(conn, state) + writer = wsutil.NewWriter(conn, state, ws.OpText) + ) + for { + header, err := reader.NextFrame() + if err != nil { + // handle error + } + + // Reset writer to write frame with right operation code. + writer.Reset(conn, state, header.OpCode) + + if _, err = io.Copy(writer, reader); err != nil { + // handle error + } + if err = writer.Flush(); err != nil { + // handle error + } + } + }() + })) +} +``` + +We can apply the same pattern to read and write structured responses through a JSON encoder and decoder.: + +```go + ... + var ( + r = wsutil.NewReader(conn, ws.StateServerSide) + w = wsutil.NewWriter(conn, ws.StateServerSide, ws.OpText) + decoder = json.NewDecoder(r) + encoder = json.NewEncoder(w) + ) + for { + hdr, err = r.NextFrame() + if err != nil { + return err + } + if hdr.OpCode == ws.OpClose { + return io.EOF + } + var req Request + if err := decoder.Decode(&req); err != nil { + return err + } + var resp Response + if err := encoder.Encode(&resp); err != nil { + return err + } + if err = w.Flush(); err != nil { + return err + } + } + ... +``` + +The lower-level example without `wsutil`: + +```go +package main + +import ( + "net" + "io" + + "github.com/gobwas/ws" +) + +func main() { + ln, err := net.Listen("tcp", "localhost:8080") + if err != nil { + log.Fatal(err) + } + + for { + conn, err := ln.Accept() + if err != nil { + // handle error + } + _, err = ws.Upgrade(conn) + if err != nil { + // handle error + } + + go func() { + defer conn.Close() + + for { + header, err := ws.ReadHeader(conn) + if err != nil { + // handle error + } + + payload := make([]byte, header.Length) + _, err = io.ReadFull(conn, payload) + if err != nil { + // handle error + } + if header.Masked { + ws.Cipher(payload, header.Mask, 0) + } + + // Reset the Masked flag, server frames must not be masked as + // RFC6455 says. + header.Masked = false + + if err := ws.WriteHeader(conn, header); err != nil { + // handle error + } + if _, err := conn.Write(payload); err != nil { + // handle error + } + + if header.OpCode == ws.OpClose { + return + } + } + }() + } +} +``` + +# Zero-copy upgrade + +Zero-copy upgrade helps to avoid unnecessary allocations and copying while +handling HTTP Upgrade request. + +Processing of all non-websocket headers is made in place with use of registered +user callbacks whose arguments are only valid until callback returns. + +The simple example looks like this: + +```go +package main + +import ( + "net" + "log" + + "github.com/gobwas/ws" +) + +func main() { + ln, err := net.Listen("tcp", "localhost:8080") + if err != nil { + log.Fatal(err) + } + u := ws.Upgrader{ + OnHeader: func(key, value []byte) (err error) { + log.Printf("non-websocket header: %q=%q", key, value) + return + }, + } + for { + conn, err := ln.Accept() + if err != nil { + // handle error + } + + _, err = u.Upgrade(conn) + if err != nil { + // handle error + } + } +} +``` + +Usage of `ws.Upgrader` here brings ability to control incoming connections on +tcp level and simply not to accept them by some logic. + +Zero-copy upgrade is for high-load services which have to control many +resources such as connections buffers. + +The real life example could be like this: + +```go +package main + +import ( + "fmt" + "io" + "log" + "net" + "net/http" + "runtime" + + "github.com/gobwas/httphead" + "github.com/gobwas/ws" +) + +func main() { + ln, err := net.Listen("tcp", "localhost:8080") + if err != nil { + // handle error + } + + // Prepare handshake header writer from http.Header mapping. + header := ws.HandshakeHeaderHTTP(http.Header{ + "X-Go-Version": []string{runtime.Version()}, + }) + + u := ws.Upgrader{ + OnHost: func(host []byte) error { + if string(host) == "github.com" { + return nil + } + return ws.RejectConnectionError( + ws.RejectionStatus(403), + ws.RejectionHeader(ws.HandshakeHeaderString( + "X-Want-Host: github.com\r\n", + )), + ) + }, + OnHeader: func(key, value []byte) error { + if string(key) != "Cookie" { + return nil + } + ok := httphead.ScanCookie(value, func(key, value []byte) bool { + // Check session here or do some other stuff with cookies. + // Maybe copy some values for future use. + return true + }) + if ok { + return nil + } + return ws.RejectConnectionError( + ws.RejectionReason("bad cookie"), + ws.RejectionStatus(400), + ) + }, + OnBeforeUpgrade: func() (ws.HandshakeHeader, error) { + return header, nil + }, + } + for { + conn, err := ln.Accept() + if err != nil { + log.Fatal(err) + } + _, err = u.Upgrade(conn) + if err != nil { + log.Printf("upgrade error: %s", err) + } + } +} +``` + +# 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 +[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 diff --git a/vendor/github.com/gobwas/ws/check.go b/vendor/github.com/gobwas/ws/check.go new file mode 100644 index 0000000000..8aa0df8cc2 --- /dev/null +++ b/vendor/github.com/gobwas/ws/check.go @@ -0,0 +1,145 @@ +package ws + +import "unicode/utf8" + +// State represents state of websocket endpoint. +// It used by some functions to be more strict when checking compatibility with RFC6455. +type State uint8 + +const ( + // StateServerSide means that endpoint (caller) is a server. + StateServerSide State = 0x1 << iota + // StateClientSide means that endpoint (caller) is a client. + StateClientSide + // StateExtended means that extension was negotiated during handshake. + StateExtended + // StateFragmented means that endpoint (caller) has received fragmented + // frame and waits for continuation parts. + StateFragmented +) + +// Is checks whether the s has v enabled. +func (s State) Is(v State) bool { + return uint8(s)&uint8(v) != 0 +} + +// Set enables v state on s. +func (s State) Set(v State) State { + return s | v +} + +// Clear disables v state on s. +func (s State) Clear(v State) State { + return s & (^v) +} + +// ServerSide reports whether states represents server side. +func (s State) ServerSide() bool { return s.Is(StateServerSide) } + +// ClientSide reports whether state represents client side. +func (s State) ClientSide() bool { return s.Is(StateClientSide) } + +// Extended reports whether state is extended. +func (s State) Extended() bool { return s.Is(StateExtended) } + +// Fragmented reports whether state is fragmented. +func (s State) Fragmented() bool { return s.Is(StateFragmented) } + +// ProtocolError describes error during checking/parsing websocket frames or +// headers. +type ProtocolError string + +// Error implements error interface. +func (p ProtocolError) Error() string { return string(p) } + +// Errors used by the protocol checkers. +var ( + ErrProtocolOpCodeReserved = ProtocolError("use of reserved op code") + ErrProtocolControlPayloadOverflow = ProtocolError("control frame payload limit exceeded") + ErrProtocolControlNotFinal = ProtocolError("control frame is not final") + ErrProtocolNonZeroRsv = ProtocolError("non-zero rsv bits with no extension negotiated") + ErrProtocolMaskRequired = ProtocolError("frames from client to server must be masked") + ErrProtocolMaskUnexpected = ProtocolError("frames from server to client must be not masked") + ErrProtocolContinuationExpected = ProtocolError("unexpected non-continuation data frame") + ErrProtocolContinuationUnexpected = ProtocolError("unexpected continuation data frame") + ErrProtocolStatusCodeNotInUse = ProtocolError("status code is not in use") + ErrProtocolStatusCodeApplicationLevel = ProtocolError("status code is only application level") + ErrProtocolStatusCodeNoMeaning = ProtocolError("status code has no meaning yet") + ErrProtocolStatusCodeUnknown = ProtocolError("status code is not defined in spec") + ErrProtocolInvalidUTF8 = ProtocolError("invalid utf8 sequence in close reason") +) + +// CheckHeader checks h to contain valid header data for given state s. +// +// Note that zero state (0) means that state is clean, +// neither server or client side, nor fragmented, nor extended. +func CheckHeader(h Header, s State) error { + if h.OpCode.IsReserved() { + return ErrProtocolOpCodeReserved + } + if h.OpCode.IsControl() { + if h.Length > MaxControlFramePayloadSize { + return ErrProtocolControlPayloadOverflow + } + if !h.Fin { + return ErrProtocolControlNotFinal + } + } + + switch { + // [RFC6455]: MUST be 0 unless an extension is negotiated that defines meanings for + // non-zero values. If a nonzero value is received and none of the + // negotiated extensions defines the meaning of such a nonzero value, the + // receiving endpoint MUST _Fail the WebSocket Connection_. + case h.Rsv != 0 && !s.Extended(): + return ErrProtocolNonZeroRsv + + // [RFC6455]: The server MUST close the connection upon receiving a frame that is not masked. + // In this case, a server MAY send a Close frame with a status code of 1002 (protocol error) + // as defined in Section 7.4.1. A server MUST NOT mask any frames that it sends to the client. + // A client MUST close a connection if it detects a masked frame. In this case, it MAY use the + // status code 1002 (protocol error) as defined in Section 7.4.1. + case s.ServerSide() && !h.Masked: + return ErrProtocolMaskRequired + case s.ClientSide() && h.Masked: + return ErrProtocolMaskUnexpected + + // [RFC6455]: See detailed explanation in 5.4 section. + case s.Fragmented() && !h.OpCode.IsControl() && h.OpCode != OpContinuation: + return ErrProtocolContinuationExpected + case !s.Fragmented() && h.OpCode == OpContinuation: + return ErrProtocolContinuationUnexpected + + default: + return nil + } +} + +// CheckCloseFrameData checks received close information +// to be valid RFC6455 compatible close info. +// +// Note that code.Empty() or code.IsAppLevel() will raise error. +// +// If endpoint sends close frame without status code (with frame.Length = 0), +// application should not check its payload. +func CheckCloseFrameData(code StatusCode, reason string) error { + switch { + case code.IsNotUsed(): + return ErrProtocolStatusCodeNotInUse + + case code.IsProtocolReserved(): + return ErrProtocolStatusCodeApplicationLevel + + case code == StatusNoMeaningYet: + return ErrProtocolStatusCodeNoMeaning + + case code.IsProtocolSpec() && !code.IsProtocolDefined(): + return ErrProtocolStatusCodeUnknown + + case !utf8.ValidString(reason): + return ErrProtocolInvalidUTF8 + + default: + return nil + } +} diff --git a/vendor/github.com/gobwas/ws/cipher.go b/vendor/github.com/gobwas/ws/cipher.go new file mode 100644 index 0000000000..3c35e6b8d1 --- /dev/null +++ b/vendor/github.com/gobwas/ws/cipher.go @@ -0,0 +1,61 @@ +package ws + +import ( + "encoding/binary" +) + +// Cipher applies XOR cipher to the payload using mask. +// Offset is used to cipher chunked data (e.g. in io.Reader implementations). +// +// To convert masked data into unmasked data, or vice versa, the following +// algorithm is applied. The same algorithm applies regardless of the +// direction of the translation, e.g., the same steps are applied to +// mask the data as to unmask the data. +func Cipher(payload []byte, mask [4]byte, offset int) { + n := len(payload) + if n < 8 { + for i := 0; i < n; i++ { + payload[i] ^= mask[(offset+i)%4] + } + return + } + + // Calculate position in mask due to previously processed bytes number. + mpos := offset % 4 + // Count number of bytes will processed one by one from the beginning of payload. + ln := remain[mpos] + // Count number of bytes will processed one by one from the end of payload. + // This is done to process payload by 8 bytes in each iteration of main loop. + rn := (n - ln) % 8 + + for i := 0; i < ln; i++ { + payload[i] ^= mask[(mpos+i)%4] + } + for i := n - rn; i < n; i++ { + payload[i] ^= mask[(mpos+i)%4] + } + + // NOTE: we use here binary.LittleEndian regardless of what is real + // 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[:]) + m2 = uint64(m)<<32 | uint64(m) + ) + // Skip already processed right part. + // Get number of uint64 parts remaining to process. + n = (n - ln - rn) >> 3 + for i := 0; i < n; i++ { + var ( + j = ln + (i << 3) + chunk = payload[j : j+8] + ) + p := binary.LittleEndian.Uint64(chunk) + p = p ^ m2 + binary.LittleEndian.PutUint64(chunk, p) + } +} + +// remain maps position in masking key [0,4) to number +// of bytes that need to be processed manually inside Cipher(). +var remain = [4]int{0, 3, 2, 1} diff --git a/vendor/github.com/gobwas/ws/dialer.go b/vendor/github.com/gobwas/ws/dialer.go new file mode 100644 index 0000000000..64d46811f1 --- /dev/null +++ b/vendor/github.com/gobwas/ws/dialer.go @@ -0,0 +1,566 @@ +package ws + +import ( + "bufio" + "bytes" + "context" + "crypto/tls" + "fmt" + "io" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/gobwas/httphead" + "github.com/gobwas/pool/pbufio" +) + +// Constants used by Dialer. +const ( + DefaultClientReadBufferSize = 4096 + DefaultClientWriteBufferSize = 4096 +) + +// Handshake represents handshake result. +type Handshake struct { + // Protocol is the subprotocol selected during handshake. + Protocol string + + // Extensions is the list of negotiated extensions. + Extensions []httphead.Option +} + +// Errors used by the websocket client. +var ( + ErrHandshakeBadStatus = fmt.Errorf("unexpected http status") + ErrHandshakeBadSubProtocol = fmt.Errorf("unexpected protocol in %q header", headerSecProtocol) + ErrHandshakeBadExtensions = fmt.Errorf("unexpected extensions in %q header", headerSecProtocol) +) + +// DefaultDialer is dialer that holds no options and is used by Dial function. +var DefaultDialer Dialer + +// Dial is like Dialer{}.Dial(). +func Dial(ctx context.Context, urlstr string) (net.Conn, *bufio.Reader, Handshake, error) { + return DefaultDialer.Dial(ctx, urlstr) +} + +// Dialer contains options for establishing websocket connection to an url. +type Dialer struct { + // ReadBufferSize and WriteBufferSize is an I/O buffer sizes. + // They used to read and write http data while upgrading to WebSocket. + // Allocated buffers are pooled with sync.Pool to avoid extra allocations. + // + // If a size is zero then default value is used. + ReadBufferSize, WriteBufferSize int + + // Timeout is the maximum amount of time a Dial() will wait for a connect + // and an handshake to complete. + // + // The default is no timeout. + Timeout time.Duration + + // Protocols is the list of subprotocols that the client wants to speak, + // ordered by preference. + // + // See https://tools.ietf.org/html/rfc6455#section-4.1 + Protocols []string + + // Extensions is the list of extensions that client wants to speak. + // + // Note that if server decides to use some of this extensions, Dial() will + // return Handshake struct containing a slice of items, which are the + // shallow copies of the items from this list. That is, internals of + // Extensions items are shared during Dial(). + // + // See https://tools.ietf.org/html/rfc6455#section-4.1 + // See https://tools.ietf.org/html/rfc6455#section-9.1 + Extensions []httphead.Option + + // Header is an optional HandshakeHeader instance that could be used to + // write additional headers to the handshake request. + // + // It used instead of any key-value mappings to avoid allocations in user + // land. + Header HandshakeHeader + + // OnStatusError is the callback that will be called after receiving non + // "101 Continue" HTTP response status. It receives an io.Reader object + // representing server response bytes. That is, it gives ability to parse + // HTTP response somehow (probably with http.ReadResponse call) and make a + // decision of further logic. + // + // The arguments are only valid until the callback returns. + OnStatusError func(status int, reason []byte, resp io.Reader) + + // OnHeader is the callback that will be called after successful parsing of + // header, that is not used during WebSocket handshake procedure. That is, + // it will be called with non-websocket headers, which could be relevant + // for application-level logic. + // + // The arguments are only valid until the callback returns. + // + // Returned value could be used to prevent processing response. + OnHeader func(key, value []byte) (err error) + + // NetDial is the function that is used to get plain tcp connection. + // If it is not nil, then it is used instead of net.Dialer. + NetDial func(ctx context.Context, network, addr string) (net.Conn, error) + + // TLSClient is the callback that will be called after successful dial with + // received connection and its remote host name. If it is nil, then the + // default tls.Client() will be used. + // If it is not nil, then TLSConfig field is ignored. + TLSClient func(conn net.Conn, hostname string) net.Conn + + // TLSConfig is passed to tls.Client() to start TLS over established + // connection. If TLSClient is not nil, then it is ignored. If TLSConfig is + // non-nil and its ServerName is empty, then for every Dial() it will be + // cloned and appropriate ServerName will be set. + TLSConfig *tls.Config + + // WrapConn is the optional callback that will be called when connection is + // ready for an i/o. That is, it will be called after successful dial and + // TLS initialization (for "wss" schemes). It may be helpful for different + // user land purposes such as end to end encryption. + // + // Note that for debugging purposes of an http handshake (e.g. sent request + // and received response), there is an wsutil.DebugDialer struct. + WrapConn func(conn net.Conn) net.Conn +} + +// Dial connects to the url host and upgrades connection to WebSocket. +// +// If server has sent frames right after successful handshake then returned +// buffer will be non-nil. In other cases buffer is always nil. For better +// memory efficiency received non-nil bufio.Reader should be returned to the +// inner pool with PutReader() function after use. +// +// Note that Dialer does not implement IDNA (RFC5895) logic as net/http does. +// If you want to dial non-ascii host name, take care of its name serialization +// avoiding bad request issues. For more info see net/http Request.Write() +// implementation, especially cleanHost() function. +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 nil, nil, hs, err + } + + // Prepare context to dial with. Initially it is the same as original, but + // if d.Timeout is non-zero and points to time that is before ctx.Deadline, + // we use more shorter context for dial. + dialctx := ctx + + var deadline time.Time + if t := d.Timeout; t != 0 { + deadline = time.Now().Add(t) + if d, ok := ctx.Deadline(); !ok || deadline.Before(d) { + var cancel context.CancelFunc + dialctx, cancel = context.WithDeadline(ctx, deadline) + defer cancel() + } + } + if conn, err = d.dial(dialctx, u); err != nil { + return conn, nil, hs, err + } + defer func() { + if err != nil { + conn.Close() + } + }() + if ctx == context.Background() { + // No need to start I/O interrupter goroutine which is not zero-cost. + conn.SetDeadline(deadline) + defer conn.SetDeadline(noDeadline) + } else { + // Context could be canceled or its deadline could be exceeded. + // Start the interrupter goroutine to handle context cancelation. + done := setupContextDeadliner(ctx, conn) + defer func() { + // Map Upgrade() error to a possible context expiration error. That + // is, even if Upgrade() err is nil, context could be already + // expired and connection be "poisoned" by SetDeadline() call. + // In that case we must not return ctx.Err() error. + done(&err) + }() + } + + br, hs, err = d.Upgrade(conn, u) + + return conn, br, hs, err +} + +var ( + // netEmptyDialer is a net.Dialer without options, used in Dialer.dial() if + // Dialer.NetDial is not provided. + netEmptyDialer net.Dialer + // tlsEmptyConfig is an empty tls.Config used as default one. + tlsEmptyConfig tls.Config +) + +func tlsDefaultConfig() *tls.Config { + return &tlsEmptyConfig +} + +func hostport(host, defaultPort string) (hostname, addr string) { + var ( + colon = strings.LastIndexByte(host, ':') + bracket = strings.IndexByte(host, ']') + ) + if colon > bracket { + return host[:colon], host + } + return host, host + defaultPort +} + +func (d Dialer) dial(ctx context.Context, u *url.URL) (conn net.Conn, err error) { + dial := d.NetDial + if dial == nil { + dial = netEmptyDialer.DialContext + } + switch u.Scheme { + case "ws": + _, addr := hostport(u.Host, ":80") + conn, err = dial(ctx, "tcp", addr) + case "wss": + hostname, addr := hostport(u.Host, ":443") + conn, err = dial(ctx, "tcp", addr) + if err != nil { + return nil, err + } + tlsClient := d.TLSClient + if tlsClient == nil { + tlsClient = d.tlsClient + } + conn = tlsClient(conn, hostname) + default: + return nil, fmt.Errorf("unexpected websocket scheme: %q", u.Scheme) + } + if wrap := d.WrapConn; wrap != nil { + conn = wrap(conn) + } + return conn, err +} + +func (d Dialer) tlsClient(conn net.Conn, hostname string) net.Conn { + config := d.TLSConfig + if config == nil { + config = tlsDefaultConfig() + } + if config.ServerName == "" { + config = tlsCloneConfig(config) + config.ServerName = hostname + } + // Do not make conn.Handshake() here because downstairs we will prepare + // i/o on this conn with proper context's timeout handling. + return tls.Client(conn, config) +} + +var ( + // This variables are set like in net/net.go. + // noDeadline is just zero value for readability. + noDeadline = time.Time{} + // aLongTimeAgo is a non-zero time, far in the past, used for immediate + // cancelation of dials. + aLongTimeAgo = time.Unix(42, 0) +) + +// Upgrade writes an upgrade request to the given io.ReadWriter conn at given +// url u and reads a response from it. +// +// It is a caller responsibility to manage I/O deadlines on conn. +// +// It returns handshake info and some bytes which could be written by the peer +// right after response and be caught by us during buffered read. +func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Handshake, err error) { + // headerSeen constants helps to report whether or not some header was seen + // during reading request bytes. + const ( + headerSeenUpgrade = 1 << iota + headerSeenConnection + headerSeenSecAccept + + // headerSeenAll is the value that we expect to receive at the end of + // headers read/parse loop. + headerSeenAll = 0 | + headerSeenUpgrade | + headerSeenConnection | + headerSeenSecAccept + ) + + br = pbufio.GetReader(conn, + nonZero(d.ReadBufferSize, DefaultClientReadBufferSize), + ) + bw := pbufio.GetWriter(conn, + nonZero(d.WriteBufferSize, DefaultClientWriteBufferSize), + ) + defer func() { + pbufio.PutWriter(bw) + if br.Buffered() == 0 || err != nil { + // Server does not wrote additional bytes to the connection or + // error occurred. That is, no reason to return buffer. + pbufio.PutReader(br) + br = nil + } + }() + + nonce := make([]byte, nonceSize) + initNonce(nonce) + + httpWriteUpgradeRequest(bw, u, nonce, d.Protocols, d.Extensions, d.Header) + 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 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 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 br, hs, err + } + if resp.status != http.StatusSwitchingProtocols { + err = StatusError(resp.status) + if onStatusError := d.OnStatusError; onStatusError != nil { + // Invoke callback with multireader of status-line bytes br. + onStatusError(resp.status, resp.reason, + io.MultiReader( + bytes.NewReader(sl), + strings.NewReader(crlf), + br, + ), + ) + } + 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 + // ability to read non-technical headers. That is, we do not distinguish + // technical errors (such as parsing error) and protocol errors. + var headerSeen byte + for { + line, e := readLine(br) + if e != nil { + err = e + return br, hs, err + } + if len(line) == 0 { + // Blank line, no more lines to read. + break + } + + k, v, ok := httpParseHeaderLine(line) + if !ok { + err = ErrMalformedResponse + return br, hs, err + } + + switch btsToString(k) { + case headerUpgradeCanonical: + headerSeen |= headerSeenUpgrade + if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) { + err = ErrHandshakeBadUpgrade + return br, hs, err + } + + case headerConnectionCanonical: + headerSeen |= headerSeenConnection + // Note that as RFC6455 says: + // > A |Connection| header field with value "Upgrade". + // That is, in server side, "Connection" header could contain + // multiple token. But in response it must contains exactly one. + if !bytes.Equal(v, specHeaderValueConnection) && !bytes.EqualFold(v, specHeaderValueConnection) { + err = ErrHandshakeBadConnection + return br, hs, err + } + + case headerSecAcceptCanonical: + headerSeen |= headerSeenSecAccept + if !checkAcceptFromNonce(v, nonce) { + err = ErrHandshakeBadSecAccept + return br, hs, err + } + + case headerSecProtocolCanonical: + // RFC6455 1.3: + // "The server selects one or none of the acceptable protocols + // and echoes that value in its handshake to indicate that it has + // selected that protocol." + for _, want := range d.Protocols { + if string(v) == want { + hs.Protocol = want + break + } + } + if hs.Protocol == "" { + // Server echoed subprotocol that is not present in client + // requested protocols. + err = ErrHandshakeBadSubProtocol + return br, hs, err + } + + case headerSecExtensionsCanonical: + hs.Extensions, err = matchSelectedExtensions(v, d.Extensions, hs.Extensions) + if err != nil { + return br, hs, err + } + + default: + if onHeader := d.OnHeader; onHeader != nil { + if e := onHeader(k, v); e != nil { + err = e + return br, hs, err + } + } + } + } + if err == nil && headerSeen != headerSeenAll { + switch { + case headerSeen&headerSeenUpgrade == 0: + err = ErrHandshakeBadUpgrade + case headerSeen&headerSeenConnection == 0: + err = ErrHandshakeBadConnection + case headerSeen&headerSeenSecAccept == 0: + err = ErrHandshakeBadSecAccept + default: + panic("unknown headers state") + } + } + return br, hs, err +} + +// PutReader returns bufio.Reader instance to the inner reuse pool. +// It is useful in rare cases, when Dialer.Dial() returns non-nil buffer which +// contains unprocessed buffered data, that was sent by the server quickly +// right after handshake. +func PutReader(br *bufio.Reader) { + pbufio.PutReader(br) +} + +// StatusError contains an unexpected status-line code from the server. +type StatusError int + +func (s StatusError) Error() string { + return "unexpected HTTP response status: " + strconv.Itoa(int(s)) +} + +func isTimeoutError(err error) bool { + t, ok := err.(net.Error) + return ok && t.Timeout() +} + +func matchSelectedExtensions(selected []byte, wanted, received []httphead.Option) ([]httphead.Option, error) { + if len(selected) == 0 { + return received, nil + } + var ( + index int + option httphead.Option + err error + ) + index = -1 + match := func() (ok bool) { + for _, want := range wanted { + // 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 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 + } + } + return false + } + ok := httphead.ScanOptions(selected, func(i int, name, attr, val []byte) httphead.Control { + if i != index { + // Met next option. + index = i + if i != 0 && !match() { + // Server returned non-requested extension. + err = ErrHandshakeBadExtensions + return httphead.ControlBreak + } + option = httphead.Option{Name: name} + } + if attr != nil { + option.Parameters.Set(attr, val) + } + return httphead.ControlContinue + }) + if !ok { + err = ErrMalformedResponse + return received, err + } + if !match() { + return received, ErrHandshakeBadExtensions + } + return received, err +} + +// setupContextDeadliner is a helper function that starts connection I/O +// interrupter goroutine. +// +// Started goroutine calls SetDeadline() with long time ago value when context +// become expired to make any I/O operations failed. It returns done function +// that stops started goroutine and maps error received from conn I/O methods +// to possible context expiration error. +// +// In concern with possible SetDeadline() call inside interrupter goroutine, +// caller passes pointer to its I/O error (even if it is nil) to done(&err). +// That is, even if I/O error is nil, context could be already expired and +// connection "poisoned" by SetDeadline() call. In that case done(&err) will +// store at *err ctx.Err() result. If err is caused not by timeout, it will +// leaved untouched. +func setupContextDeadliner(ctx context.Context, conn net.Conn) (done func(*error)) { + var ( + quit = make(chan struct{}) + interrupt = make(chan error, 1) + ) + go func() { + select { + case <-quit: + interrupt <- nil + case <-ctx.Done(): + // Cancel i/o immediately. + conn.SetDeadline(aLongTimeAgo) + interrupt <- ctx.Err() + } + }() + return func(err *error) { + close(quit) + // If ctx.Err() is non-nil and the original err is net.Error with + // Timeout() == true, then it means that I/O was canceled by us by + // SetDeadline(aLongTimeAgo) call, or by somebody else previously + // by conn.SetDeadline(x). + // + // Even on race condition when both deadlines are expired + // (SetDeadline() made not by us and context's), we prefer ctx.Err() to + // be returned. + if ctxErr := <-interrupt; ctxErr != nil && (*err == nil || isTimeoutError(*err)) { + *err = ctxErr + } + } +} diff --git a/vendor/github.com/gobwas/ws/dialer_tls_go17.go b/vendor/github.com/gobwas/ws/dialer_tls_go17.go new file mode 100644 index 0000000000..b606e0ad90 --- /dev/null +++ b/vendor/github.com/gobwas/ws/dialer_tls_go17.go @@ -0,0 +1,35 @@ +// +build !go1.8 + +package ws + +import "crypto/tls" + +func tlsCloneConfig(c *tls.Config) *tls.Config { + // NOTE: we copying SessionTicketsDisabled and SessionTicketKey here + // without calling inner c.initOnceServer somehow because we only could get + // here from the ws.Dialer code, which is obviously a client and makes + // tls.Client() when it gets new net.Conn. + return &tls.Config{ + Rand: c.Rand, + Time: c.Time, + Certificates: c.Certificates, + NameToCertificate: c.NameToCertificate, + GetCertificate: c.GetCertificate, + RootCAs: c.RootCAs, + NextProtos: c.NextProtos, + ServerName: c.ServerName, + ClientAuth: c.ClientAuth, + ClientCAs: c.ClientCAs, + InsecureSkipVerify: c.InsecureSkipVerify, + CipherSuites: c.CipherSuites, + PreferServerCipherSuites: c.PreferServerCipherSuites, + SessionTicketsDisabled: c.SessionTicketsDisabled, + SessionTicketKey: c.SessionTicketKey, + ClientSessionCache: c.ClientSessionCache, + MinVersion: c.MinVersion, + MaxVersion: c.MaxVersion, + CurvePreferences: c.CurvePreferences, + DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, + Renegotiation: c.Renegotiation, + } +} diff --git a/vendor/github.com/gobwas/ws/dialer_tls_go18.go b/vendor/github.com/gobwas/ws/dialer_tls_go18.go new file mode 100644 index 0000000000..5589ee5e79 --- /dev/null +++ b/vendor/github.com/gobwas/ws/dialer_tls_go18.go @@ -0,0 +1,10 @@ +//go:build go1.8 +// +build go1.8 + +package ws + +import "crypto/tls" + +func tlsCloneConfig(c *tls.Config) *tls.Config { + return c.Clone() +} diff --git a/vendor/github.com/gobwas/ws/doc.go b/vendor/github.com/gobwas/ws/doc.go new file mode 100644 index 0000000000..0118ce2cbb --- /dev/null +++ b/vendor/github.com/gobwas/ws/doc.go @@ -0,0 +1,81 @@ +/* +Package ws implements a client and server for the WebSocket protocol as +specified in RFC 6455. + +The main purpose of this package is to provide simple low-level API for +efficient work with protocol. + +Overview. + +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) + }) + +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 + } + + conn, err := ln.Accept() + 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 + } + + 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 + } + +As you can see, it stream friendly: + + const N = 42 + + ws.WriteHeader(ws.Header{ + Fin: true, + Length: N, + OpCode: ws.OpBinary, + }) + + io.CopyN(conn, rand.Reader, N) + +Or: + + header, err := ws.ReadHeader(conn) + if err != nil { + // handle err + } + + io.CopyN(ioutil.Discard, conn, header.Length) + +For more info see the documentation. +*/ +package ws diff --git a/vendor/github.com/gobwas/ws/errors.go b/vendor/github.com/gobwas/ws/errors.go new file mode 100644 index 0000000000..f5668b2be9 --- /dev/null +++ b/vendor/github.com/gobwas/ws/errors.go @@ -0,0 +1,59 @@ +package ws + +// RejectOption represents an option used to control the way connection is +// rejected. +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 *ConnectionRejectedError) { + err.reason = reason + } +} + +// RejectionStatus returns an option that makes connection to be rejected with +// given HTTP status code. +func RejectionStatus(code int) RejectOption { + return func(err *ConnectionRejectedError) { + err.code = code + } +} + +// RejectionHeader returns an option that makes connection to be rejected with +// given HTTP headers. +func RejectionHeader(h HandshakeHeader) RejectOption { + return func(err *ConnectionRejectedError) { + err.header = h + } +} + +// RejectConnectionError constructs an error that could be used to control the +// way handshake is rejected by Upgrader. +func RejectConnectionError(options ...RejectOption) error { + err := new(ConnectionRejectedError) + for _, opt := range options { + opt(err) + } + return err +} + +// ConnectionRejectedError represents a rejection of connection during +// WebSocket handshake error. +// +// 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 *ConnectionRejectedError) Error() string { + return r.reason +} + +func (r *ConnectionRejectedError) StatusCode() int { + return r.code +} diff --git a/vendor/github.com/gobwas/ws/frame.go b/vendor/github.com/gobwas/ws/frame.go new file mode 100644 index 0000000000..ae10144e17 --- /dev/null +++ b/vendor/github.com/gobwas/ws/frame.go @@ -0,0 +1,420 @@ +package ws + +import ( + "bytes" + "encoding/binary" + "math/rand" +) + +// Constants defined by specification. +const ( + // All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented. + MaxControlFramePayloadSize = 125 +) + +// OpCode represents operation code. +type OpCode byte + +// Operation codes defined by specification. +// See https://tools.ietf.org/html/rfc6455#section-5.2 +const ( + OpContinuation OpCode = 0x0 + OpText OpCode = 0x1 + OpBinary OpCode = 0x2 + OpClose OpCode = 0x8 + OpPing OpCode = 0x9 + OpPong OpCode = 0xa +) + +// IsControl checks whether the c is control operation code. +// See https://tools.ietf.org/html/rfc6455#section-5.5 +func (c OpCode) IsControl() bool { + // RFC6455: Control frames are identified by opcodes where + // the most significant bit of the opcode is 1. + // + // Note that OpCode is only 4 bit length. + return c&0x8 != 0 +} + +// IsData checks whether the c is data operation code. +// See https://tools.ietf.org/html/rfc6455#section-5.6 +func (c OpCode) IsData() bool { + // RFC6455: Data frames (e.g., non-control frames) are identified by opcodes + // where the most significant bit of the opcode is 0. + // + // Note that OpCode is only 4 bit length. + return c&0x8 == 0 +} + +// IsReserved checks whether the c is reserved operation code. +// See https://tools.ietf.org/html/rfc6455#section-5.2 +func (c OpCode) IsReserved() bool { + // RFC6455: + // %x3-7 are reserved for further non-control frames + // %xB-F are reserved for further control frames + return (0x3 <= c && c <= 0x7) || (0xb <= c && c <= 0xf) +} + +// StatusCode represents the encoded reason for closure of websocket connection. +// +// There are few helper methods on StatusCode that helps to define a range in +// which given code is lay in. accordingly to ranges defined in specification. +// +// See https://tools.ietf.org/html/rfc6455#section-7.4 +type StatusCode uint16 + +// StatusCodeRange describes range of StatusCode values. +type StatusCodeRange struct { + Min, Max StatusCode +} + +// Status code ranges defined by specification. +// See https://tools.ietf.org/html/rfc6455#section-7.4.2 +var ( + StatusRangeNotInUse = StatusCodeRange{0, 999} + StatusRangeProtocol = StatusCodeRange{1000, 2999} + StatusRangeApplication = StatusCodeRange{3000, 3999} + StatusRangePrivate = StatusCodeRange{4000, 4999} +) + +// Status codes defined by specification. +// See https://tools.ietf.org/html/rfc6455#section-7.4.1 +const ( + StatusNormalClosure StatusCode = 1000 + StatusGoingAway StatusCode = 1001 + StatusProtocolError StatusCode = 1002 + StatusUnsupportedData StatusCode = 1003 + StatusNoMeaningYet StatusCode = 1004 + StatusInvalidFramePayloadData StatusCode = 1007 + StatusPolicyViolation StatusCode = 1008 + StatusMessageTooBig StatusCode = 1009 + StatusMandatoryExt StatusCode = 1010 + StatusInternalServerError StatusCode = 1011 + StatusTLSHandshake StatusCode = 1015 + + // StatusAbnormalClosure is a special code designated for use in + // applications. + StatusAbnormalClosure StatusCode = 1006 + + // StatusNoStatusRcvd is a special code designated for use in applications. + StatusNoStatusRcvd StatusCode = 1005 +) + +// In reports whether the code is defined in given range. +func (s StatusCode) In(r StatusCodeRange) bool { + return r.Min <= s && s <= r.Max +} + +// Empty reports whether the code is empty. +// Empty code has no any meaning neither app level codes nor other. +// This method is useful just to check that code is golang default value 0. +func (s StatusCode) Empty() bool { + return s == 0 +} + +// IsNotUsed reports whether the code is predefined in not used range. +func (s StatusCode) IsNotUsed() bool { + return s.In(StatusRangeNotInUse) +} + +// IsApplicationSpec reports whether the code should be defined by +// application, framework or libraries specification. +func (s StatusCode) IsApplicationSpec() bool { + return s.In(StatusRangeApplication) +} + +// IsPrivateSpec reports whether the code should be defined privately. +func (s StatusCode) IsPrivateSpec() bool { + return s.In(StatusRangePrivate) +} + +// IsProtocolSpec reports whether the code should be defined by protocol specification. +func (s StatusCode) IsProtocolSpec() bool { + return s.In(StatusRangeProtocol) +} + +// IsProtocolDefined reports whether the code is already defined by protocol specification. +func (s StatusCode) IsProtocolDefined() bool { + switch s { + case StatusNormalClosure, + StatusGoingAway, + StatusProtocolError, + StatusUnsupportedData, + StatusInvalidFramePayloadData, + StatusPolicyViolation, + StatusMessageTooBig, + StatusMandatoryExt, + StatusInternalServerError, + StatusNoStatusRcvd, + StatusAbnormalClosure, + StatusTLSHandshake: + return true + } + return false +} + +// IsProtocolReserved reports whether the code is defined by protocol specification +// to be reserved only for application usage purpose. +func (s StatusCode) IsProtocolReserved() bool { + switch s { + // [RFC6455]: {1005,1006,1015} is a reserved value and MUST NOT be set as a status code in a + // Close control frame by an endpoint. + case StatusNoStatusRcvd, StatusAbnormalClosure, StatusTLSHandshake: + return true + default: + return false + } +} + +// Compiled control frames for common use cases. +// For construct-serialize optimizations. +var ( + CompiledPing = MustCompileFrame(NewPingFrame(nil)) + CompiledPong = MustCompileFrame(NewPongFrame(nil)) + CompiledClose = MustCompileFrame(NewCloseFrame(nil)) + + CompiledCloseNormalClosure = MustCompileFrame(closeFrameNormalClosure) + CompiledCloseGoingAway = MustCompileFrame(closeFrameGoingAway) + CompiledCloseProtocolError = MustCompileFrame(closeFrameProtocolError) + CompiledCloseUnsupportedData = MustCompileFrame(closeFrameUnsupportedData) + CompiledCloseNoMeaningYet = MustCompileFrame(closeFrameNoMeaningYet) + CompiledCloseInvalidFramePayloadData = MustCompileFrame(closeFrameInvalidFramePayloadData) + CompiledClosePolicyViolation = MustCompileFrame(closeFramePolicyViolation) + CompiledCloseMessageTooBig = MustCompileFrame(closeFrameMessageTooBig) + CompiledCloseMandatoryExt = MustCompileFrame(closeFrameMandatoryExt) + CompiledCloseInternalServerError = MustCompileFrame(closeFrameInternalServerError) + CompiledCloseTLSHandshake = MustCompileFrame(closeFrameTLSHandshake) +) + +// Header represents websocket frame header. +// See https://tools.ietf.org/html/rfc6455#section-5.2 +type Header struct { + Fin bool + Rsv byte + OpCode OpCode + Masked bool + Mask [4]byte + Length int64 +} + +// Rsv1 reports whether the header has first rsv bit set. +func (h Header) Rsv1() bool { return h.Rsv&bit5 != 0 } + +// Rsv2 reports whether the header has second rsv bit set. +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 { + Header Header + Payload []byte +} + +// NewFrame creates frame with given operation code, +// flag of completeness and payload bytes. +func NewFrame(op OpCode, fin bool, p []byte) Frame { + return Frame{ + Header: Header{ + Fin: fin, + OpCode: op, + Length: int64(len(p)), + }, + Payload: p, + } +} + +// NewTextFrame creates text frame with p as payload. +// Note that p is not copied. +func NewTextFrame(p []byte) Frame { + return NewFrame(OpText, true, p) +} + +// NewBinaryFrame creates binary frame with p as payload. +// Note that p is not copied. +func NewBinaryFrame(p []byte) Frame { + return NewFrame(OpBinary, true, p) +} + +// NewPingFrame creates ping frame with p as payload. +// Note that p is not copied. +// Note that p must have length of MaxControlFramePayloadSize bytes or less due +// to RFC. +func NewPingFrame(p []byte) Frame { + return NewFrame(OpPing, true, p) +} + +// NewPongFrame creates pong frame with p as payload. +// Note that p is not copied. +// Note that p must have length of MaxControlFramePayloadSize bytes or less due +// to RFC. +func NewPongFrame(p []byte) Frame { + return NewFrame(OpPong, true, p) +} + +// NewCloseFrame creates close frame with given close body. +// Note that p is not copied. +// Note that p must have length of MaxControlFramePayloadSize bytes or less due +// to RFC. +func NewCloseFrame(p []byte) Frame { + return NewFrame(OpClose, true, p) +} + +// NewCloseFrameBody encodes a closure code and a reason into a binary +// representation. +// +// It returns slice which is at most MaxControlFramePayloadSize bytes length. +// If the reason is too big it will be cropped to fit the limit defined by the +// spec. +// +// See https://tools.ietf.org/html/rfc6455#section-5.5 +func NewCloseFrameBody(code StatusCode, reason string) []byte { + n := min(2+len(reason), MaxControlFramePayloadSize) + p := make([]byte, n) + + crop := min(MaxControlFramePayloadSize-2, len(reason)) + PutCloseFrameBody(p, code, reason[:crop]) + + return p +} + +// PutCloseFrameBody encodes code and reason into buf. +// +// It will panic if the buffer is too small to accommodate a code or a reason. +// +// PutCloseFrameBody does not check buffer to be RFC compliant, but note that +// by RFC it must be at most MaxControlFramePayloadSize. +func PutCloseFrameBody(p []byte, code StatusCode, reason string) { + _ = p[1+len(reason)] + binary.BigEndian.PutUint16(p, uint16(code)) + copy(p[2:], reason) +} + +// MaskFrame masks frame and returns frame with masked payload and Mask header's field set. +// Note that it copies f payload to prevent collisions. +// For less allocations you could use MaskFrameInPlace or construct frame manually. +func MaskFrame(f Frame) Frame { + return MaskFrameWith(f, NewMask()) +} + +// MaskFrameWith masks frame with given mask and returns frame +// with masked payload and Mask header's field set. +// Note that it copies f payload to prevent collisions. +// For less allocations you could use MaskFrameInPlaceWith or construct frame manually. +func MaskFrameWith(f Frame, mask [4]byte) Frame { + // TODO(gobwas): check CopyCipher ws copy() Cipher(). + p := make([]byte, len(f.Payload)) + copy(p, f.Payload) + f.Payload = p + return MaskFrameInPlaceWith(f, mask) +} + +// MaskFrameInPlace masks frame 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 +// modifies f.Payload inplace. +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 +// modifies f.Payload inplace. +func MaskFrameInPlaceWith(f Frame, m [4]byte) Frame { + f.Header.Masked = true + f.Header.Mask = m + Cipher(f.Payload, m, 0) + return f +} + +// NewMask creates new random mask. +func NewMask() (ret [4]byte) { + binary.BigEndian.PutUint32(ret[:], rand.Uint32()) + return ret +} + +// CompileFrame returns byte representation of given frame. +// In terms of memory consumption it is useful to precompile static frames +// which are often used. +func CompileFrame(f Frame) (bts []byte, err error) { + buf := bytes.NewBuffer(make([]byte, 0, 16)) + err = WriteFrame(buf, f) + bts = buf.Bytes() + return bts, err +} + +// MustCompileFrame is like CompileFrame but panics if frame can not be +// encoded. +func MustCompileFrame(f Frame) []byte { + bts, err := CompileFrame(f) + if err != nil { + panic(err) + } + return bts +} + +func makeCloseFrame(code StatusCode) Frame { + return NewCloseFrame(NewCloseFrameBody(code, "")) +} + +var ( + closeFrameNormalClosure = makeCloseFrame(StatusNormalClosure) + closeFrameGoingAway = makeCloseFrame(StatusGoingAway) + closeFrameProtocolError = makeCloseFrame(StatusProtocolError) + closeFrameUnsupportedData = makeCloseFrame(StatusUnsupportedData) + closeFrameNoMeaningYet = makeCloseFrame(StatusNoMeaningYet) + closeFrameInvalidFramePayloadData = makeCloseFrame(StatusInvalidFramePayloadData) + closeFramePolicyViolation = makeCloseFrame(StatusPolicyViolation) + closeFrameMessageTooBig = makeCloseFrame(StatusMessageTooBig) + closeFrameMandatoryExt = makeCloseFrame(StatusMandatoryExt) + closeFrameInternalServerError = makeCloseFrame(StatusInternalServerError) + closeFrameTLSHandshake = makeCloseFrame(StatusTLSHandshake) +) diff --git a/vendor/github.com/gobwas/ws/http.go b/vendor/github.com/gobwas/ws/http.go new file mode 100644 index 0000000000..129e77ec14 --- /dev/null +++ b/vendor/github.com/gobwas/ws/http.go @@ -0,0 +1,503 @@ +package ws + +import ( + "bufio" + "bytes" + "io" + "net/http" + "net/url" + "strconv" + + "github.com/gobwas/httphead" +) + +const ( + crlf = "\r\n" + colonAndSpace = ": " + commaAndSpace = ", " +) + +const ( + textHeadUpgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n" +) + +var ( + textHeadBadRequest = statusText(http.StatusBadRequest) + textHeadInternalServerError = statusText(http.StatusInternalServerError) + textHeadUpgradeRequired = statusText(http.StatusUpgradeRequired) + + textTailErrHandshakeBadProtocol = errorText(ErrHandshakeBadProtocol) + textTailErrHandshakeBadMethod = errorText(ErrHandshakeBadMethod) + textTailErrHandshakeBadHost = errorText(ErrHandshakeBadHost) + textTailErrHandshakeBadUpgrade = errorText(ErrHandshakeBadUpgrade) + textTailErrHandshakeBadConnection = errorText(ErrHandshakeBadConnection) + textTailErrHandshakeBadSecAccept = errorText(ErrHandshakeBadSecAccept) + textTailErrHandshakeBadSecKey = errorText(ErrHandshakeBadSecKey) + textTailErrHandshakeBadSecVersion = errorText(ErrHandshakeBadSecVersion) + textTailErrUpgradeRequired = errorText(ErrHandshakeUpgradeRequired) +) + +const ( + // Every new header must be added to TestHeaderNames test. + headerHost = "Host" + headerUpgrade = "Upgrade" + headerConnection = "Connection" + headerSecVersion = "Sec-WebSocket-Version" + headerSecProtocol = "Sec-WebSocket-Protocol" + headerSecExtensions = "Sec-WebSocket-Extensions" + headerSecKey = "Sec-WebSocket-Key" + headerSecAccept = "Sec-WebSocket-Accept" + + 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 ( + specHeaderValueUpgrade = []byte("websocket") + specHeaderValueConnection = []byte("Upgrade") + specHeaderValueConnectionLower = []byte("upgrade") + specHeaderValueSecVersion = []byte("13") +) + +var ( + httpVersion1_0 = []byte("HTTP/1.0") + httpVersion1_1 = []byte("HTTP/1.1") + httpVersionPrefix = []byte("HTTP/") +) + +type httpRequestLine struct { + method, uri []byte + major, minor int +} + +type httpResponseLine struct { + major, minor int + status int + reason []byte +} + +// httpParseRequestLine parses http request line like "GET / HTTP/1.0". +func httpParseRequestLine(line []byte) (req httpRequestLine, err error) { + var proto []byte + req.method, req.uri, proto = bsplit3(line, ' ') + + var ok bool + req.major, req.minor, ok = httpParseVersion(proto) + if !ok { + err = ErrMalformedRequest + } + return req, err +} + +func httpParseResponseLine(line []byte) (resp httpResponseLine, err error) { + var ( + proto []byte + status []byte + ) + proto, status, resp.reason = bsplit3(line, ' ') + + var ok bool + resp.major, resp.minor, ok = httpParseVersion(proto) + if !ok { + return resp, ErrMalformedResponse + } + + var convErr error + resp.status, convErr = asciiToInt(status) + if convErr != nil { + return resp, ErrMalformedResponse + } + + return resp, nil +} + +// httpParseVersion parses major and minor version of HTTP protocol. It returns +// parsed values and true if parse is ok. +func httpParseVersion(bts []byte) (major, minor int, ok bool) { + switch { + case bytes.Equal(bts, httpVersion1_0): + return 1, 0, true + case bytes.Equal(bts, httpVersion1_1): + return 1, 1, true + case len(bts) < 8: + return 0, 0, false + case !bytes.Equal(bts[:5], httpVersionPrefix): + return 0, 0, false + } + + bts = bts[5:] + + dot := bytes.IndexByte(bts, '.') + if dot == -1 { + return 0, 0, false + } + var err error + major, err = asciiToInt(bts[:dot]) + if err != nil { + return major, 0, false + } + minor, err = asciiToInt(bts[dot+1:]) + if err != nil { + return major, minor, false + } + + return major, minor, true +} + +// httpParseHeaderLine parses HTTP header as key-value pair. It returns parsed +// values and true if parse is ok. +func httpParseHeaderLine(line []byte) (k, v []byte, ok bool) { + colon := bytes.IndexByte(line, ':') + if colon == -1 { + return nil, nil, false + } + + k = btrim(line[:colon]) + // TODO(gobwas): maybe use just lower here? + canonicalizeHeaderKey(k) + + v = btrim(line[colon+1:]) + + return k, v, true +} + +// httpGetHeader is the same as textproto.MIMEHeader.Get, except the thing, +// that key is already canonical. This helps to increase performance. +func httpGetHeader(h http.Header, key string) string { + if h == nil { + return "" + } + v := h[key] + if len(v) == 0 { + return "" + } + return v[0] +} + +// The request MAY include a header field with the name +// |Sec-WebSocket-Protocol|. If present, this value indicates one or more +// comma-separated subprotocol the client wishes to speak, ordered by +// preference. The elements that comprise this value MUST be non-empty strings +// with characters in the range U+0021 to U+007E not including separator +// characters as defined in [RFC2616] and MUST all be unique strings. The ABNF +// for the value of this header field is 1#token, where the definitions of +// constructs and rules are as given in [RFC2616]. +func strSelectProtocol(h string, check func(string) bool) (ret string, ok bool) { + ok = httphead.ScanTokens(strToBytes(h), func(v []byte) bool { + if check(btsToString(v)) { + ret = string(v) + return false + } + return true + }) + 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 { + if check(v) { + selected = v + return false + } + return true + }) + if ok && selected != nil { + return string(selected), true + } + return ret, ok +} + +func btsSelectExtensions(h []byte, selected []httphead.Option, check func(httphead.Option) bool) ([]httphead.Option, bool) { + s := httphead.OptionSelector{ + 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) + bw.WriteString(crlf) +} + +func httpWriteHeaderBts(bw *bufio.Writer, key string, value []byte) { + httpWriteHeaderKey(bw, key) + bw.Write(value) + bw.WriteString(crlf) +} + +func httpWriteHeaderKey(bw *bufio.Writer, key string) { + bw.WriteString(key) + bw.WriteString(colonAndSpace) +} + +func httpWriteUpgradeRequest( + bw *bufio.Writer, + u *url.URL, + nonce []byte, + protocols []string, + extensions []httphead.Option, + header HandshakeHeader, +) { + bw.WriteString("GET ") + bw.WriteString(u.RequestURI()) + bw.WriteString(" HTTP/1.1\r\n") + + httpWriteHeader(bw, headerHost, u.Host) + + httpWriteHeaderBts(bw, headerUpgrade, specHeaderValueUpgrade) + httpWriteHeaderBts(bw, headerConnection, specHeaderValueConnection) + httpWriteHeaderBts(bw, headerSecVersion, specHeaderValueSecVersion) + + // NOTE: write nonce bytes as a string to prevent heap allocation – + // WriteString() copy given string into its inner buffer, unlike Write() + // which may write p directly to the underlying io.Writer – which in turn + // will lead to p escape. + httpWriteHeader(bw, headerSecKey, btsToString(nonce)) + + if len(protocols) > 0 { + httpWriteHeaderKey(bw, headerSecProtocol) + for i, p := range protocols { + if i > 0 { + bw.WriteString(commaAndSpace) + } + bw.WriteString(p) + } + bw.WriteString(crlf) + } + + if len(extensions) > 0 { + httpWriteHeaderKey(bw, headerSecExtensions) + httphead.WriteOptions(bw, extensions) + bw.WriteString(crlf) + } + + if header != nil { + header.WriteTo(bw) + } + + bw.WriteString(crlf) +} + +func httpWriteResponseUpgrade(bw *bufio.Writer, nonce []byte, hs Handshake, header HandshakeHeaderFunc) { + bw.WriteString(textHeadUpgrade) + + httpWriteHeaderKey(bw, headerSecAccept) + writeAccept(bw, nonce) + bw.WriteString(crlf) + + if hs.Protocol != "" { + httpWriteHeader(bw, headerSecProtocol, hs.Protocol) + } + if len(hs.Extensions) > 0 { + httpWriteHeaderKey(bw, headerSecExtensions) + httphead.WriteOptions(bw, hs.Extensions) + bw.WriteString(crlf) + } + if header != nil { + header(bw) + } + + bw.WriteString(crlf) +} + +func httpWriteResponseError(bw *bufio.Writer, err error, code int, header HandshakeHeaderFunc) { + switch code { + case http.StatusBadRequest: + bw.WriteString(textHeadBadRequest) + case http.StatusInternalServerError: + bw.WriteString(textHeadInternalServerError) + case http.StatusUpgradeRequired: + bw.WriteString(textHeadUpgradeRequired) + default: + writeStatusText(bw, code) + } + + // Write custom headers. + if header != nil { + header(bw) + } + + switch err { + case ErrHandshakeBadProtocol: + bw.WriteString(textTailErrHandshakeBadProtocol) + case ErrHandshakeBadMethod: + bw.WriteString(textTailErrHandshakeBadMethod) + case ErrHandshakeBadHost: + bw.WriteString(textTailErrHandshakeBadHost) + case ErrHandshakeBadUpgrade: + bw.WriteString(textTailErrHandshakeBadUpgrade) + case ErrHandshakeBadConnection: + bw.WriteString(textTailErrHandshakeBadConnection) + case ErrHandshakeBadSecAccept: + bw.WriteString(textTailErrHandshakeBadSecAccept) + case ErrHandshakeBadSecKey: + bw.WriteString(textTailErrHandshakeBadSecKey) + case ErrHandshakeBadSecVersion: + bw.WriteString(textTailErrHandshakeBadSecVersion) + case ErrHandshakeUpgradeRequired: + bw.WriteString(textTailErrUpgradeRequired) + case nil: + bw.WriteString(crlf) + default: + writeErrorText(bw, err) + } +} + +func writeStatusText(bw *bufio.Writer, code int) { + bw.WriteString("HTTP/1.1 ") + bw.WriteString(strconv.Itoa(code)) + bw.WriteByte(' ') + bw.WriteString(http.StatusText(code)) + bw.WriteString(crlf) + bw.WriteString("Content-Type: text/plain; charset=utf-8") + bw.WriteString(crlf) +} + +func writeErrorText(bw *bufio.Writer, err error) { + body := err.Error() + bw.WriteString("Content-Length: ") + bw.WriteString(strconv.Itoa(len(body))) + bw.WriteString(crlf) + bw.WriteString(crlf) + bw.WriteString(body) +} + +// httpError is like the http.Error with WebSocket context exception. +func httpError(w http.ResponseWriter, body string, code int) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("Content-Length", strconv.Itoa(len(body))) + w.WriteHeader(code) + w.Write([]byte(body)) +} + +// statusText is a non-performant status text generator. +// NOTE: Used only to generate constants. +func statusText(code int) string { + var buf bytes.Buffer + bw := bufio.NewWriter(&buf) + writeStatusText(bw, code) + bw.Flush() + return buf.String() +} + +// errorText is a non-performant error text generator. +// NOTE: Used only to generate constants. +func errorText(err error) string { + var buf bytes.Buffer + bw := bufio.NewWriter(&buf) + writeErrorText(bw, err) + bw.Flush() + return buf.String() +} + +// HandshakeHeader is the interface that writes both upgrade request or +// response headers into a given io.Writer. +type HandshakeHeader interface { + io.WriterTo +} + +// HandshakeHeaderString is an adapter to allow the use of headers represented +// by ordinary string as HandshakeHeader. +type HandshakeHeaderString string + +// WriteTo implements HandshakeHeader (and io.WriterTo) interface. +func (s HandshakeHeaderString) WriteTo(w io.Writer) (int64, error) { + n, err := io.WriteString(w, string(s)) + return int64(n), err +} + +// HandshakeHeaderBytes is an adapter to allow the use of headers represented +// by ordinary slice of bytes as HandshakeHeader. +type HandshakeHeaderBytes []byte + +// WriteTo implements HandshakeHeader (and io.WriterTo) interface. +func (b HandshakeHeaderBytes) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(b) + return int64(n), err +} + +// HandshakeHeaderFunc is an adapter to allow the use of headers represented by +// ordinary function as HandshakeHeader. +type HandshakeHeaderFunc func(io.Writer) (int64, error) + +// WriteTo implements HandshakeHeader (and io.WriterTo) interface. +func (f HandshakeHeaderFunc) WriteTo(w io.Writer) (int64, error) { + return f(w) +} + +// HandshakeHeaderHTTP is an adapter to allow the use of http.Header as +// HandshakeHeader. +type HandshakeHeaderHTTP http.Header + +// WriteTo implements HandshakeHeader (and io.WriterTo) interface. +func (h HandshakeHeaderHTTP) WriteTo(w io.Writer) (int64, error) { + wr := writer{w: w} + err := http.Header(h).Write(&wr) + return wr.n, err +} + +type writer struct { + n int64 + w io.Writer +} + +func (w *writer) WriteString(s string) (int, error) { + n, err := io.WriteString(w.w, s) + w.n += int64(n) + return n, err +} + +func (w *writer) Write(p []byte) (int, error) { + n, err := w.w.Write(p) + w.n += int64(n) + return n, err +} diff --git a/vendor/github.com/gobwas/ws/nonce.go b/vendor/github.com/gobwas/ws/nonce.go new file mode 100644 index 0000000000..7b0edd9725 --- /dev/null +++ b/vendor/github.com/gobwas/ws/nonce.go @@ -0,0 +1,78 @@ +package ws + +import ( + "bufio" + "bytes" + "crypto/sha1" + "encoding/base64" + "fmt" + "math/rand" +) + +const ( + // RFC6455: The value of this header field MUST be a nonce consisting of a + // randomly selected 16-byte value that has been base64-encoded (see + // Section 4 of [RFC4648]). The nonce MUST be selected randomly for each + // connection. + nonceKeySize = 16 + nonceSize = 24 // base64.StdEncoding.EncodedLen(nonceKeySize) + + // RFC6455: The value of this header field is constructed by concatenating + // /key/, defined above in step 4 in Section 4.2.2, with the string + // "258EAFA5- E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this + // concatenated value to obtain a 20-byte value and base64- encoding (see + // Section 4 of [RFC4648]) this 20-byte hash. + acceptSize = 28 // base64.StdEncoding.EncodedLen(sha1.Size) +) + +// initNonce fills given slice with random base64-encoded nonce bytes. +func initNonce(dst []byte) { + // NOTE: bts does not escape. + bts := make([]byte, nonceKeySize) + if _, err := rand.Read(bts); err != nil { + panic(fmt.Sprintf("rand read error: %s", err)) + } + base64.StdEncoding.Encode(dst, bts) +} + +// checkAcceptFromNonce reports whether given accept bytes are valid for given +// nonce bytes. +func checkAcceptFromNonce(accept, nonce []byte) bool { + if len(accept) != acceptSize { + return false + } + // NOTE: expect does not escape. + expect := make([]byte, acceptSize) + initAcceptFromNonce(expect, nonce) + return bytes.Equal(expect, accept) +} + +// initAcceptFromNonce fills given slice with accept bytes generated from given +// nonce bytes. Given buffer should be exactly acceptSize bytes. +func initAcceptFromNonce(accept, nonce []byte) { + const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + if len(accept) != acceptSize { + panic("accept buffer is invalid") + } + if len(nonce) != nonceSize { + panic("nonce is invalid") + } + + p := make([]byte, nonceSize+len(magic)) + copy(p[:nonceSize], nonce) + copy(p[nonceSize:], magic) + + sum := sha1.Sum(p) + base64.StdEncoding.Encode(accept, sum[:]) +} + +func writeAccept(bw *bufio.Writer, nonce []byte) (int, error) { + accept := make([]byte, acceptSize) + initAcceptFromNonce(accept, nonce) + // NOTE: write accept bytes as a string to prevent heap allocation – + // WriteString() copy given string into its inner buffer, unlike Write() + // which may write p directly to the underlying io.Writer – which in turn + // will lead to p escape. + return bw.WriteString(btsToString(accept)) +} diff --git a/vendor/github.com/gobwas/ws/read.go b/vendor/github.com/gobwas/ws/read.go new file mode 100644 index 0000000000..1771816a1d --- /dev/null +++ b/vendor/github.com/gobwas/ws/read.go @@ -0,0 +1,147 @@ +package ws + +import ( + "encoding/binary" + "fmt" + "io" +) + +// Errors used by frame reader. +var ( + ErrHeaderLengthMSB = fmt.Errorf("header error: the most significant bit must be 0") + ErrHeaderLengthUnexpected = fmt.Errorf("header error: unexpected payload length bits") +) + +// ReadHeader reads a frame header from r. +func ReadHeader(r io.Reader) (h Header, err error) { + // Make slice of bytes with capacity 12 that could hold any header. + // + // The maximum header size is 14, but due to the 2 hop reads, + // after first hop that reads first 2 constant bytes, we could reuse 2 bytes. + // So 14 - 2 = 12. + bts := make([]byte, 2, MaxHeaderSize-2) + + // Prepare to hold first 2 bytes to choose size of next read. + _, err = io.ReadFull(r, bts) + if err != nil { + return h, err + } + + h.Fin = bts[0]&bit0 != 0 + h.Rsv = (bts[0] & 0x70) >> 4 + h.OpCode = OpCode(bts[0] & 0x0f) + + var extra int + + if bts[1]&bit0 != 0 { + h.Masked = true + extra += 4 + } + + length := bts[1] & 0x7f + switch { + case length < 126: + h.Length = int64(length) + + case length == 126: + extra += 2 + + case length == 127: + extra += 8 + + default: + err = ErrHeaderLengthUnexpected + return h, err + } + + if extra == 0 { + return h, err + } + + // Increase len of bts to extra bytes need to read. + // Overwrite first 2 bytes that was read before. + bts = bts[:extra] + _, err = io.ReadFull(r, bts) + if err != nil { + return h, err + } + + switch { + case length == 126: + h.Length = int64(binary.BigEndian.Uint16(bts[:2])) + bts = bts[2:] + + case length == 127: + if bts[0]&0x80 != 0 { + err = ErrHeaderLengthMSB + return h, err + } + h.Length = int64(binary.BigEndian.Uint64(bts[:8])) + bts = bts[8:] + } + + if h.Masked { + copy(h.Mask[:], bts) + } + + return h, nil +} + +// ReadFrame reads a frame from r. +// It is not designed for high optimized use case cause it makes allocation +// for frame.Header.Length size inside to read frame payload into. +// +// Note that ReadFrame does not unmask payload. +func ReadFrame(r io.Reader) (f Frame, err error) { + f.Header, err = ReadHeader(r) + if err != nil { + return f, err + } + + if f.Header.Length > 0 { + // int(f.Header.Length) is safe here cause we have + // checked it for overflow above in ReadHeader. + f.Payload = make([]byte, int(f.Header.Length)) + _, err = io.ReadFull(r, f.Payload) + } + + return f, err +} + +// MustReadFrame is like ReadFrame but panics if frame can not be read. +func MustReadFrame(r io.Reader) Frame { + f, err := ReadFrame(r) + if err != nil { + panic(err) + } + return f +} + +// ParseCloseFrameData parses close frame status code and closure reason if any provided. +// If there is no status code in the payload +// the empty status code is returned (code.Empty()) with empty string as a reason. +func ParseCloseFrameData(payload []byte) (code StatusCode, reason string) { + if len(payload) < 2 { + // We returning empty StatusCode here, preventing the situation + // when endpoint really sent code 1005 and we should return ProtocolError on that. + // + // 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 code, reason + } + code = StatusCode(binary.BigEndian.Uint16(payload)) + reason = string(payload[2:]) + 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 code, reason + } + code = StatusCode(binary.BigEndian.Uint16(payload)) + reason = btsToString(payload[2:]) + return code, reason +} diff --git a/vendor/github.com/gobwas/ws/server.go b/vendor/github.com/gobwas/ws/server.go new file mode 100644 index 0000000000..f6cc8af32c --- /dev/null +++ b/vendor/github.com/gobwas/ws/server.go @@ -0,0 +1,663 @@ +package ws + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net" + "net/http" + "strings" + "time" + + "github.com/gobwas/httphead" + "github.com/gobwas/pool/pbufio" +) + +// Constants used by ConnUpgrader. +const ( + DefaultServerReadBufferSize = 4096 + DefaultServerWriteBufferSize = 512 +) + +// Errors used by both client and server when preparing WebSocket handshake. +var ( + ErrHandshakeBadProtocol = RejectConnectionError( + RejectionStatus(http.StatusHTTPVersionNotSupported), + RejectionReason("handshake error: bad HTTP protocol version"), + ) + ErrHandshakeBadMethod = RejectConnectionError( + RejectionStatus(http.StatusMethodNotAllowed), + RejectionReason("handshake error: bad HTTP request method"), + ) + ErrHandshakeBadHost = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerHost)), + ) + ErrHandshakeBadUpgrade = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerUpgrade)), + ) + ErrHandshakeBadConnection = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerConnection)), + ) + ErrHandshakeBadSecAccept = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecAccept)), + ) + ErrHandshakeBadSecKey = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecKey)), + ) + ErrHandshakeBadSecVersion = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)), + ) +) + +// ErrMalformedResponse is returned by Dialer to indicate that server response +// can not be parsed. +var ErrMalformedResponse = fmt.Errorf("malformed HTTP response") + +// ErrMalformedRequest is returned when HTTP request can not be parsed. +var ErrMalformedRequest = RejectConnectionError( + RejectionStatus(http.StatusBadRequest), + RejectionReason("malformed HTTP request"), +) + +// ErrHandshakeUpgradeRequired is returned by Upgrader to indicate that +// connection is rejected because given WebSocket version is malformed. +// +// According to RFC6455: +// If this version does not match a version understood by the server, the +// server MUST abort the WebSocket handshake described in this section and +// instead send an appropriate HTTP error code (such as 426 Upgrade Required) +// and a |Sec-WebSocket-Version| header field indicating the version(s) the +// server is capable of understanding. +var ErrHandshakeUpgradeRequired = RejectConnectionError( + RejectionStatus(http.StatusUpgradeRequired), + RejectionHeader(HandshakeHeaderString(headerSecVersion+": 13\r\n")), + RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)), +) + +// ErrNotHijacker is an error returned when http.ResponseWriter does not +// implement http.Hijacker interface. +var ErrNotHijacker = RejectConnectionError( + RejectionStatus(http.StatusInternalServerError), + RejectionReason("given http.ResponseWriter is not a http.Hijacker"), +) + +// DefaultHTTPUpgrader is an HTTPUpgrader that holds no options and is used by +// UpgradeHTTP function. +var DefaultHTTPUpgrader HTTPUpgrader + +// UpgradeHTTP is like HTTPUpgrader{}.Upgrade(). +func UpgradeHTTP(r *http.Request, w http.ResponseWriter) (net.Conn, *bufio.ReadWriter, Handshake, error) { + return DefaultHTTPUpgrader.Upgrade(r, w) +} + +// DefaultUpgrader is an Upgrader that holds no options and is used by Upgrade +// function. +var DefaultUpgrader Upgrader + +// Upgrade is like Upgrader{}.Upgrade(). +func Upgrade(conn io.ReadWriter) (Handshake, error) { + return DefaultUpgrader.Upgrade(conn) +} + +// HTTPUpgrader contains options for upgrading connection to websocket from +// net/http Handler arguments. +type HTTPUpgrader struct { + // Timeout is the maximum amount of time an Upgrade() will spent while + // writing handshake response. + // + // The default is no timeout. + Timeout time.Duration + + // Header is an optional http.Header mapping that could be used to + // write additional headers to the handshake response. + // + // Note that if present, it will be written in any result of handshake. + Header http.Header + + // Protocol is the select function that is used to select subprotocol from + // list requested by client. If this field is set, then the first matched + // protocol is sent to a client as negotiated. + Protocol func(string) bool + + // 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. +// +// It hijacks net.Conn from w and returns received net.Conn and +// bufio.ReadWriter. On successful handshake it returns Handshake struct +// describing handshake info. +func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.Conn, rw *bufio.ReadWriter, hs Handshake, err error) { + // Hijack connection first to get the ability to write rejection errors the + // same way as in Upgrader. + hj, ok := w.(http.Hijacker) + if ok { + conn, rw, err = hj.Hijack() + } else { + err = ErrNotHijacker + } + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return conn, rw, hs, err + } + + // See https://tools.ietf.org/html/rfc6455#section-4.1 + // The method of the request MUST be GET, and the HTTP version MUST be at least 1.1. + var nonce string + if r.Method != http.MethodGet { + err = ErrHandshakeBadMethod + } else if r.ProtoMajor < 1 || (r.ProtoMajor == 1 && r.ProtoMinor < 1) { + err = ErrHandshakeBadProtocol + } else if r.Host == "" { + err = ErrHandshakeBadHost + } else if u := httpGetHeader(r.Header, headerUpgradeCanonical); u != "websocket" && !strings.EqualFold(u, "websocket") { + err = ErrHandshakeBadUpgrade + } else if c := httpGetHeader(r.Header, headerConnectionCanonical); c != "Upgrade" && !strHasToken(c, "upgrade") { + err = ErrHandshakeBadConnection + } else if nonce = httpGetHeader(r.Header, headerSecKeyCanonical); len(nonce) != nonceSize { + err = ErrHandshakeBadSecKey + } else if v := httpGetHeader(r.Header, headerSecVersionCanonical); v != "13" { + // According to RFC6455: + // + // If this version does not match a version understood by the server, + // the server MUST abort the WebSocket handshake described in this + // section and instead send an appropriate HTTP error code (such as 426 + // Upgrade Required) and a |Sec-WebSocket-Version| header field + // indicating the version(s) the server is capable of understanding. + // + // So we branching here cause empty or not present version does not + // meet the ABNF rules of RFC6455: + // + // version = DIGIT | (NZDIGIT DIGIT) | + // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT) + // ; Limited to 0-255 range, with no leading zeros + // + // That is, if version is really invalid – we sent 426 status, if it + // not present or empty – it is 400. + if v != "" { + err = ErrHandshakeUpgradeRequired + } else { + err = ErrHandshakeBadSecVersion + } + } + if check := u.Protocol; err == nil && check != nil { + ps := r.Header[headerSecProtocolCanonical] + for i := 0; i < len(ps) && err == nil && hs.Protocol == ""; i++ { + var ok bool + hs.Protocol, ok = strSelectProtocol(ps[i], check) + if !ok { + err = ErrMalformedRequest + } + } + } + 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 = btsSelectExtensions(strToBytes(xs[i]), hs.Extensions, check) + if !ok { + err = ErrMalformedRequest + } + } + } + + // Clear deadlines set by server. + conn.SetDeadline(noDeadline) + if t := u.Timeout; t != 0 { + conn.SetWriteDeadline(time.Now().Add(t)) + defer conn.SetWriteDeadline(noDeadline) + } + + var header handshakeHeader + if h := u.Header; h != nil { + header[0] = HandshakeHeaderHTTP(h) + } + if err == nil { + httpWriteResponseUpgrade(rw.Writer, strToBytes(nonce), hs, header.WriteTo) + err = rw.Writer.Flush() + } else { + var code int + if rej, ok := err.(*ConnectionRejectedError); ok { + code = rej.code + header[1] = rej.header + } + if code == 0 { + code = http.StatusInternalServerError + } + httpWriteResponseError(rw.Writer, err, code, header.WriteTo) + // Do not store Flush() error to not override already existing one. + _ = rw.Writer.Flush() + } + return conn, rw, hs, err +} + +// Upgrader contains options for upgrading connection to websocket. +type Upgrader struct { + // ReadBufferSize and WriteBufferSize is an I/O buffer sizes. + // They used to read and write http data while upgrading to WebSocket. + // Allocated buffers are pooled with sync.Pool to avoid extra allocations. + // + // If a size is zero then default value is used. + // + // Usually it is useful to set read buffer size bigger than write buffer + // size because incoming request could contain long header values, such as + // Cookie. Response, in other way, could be big only if user write multiple + // custom headers. Usually response takes less than 256 bytes. + ReadBufferSize, WriteBufferSize int + + // Protocol is a select function that is used to select subprotocol + // from list requested by client. If this field is set, then the first matched + // protocol is sent to a client as negotiated. + // + // The argument is only valid until the callback returns. + Protocol func([]byte) bool + + // ProtocolCustrom allow user to parse Sec-WebSocket-Protocol header manually. + // Note that returned bytes must be valid until Upgrade returns. + // If ProtocolCustom is set, it used instead of Protocol function. + ProtocolCustom func([]byte) (string, bool) + + // Extension is a 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. + // + // 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 + // significant. That is, returning true from this function means that no + // other extension with the same name should be checked because server + // accepted the most preferable extension right now: + // "Note that the order of extensions is significant. Any interactions between + // multiple extensions MAY be defined in the documents defining the extensions. + // In the absence of such definitions, the interpretation is that the header + // 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. + // + // 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. + // + // It used instead of any key-value mappings to avoid allocations in user + // land. + // + // Note that if present, it will be written in any result of handshake. + Header HandshakeHeader + + // OnRequest is a callback that will be called after request line + // successful parsing. + // + // The arguments are only valid until the 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. + OnRequest func(uri []byte) error + + // OnHost is a callback that will be called after "Host" header successful + // parsing. + // + // It is separated from OnHeader callback because the Host header must be + // present in each request since HTTP/1.1. Thus Host header is non-optional + // and required for every WebSocket handshake. + // + // The arguments are only valid until the 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. + OnHost func(host []byte) error + + // OnHeader is a callback that will be called after successful parsing of + // header, that is not used during WebSocket handshake procedure. That is, + // it will be called with non-websocket headers, which could be relevant + // for application-level logic. + // + // The arguments are only valid until the 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. + OnHeader func(key, value []byte) error + + // OnBeforeUpgrade is a callback that will be called before sending + // successful upgrade response. + // + // Setting OnBeforeUpgrade allows user to make final application-level + // checks and decide whether this connection is allowed to successfully + // upgrade to WebSocket. + // + // It must return non-nil either HandshakeHeader or error and never both. + // + // 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. + OnBeforeUpgrade func() (header HandshakeHeader, err error) +} + +// Upgrade zero-copy upgrades connection to WebSocket. It interprets given conn +// as connection with incoming HTTP Upgrade request. +// +// It is a caller responsibility to manage i/o timeouts on conn. +// +// Non-nil error means that request for the WebSocket upgrade is invalid or +// malformed and usually connection should be closed. +// Even when error is non-nil Upgrade will write appropriate response into +// connection in compliance with RFC. +func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) { + // headerSeen constants helps to report whether or not some header was seen + // during reading request bytes. + const ( + headerSeenHost = 1 << iota + headerSeenUpgrade + headerSeenConnection + headerSeenSecVersion + headerSeenSecKey + + // headerSeenAll is the value that we expect to receive at the end of + // headers read/parse loop. + headerSeenAll = 0 | + headerSeenHost | + headerSeenUpgrade | + headerSeenConnection | + headerSeenSecVersion | + headerSeenSecKey + ) + + // Prepare I/O buffers. + // TODO(gobwas): make it configurable. + br := pbufio.GetReader(conn, + nonZero(u.ReadBufferSize, DefaultServerReadBufferSize), + ) + bw := pbufio.GetWriter(conn, + nonZero(u.WriteBufferSize, DefaultServerWriteBufferSize), + ) + defer func() { + pbufio.PutReader(br) + pbufio.PutWriter(bw) + }() + + // Read HTTP request line like "GET /ws HTTP/1.1". + rl, err := readLine(br) + if err != nil { + return hs, err + } + // Parse request line data like HTTP version, uri and method. + req, err := httpParseRequestLine(rl) + if err != nil { + return hs, err + } + + // Prepare stack-based handshake header list. + header := handshakeHeader{ + 0: u.Header, + } + + // Parse and check HTTP request. + // As RFC6455 says: + // The client's opening handshake consists of the following parts. If the + // server, while reading the handshake, finds that the client did not + // send a handshake that matches the description below (note that as per + // [RFC2616], the order of the header fields is not important), including + // but not limited to any violations of the ABNF grammar specified for + // the components of the handshake, the server MUST stop processing the + // client's handshake and return an HTTP response with an appropriate + // error code (such as 400 Bad Request). + // + // See https://tools.ietf.org/html/rfc6455#section-4.2.1 + + // An HTTP/1.1 or higher GET request, including a "Request-URI". + // + // Even if RFC says "1.1 or higher" without mentioning the part of the + // version, we apply it only to minor part. + switch { + case req.major != 1 || req.minor < 1: + // Abort processing the whole request because we do not even know how + // to actually parse it. + err = ErrHandshakeBadProtocol + + case btsToString(req.method) != http.MethodGet: + err = ErrHandshakeBadMethod + + default: + if onRequest := u.OnRequest; onRequest != nil { + err = onRequest(req.uri) + } + } + // Start headers read/parse loop. + var ( + // headerSeen reports which header was seen by setting corresponding + // bit on. + headerSeen byte + + nonce = make([]byte, nonceSize) + ) + for err == nil { + line, e := readLine(br) + if e != nil { + return hs, e + } + if len(line) == 0 { + // Blank line, no more lines to read. + break + } + + k, v, ok := httpParseHeaderLine(line) + if !ok { + err = ErrMalformedRequest + break + } + + switch btsToString(k) { + case headerHostCanonical: + headerSeen |= headerSeenHost + if onHost := u.OnHost; onHost != nil { + err = onHost(v) + } + + case headerUpgradeCanonical: + headerSeen |= headerSeenUpgrade + if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) { + err = ErrHandshakeBadUpgrade + } + + case headerConnectionCanonical: + headerSeen |= headerSeenConnection + if !bytes.Equal(v, specHeaderValueConnection) && !btsHasToken(v, specHeaderValueConnectionLower) { + err = ErrHandshakeBadConnection + } + + case headerSecVersionCanonical: + headerSeen |= headerSeenSecVersion + if !bytes.Equal(v, specHeaderValueSecVersion) { + err = ErrHandshakeUpgradeRequired + } + + case headerSecKeyCanonical: + headerSeen |= headerSeenSecKey + if len(v) != nonceSize { + err = ErrHandshakeBadSecKey + } else { + copy(nonce, v) + } + + case headerSecProtocolCanonical: + if custom, check := u.ProtocolCustom, u.Protocol; hs.Protocol == "" && (custom != nil || check != nil) { + var ok bool + if custom != nil { + hs.Protocol, ok = custom(v) + } else { + hs.Protocol, ok = btsSelectProtocol(v, check) + } + if !ok { + err = ErrMalformedRequest + } + } + + case headerSecExtensionsCanonical: + 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) + } else { + hs.Extensions, ok = btsSelectExtensions(v, hs.Extensions, check) + } + if !ok { + err = ErrMalformedRequest + } + } + + default: + if onHeader := u.OnHeader; onHeader != nil { + err = onHeader(k, v) + } + } + } + switch { + case err == nil && headerSeen != headerSeenAll: + switch { + case headerSeen&headerSeenHost == 0: + // As RFC2616 says: + // A client MUST include a Host header field in all HTTP/1.1 + // request messages. If the requested URI does not include an + // Internet host name for the service being requested, then the + // Host header field MUST be given with an empty value. An + // HTTP/1.1 proxy MUST ensure that any request message it + // forwards does contain an appropriate Host header field that + // identifies the service being requested by the proxy. All + // Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad + // Request) status code to any HTTP/1.1 request message which + // lacks a Host header field. + err = ErrHandshakeBadHost + case headerSeen&headerSeenUpgrade == 0: + err = ErrHandshakeBadUpgrade + case headerSeen&headerSeenConnection == 0: + err = ErrHandshakeBadConnection + case headerSeen&headerSeenSecVersion == 0: + // In case of empty or not present version we do not send 426 status, + // because it does not meet the ABNF rules of RFC6455: + // + // version = DIGIT | (NZDIGIT DIGIT) | + // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT) + // ; Limited to 0-255 range, with no leading zeros + // + // That is, if version is really invalid – we sent 426 status as above, if it + // not present – it is 400. + err = ErrHandshakeBadSecVersion + case headerSeen&headerSeenSecKey == 0: + err = ErrHandshakeBadSecKey + default: + panic("unknown headers state") + } + + case err == nil && u.OnBeforeUpgrade != nil: + header[1], err = u.OnBeforeUpgrade() + } + if err != nil { + var code int + if rej, ok := err.(*ConnectionRejectedError); ok { + code = rej.code + header[1] = rej.header + } + if code == 0 { + code = http.StatusInternalServerError + } + httpWriteResponseError(bw, err, code, header.WriteTo) + // Do not store Flush() error to not override already existing one. + _ = bw.Flush() + return hs, err + } + + httpWriteResponseUpgrade(bw, nonce, hs, header.WriteTo) + err = bw.Flush() + + return hs, err +} + +type handshakeHeader [2]HandshakeHeader + +func (hs handshakeHeader) WriteTo(w io.Writer) (n int64, err error) { + for i := 0; i < len(hs) && err == nil; i++ { + if h := hs[i]; h != nil { + var m int64 + m, err = h.WriteTo(w) + n += m + } + } + return n, err +} diff --git a/vendor/github.com/gobwas/ws/util.go b/vendor/github.com/gobwas/ws/util.go new file mode 100644 index 0000000000..1dd5aa608c --- /dev/null +++ b/vendor/github.com/gobwas/ws/util.go @@ -0,0 +1,199 @@ +package ws + +import ( + "bufio" + "bytes" + "fmt" + + "github.com/gobwas/httphead" +) + +// SelectFromSlice creates accept function that could be used as Protocol/Extension +// select during upgrade. +func SelectFromSlice(accept []string) func(string) bool { + if len(accept) > 16 { + mp := make(map[string]struct{}, len(accept)) + for _, p := range accept { + mp[p] = struct{}{} + } + return func(p string) bool { + _, ok := mp[p] + return ok + } + } + return func(p string) bool { + for _, ok := range accept { + if p == ok { + return true + } + } + return false + } +} + +// SelectEqual creates accept function that could be used as Protocol/Extension +// select during upgrade. +func SelectEqual(v string) func(string) bool { + return func(p string) bool { + return v == p + } +} + +// asciiToInt converts bytes to int. +func asciiToInt(bts []byte) (ret int, err error) { + // ASCII numbers all start with the high-order bits 0011. + // If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those + // bits and interpret them directly as an integer. + var n int + if n = len(bts); n < 1 { + return 0, fmt.Errorf("converting empty bytes to int") + } + for i := 0; i < n; i++ { + if bts[i]&0xf0 != 0x30 { + return 0, fmt.Errorf("%s is not a numeric character", string(bts[i])) + } + ret += int(bts[i]&0xf) * pow(10, n-i-1) + } + return ret, nil +} + +// pow for integers implementation. +// 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 { + if b&1 != 0 { + p *= a + } + b >>= 1 + a *= a + } + return p +} + +func bsplit3(bts []byte, sep byte) (b1, b2, b3 []byte) { + a := bytes.IndexByte(bts, sep) + b := bytes.IndexByte(bts[a+1:], sep) + if a == -1 || b == -1 { + return bts, nil, nil + } + b += a + 1 + return bts[:a], bts[a+1 : b], bts[b+1:] +} + +func btrim(bts []byte) []byte { + var i, j int + for i = 0; i < len(bts) && (bts[i] == ' ' || bts[i] == '\t'); { + i++ + } + for j = len(bts); j > i && (bts[j-1] == ' ' || bts[j-1] == '\t'); { + j-- + } + return bts[i:j] +} + +func strHasToken(header, token string) (has bool) { + return btsHasToken(strToBytes(header), strToBytes(token)) +} + +func btsHasToken(header, token []byte) (has bool) { + httphead.ScanTokens(header, func(v []byte) bool { + has = bytes.EqualFold(v, token) + return !has + }) + return has +} + +const ( + toLower = 'a' - 'A' // for use with OR. + toUpper = ^byte(toLower) // for use with AND. + toLower8 = uint64(toLower) | + uint64(toLower)<<8 | + uint64(toLower)<<16 | + uint64(toLower)<<24 | + uint64(toLower)<<32 | + uint64(toLower)<<40 | + uint64(toLower)<<48 | + uint64(toLower)<<56 +) + +// Algorithm below is like standard textproto/CanonicalMIMEHeaderKey, except +// that it operates with slice of bytes and modifies it inplace without copying. +func canonicalizeHeaderKey(k []byte) { + upper := true + for i, c := range k { + if upper && 'a' <= c && c <= 'z' { + k[i] &= toUpper + } else if !upper && 'A' <= c && c <= 'Z' { + k[i] |= toLower + } + upper = c == '-' + } +} + +// readLine reads line from br. It reads until '\n' and returns bytes without +// '\n' or '\r\n' at the end. +// It returns err if and only if line does not end in '\n'. Note that read +// bytes returned in any case of error. +// +// It is much like the textproto/Reader.ReadLine() except the thing that it +// returns raw bytes, instead of string. That is, it avoids copying bytes read +// from br. +// +// textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be +// safe with future I/O operations on br. +// +// We could control I/O operations on br and do not need to make additional +// copy for safety. +// +// NOTE: it may return copied flag to notify that returned buffer is safe to +// use. +func readLine(br *bufio.Reader) ([]byte, error) { + var line []byte + for { + bts, err := br.ReadSlice('\n') + if err == bufio.ErrBufferFull { + // Copy bytes because next read will discard them. + line = append(line, bts...) + continue + } + + // Avoid copy of single read. + if line == nil { + line = bts + } else { + line = append(line, bts...) + } + + if err != nil { + return line, err + } + + // Size of line is at least 1. + // In other case bufio.ReadSlice() returns error. + n := len(line) + + // Cut '\n' or '\r\n'. + if n > 1 && line[n-2] == '\r' { + line = line[:n-2] + } else { + line = line[:n-1] + } + + return line, nil + } +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func nonZero(a, b int) int { + if a != 0 { + return a + } + return b +} diff --git a/vendor/github.com/gobwas/ws/util_purego.go b/vendor/github.com/gobwas/ws/util_purego.go new file mode 100644 index 0000000000..4a8befd1c6 --- /dev/null +++ b/vendor/github.com/gobwas/ws/util_purego.go @@ -0,0 +1,11 @@ +//go:build purego + +package ws + +func strToBytes(str string) (bts []byte) { + return []byte(str) +} + +func btsToString(bts []byte) (str string) { + return string(bts) +} diff --git a/vendor/github.com/gobwas/ws/util_unsafe.go b/vendor/github.com/gobwas/ws/util_unsafe.go new file mode 100644 index 0000000000..779f719037 --- /dev/null +++ b/vendor/github.com/gobwas/ws/util_unsafe.go @@ -0,0 +1,21 @@ +//go: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)) +} diff --git a/vendor/github.com/gobwas/ws/write.go b/vendor/github.com/gobwas/ws/write.go new file mode 100644 index 0000000000..94557c6963 --- /dev/null +++ b/vendor/github.com/gobwas/ws/write.go @@ -0,0 +1,104 @@ +package ws + +import ( + "encoding/binary" + "io" +) + +// Header size length bounds in bytes. +const ( + MaxHeaderSize = 14 + MinHeaderSize = 2 +) + +const ( + bit0 = 0x80 + bit1 = 0x40 + bit2 = 0x20 + bit3 = 0x10 + bit4 = 0x08 + bit5 = 0x04 + bit6 = 0x02 + bit7 = 0x01 + + len7 = int64(125) + len16 = int64(^(uint16(0))) + len64 = int64(^(uint64(0)) >> 1) +) + +// HeaderSize returns number of bytes that are needed to encode given header. +// It returns -1 if header is malformed. +func HeaderSize(h Header) (n int) { + switch { + case h.Length < 126: + n = 2 + case h.Length <= len16: + n = 4 + case h.Length <= len64: + n = 10 + default: + return -1 + } + if h.Masked { + n += len(h.Mask) + } + return n +} + +// WriteHeader writes header binary representation into w. +func WriteHeader(w io.Writer, h Header) error { + // Make slice of bytes with capacity 14 that could hold any header. + bts := make([]byte, MaxHeaderSize) + + if h.Fin { + bts[0] |= bit0 + } + bts[0] |= h.Rsv << 4 + bts[0] |= byte(h.OpCode) + + var n int + switch { + case h.Length <= len7: + bts[1] = byte(h.Length) + n = 2 + + case h.Length <= len16: + bts[1] = 126 + binary.BigEndian.PutUint16(bts[2:4], uint16(h.Length)) + n = 4 + + case h.Length <= len64: + bts[1] = 127 + binary.BigEndian.PutUint64(bts[2:10], uint64(h.Length)) + n = 10 + + default: + return ErrHeaderLengthUnexpected + } + + if h.Masked { + bts[1] |= bit0 + n += copy(bts[n:], h.Mask[:]) + } + + _, err := w.Write(bts[:n]) + + return err +} + +// WriteFrame writes frame binary representation into w. +func WriteFrame(w io.Writer, f Frame) error { + err := WriteHeader(w, f.Header) + if err != nil { + return err + } + _, err = w.Write(f.Payload) + return err +} + +// MustWriteFrame is like WriteFrame but panics if frame can not be read. +func MustWriteFrame(w io.Writer, f Frame) { + if err := WriteFrame(w, f); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/gobwas/ws/wsutil/cipher.go b/vendor/github.com/gobwas/ws/wsutil/cipher.go new file mode 100644 index 0000000000..bc25064f53 --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/cipher.go @@ -0,0 +1,72 @@ +package wsutil + +import ( + "io" + + "github.com/gobwas/pool/pbytes" + "github.com/gobwas/ws" +) + +// CipherReader implements io.Reader that applies xor-cipher to the bytes read +// from source. +// It could help to unmask WebSocket frame payload on the fly. +type CipherReader struct { + r io.Reader + mask [4]byte + pos int +} + +// NewCipherReader creates xor-cipher reader from r with given mask. +func NewCipherReader(r io.Reader, mask [4]byte) *CipherReader { + return &CipherReader{r, mask, 0} +} + +// Reset resets CipherReader to read from r with given mask. +func (c *CipherReader) Reset(r io.Reader, mask [4]byte) { + c.r = r + c.mask = mask + c.pos = 0 +} + +// Read implements io.Reader interface. It applies mask given during +// initialization to every read byte. +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 n, err +} + +// CipherWriter implements io.Writer that applies xor-cipher to the bytes +// written to the destination writer. It does not modify the original bytes. +type CipherWriter struct { + w io.Writer + mask [4]byte + pos int +} + +// NewCipherWriter creates xor-cipher writer to w with given mask. +func NewCipherWriter(w io.Writer, mask [4]byte) *CipherWriter { + return &CipherWriter{w, mask, 0} +} + +// Reset reset CipherWriter to write to w with given mask. +func (c *CipherWriter) Reset(w io.Writer, mask [4]byte) { + c.w = w + c.mask = mask + c.pos = 0 +} + +// Write implements io.Writer interface. It applies masking during +// initialization to every sent byte. It does not modify original slice. +func (c *CipherWriter) Write(p []byte) (n int, err error) { + cp := pbytes.GetLen(len(p)) + defer pbytes.Put(cp) + + copy(cp, p) + ws.Cipher(cp, c.mask, c.pos) + n, err = c.w.Write(cp) + c.pos += n + + return n, err +} diff --git a/vendor/github.com/gobwas/ws/wsutil/dialer.go b/vendor/github.com/gobwas/ws/wsutil/dialer.go new file mode 100644 index 0000000000..4f8788fb0e --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/dialer.go @@ -0,0 +1,147 @@ +package wsutil + +import ( + "bufio" + "bytes" + "context" + "io" + "io/ioutil" + "net" + "net/http" + + "github.com/gobwas/ws" +) + +// DebugDialer is a wrapper around ws.Dialer. It tracks i/o of WebSocket +// handshake. That is, it gives ability to receive copied HTTP request and +// response bytes that made inside Dialer.Dial(). +// +// Note that it must not be used in production applications that requires +// Dial() to be efficient. +type DebugDialer struct { + // Dialer contains WebSocket connection establishment options. + Dialer ws.Dialer + + // OnRequest and OnResponse are the callbacks that will be called with the + // HTTP request and response respectively. + OnRequest, OnResponse func([]byte) +} + +// Dial connects to the url host and upgrades connection to WebSocket. It makes +// it by calling d.Dialer.Dial(). +func (d *DebugDialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *bufio.Reader, hs ws.Handshake, err error) { + // Need to copy Dialer to prevent original object mutation. + dialer := d.Dialer + var ( + reqBuf bytes.Buffer + resBuf bytes.Buffer + + resContentLength int64 + ) + userWrap := dialer.WrapConn + dialer.WrapConn = func(c net.Conn) net.Conn { + if userWrap != nil { + c = userWrap(c) + } + + // Save the pointer to the raw connection. + conn = c + + var ( + r io.Reader = conn + w io.Writer = conn + ) + if d.OnResponse != nil { + r = &prefetchResponseReader{ + source: conn, + buffer: &resBuf, + contentLength: &resContentLength, + } + } + if d.OnRequest != nil { + w = io.MultiWriter(conn, &reqBuf) + } + return rwConn{conn, r, w} + } + + _, br, hs, err = dialer.Dial(ctx, urlstr) + + if onRequest := d.OnRequest; onRequest != nil { + onRequest(reqBuf.Bytes()) + } + if onResponse := d.OnResponse; onResponse != nil { + // We must split response inside buffered bytes from other received + // bytes from server. + p := resBuf.Bytes() + n := bytes.Index(p, headEnd) + h := n + len(headEnd) // Head end index. + n = h + int(resContentLength) // Body end index. + + onResponse(p[:n]) + + if br != nil { + // If br is non-nil, then it mean two things. First is that + // handshake is OK and server has sent additional bytes – probably + // immediate sent frames (or weird but possible response body). + // Second, the bad one, is that br buffer's source is now rwConn + // instance from above WrapConn call. It is incorrect, so we must + // fix it. + var r io.Reader = conn + if len(p) > h { + // Buffer contains more than just HTTP headers bytes. + r = io.MultiReader( + bytes.NewReader(p[h:]), + conn, + ) + } + br.Reset(r) + // Must make br.Buffered() to be non-zero. + br.Peek(len(p[h:])) + } + } + + return conn, br, hs, err +} + +type rwConn struct { + net.Conn + + r io.Reader + w io.Writer +} + +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) +} + +var headEnd = []byte("\r\n\r\n") + +type prefetchResponseReader struct { + source io.Reader // Original connection source. + reader io.Reader // Wrapped reader used to read from by clients. + buffer *bytes.Buffer + + contentLength *int64 +} + +func (r *prefetchResponseReader) Read(p []byte) (int, error) { + if r.reader == nil { + resp, err := http.ReadResponse(bufio.NewReader( + io.TeeReader(r.source, r.buffer), + ), nil) + if err == nil { + *r.contentLength, _ = io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + } + bts := r.buffer.Bytes() + r.reader = io.MultiReader( + bytes.NewReader(bts), + r.source, + ) + } + return r.reader.Read(p) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/extenstion.go b/vendor/github.com/gobwas/ws/wsutil/extenstion.go new file mode 100644 index 0000000000..6e1ebbf4bc --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/extenstion.go @@ -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) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/handler.go b/vendor/github.com/gobwas/ws/wsutil/handler.go new file mode 100644 index 0000000000..44fd360efa --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/handler.go @@ -0,0 +1,219 @@ +package wsutil + +import ( + "errors" + "io" + "io/ioutil" + "strconv" + + "github.com/gobwas/pool/pbytes" + "github.com/gobwas/ws" +) + +// ClosedError returned when peer has closed the connection with appropriate +// code and a textual reason. +type ClosedError struct { + Code ws.StatusCode + Reason string +} + +// Error implements error interface. +func (err ClosedError) Error() string { + return "ws closed: " + strconv.FormatUint(uint64(err.Code), 10) + " " + err.Reason +} + +// ControlHandler contains logic of handling control frames. +// +// The intentional way to use it is to read the next frame header from the +// connection, optionally check its validity via ws.CheckHeader() and if it is +// not a ws.OpText of ws.OpBinary (or ws.OpContinuation) – pass it to Handle() +// method. +// +// That is, passed header should be checked to get rid of unexpected errors. +// +// The Handle() method will read out all control frame payload (if any) and +// write necessary bytes as a rfc compatible response. +type ControlHandler struct { + Src io.Reader + Dst io.Writer + State ws.State + + // DisableSrcCiphering disables unmasking payload data read from Src. + // It is useful when wsutil.Reader is used or when frame payload already + // pulled and ciphered out from the connection (and introduced by + // bytes.Reader, for example). + DisableSrcCiphering bool +} + +// ErrNotControlFrame is returned by ControlHandler to indicate that given +// header could not be handled. +var ErrNotControlFrame = errors.New("not a control frame") + +// Handle handles control frames regarding to the c.State and writes responses +// to the c.Dst when needed. +// +// It returns ErrNotControlFrame when given header is not of ws.OpClose, +// ws.OpPing or ws.OpPong operation code. +func (c ControlHandler) Handle(h ws.Header) error { + switch h.OpCode { + case ws.OpPing: + return c.HandlePing(h) + case ws.OpPong: + return c.HandlePong(h) + case ws.OpClose: + return c.HandleClose(h) + } + return ErrNotControlFrame +} + +// HandlePing handles ping frame and writes specification compatible response +// to the c.Dst. +func (c ControlHandler) HandlePing(h ws.Header) error { + if h.Length == 0 { + // The most common case when ping is empty. + // Note that when sending masked frame the mask for empty payload is + // just four zero bytes. + return ws.WriteHeader(c.Dst, ws.Header{ + Fin: true, + OpCode: ws.OpPong, + Masked: c.State.ClientSide(), + }) + } + + // In other way reply with Pong frame with copied payload. + p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{ + Length: h.Length, + Masked: c.State.ClientSide(), + })) + defer pbytes.Put(p) + + // Deal with ciphering i/o: + // Masking key is used to mask the "Payload data" defined in the same + // section as frame-payload-data, which includes "Extension data" and + // "Application data". + // + // See https://tools.ietf.org/html/rfc6455#section-5.3 + // + // NOTE: We prefer ControlWriter with preallocated buffer to + // ws.WriteHeader because it performs one syscall instead of two. + w := NewControlWriterBuffer(c.Dst, c.State, ws.OpPong, p) + r := c.Src + if c.State.ServerSide() && !c.DisableSrcCiphering { + r = NewCipherReader(r, h.Mask) + } + + _, err := io.Copy(w, r) + if err == nil { + err = w.Flush() + } + + return err +} + +// HandlePong handles pong frame by discarding it. +func (c ControlHandler) HandlePong(h ws.Header) error { + if h.Length == 0 { + return nil + } + + buf := pbytes.GetLen(int(h.Length)) + defer pbytes.Put(buf) + + // Discard pong message according to the RFC6455: + // A Pong frame MAY be sent unsolicited. This serves as a + // unidirectional heartbeat. A response to an unsolicited Pong frame + // is not expected. + _, err := io.CopyBuffer(ioutil.Discard, c.Src, buf) + + return err +} + +// HandleClose handles close frame, makes protocol validity checks and writes +// specification compatible response to the c.Dst. +func (c ControlHandler) HandleClose(h ws.Header) error { + if h.Length == 0 { + err := ws.WriteHeader(c.Dst, ws.Header{ + Fin: true, + OpCode: ws.OpClose, + Masked: c.State.ClientSide(), + }) + if err != nil { + return err + } + + // Due to RFC, we should interpret the code as no status code + // received: + // If this Close control frame contains no status code, _The WebSocket + // Connection Close Code_ is considered to be 1005. + // + // See https://tools.ietf.org/html/rfc6455#section-7.1.5 + return ClosedError{ + Code: ws.StatusNoStatusRcvd, + } + } + + // Prepare bytes both for reading reason and sending response. + p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{ + Length: h.Length, + Masked: c.State.ClientSide(), + })) + defer pbytes.Put(p) + + // Get the subslice to read the frame payload out. + subp := p[:h.Length] + + r := c.Src + if c.State.ServerSide() && !c.DisableSrcCiphering { + r = NewCipherReader(r, h.Mask) + } + if _, err := io.ReadFull(r, subp); err != nil { + return err + } + + code, reason := ws.ParseCloseFrameData(subp) + if err := ws.CheckCloseFrameData(code, reason); err != nil { + // Here we could not use the prepared bytes because there is no + // guarantee that it may fit our protocol error closure code and a + // reason. + c.closeWithProtocolError(err) + return err + } + + // Deal with ciphering i/o: + // Masking key is used to mask the "Payload data" defined in the same + // section as frame-payload-data, which includes "Extension data" and + // "Application data". + // + // See https://tools.ietf.org/html/rfc6455#section-5.3 + // + // NOTE: We prefer ControlWriter with preallocated buffer to + // ws.WriteHeader because it performs one syscall instead of two. + w := NewControlWriterBuffer(c.Dst, c.State, ws.OpClose, p) + + // RFC6455#5.5.1: + // If an endpoint receives a Close frame and did not previously + // send a Close frame, the endpoint MUST send a Close frame in + // response. (When sending a Close frame in response, the endpoint + // typically echoes the status code it received.) + _, err := w.Write(p[:2]) + if err != nil { + return err + } + if err := w.Flush(); err != nil { + return err + } + return ClosedError{ + Code: code, + Reason: reason, + } +} + +func (c ControlHandler) closeWithProtocolError(reason error) error { + f := ws.NewCloseFrame(ws.NewCloseFrameBody( + ws.StatusProtocolError, reason.Error(), + )) + if c.State.ClientSide() { + ws.MaskFrameInPlace(f) + } + return ws.WriteFrame(c.Dst, f) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/helper.go b/vendor/github.com/gobwas/ws/wsutil/helper.go new file mode 100644 index 0000000000..231760bc9a --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/helper.go @@ -0,0 +1,279 @@ +package wsutil + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/gobwas/ws" +) + +// Message represents a message from peer, that could be presented in one or +// more frames. That is, it contains payload of all message fragments and +// operation code of initial frame for this message. +type Message struct { + OpCode ws.OpCode + Payload []byte +} + +// ReadMessage is a helper function that reads next message from r. It appends +// received message(s) to the third argument and returns the result of it and +// an error if some failure happened. That is, it probably could receive more +// than one message when peer sending fragmented message in multiple frames and +// want to send some control frame between fragments. Then returned slice will +// contain those control frames at first, and then result of gluing fragments. +// +// TODO(gobwas): add DefaultReader with buffer size options. +func ReadMessage(r io.Reader, s ws.State, m []Message) ([]Message, error) { + rd := Reader{ + Source: r, + State: s, + CheckUTF8: true, + OnIntermediate: func(hdr ws.Header, src io.Reader) error { + bts, err := ioutil.ReadAll(src) + if err != nil { + return err + } + m = append(m, Message{hdr.OpCode, bts}) + return nil + }, + } + h, err := rd.NextFrame() + if err != nil { + return m, err + } + var p []byte + if h.Fin { + // No more frames will be read. Use fixed sized buffer to read payload. + p = make([]byte, h.Length) + // It is not possible to receive io.EOF here because Reader does not + // return EOF if frame payload was successfully fetched. + // Thus we consistent here with io.Reader behavior. + _, err = io.ReadFull(&rd, p) + } else { + // Frame is fragmented, thus use ioutil.ReadAll behavior. + var buf bytes.Buffer + _, err = buf.ReadFrom(&rd) + p = buf.Bytes() + } + if err != nil { + return m, err + } + return append(m, Message{h.OpCode, p}), nil +} + +// ReadClientMessage reads next message from r, considering that caller +// represents server side. +// 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). +func ReadServerMessage(r io.Reader, m []Message) ([]Message, error) { + return ReadMessage(r, ws.StateClientSide, m) +} + +// ReadData is a helper function that reads next data (non-control) message +// from rw. +// It takes care on handling all control frames. It will write response on +// control frames to the write part of rw. It blocks until some data frame +// will be received. +// +// Note this may handle and write control frames into the writer part of a +// given io.ReadWriter. +func ReadData(rw io.ReadWriter, s ws.State) ([]byte, ws.OpCode, error) { + return readData(rw, s, ws.OpText|ws.OpBinary) +} + +// ReadClientData reads next data message from rw, considering that caller +// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide). +// +// Note this may handle and write control frames into the writer part of a +// given io.ReadWriter. +func ReadClientData(rw io.ReadWriter) ([]byte, ws.OpCode, error) { + return ReadData(rw, ws.StateServerSide) +} + +// ReadClientText reads next text message from rw, considering that caller +// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide). +// It discards received binary messages. +// +// Note this may handle and write control frames into the writer part of a +// given io.ReadWriter. +func ReadClientText(rw io.ReadWriter) ([]byte, error) { + p, _, err := readData(rw, ws.StateServerSide, ws.OpText) + return p, err +} + +// ReadClientBinary reads next binary message from rw, considering that caller +// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide). +// It discards received text messages. +// +// Note this may handle and write control frames into the writer part of a given +// io.ReadWriter. +func ReadClientBinary(rw io.ReadWriter) ([]byte, error) { + p, _, err := readData(rw, ws.StateServerSide, ws.OpBinary) + return p, err +} + +// ReadServerData reads next data message from rw, considering that caller +// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide). +// +// Note this may handle and write control frames into the writer part of a +// given io.ReadWriter. +func ReadServerData(rw io.ReadWriter) ([]byte, ws.OpCode, error) { + return ReadData(rw, ws.StateClientSide) +} + +// ReadServerText reads next text message from rw, considering that caller +// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide). +// It discards received binary messages. +// +// Note this may handle and write control frames into the writer part of a given +// io.ReadWriter. +func ReadServerText(rw io.ReadWriter) ([]byte, error) { + p, _, err := readData(rw, ws.StateClientSide, ws.OpText) + return p, err +} + +// ReadServerBinary reads next binary message from rw, considering that caller +// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide). +// It discards received text messages. +// +// Note this may handle and write control frames into the writer part of a +// given io.ReadWriter. +func ReadServerBinary(rw io.ReadWriter) ([]byte, error) { + p, _, err := readData(rw, ws.StateClientSide, ws.OpBinary) + return p, err +} + +// WriteMessage is a helper function that writes message to the w. It +// constructs single frame with given operation code and payload. +// It uses given state to prepare side-dependent things, like cipher +// payload bytes from client to server. It will not mutate p bytes if +// cipher must be made. +// +// If you want to write message in fragmented frames, use Writer instead. +func WriteMessage(w io.Writer, s ws.State, op ws.OpCode, p []byte) error { + return writeFrame(w, s, op, true, p) +} + +// WriteServerMessage writes message to w, considering that caller +// represents server side. +func WriteServerMessage(w io.Writer, op ws.OpCode, p []byte) error { + return WriteMessage(w, ws.StateServerSide, op, p) +} + +// WriteServerText is the same as WriteServerMessage with +// ws.OpText. +func WriteServerText(w io.Writer, p []byte) error { + return WriteServerMessage(w, ws.OpText, p) +} + +// WriteServerBinary is the same as WriteServerMessage with +// ws.OpBinary. +func WriteServerBinary(w io.Writer, p []byte) error { + return WriteServerMessage(w, ws.OpBinary, p) +} + +// WriteClientMessage writes message to w, considering that caller +// represents client side. +func WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error { + return WriteMessage(w, ws.StateClientSide, op, p) +} + +// WriteClientText is the same as WriteClientMessage with +// ws.OpText. +func WriteClientText(w io.Writer, p []byte) error { + return WriteClientMessage(w, ws.OpText, p) +} + +// WriteClientBinary is the same as WriteClientMessage with +// ws.OpBinary. +func WriteClientBinary(w io.Writer, p []byte) error { + return WriteClientMessage(w, ws.OpBinary, p) +} + +// HandleClientControlMessage handles control frame from conn and writes +// response when needed. +// +// It considers that caller represents server side. +func HandleClientControlMessage(conn io.Writer, msg Message) error { + return HandleControlMessage(conn, ws.StateServerSide, msg) +} + +// HandleServerControlMessage handles control frame from conn and writes +// response when needed. +// +// It considers that caller represents client side. +func HandleServerControlMessage(conn io.Writer, msg Message) error { + return HandleControlMessage(conn, ws.StateClientSide, msg) +} + +// HandleControlMessage handles message which was read by ReadMessage() +// functions. +// +// That is, it is expected, that payload is already unmasked and frame header +// were checked by ws.CheckHeader() call. +func HandleControlMessage(conn io.Writer, state ws.State, msg Message) error { + return (ControlHandler{ + DisableSrcCiphering: true, + Src: bytes.NewReader(msg.Payload), + Dst: conn, + State: state, + }).Handle(ws.Header{ + Length: int64(len(msg.Payload)), + OpCode: msg.OpCode, + Fin: true, + Masked: state.ServerSide(), + }) +} + +// ControlFrameHandler returns FrameHandlerFunc for handling control frames. +// For more info see ControlHandler docs. +func ControlFrameHandler(w io.Writer, state ws.State) FrameHandlerFunc { + return func(h ws.Header, r io.Reader) error { + return (ControlHandler{ + DisableSrcCiphering: true, + Src: r, + Dst: w, + State: state, + }).Handle(h) + } +} + +func readData(rw io.ReadWriter, s ws.State, want ws.OpCode) ([]byte, ws.OpCode, error) { + controlHandler := ControlFrameHandler(rw, s) + rd := Reader{ + Source: rw, + State: s, + CheckUTF8: true, + SkipHeaderCheck: false, + OnIntermediate: controlHandler, + } + for { + hdr, err := rd.NextFrame() + if err != nil { + return nil, 0, err + } + if hdr.OpCode.IsControl() { + if err := controlHandler(hdr, &rd); err != nil { + return nil, 0, err + } + continue + } + if hdr.OpCode&want == 0 { + if err := rd.Discard(); err != nil { + return nil, 0, err + } + continue + } + + bts, err := ioutil.ReadAll(&rd) + + return bts, hdr.OpCode, err + } +} diff --git a/vendor/github.com/gobwas/ws/wsutil/reader.go b/vendor/github.com/gobwas/ws/wsutil/reader.go new file mode 100644 index 0000000000..ff2e5b6330 --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/reader.go @@ -0,0 +1,289 @@ +package wsutil + +import ( + "errors" + "io" + "io/ioutil" + + "github.com/gobwas/ws" +) + +// ErrNoFrameAdvance means that Reader's Read() method was called without +// 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. +// +// Note that reader represents already unmasked body. +type FrameHandlerFunc func(ws.Header, io.Reader) error + +// Reader is a wrapper around source io.Reader which represents WebSocket +// connection. It contains options for reading messages from source. +// +// Reader implements io.Reader, which Read() method reads payload of incoming +// WebSocket frames. It also takes care on fragmented frames and possibly +// intermediate control frames between them. +// +// Note that Reader's methods are not goroutine safe. +type Reader struct { + Source io.Reader + State ws.State + + // SkipHeaderCheck disables checking header bits to be RFC6455 compliant. + SkipHeaderCheck bool + + // CheckUTF8 enables UTF-8 checks for text frames payload. If incoming + // bytes are not valid UTF-8 sequence, ErrInvalidUTF8 returned. + CheckUTF8 bool + + // 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 + + opCode ws.OpCode // Used to store message op code on fragmentation. + frame io.Reader // Used to as frame reader. + raw io.LimitedReader // Used to discard frames without cipher. + utf8 UTF8Reader // Used to check UTF8 sequences if CheckUTF8 is true. +} + +// NewReader creates new frame reader that reads from r keeping given state to +// make some protocol validity checks when it needed. +func NewReader(r io.Reader, s ws.State) *Reader { + return &Reader{ + Source: r, + State: s, + } +} + +// NewClientSideReader is a helper function that calls NewReader with r and +// ws.StateClientSide. +func NewClientSideReader(r io.Reader) *Reader { + return NewReader(r, ws.StateClientSide) +} + +// NewServerSideReader is a helper function that calls NewReader with r and +// ws.StateServerSide. +func NewServerSideReader(r io.Reader) *Reader { + return NewReader(r, ws.StateServerSide) +} + +// Read implements io.Reader. It reads the next message payload into p. +// It takes care on fragmented messages. +// +// The error is io.EOF only if all of message bytes were read. +// If an io.EOF happens during reading some but not all the message bytes +// Read() returns io.ErrUnexpectedEOF. +// +// The error is ErrNoFrameAdvance if no NextFrame() call was made before +// reading next message bytes. +func (r *Reader) Read(p []byte) (n int, err error) { + if r.frame == nil { + if !r.fragmented() { + // Every new Read() must be preceded by NextFrame() call. + return 0, ErrNoFrameAdvance + } + // Read next continuation or intermediate control frame. + _, err := r.NextFrame() + if err != nil { + return 0, err + } + if r.frame == nil { + // We handled intermediate control and now got nothing to read. + return 0, nil + } + } + + n, err = r.frame.Read(p) + if err != nil && err != io.EOF { + return n, err + } + if err == nil && r.raw.N != 0 { + 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 + + case r.fragmented(): + err = nil + 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 + + default: + r.reset() + err = io.EOF + } + + return n, err +} + +// Discard discards current message unread bytes. +// It discards all frames of fragmented message. +func (r *Reader) Discard() (err error) { + for { + _, err = io.Copy(ioutil.Discard, &r.raw) + if err != nil { + break + } + if !r.fragmented() { + break + } + if _, err = r.NextFrame(); err != nil { + break + } + } + r.reset() + return err +} + +// NextFrame prepares r to read next message. It returns received frame header +// and non-nil error on failure. +// +// Note that next NextFrame() call must be done after receiving or discarding +// all current message bytes. +func (r *Reader) NextFrame() (hdr ws.Header, err error) { + hdr, err = ws.ReadHeader(r.Source) + if err == io.EOF && r.fragmented() { + // If we are in fragmented state EOF means that is was totally + // unexpected. + // + // NOTE: This is necessary to prevent callers such that + // ioutil.ReadAll to receive some amount of bytes without an error. + // ReadAll() ignores an io.EOF error, thus caller may think that + // whole message fetched, but actually only part of it. + err = io.ErrUnexpectedEOF + } + if err == nil && !r.SkipHeaderCheck { + err = ws.CheckHeader(hdr, r.State) + } + if err != nil { + 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: 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 { + err = cb(hdr, frame) + } + if err == nil { + // Ensure that src is empty. + _, err = io.Copy(ioutil.Discard, &r.raw) + } + return hdr, err + } + } else { + r.opCode = hdr.OpCode + } + if r.CheckUTF8 && (hdr.OpCode == ws.OpText || (r.fragmented() && r.opCode == ws.OpText)) { + r.utf8.Source = frame + frame = &r.utf8 + } + + // Save reader with ciphering and other streaming checks. + r.frame = frame + + if hdr.OpCode == ws.OpContinuation { + if cb := r.OnContinuation; cb != nil { + err = cb(hdr, frame) + } + } + + if hdr.Fin { + r.State = r.State.Clear(ws.StateFragmented) + } else { + r.State = r.State.Set(ws.StateFragmented) + } + + return hdr, err +} + +func (r *Reader) fragmented() bool { + return r.State.Fragmented() +} + +func (r *Reader) resetFragment() { + r.raw = io.LimitedReader{} + r.frame = nil + // Reset source of the UTF8Reader, but not the state. + r.utf8.Source = nil +} + +func (r *Reader) reset() { + r.raw = io.LimitedReader{} + r.frame = nil + r.utf8 = UTF8Reader{} + r.opCode = 0 +} + +// NextReader prepares next message read from r. It returns header that +// describes the message and io.Reader to read message's payload. It returns +// non-nil error when it is not possible to read message's initial frame. +// +// Note that next NextReader() on the same r should be done after reading all +// bytes from previously returned io.Reader. For more performant way to discard +// message use Reader and its Discard() method. +// +// Note that it will not handle any "intermediate" frames, that possibly could +// be received between text/binary continuation frames. That is, if peer sent +// text/binary frame with fin flag "false", then it could send ping frame, and +// eventually remaining part of text/binary frame with fin "true" – with +// NextReader() the ping frame will be dropped without any notice. To handle +// this rare, but possible situation (and if you do not know exactly which +// frames peer could send), you could use Reader with OnIntermediate field set. +func NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) { + rd := &Reader{ + Source: r, + State: s, + } + header, err := rd.NextFrame() + if err != nil { + return header, nil, err + } + return header, rd, nil +} diff --git a/vendor/github.com/gobwas/ws/wsutil/upgrader.go b/vendor/github.com/gobwas/ws/wsutil/upgrader.go new file mode 100644 index 0000000000..2ed351e088 --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/upgrader.go @@ -0,0 +1,68 @@ +package wsutil + +import ( + "bufio" + "bytes" + "io" + "io/ioutil" + "net/http" + + "github.com/gobwas/ws" +) + +// DebugUpgrader is a wrapper around ws.Upgrader. It tracks I/O of a +// WebSocket handshake. +// +// Note that it must not be used in production applications that requires +// Upgrade() to be efficient. +type DebugUpgrader struct { + // Upgrader contains upgrade to WebSocket options. + Upgrader ws.Upgrader + + // OnRequest and OnResponse are the callbacks that will be called with the + // HTTP request and response respectively. + OnRequest, OnResponse func([]byte) +} + +// Upgrade calls Upgrade() on underlying ws.Upgrader and tracks I/O on conn. +func (d *DebugUpgrader) Upgrade(conn io.ReadWriter) (hs ws.Handshake, err error) { + var ( + // Take the Reader and Writer parts from conn to be probably replaced + // below. + r io.Reader = conn + w io.Writer = conn + ) + if onRequest := d.OnRequest; onRequest != nil { + var buf bytes.Buffer + // First, we must read the entire request. + req, err := http.ReadRequest(bufio.NewReader( + io.TeeReader(conn, &buf), + )) + if err == nil { + // Fulfill the buffer with the response body. + io.Copy(ioutil.Discard, req.Body) + req.Body.Close() + } + onRequest(buf.Bytes()) + + r = io.MultiReader( + &buf, conn, + ) + } + + if onResponse := d.OnResponse; onResponse != nil { + var buf bytes.Buffer + // Intercept the response stream written by the Upgrade(). + w = io.MultiWriter( + conn, &buf, + ) + defer func() { + onResponse(buf.Bytes()) + }() + } + + return d.Upgrader.Upgrade(struct { + io.Reader + io.Writer + }{r, w}) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/utf8.go b/vendor/github.com/gobwas/ws/wsutil/utf8.go new file mode 100644 index 0000000000..b8dc7264ef --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/utf8.go @@ -0,0 +1,140 @@ +package wsutil + +import ( + "fmt" + "io" +) + +// ErrInvalidUTF8 is returned by UTF8 reader on invalid utf8 sequence. +var ErrInvalidUTF8 = fmt.Errorf("invalid utf8") + +// UTF8Reader implements io.Reader that calculates utf8 validity state after +// every read byte from Source. +// +// Note that in some cases client must call r.Valid() after all bytes are read +// to ensure that all of them are valid utf8 sequences. That is, some io helper +// functions such io.ReadAtLeast or io.ReadFull could discard the error +// information returned by the reader when they receive all of requested bytes. +// For example, the last read sequence is invalid and UTF8Reader returns number +// of bytes read and an error. But helper function decides to discard received +// error due to all requested bytes are completely read from the source. +// +// Another possible case is when some valid sequence become split by the read +// bound. Then UTF8Reader can not make decision about validity of the last +// sequence cause it is not fully read yet. And if the read stops, Valid() will +// return false, even if Read() by itself dit not. +type UTF8Reader struct { + Source io.Reader + + accepted int + + state uint32 + codep uint32 +} + +// NewUTF8Reader creates utf8 reader that reads from r. +func NewUTF8Reader(r io.Reader) *UTF8Reader { + return &UTF8Reader{ + Source: r, + } +} + +// Reset resets utf8 reader to read from r. +func (u *UTF8Reader) Reset(r io.Reader) { + u.Source = r + u.state = 0 + u.codep = 0 +} + +// Read implements io.Reader. +func (u *UTF8Reader) Read(p []byte) (n int, err error) { + n, err = u.Source.Read(p) + + accepted := 0 + s, c := u.state, u.codep + for i := 0; i < n; i++ { + c, s = decode(s, c, p[i]) + if s == utf8Reject { + u.state = s + return accepted, ErrInvalidUTF8 + } + if s == utf8Accept { + accepted = i + 1 + } + } + u.state, u.codep = s, c + u.accepted = accepted + + return n, err +} + +// Valid checks current reader state. It returns true if all read bytes are +// valid UTF-8 sequences, and false if not. +func (u *UTF8Reader) Valid() bool { + return u.state == utf8Accept +} + +// Accepted returns number of valid bytes in last Read(). +func (u *UTF8Reader) Accepted() int { + return u.accepted +} + +// Below is port of UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ +// +// Copyright (c) 2008-2009 Bjoern Hoehrmann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +const ( + utf8Accept = 0 + utf8Reject = 12 +) + +var utf8d = [...]byte{ + // The first part of the table maps bytes to character classes that + // to reduce the size of the transition table and create bitmasks. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + + // The second part is a transition table that maps a combination + // of a state of the automaton and a character class to a state. + 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, + 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +} + +func decode(state, codep uint32, b byte) (uint32, uint32) { + t := uint32(utf8d[b]) + + if state != utf8Accept { + codep = (uint32(b) & 0x3f) | (codep << 6) + } else { + codep = (0xff >> t) & uint32(b) + } + + return codep, uint32(utf8d[256+state+t]) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/writer.go b/vendor/github.com/gobwas/ws/wsutil/writer.go new file mode 100644 index 0000000000..6a837cf655 --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/writer.go @@ -0,0 +1,599 @@ +package wsutil + +import ( + "fmt" + "io" + + "github.com/gobwas/pool" + "github.com/gobwas/pool/pbytes" + "github.com/gobwas/ws" +) + +// DefaultWriteBuffer contains size of Writer's default buffer. It used by +// Writer constructor functions. +var DefaultWriteBuffer = 4096 + +var ( + // ErrNotEmpty is returned by Writer.WriteThrough() to indicate that buffer is + // not empty and write through could not be done. That is, caller should call + // Writer.FlushFragment() to make buffer empty. + ErrNotEmpty = fmt.Errorf("writer not empty") + + // ErrControlOverflow is returned by ControlWriter.Write() to indicate that + // no more data could be written to the underlying io.Writer because + // MaxControlFramePayloadSize limit is reached. + ErrControlOverflow = fmt.Errorf("control frame payload overflow") +) + +// Constants which are represent frame length ranges. +const ( + len7 = int64(125) // 126 and 127 are reserved values + len16 = int64(^uint16(0)) + len64 = int64((^uint64(0)) >> 1) +) + +// ControlWriter is a wrapper around Writer that contains some guards for +// buffered writes of control frames. +type ControlWriter struct { + w *Writer + limit int + n int +} + +// NewControlWriter contains ControlWriter with Writer inside whose buffer size +// is at most ws.MaxControlFramePayloadSize + ws.MaxHeaderSize. +func NewControlWriter(dest io.Writer, state ws.State, op ws.OpCode) *ControlWriter { + return &ControlWriter{ + w: NewWriterSize(dest, state, op, ws.MaxControlFramePayloadSize), + limit: ws.MaxControlFramePayloadSize, + } +} + +// NewControlWriterBuffer returns a new ControlWriter with buf as a buffer. +// +// Note that it reserves x bytes of buf for header data, where x could be +// ws.MinHeaderSize or ws.MinHeaderSize+4 (depending on state). At most +// (ws.MaxControlFramePayloadSize + x) bytes of buf will be used. +// +// It panics if len(buf) <= ws.MinHeaderSize + x. +func NewControlWriterBuffer(dest io.Writer, state ws.State, op ws.OpCode, buf []byte) *ControlWriter { + max := ws.MaxControlFramePayloadSize + headerSize(state, ws.MaxControlFramePayloadSize) + if len(buf) > max { + buf = buf[:max] + } + + w := NewWriterBuffer(dest, state, op, buf) + + return &ControlWriter{ + w: w, + limit: len(w.buf), + } +} + +// Write implements io.Writer. It writes to the underlying Writer until it +// returns error or until ControlWriter write limit will be exceeded. +func (c *ControlWriter) Write(p []byte) (n int, err error) { + if c.n+len(p) > c.limit { + return 0, ErrControlOverflow + } + return c.w.Write(p) +} + +// Flush flushes all buffered data to the underlying io.Writer. +func (c *ControlWriter) Flush() error { + return c.w.Flush() +} + +var writers = pool.New(128, 65536) + +// GetWriter tries to reuse Writer getting it from the pool. +// +// This function is intended for memory consumption optimizations, because +// NewWriter*() functions make allocations for inner buffer. +// +// Note the it ceils n to the power of two. +// +// If you have your own bytes buffer pool you could use NewWriterBuffer to use +// pooled bytes in writer. +func GetWriter(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer { + x, m := writers.Get(n) + if x != nil { + w := x.(*Writer) + w.Reset(dest, state, op) + return w + } + // NOTE: we use m instead of n, because m is an attempt to reuse w of such + // size in the future. + return NewWriterBufferSize(dest, state, op, m) +} + +// PutWriter puts w for future reuse by GetWriter(). +func PutWriter(w *Writer) { + w.Reset(nil, 0, 0) + 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) +} + +// NewWriterSize returns a new Writer whose buffer size is at most n + ws.MaxHeaderSize. +// That is, output frames payload length could be up to n, except the case when +// Write() is called on empty Writer with len(p) > n. +// +// If n <= 0 then the default buffer size is used as Writer's buffer size. +func NewWriterSize(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer { + if n > 0 { + n += headerSize(state, n) + } + return NewWriterBufferSize(dest, state, op, n) +} + +// NewWriterBufferSize returns a new Writer whose buffer size is equal to n. +// If n <= ws.MinHeaderSize then the default buffer size is used. +// +// Note that Writer will reserve x bytes for header data, where x is in range +// [ws.MinHeaderSize,ws.MaxHeaderSize]. That is, frames flushed by Writer +// will not have payload length equal to n, except the case when Write() is +// called on empty Writer with len(p) > n. +func NewWriterBufferSize(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer { + if n <= ws.MinHeaderSize { + n = DefaultWriteBuffer + } + return NewWriterBuffer(dest, state, op, make([]byte, n)) +} + +// NewWriterBuffer returns a new Writer with buf as a buffer. +// +// Note that it reserves x bytes of buf for header data, where x is in range +// [ws.MinHeaderSize,ws.MaxHeaderSize] (depending on state and buf size). +// +// You could use ws.HeaderSize() to calculate number of bytes needed to store +// header data. +// +// 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 { + w := &Writer{ + dest: dest, + state: state, + op: op, + raw: buf, + } + w.initBuf() + return w +} + +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:] +} + +// 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.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 +} + +// 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) +} + +// Available returns how many bytes are unused in the buffer. +func (w *Writer) Available() int { + return len(w.buf) - w.n +} + +// Buffered returns the number of bytes that have been written into the current +// buffer. +func (w *Writer) Buffered() int { + return w.n +} + +// Write implements io.Writer. +// +// Note that even if the Writer was created to have N-sized buffer, Write() +// with payload of N bytes will not fit into that buffer. Writer reserves some +// space to fit WebSocket header data. +func (w *Writer) Write(p []byte) (n int, err error) { + // Even empty p may make a sense. + w.dirty = true + + 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 + // io.Writer when writing frame header. + // + // On large buffers additional write is better than copying. + nn, _ = w.WriteThrough(p) + } else { + nn = copy(w.buf[w.n:], p) + w.n += nn + w.FlushFragment() + } + n += nn + p = p[nn:] + } + if w.err != nil { + return n, w.err + } + nn = copy(w.buf[w.n:], p) + w.n += nn + n += nn + + // Even if w.Available() == 0 we will not flush buffer preventively because + // this could bring unwanted fragmentation. That is, user could create + // buffer with size that fits exactly all further Write() call, and then + // call Flush(), excepting that single and not fragmented frame will be + // sent. With preemptive flush this case will produce two frames – last one + // will be empty and just to set fin = true. + + 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) { + if w.err != nil { + return 0, w.err + } + if w.Buffered() != 0 { + return 0, ErrNotEmpty + } + + 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.fseq++ + + return n, w.err +} + +// ReadFrom implements io.ReaderFrom. +func (w *Writer) ReadFrom(src io.Reader) (n int64, err error) { + var nn int + for err == nil { + if w.Available() == 0 { + if w.noFlush { + w.Grow(w.Buffered()) // Twice bigger. + } else { + err = w.FlushFragment() + } + continue + } + + // We copy the behavior of bufio.Writer here. + // Also, from the docs on io.ReaderFrom: + // ReadFrom reads data from r until EOF or error. + // + // See https://codereview.appspot.com/76400048/#ps1 + const maxEmptyReads = 100 + var nr int + for nr < maxEmptyReads { + nn, err = src.Read(w.buf[w.n:]) + if nn != 0 || err != nil { + break + } + nr++ + } + if nr == maxEmptyReads { + return n, io.ErrNoProgress + } + + w.n += nn + n += int64(nn) + } + if err == io.EOF { + // NOTE: Do not flush preemptively. + // See the Write() sources for more info. + err = nil + w.dirty = true + } + return n, err +} + +// Flush writes any buffered data to the underlying io.Writer. +// It sends the frame with "fin" flag set to true. +// +// If no Write() or ReadFrom() was made, then Flush() does nothing. +func (w *Writer) Flush() error { + if (!w.dirty && w.Buffered() == 0) || w.err != nil { + return w.err + } + + w.err = w.flushFragment(true) + w.n = 0 + w.dirty = false + w.fseq = 0 + + return w.err +} + +// FlushFragment writes any buffered data to the underlying io.Writer. +// It sends the frame with "fin" flag set to false. +func (w *Writer) FlushFragment() error { + if w.Buffered() == 0 || w.err != nil { + return w.err + } + + w.err = w.flushFragment(false) + w.n = 0 + w.fseq++ + + return w.err +} + +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() { + header.Masked = true + header.Mask = ws.NewMask() + ws.Cipher(payload, header.Mask, 0) + } + // Write header to the header segment of the raw buffer. + var ( + offset = len(w.raw) - len(w.buf) + skip = offset - ws.HeaderSize(header) + ) + buf := bytesWriter{ + buf: w.raw[skip:offset], + } + if err := ws.WriteHeader(&buf, header); err != nil { + // Must never be reached. + panic("dump header error: " + err.Error()) + } + _, err = w.dest.Write(w.raw[skip : offset+w.n]) + return err +} + +func (w *Writer) opCode() ws.OpCode { + if w.fseq > 0 { + return ws.OpContinuation + } + return w.op +} + +var errNoSpace = fmt.Errorf("not enough buffer space") + +type bytesWriter struct { + buf []byte + pos int +} + +func (w *bytesWriter) Write(p []byte) (int, error) { + n := copy(w.buf[w.pos:], p) + w.pos += n + if n != len(p) { + return n, errNoSpace + } + return n, nil +} + +func writeFrame(w io.Writer, s ws.State, op ws.OpCode, fin bool, p []byte) error { + var frame ws.Frame + if s.ClientSide() { + // Should copy bytes to prevent corruption of caller data. + payload := pbytes.GetLen(len(p)) + defer pbytes.Put(payload) + + copy(payload, p) + + frame = ws.NewFrame(op, fin, payload) + frame = ws.MaskFrameInPlace(frame) + } else { + frame = ws.NewFrame(op, fin, p) + } + + 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(), + }) +} diff --git a/vendor/github.com/gobwas/ws/wsutil/wsutil.go b/vendor/github.com/gobwas/ws/wsutil/wsutil.go new file mode 100644 index 0000000000..86211f3e53 --- /dev/null +++ b/vendor/github.com/gobwas/ws/wsutil/wsutil.go @@ -0,0 +1,57 @@ +/* +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 + } + + // 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 + } + +You could get the same behavior using just `wsutil.Reader`: + + r := wsutil.Reader{ + Source: conn, + CheckUTF8: true, + } + + payload, err := ioutil.ReadAll(r) + if err != nil { + // handle err + } + +Or even simplest: + + 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) + + _, 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. +*/ +package wsutil diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore deleted file mode 100644 index cd3fcd1ef7..0000000000 --- a/vendor/github.com/gorilla/websocket/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -.idea/ -*.iml diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS deleted file mode 100644 index 1931f40068..0000000000 --- a/vendor/github.com/gorilla/websocket/AUTHORS +++ /dev/null @@ -1,9 +0,0 @@ -# This is the official list of Gorilla WebSocket authors for copyright -# purposes. -# -# Please keep the list sorted. - -Gary Burd -Google LLC (https://opensource.google.com/) -Joachim Bauch - diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE deleted file mode 100644 index 9171c97225..0000000000 --- a/vendor/github.com/gorilla/websocket/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md deleted file mode 100644 index 19aa2e75c8..0000000000 --- a/vendor/github.com/gorilla/websocket/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Gorilla WebSocket - -[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) -[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) - -Gorilla WebSocket is a [Go](http://golang.org/) implementation of the -[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. - -### Documentation - -* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) -* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) -* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) -* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) -* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) - -### Status - -The Gorilla WebSocket package provides a complete and tested implementation of -the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The -package API is stable. - -### Installation - - go get github.com/gorilla/websocket - -### Protocol Compliance - -The Gorilla WebSocket package passes the server tests in the [Autobahn Test -Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn -subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). - -### Gorilla WebSocket compared with other packages - - - - - - - - - - - - - - - - - - -
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
- -Notes: - -1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). -2. The application can get the type of a received data message by implementing - a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) - function. -3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. - Read returns when the input buffer is full or a frame boundary is - encountered. Each call to Write sends a single frame message. The Gorilla - io.Reader and io.WriteCloser operate on a single WebSocket message. - diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go deleted file mode 100644 index 962c06a391..0000000000 --- a/vendor/github.com/gorilla/websocket/client.go +++ /dev/null @@ -1,395 +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 websocket - -import ( - "bytes" - "context" - "crypto/tls" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httptrace" - "net/url" - "strings" - "time" -) - -// ErrBadHandshake is returned when the server response to opening handshake is -// 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 -// (Cookie). Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etc. -// -// Deprecated: Use Dialer instead. -func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { - d := Dialer{ - ReadBufferSize: readBufSize, - WriteBufferSize: writeBufSize, - NetDial: func(net, addr string) (net.Conn, error) { - return netConn, nil - }, - } - return d.Dial(u.String(), requestHeader) -} - -// A Dialer contains options for connecting to WebSocket server. -type Dialer struct { - // NetDial specifies the dial function for creating TCP connections. If - // NetDial is nil, net.Dial is used. - NetDial func(network, addr string) (net.Conn, error) - - // NetDialContext specifies the dial function for creating TCP connections. If - // NetDialContext is nil, net.DialContext is used. - NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) - - // Proxy specifies a function to return a proxy for a given - // Request. If the function returns a non-nil error, the - // request is aborted with the provided error. - // If Proxy is nil or returns a nil *URL, no proxy is used. - Proxy func(*http.Request) (*url.URL, error) - - // TLSClientConfig specifies the TLS configuration to use with tls.Client. - // If nil, the default configuration is used. - TLSClientConfig *tls.Config - - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer - // size is zero, then a useful default size is used. The I/O buffer sizes - // do not limit the size of the messages that can be sent or received. - ReadBufferSize, WriteBufferSize int - - // WriteBufferPool is a pool of buffers for write operations. If the value - // is not set, then write buffers are allocated to the connection for the - // lifetime of the connection. - // - // A pool is most useful when the application has a modest volume of writes - // across a large number of connections. - // - // Applications should use a single pool for each unique value of - // WriteBufferSize. - WriteBufferPool BufferPool - - // 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 -} - -// Dial creates a new client connection by calling DialContext with a background context. -func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { - return d.DialContext(context.Background(), urlStr, requestHeader) -} - -var errMalformedURL = errors.New("malformed ws or wss URL") - -func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { - hostPort = u.Host - hostNoPort = u.Host - if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { - hostNoPort = hostNoPort[:i] - } else { - switch u.Scheme { - case "wss": - hostPort += ":443" - case "https": - hostPort += ":443" - default: - hostPort += ":80" - } - } - return hostPort, hostNoPort -} - -// DefaultDialer is a dialer with all fields set to the default values. -var DefaultDialer = &Dialer{ - Proxy: http.ProxyFromEnvironment, - HandshakeTimeout: 45 * time.Second, -} - -// nilDialer is dialer to use when receiver is nil. -var nilDialer = *DefaultDialer - -// DialContext creates a new client connection. Use requestHeader to specify the -// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). -// Use the response.Header to get the selected subprotocol -// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). -// -// The context will be used in the request and in the Dialer. -// -// If the WebSocket handshake fails, ErrBadHandshake is returned along with a -// non-nil *http.Response so that callers can handle redirects, authentication, -// etcetera. The response body may not contain the entire response and does not -// need to be closed by the application. -func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { - if d == nil { - d = &nilDialer - } - - challengeKey, err := generateChallengeKey() - if err != nil { - return nil, nil, err - } - - u, err := url.Parse(urlStr) - if err != nil { - return nil, nil, err - } - - switch u.Scheme { - case "ws": - u.Scheme = "http" - case "wss": - u.Scheme = "https" - default: - return nil, nil, errMalformedURL - } - - if u.User != nil { - // User name and password are not allowed in websocket URIs. - return nil, nil, errMalformedURL - } - - req := &http.Request{ - Method: "GET", - URL: u, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - Host: u.Host, - } - req = req.WithContext(ctx) - - // 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 - // method canonicalizes the header names. - req.Header["Upgrade"] = []string{"websocket"} - req.Header["Connection"] = []string{"Upgrade"} - req.Header["Sec-WebSocket-Key"] = []string{challengeKey} - req.Header["Sec-WebSocket-Version"] = []string{"13"} - if len(d.Subprotocols) > 0 { - req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} - } - for k, vs := range requestHeader { - switch { - case k == "Host": - if len(vs) > 0 { - req.Host = vs[0] - } - case k == "Upgrade" || - 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) - case k == "Sec-Websocket-Protocol": - req.Header["Sec-WebSocket-Protocol"] = vs - default: - req.Header[k] = vs - } - } - - if d.EnableCompression { - req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} - } - - if d.HandshakeTimeout != 0 { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout) - defer cancel() - } - - // Get network dial function. - var netDial func(network, add string) (net.Conn, error) - - if d.NetDialContext != nil { - netDial = func(network, addr string) (net.Conn, error) { - return d.NetDialContext(ctx, network, addr) - } - } else if d.NetDial != nil { - netDial = d.NetDial - } else { - netDialer := &net.Dialer{} - netDial = func(network, addr string) (net.Conn, error) { - return netDialer.DialContext(ctx, network, addr) - } - } - - // If needed, wrap the dial function to set the connection deadline. - if deadline, ok := ctx.Deadline(); ok { - forwardDial := netDial - netDial = func(network, addr string) (net.Conn, error) { - c, err := forwardDial(network, addr) - if err != nil { - return nil, err - } - err = c.SetDeadline(deadline) - if err != nil { - c.Close() - return nil, err - } - return c, nil - } - } - - // If needed, wrap the dial function to connect through a proxy. - if d.Proxy != nil { - proxyURL, err := d.Proxy(req) - if err != nil { - return nil, nil, err - } - if proxyURL != nil { - dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) - if err != nil { - return nil, nil, err - } - netDial = dialer.Dial - } - } - - hostPort, hostNoPort := hostPortNoPort(u) - trace := httptrace.ContextClientTrace(ctx) - if trace != nil && trace.GetConn != nil { - trace.GetConn(hostPort) - } - - netConn, err := netDial("tcp", hostPort) - if trace != nil && trace.GotConn != nil { - trace.GotConn(httptrace.GotConnInfo{ - Conn: netConn, - }) - } - if err != nil { - return nil, nil, err - } - - defer func() { - if netConn != nil { - netConn.Close() - } - }() - - if u.Scheme == "https" { - cfg := cloneTLSConfig(d.TLSClientConfig) - if cfg.ServerName == "" { - cfg.ServerName = hostNoPort - } - tlsConn := tls.Client(netConn, cfg) - netConn = tlsConn - - var err error - if trace != nil { - err = doHandshakeWithTrace(trace, tlsConn, cfg) - } else { - err = doHandshake(tlsConn, cfg) - } - - if err != nil { - return nil, nil, err - } - } - - conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil) - - if err := req.Write(netConn); err != nil { - return nil, nil, err - } - - if trace != nil && trace.GotFirstResponseByte != nil { - if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 { - trace.GotFirstResponseByte() - } - } - - resp, err := http.ReadResponse(conn.br, req) - 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") || - resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { - // Before closing the network connection on return from this - // function, slurp up some of the response to aid application - // debugging. - buf := make([]byte, 1024) - n, _ := io.ReadFull(resp.Body, buf) - resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) - return nil, resp, ErrBadHandshake - } - - for _, ext := range parseExtensions(resp.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") - - netConn.SetDeadline(time.Time{}) - netConn = nil // to avoid close in defer. - return conn, resp, nil -} - -func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error { - if err := tlsConn.Handshake(); err != nil { - return err - } - if !cfg.InsecureSkipVerify { - if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { - return err - } - } - return nil -} diff --git a/vendor/github.com/gorilla/websocket/client_clone.go b/vendor/github.com/gorilla/websocket/client_clone.go deleted file mode 100644 index 4f0d943723..0000000000 --- a/vendor/github.com/gorilla/websocket/client_clone.go +++ /dev/null @@ -1,16 +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. - -// +build go1.8 - -package websocket - -import "crypto/tls" - -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return cfg.Clone() -} diff --git a/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/vendor/github.com/gorilla/websocket/client_clone_legacy.go deleted file mode 100644 index babb007fb4..0000000000 --- a/vendor/github.com/gorilla/websocket/client_clone_legacy.go +++ /dev/null @@ -1,38 +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. - -// +build !go1.8 - -package websocket - -import "crypto/tls" - -// cloneTLSConfig clones all public fields except the fields -// SessionTicketsDisabled and SessionTicketKey. This avoids copying the -// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a -// config in active use. -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, - } -} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go deleted file mode 100644 index 813ffb1e84..0000000000 --- a/vendor/github.com/gorilla/websocket/compression.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2017 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" - "sync" -) - -const ( - minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 - maxCompressionLevel = flate.BestCompression - defaultCompressionLevel = 1 -) - -var ( - flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool - flateReaderPool = sync.Pool{New: func() interface{} { - return flate.NewReader(nil) - }} -) - -func decompressNoContextTakeover(r io.Reader) io.ReadCloser { - 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" - - fr, _ := flateReaderPool.Get().(io.ReadCloser) - fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) - return &flateReadWrapper{fr} -} - -func isValidCompressionLevel(level int) bool { - return minCompressionLevel <= level && level <= maxCompressionLevel -} - -func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { - p := &flateWriterPools[level-minCompressionLevel] - tw := &truncWriter{w: w} - fw, _ := p.Get().(*flate.Writer) - if fw == nil { - fw, _ = flate.NewWriter(tw, level) - } else { - fw.Reset(tw) - } - return &flateWriteWrapper{fw: fw, tw: tw, p: p} -} - -// 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 flateWriteWrapper struct { - fw *flate.Writer - tw *truncWriter - p *sync.Pool -} - -func (w *flateWriteWrapper) Write(p []byte) (int, error) { - if w.fw == nil { - return 0, errWriteClosed - } - return w.fw.Write(p) -} - -func (w *flateWriteWrapper) Close() error { - if w.fw == nil { - return errWriteClosed - } - err1 := w.fw.Flush() - w.p.Put(w.fw) - w.fw = nil - 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 -} - -type flateReadWrapper struct { - fr io.ReadCloser -} - -func (r *flateReadWrapper) Read(p []byte) (int, error) { - if r.fr == nil { - return 0, io.ErrClosedPipe - } - n, err := r.fr.Read(p) - if err == io.EOF { - // Preemptively place the reader back in the pool. This helps with - // scenarios where the application does not call NextReader() soon after - // this final read. - r.Close() - } - return n, err -} - -func (r *flateReadWrapper) Close() error { - if r.fr == nil { - return io.ErrClosedPipe - } - err := r.fr.Close() - flateReaderPool.Put(r.fr) - r.fr = nil - return err -} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go deleted file mode 100644 index ca46d2f793..0000000000 --- a/vendor/github.com/gorilla/websocket/conn.go +++ /dev/null @@ -1,1201 +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 websocket - -import ( - "bufio" - "encoding/binary" - "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 - - writeWait = time.Second - - defaultReadBufferSize = 4096 - defaultWriteBufferSize = 4096 - - continuationFrame = 0 - noFrame = -1 -) - -// Close codes defined in RFC 6455, section 11.7. -const ( - CloseNormalClosure = 1000 - CloseGoingAway = 1001 - CloseProtocolError = 1002 - CloseUnsupportedData = 1003 - CloseNoStatusReceived = 1005 - CloseAbnormalClosure = 1006 - CloseInvalidFramePayloadData = 1007 - ClosePolicyViolation = 1008 - CloseMessageTooBig = 1009 - CloseMandatoryExtension = 1010 - CloseInternalServerErr = 1011 - CloseServiceRestart = 1012 - CloseTryAgainLater = 1013 - CloseTLSHandshake = 1015 -) - -// The message types are defined in RFC 6455, section 11.8. -const ( - // TextMessage denotes a text data message. The text message payload is - // interpreted as UTF-8 encoded text data. - TextMessage = 1 - - // BinaryMessage denotes a binary data message. - BinaryMessage = 2 - - // CloseMessage denotes a close control message. The optional message - // payload contains a numeric code and text. Use the FormatCloseMessage - // function to format a close message payload. - CloseMessage = 8 - - // PingMessage denotes a ping control message. The optional message payload - // is UTF-8 encoded text. - PingMessage = 9 - - // PongMessage denotes a pong control message. The optional message payload - // is UTF-8 encoded text. - PongMessage = 10 -) - -// ErrCloseSent is returned when the application writes a message to the -// connection after sending a close message. -var ErrCloseSent = errors.New("websocket: close sent") - -// ErrReadLimit is returned when reading a message that is larger than the -// read limit set for the connection. -var ErrReadLimit = errors.New("websocket: read limit exceeded") - -// netError satisfies the net Error interface. -type netError struct { - msg string - temporary bool - timeout bool -} - -func (e *netError) Error() string { return e.msg } -func (e *netError) Temporary() bool { return e.temporary } -func (e *netError) Timeout() bool { return e.timeout } - -// CloseError represents a close message. -type CloseError struct { - // Code is defined in RFC 6455, section 11.7. - Code int - - // Text is the optional text payload. - Text string -} - -func (e *CloseError) Error() string { - s := []byte("websocket: close ") - s = strconv.AppendInt(s, int64(e.Code), 10) - switch e.Code { - case CloseNormalClosure: - s = append(s, " (normal)"...) - case CloseGoingAway: - s = append(s, " (going away)"...) - case CloseProtocolError: - s = append(s, " (protocol error)"...) - case CloseUnsupportedData: - s = append(s, " (unsupported data)"...) - case CloseNoStatusReceived: - s = append(s, " (no status)"...) - case CloseAbnormalClosure: - s = append(s, " (abnormal closure)"...) - case CloseInvalidFramePayloadData: - s = append(s, " (invalid payload data)"...) - case ClosePolicyViolation: - s = append(s, " (policy violation)"...) - case CloseMessageTooBig: - s = append(s, " (message too big)"...) - case CloseMandatoryExtension: - s = append(s, " (mandatory extension missing)"...) - case CloseInternalServerErr: - s = append(s, " (internal server error)"...) - case CloseTLSHandshake: - s = append(s, " (TLS handshake error)"...) - } - if e.Text != "" { - s = append(s, ": "...) - s = append(s, e.Text...) - } - return string(s) -} - -// IsCloseError returns boolean indicating whether the error is a *CloseError -// with one of the specified codes. -func IsCloseError(err error, codes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range codes { - if e.Code == code { - return true - } - } - } - return false -} - -// IsUnexpectedCloseError returns boolean indicating whether the error is a -// *CloseError with a code not in the list of expected codes. -func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { - if e, ok := err.(*CloseError); ok { - for _, code := range expectedCodes { - if e.Code == code { - return false - } - } - return true - } - return false -} - -var ( - errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} - errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} - errBadWriteOpCode = errors.New("websocket: bad write message type") - errWriteClosed = errors.New("websocket: write closed") - errInvalidControlFrame = errors.New("websocket: invalid control frame") -) - -func newMaskKey() [4]byte { - n := rand.Uint32() - return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} -} - -func hideTempErr(err error) error { - if e, ok := err.(net.Error); ok && e.Temporary() { - err = &netError{msg: e.Error(), timeout: e.Timeout()} - } - return err -} - -func isControl(frameType int) bool { - return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage -} - -func isData(frameType int) bool { - return frameType == TextMessage || frameType == BinaryMessage -} - -var validReceivedCloseCodes = map[int]bool{ - // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number - - CloseNormalClosure: true, - CloseGoingAway: true, - CloseProtocolError: true, - CloseUnsupportedData: true, - CloseNoStatusReceived: false, - CloseAbnormalClosure: false, - CloseInvalidFramePayloadData: true, - ClosePolicyViolation: true, - CloseMessageTooBig: true, - CloseMandatoryExtension: true, - CloseInternalServerErr: true, - CloseServiceRestart: true, - CloseTryAgainLater: true, - CloseTLSHandshake: false, -} - -func isValidReceivedCloseCode(code int) bool { - return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) -} - -// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this -// interface. The type of the value stored in a pool is not specified. -type BufferPool interface { - // Get gets a value from the pool or returns nil if the pool is empty. - Get() interface{} - // Put adds a value to the pool. - Put(interface{}) -} - -// writePoolData is the type added to the write buffer pool. This wrapper is -// used to prevent applications from peeking at and depending on the values -// added to the pool. -type writePoolData struct{ buf []byte } - -// The Conn type represents a WebSocket connection. -type Conn struct { - conn net.Conn - isServer bool - subprotocol string - - // Write fields - mu chan struct{} // used as mutex to protect write to conn - writeBuf []byte // frame is constructed in this buffer. - writePool BufferPool - writeBufSize int - writeDeadline time.Time - writer io.WriteCloser // the current writer returned to the application - isWriting bool // for best-effort concurrent write detection - - writeErrMu sync.Mutex - writeErr error - - enableWriteCompression bool - compressionLevel int - newCompressionWriter func(io.WriteCloser, int) io.WriteCloser - - // Read fields - reader io.ReadCloser // the current reader returned to the application - readErr error - br *bufio.Reader - // bytes remaining in current frame. - // set setReadRemaining to safely update this value and prevent overflow - readRemaining int64 - readFinal bool // true the current message has more frames. - 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.ReadCloser -} - -func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn { - - if br == nil { - if readBufferSize == 0 { - readBufferSize = defaultReadBufferSize - } else if readBufferSize < maxControlFramePayloadSize { - // must be large enough for control frame - readBufferSize = maxControlFramePayloadSize - } - br = bufio.NewReaderSize(conn, readBufferSize) - } - - if writeBufferSize <= 0 { - writeBufferSize = defaultWriteBufferSize - } - writeBufferSize += maxFrameHeaderSize - - if writeBuf == nil && writeBufferPool == nil { - writeBuf = make([]byte, writeBufferSize) - } - - mu := make(chan struct{}, 1) - mu <- struct{}{} - c := &Conn{ - isServer: isServer, - br: br, - conn: conn, - mu: mu, - readFinal: true, - writeBuf: writeBuf, - writePool: writeBufferPool, - writeBufSize: writeBufferSize, - enableWriteCompression: true, - compressionLevel: defaultCompressionLevel, - } - c.SetCloseHandler(nil) - c.SetPingHandler(nil) - c.SetPongHandler(nil) - return c -} - -// setReadRemaining tracks the number of bytes remaining on the connection. If n -// overflows, an ErrReadLimit is returned. -func (c *Conn) setReadRemaining(n int64) error { - if n < 0 { - return ErrReadLimit - } - - c.readRemaining = n - return nil -} - -// Subprotocol returns the negotiated protocol for the connection. -func (c *Conn) Subprotocol() string { - return c.subprotocol -} - -// Close closes the underlying network connection without sending or waiting -// for a close message. -func (c *Conn) Close() error { - return c.conn.Close() -} - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// 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) 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 -} - -func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { - <-c.mu - defer func() { c.mu <- struct{}{} }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - c.conn.SetWriteDeadline(deadline) - if len(buf1) == 0 { - _, err = c.conn.Write(buf0) - } else { - err = c.writeBufs(buf0, buf1) - } - if err != nil { - return c.writeFatal(err) - } - if frameType == CloseMessage { - c.writeFatal(ErrCloseSent) - } - return nil -} - -// WriteControl writes a control message with the given deadline. The allowed -// message types are CloseMessage, PingMessage and PongMessage. -func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { - if !isControl(messageType) { - return errBadWriteOpCode - } - if len(data) > maxControlFramePayloadSize { - return errInvalidControlFrame - } - - b0 := byte(messageType) | finalBit - b1 := byte(len(data)) - if !c.isServer { - b1 |= maskBit - } - - buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) - buf = append(buf, b0, b1) - - if c.isServer { - buf = append(buf, data...) - } else { - key := newMaskKey() - buf = append(buf, key[:]...) - buf = append(buf, data...) - maskBytes(key, 0, buf[6:]) - } - - d := 1000 * time.Hour - if !deadline.IsZero() { - d = deadline.Sub(time.Now()) - if d < 0 { - return errWriteTimeout - } - } - - timer := time.NewTimer(d) - select { - case <-c.mu: - timer.Stop() - case <-timer.C: - return errWriteTimeout - } - defer func() { c.mu <- struct{}{} }() - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - c.conn.SetWriteDeadline(deadline) - _, err = c.conn.Write(buf) - if err != nil { - return c.writeFatal(err) - } - if messageType == CloseMessage { - c.writeFatal(ErrCloseSent) - } - return err -} - -// beginMessage prepares a connection and message writer for a new message. -func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { - // 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 errBadWriteOpCode - } - - c.writeErrMu.Lock() - err := c.writeErr - c.writeErrMu.Unlock() - if err != nil { - return err - } - - mw.c = c - mw.frameType = messageType - mw.pos = maxFrameHeaderSize - - if c.writeBuf == nil { - wpd, ok := c.writePool.Get().(writePoolData) - if ok { - c.writeBuf = wpd.buf - } else { - c.writeBuf = make([]byte, c.writeBufSize) - } - } - return nil -} - -// 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. -// -// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and -// PongMessage) are supported. -func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { - var mw messageWriter - if err := c.beginMessage(&mw, messageType); err != nil { - return nil, err - } - c.writer = &mw - if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { - w := c.newCompressionWriter(c.writer, c.compressionLevel) - 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) endMessage(err error) error { - if w.err != nil { - return err - } - c := w.c - w.err = err - c.writer = nil - if c.writePool != nil { - c.writePool.Put(writePoolData{buf: c.writeBuf}) - c.writeBuf = 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.endMessage(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 - } - - // Assume that the frame starts at beginning of c.writeBuf. - framePos := 0 - if c.isServer { - // Adjust up if mask not included in the header. - framePos = 4 - } - - switch { - case length >= 65536: - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 127 - binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) - case length > 125: - framePos += 6 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | 126 - binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) - default: - framePos += 8 - c.writeBuf[framePos] = b0 - c.writeBuf[framePos+1] = b1 | byte(length) - } - - if !c.isServer { - key := newMaskKey() - copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) - maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) - if len(extra) > 0 { - return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) - } - } - - // Write the buffers to the connection with best-effort detection of - // concurrent writes. See the concurrency section in the package - // documentation for more info. - - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - - 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 - - if err != nil { - return w.endMessage(err) - } - - if final { - w.endMessage(errWriteClosed) - return nil - } - - // 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.pos - if n <= 0 { - if err := w.flushFrame(false, nil); err != nil { - return 0, err - } - n = len(w.c.writeBuf) - w.pos - } - if n > max { - n = max - } - return n, nil -} - -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.flushFrame(false, p) - if err != nil { - return 0, err - } - return len(p), nil - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - copy(w.c.writeBuf[w.pos:], p[:n]) - w.pos += n - p = p[n:] - } - return nn, nil -} - -func (w *messageWriter) WriteString(p string) (int, error) { - if w.err != nil { - return 0, w.err - } - - nn := len(p) - for len(p) > 0 { - n, err := w.ncopy(len(p)) - if err != nil { - return 0, err - } - 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 w.err != nil { - return 0, w.err - } - for { - 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.pos:]) - w.pos += n - nn += int64(n) - if err != nil { - if err == io.EOF { - err = nil - } - break - } - } - return nn, err -} - -func (w *messageWriter) Close() error { - if w.err != nil { - return w.err - } - return w.flushFrame(true, nil) -} - -// WritePreparedMessage writes prepared message into connection. -func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { - frameType, frameData, err := pm.frame(prepareKey{ - isServer: c.isServer, - compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), - compressionLevel: c.compressionLevel, - }) - if err != nil { - return err - } - if c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = true - err = c.write(frameType, c.writeDeadline, frameData, nil) - if !c.isWriting { - panic("concurrent write to websocket connection") - } - c.isWriting = false - return err -} - -// 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 { - - if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { - // Fast path with no allocations and single frame. - - var mw messageWriter - if err := c.beginMessage(&mw, messageType); err != nil { - return err - } - n := copy(c.writeBuf[mw.pos:], data) - mw.pos += n - data = data[n:] - return mw.flushFrame(true, data) - } - - w, err := c.NextWriter(messageType) - if err != nil { - return err - } - if _, err = w.Write(data); err != nil { - return err - } - return w.Close() -} - -// SetWriteDeadline sets the write deadline on the underlying network -// connection. After a write has timed out, the websocket state is corrupt and -// all future writes will return an error. A zero value for t means writes will -// not time out. -func (c *Conn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = t - return nil -} - -// Read methods - -func (c *Conn) advanceFrame() (int, error) { - // 1. Skip remainder of previous frame. - - if c.readRemaining > 0 { - if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { - return noFrame, err - } - } - - // 2. Read and parse first two bytes of frame header. - - p, err := c.read(2) - if err != nil { - return noFrame, err - } - - final := p[0]&finalBit != 0 - frameType := int(p[0] & 0xf) - mask := p[1]&maskBit != 0 - c.setReadRemaining(int64(p[1] & 0x7f)) - - 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 { - case CloseMessage, PingMessage, PongMessage: - if c.readRemaining > maxControlFramePayloadSize { - return noFrame, c.handleProtocolError("control frame length > 125") - } - if !final { - return noFrame, c.handleProtocolError("control frame not final") - } - case TextMessage, BinaryMessage: - if !c.readFinal { - return noFrame, c.handleProtocolError("message start before final message frame") - } - c.readFinal = final - case continuationFrame: - if c.readFinal { - return noFrame, c.handleProtocolError("continuation after final message frame") - } - c.readFinal = final - default: - return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) - } - - // 3. Read and parse frame length as per - // https://tools.ietf.org/html/rfc6455#section-5.2 - // - // The length of the "Payload data", in bytes: if 0-125, that is the payload - // length. - // - If 126, the following 2 bytes interpreted as a 16-bit unsigned - // integer are the payload length. - // - If 127, the following 8 bytes interpreted as - // a 64-bit unsigned integer (the most significant bit MUST be 0) are the - // payload length. Multibyte length quantities are expressed in network byte - // order. - - switch c.readRemaining { - case 126: - p, err := c.read(2) - if err != nil { - return noFrame, err - } - - if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { - return noFrame, err - } - case 127: - p, err := c.read(8) - if err != nil { - return noFrame, err - } - - if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { - return noFrame, err - } - } - - // 4. Handle frame masking. - - if mask != c.isServer { - return noFrame, c.handleProtocolError("incorrect mask flag") - } - - if mask { - c.readMaskPos = 0 - 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. - - if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { - - c.readLength += c.readRemaining - // Don't allow readLength to overflow in the presence of a large readRemaining - // counter. - if c.readLength < 0 { - return noFrame, ErrReadLimit - } - - if c.readLimit > 0 && c.readLength > c.readLimit { - c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) - return noFrame, ErrReadLimit - } - - return frameType, nil - } - - // 6. Read control frame payload. - - var payload []byte - if c.readRemaining > 0 { - payload, err = c.read(int(c.readRemaining)) - c.setReadRemaining(0) - if err != nil { - return noFrame, err - } - if c.isServer { - maskBytes(c.readMaskKey, 0, payload) - } - } - - // 7. Process control frame payload. - - switch frameType { - case PongMessage: - if err := c.handlePong(string(payload)); err != nil { - return noFrame, err - } - case PingMessage: - if err := c.handlePing(string(payload)); err != nil { - return noFrame, err - } - case CloseMessage: - closeCode := CloseNoStatusReceived - closeText := "" - if len(payload) >= 2 { - closeCode = int(binary.BigEndian.Uint16(payload)) - if !isValidReceivedCloseCode(closeCode) { - return noFrame, c.handleProtocolError("invalid close code") - } - closeText = string(payload[2:]) - if !utf8.ValidString(closeText) { - return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") - } - } - if err := c.handleClose(closeCode, closeText); err != nil { - return noFrame, err - } - return noFrame, &CloseError{Code: closeCode, Text: closeText} - } - - return frameType, nil -} - -func (c *Conn) handleProtocolError(message string) error { - c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) - return errors.New("websocket: " + message) -} - -// NextReader returns the next data message received from the peer. The -// returned messageType is either TextMessage or BinaryMessage. -// -// There can be at most one open reader on a connection. NextReader discards -// the previous message if the application has not already consumed it. -// -// Applications must break out of the application's read loop when this method -// returns a non-nil error value. Errors returned from this method are -// permanent. Once this method returns a non-nil error, all subsequent calls to -// this method return the same error. -func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { - // Close previous reader, only relevant for decompression. - if c.reader != nil { - c.reader.Close() - c.reader = nil - } - - c.messageReader = nil - c.readLength = 0 - - for c.readErr == nil { - frameType, err := c.advanceFrame() - if err != nil { - c.readErr = hideTempErr(err) - break - } - - if frameType == TextMessage || frameType == BinaryMessage { - c.messageReader = &messageReader{c} - c.reader = c.messageReader - if c.readDecompress { - c.reader = c.newDecompressionReader(c.reader) - } - return frameType, c.reader, nil - } - } - - // Applications that do handle the error returned from this method spin in - // tight loop on connection failure. To help application developers detect - // this error, panic on repeated reads to the failed connection. - c.readErrCount++ - if c.readErrCount >= 1000 { - panic("repeated read on failed websocket connection") - } - - return noFrame, nil, c.readErr -} - -type messageReader struct{ c *Conn } - -func (r *messageReader) Read(b []byte) (int, error) { - c := r.c - if c.messageReader != r { - return 0, io.EOF - } - - for c.readErr == nil { - - if c.readRemaining > 0 { - if int64(len(b)) > c.readRemaining { - b = b[:c.readRemaining] - } - n, err := c.br.Read(b) - c.readErr = hideTempErr(err) - if c.isServer { - c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) - } - rem := c.readRemaining - rem -= int64(n) - c.setReadRemaining(rem) - if c.readRemaining > 0 && c.readErr == io.EOF { - c.readErr = errUnexpectedEOF - } - return n, c.readErr - } - - if c.readFinal { - c.messageReader = nil - return 0, io.EOF - } - - frameType, err := c.advanceFrame() - switch { - case err != nil: - c.readErr = hideTempErr(err) - case frameType == TextMessage || frameType == BinaryMessage: - c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") - } - } - - err := c.readErr - if err == io.EOF && c.messageReader == r { - err = errUnexpectedEOF - } - return 0, err -} - -func (r *messageReader) Close() error { - return nil -} - -// ReadMessage is a helper method for getting a reader using NextReader and -// reading from that reader to a buffer. -func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { - var r io.Reader - messageType, r, err = c.NextReader() - if err != nil { - return messageType, nil, err - } - p, err = ioutil.ReadAll(r) - return messageType, p, err -} - -// SetReadDeadline sets the read deadline on the underlying network connection. -// After a read has timed out, the websocket connection state is corrupt and -// all future reads will return an error. A zero value for t means reads will -// not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a -// message exceeds the limit, the connection sends a close message to the peer -// and returns ErrReadLimit to the application. -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 -// message back to the peer. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// close messages as described in the section on Control Messages above. -// -// The connection read methods return a CloseError when a close message is -// received. Most applications should handle close messages as part of their -// normal error handling. Applications should only set a close handler when the -// application must perform some action before sending a close message 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 := 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 message application data. The default -// ping handler sends a pong to the peer. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// ping messages as described in the section on Control Messages above. -func (c *Conn) SetPingHandler(h func(appData string) error) { - if h == nil { - h = func(message string) error { - err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) - if err == ErrCloseSent { - return nil - } else if e, ok := err.(net.Error); ok && e.Temporary() { - return nil - } - return err - } - } - 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 message application data. The default -// pong handler does nothing. -// -// The handler function is called from the NextReader, ReadMessage and message -// reader Read methods. The application must read the connection to process -// pong messages as described in the section on Control Messages above. -func (c *Conn) SetPongHandler(h func(appData string) error) { - if h == nil { - h = func(string) error { return nil } - } - c.handlePong = h -} - -// UnderlyingConn returns the internal net.Conn. This can be used to further -// modifications to connection specific flags. -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 -} - -// SetCompressionLevel sets the flate compression level for subsequent text and -// binary messages. This function is a noop if compression was not negotiated -// with the peer. See the compress/flate package for a description of -// compression levels. -func (c *Conn) SetCompressionLevel(level int) error { - if !isValidCompressionLevel(level) { - return errors.New("websocket: invalid compression level") - } - c.compressionLevel = level - return nil -} - -// FormatCloseMessage formats closeCode and text as a WebSocket close message. -// An empty message is returned for code CloseNoStatusReceived. -func FormatCloseMessage(closeCode int, text string) []byte { - if closeCode == CloseNoStatusReceived { - // Return empty message because it's illegal to send - // CloseNoStatusReceived. Return non-nil value in case application - // checks for nil. - return []byte{} - } - buf := make([]byte, 2+len(text)) - binary.BigEndian.PutUint16(buf, uint16(closeCode)) - copy(buf[2:], text) - return buf -} diff --git a/vendor/github.com/gorilla/websocket/conn_write.go b/vendor/github.com/gorilla/websocket/conn_write.go deleted file mode 100644 index a509a21f87..0000000000 --- a/vendor/github.com/gorilla/websocket/conn_write.go +++ /dev/null @@ -1,15 +0,0 @@ -// 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.8 - -package websocket - -import "net" - -func (c *Conn) writeBufs(bufs ...[]byte) error { - b := net.Buffers(bufs) - _, err := b.WriteTo(c.conn) - return err -} diff --git a/vendor/github.com/gorilla/websocket/conn_write_legacy.go b/vendor/github.com/gorilla/websocket/conn_write_legacy.go deleted file mode 100644 index 37edaff5a5..0000000000 --- a/vendor/github.com/gorilla/websocket/conn_write_legacy.go +++ /dev/null @@ -1,18 +0,0 @@ -// 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.8 - -package websocket - -func (c *Conn) writeBufs(bufs ...[]byte) error { - for _, buf := range bufs { - if len(buf) > 0 { - if _, err := c.conn.Write(buf); err != nil { - return err - } - } - } - return nil -} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go deleted file mode 100644 index 8db0cef95a..0000000000 --- a/vendor/github.com/gorilla/websocket/doc.go +++ /dev/null @@ -1,227 +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 websocket implements the WebSocket protocol defined in RFC 6455. -// -// Overview -// -// The Conn type represents a WebSocket connection. A server application calls -// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: -// -// var upgrader = websocket.Upgrader{ -// ReadBufferSize: 1024, -// WriteBufferSize: 1024, -// } -// -// func handler(w http.ResponseWriter, r *http.Request) { -// conn, err := upgrader.Upgrade(w, r, nil) -// if err != nil { -// log.Println(err) -// return -// } -// ... Use conn to send and receive messages. -// } -// -// Call the connection's WriteMessage and ReadMessage methods to send and -// receive messages as a slice of bytes. This snippet of code shows how to echo -// messages using these methods: -// -// for { -// messageType, p, err := conn.ReadMessage() -// if err != nil { -// log.Println(err) -// return -// } -// if err := conn.WriteMessage(messageType, p); err != nil { -// log.Println(err) -// return -// } -// } -// -// In above snippet of code, p is a []byte and messageType is an int with value -// websocket.BinaryMessage or websocket.TextMessage. -// -// An application can also send and receive messages using the io.WriteCloser -// and io.Reader interfaces. To send a message, call the connection NextWriter -// method to get an io.WriteCloser, write the message to the writer and close -// the writer when done. To receive a message, call the connection NextReader -// method to get an io.Reader and read until io.EOF is returned. This snippet -// shows how to echo messages using the NextWriter and NextReader methods: -// -// for { -// messageType, r, err := conn.NextReader() -// if err != nil { -// return -// } -// w, err := conn.NextWriter(messageType) -// if err != nil { -// return err -// } -// if _, err := io.Copy(w, r); err != nil { -// return err -// } -// if err := w.Close(); err != nil { -// return err -// } -// } -// -// Data Messages -// -// The WebSocket protocol distinguishes between text and binary data messages. -// Text messages are interpreted as UTF-8 encoded text. The interpretation of -// binary messages is left to the application. -// -// This package uses the TextMessage and BinaryMessage integer constants to -// identify the two data message types. The ReadMessage and NextReader methods -// return the type of the received message. The messageType argument to the -// WriteMessage and NextWriter methods specifies the type of a sent message. -// -// It is the application's responsibility to ensure that text messages are -// valid UTF-8 encoded text. -// -// Control Messages -// -// The WebSocket protocol defines three types of control messages: close, ping -// and pong. Call the connection WriteControl, WriteMessage or NextWriter -// methods to send a control message to the peer. -// -// Connections handle received close messages by calling the handler function -// set with the SetCloseHandler method and by returning a *CloseError from the -// NextReader, ReadMessage or the message Read method. The default close -// handler sends a close message to the peer. -// -// Connections handle received ping messages by calling the handler function -// set with the SetPingHandler method. The default ping handler sends a pong -// message to the peer. -// -// Connections handle received pong messages by calling the handler function -// set with the SetPongHandler method. The default pong handler does nothing. -// If an application sends ping messages, then the application should set a -// pong handler to receive the corresponding pong. -// -// The control message handler functions are called from the NextReader, -// ReadMessage and message reader Read methods. The default close and ping -// handlers can block these methods for a short time when the handler writes to -// the connection. -// -// The application must read the connection to process close, ping and pong -// messages sent from the peer. If the application is not otherwise interested -// in messages from the peer, then the application should start a goroutine to -// read and discard messages from the peer. A simple example is: -// -// func readLoop(c *websocket.Conn) { -// for { -// if _, _, err := c.NextReader(); err != nil { -// c.Close() -// break -// } -// } -// } -// -// Concurrency -// -// Connections support one concurrent reader and one concurrent writer. -// -// Applications are responsible for ensuring that no more than one goroutine -// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, -// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and -// that no more than one goroutine calls the read methods (NextReader, -// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) -// concurrently. -// -// The Close and WriteControl methods can be called concurrently with all other -// methods. -// -// Origin Considerations -// -// Web browsers allow Javascript applications to open a WebSocket connection to -// any host. It's up to the server to enforce an origin policy using the Origin -// request header sent by the browser. -// -// The Upgrader calls the function specified in the CheckOrigin field to check -// the origin. If the CheckOrigin function returns false, then the Upgrade -// method fails the WebSocket handshake with HTTP status 403. -// -// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail -// the handshake if the Origin request header is present and the Origin host is -// not equal to the Host request header. -// -// The deprecated package-level Upgrade function does not perform origin -// checking. The application is responsible for checking the Origin header -// before calling the Upgrade function. -// -// Buffers -// -// Connections buffer network input and output to reduce the number -// of system calls when reading or writing messages. -// -// Write buffers are also used for constructing WebSocket frames. See RFC 6455, -// Section 5 for a discussion of message framing. A WebSocket frame header is -// written to the network each time a write buffer is flushed to the network. -// Decreasing the size of the write buffer can increase the amount of framing -// overhead on the connection. -// -// The buffer sizes in bytes are specified by the ReadBufferSize and -// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default -// size of 4096 when a buffer size field is set to zero. The Upgrader reuses -// buffers created by the HTTP server when a buffer size field is set to zero. -// The HTTP server buffers have a size of 4096 at the time of this writing. -// -// The buffer sizes do not limit the size of a message that can be read or -// written by a connection. -// -// Buffers are held for the lifetime of the connection by default. If the -// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the -// write buffer only when writing a message. -// -// Applications should tune the buffer sizes to balance memory use and -// performance. Increasing the buffer size uses more memory, but can reduce the -// number of system calls to read or write the network. In the case of writing, -// increasing the buffer size can reduce the number of frame headers written to -// the network. -// -// Some guidelines for setting buffer parameters are: -// -// Limit the buffer sizes to the maximum expected message size. Buffers larger -// than the largest message do not provide any benefit. -// -// Depending on the distribution of message sizes, setting the buffer size to -// a value less than the maximum expected message size can greatly reduce memory -// use with a small impact on performance. Here's an example: If 99% of the -// messages are smaller than 256 bytes and the maximum message size is 512 -// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls -// than a buffer size of 512 bytes. The memory savings is 50%. -// -// A write buffer pool is useful when the application has a modest number -// writes over a large number of connections. when buffers are pooled, a larger -// buffer size has a reduced impact on total memory use and has the benefit of -// reducing system calls and frame overhead. -// -// 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. -// -// var upgrader = websocket.Upgrader{ -// EnableCompression: true, -// } -// -// 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(false) -// -// 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 diff --git a/vendor/github.com/gorilla/websocket/join.go b/vendor/github.com/gorilla/websocket/join.go deleted file mode 100644 index c64f8c8290..0000000000 --- a/vendor/github.com/gorilla/websocket/join.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 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 ( - "io" - "strings" -) - -// JoinMessages concatenates received messages to create a single io.Reader. -// The string term is appended to each message. The returned reader does not -// support concurrent calls to the Read method. -func JoinMessages(c *Conn, term string) io.Reader { - return &joinReader{c: c, term: term} -} - -type joinReader struct { - c *Conn - term string - r io.Reader -} - -func (r *joinReader) Read(p []byte) (int, error) { - if r.r == nil { - var err error - _, r.r, err = r.c.NextReader() - if err != nil { - return 0, err - } - if r.term != "" { - r.r = io.MultiReader(r.r, strings.NewReader(r.term)) - } - } - n, err := r.r.Read(p) - if err == io.EOF { - err = nil - r.r = nil - } - return n, err -} diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go deleted file mode 100644 index dc2c1f6415..0000000000 --- a/vendor/github.com/gorilla/websocket/json.go +++ /dev/null @@ -1,60 +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 websocket - -import ( - "encoding/json" - "io" -) - -// WriteJSON writes the JSON encoding of v as a message. -// -// Deprecated: Use c.WriteJSON instead. -func WriteJSON(c *Conn, v interface{}) error { - return c.WriteJSON(v) -} - -// WriteJSON writes the JSON encoding of v as a message. -// -// See the documentation for encoding/json Marshal for details about the -// conversion of Go values to JSON. -func (c *Conn) WriteJSON(v interface{}) error { - w, err := c.NextWriter(TextMessage) - if err != nil { - return err - } - err1 := json.NewEncoder(w).Encode(v) - err2 := w.Close() - if err1 != nil { - return err1 - } - return err2 -} - -// ReadJSON reads the next JSON-encoded message from the connection and stores -// it in the value pointed to by v. -// -// Deprecated: Use c.ReadJSON instead. -func ReadJSON(c *Conn, v interface{}) error { - return c.ReadJSON(v) -} - -// ReadJSON reads the next JSON-encoded message from the connection and stores -// it in the value pointed to by v. -// -// See the documentation for the encoding/json Unmarshal function for details -// about the conversion of JSON to a Go value. -func (c *Conn) ReadJSON(v interface{}) error { - _, r, err := c.NextReader() - if err != nil { - return err - } - err = json.NewDecoder(r).Decode(v) - if err == io.EOF { - // One value is expected in the message. - err = io.ErrUnexpectedEOF - } - return err -} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go deleted file mode 100644 index 577fce9efd..0000000000 --- a/vendor/github.com/gorilla/websocket/mask.go +++ /dev/null @@ -1,54 +0,0 @@ -// 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 !appengine - -package websocket - -import "unsafe" - -const wordSize = int(unsafe.Sizeof(uintptr(0))) - -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 -} diff --git a/vendor/github.com/gorilla/websocket/mask_safe.go b/vendor/github.com/gorilla/websocket/mask_safe.go deleted file mode 100644 index 2aac060e52..0000000000 --- a/vendor/github.com/gorilla/websocket/mask_safe.go +++ /dev/null @@ -1,15 +0,0 @@ -// 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 appengine - -package websocket - -func maskBytes(key [4]byte, pos int, b []byte) int { - for i := range b { - b[i] ^= key[pos&3] - pos++ - } - return pos & 3 -} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go deleted file mode 100644 index c854225e96..0000000000 --- a/vendor/github.com/gorilla/websocket/prepared.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2017 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 ( - "bytes" - "net" - "sync" - "time" -) - -// PreparedMessage caches on the wire representations of a message payload. -// Use PreparedMessage to efficiently send a message payload to multiple -// connections. PreparedMessage is especially useful when compression is used -// because the CPU and memory expensive compression operation can be executed -// once for a given set of compression options. -type PreparedMessage struct { - messageType int - data []byte - mu sync.Mutex - frames map[prepareKey]*preparedFrame -} - -// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. -type prepareKey struct { - isServer bool - compress bool - compressionLevel int -} - -// preparedFrame contains data in wire representation. -type preparedFrame struct { - once sync.Once - data []byte -} - -// NewPreparedMessage returns an initialized PreparedMessage. You can then send -// it to connection using WritePreparedMessage method. Valid wire -// representation will be calculated lazily only once for a set of current -// connection options. -func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { - pm := &PreparedMessage{ - messageType: messageType, - frames: make(map[prepareKey]*preparedFrame), - data: data, - } - - // Prepare a plain server frame. - _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) - if err != nil { - return nil, err - } - - // To protect against caller modifying the data argument, remember the data - // copied to the plain server frame. - pm.data = frameData[len(frameData)-len(data):] - return pm, nil -} - -func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { - pm.mu.Lock() - frame, ok := pm.frames[key] - if !ok { - frame = &preparedFrame{} - pm.frames[key] = frame - } - pm.mu.Unlock() - - var err error - frame.once.Do(func() { - // Prepare a frame using a 'fake' connection. - // TODO: Refactor code in conn.go to allow more direct construction of - // the frame. - mu := make(chan struct{}, 1) - mu <- struct{}{} - var nc prepareConn - c := &Conn{ - conn: &nc, - mu: mu, - isServer: key.isServer, - compressionLevel: key.compressionLevel, - enableWriteCompression: true, - writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), - } - if key.compress { - c.newCompressionWriter = compressNoContextTakeover - } - err = c.WriteMessage(pm.messageType, pm.data) - frame.data = nc.buf.Bytes() - }) - return pm.messageType, frame.data, err -} - -type prepareConn struct { - buf bytes.Buffer - net.Conn -} - -func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } -func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/proxy.go b/vendor/github.com/gorilla/websocket/proxy.go deleted file mode 100644 index e87a8c9f0c..0000000000 --- a/vendor/github.com/gorilla/websocket/proxy.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 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 ( - "bufio" - "encoding/base64" - "errors" - "net" - "net/http" - "net/url" - "strings" -) - -type netDialerFunc func(network, addr string) (net.Conn, error) - -func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { - return fn(network, addr) -} - -func init() { - proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil - }) -} - -type httpProxyDialer struct { - proxyURL *url.URL - forwardDial func(network, addr string) (net.Conn, error) -} - -func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { - hostPort, _ := hostPortNoPort(hpd.proxyURL) - conn, err := hpd.forwardDial(network, hostPort) - if err != nil { - return nil, err - } - - connectHeader := make(http.Header) - if user := hpd.proxyURL.User; user != nil { - proxyUser := user.Username() - if proxyPassword, passwordSet := user.Password(); passwordSet { - credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) - connectHeader.Set("Proxy-Authorization", "Basic "+credential) - } - } - - connectReq := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Opaque: addr}, - Host: addr, - Header: connectHeader, - } - - if err := connectReq.Write(conn); err != nil { - conn.Close() - return nil, err - } - - // Read response. It's OK to use and discard buffered reader here becaue - // the remote server does not speak until spoken to. - br := bufio.NewReader(conn) - resp, err := http.ReadResponse(br, connectReq) - if err != nil { - conn.Close() - return nil, err - } - - if resp.StatusCode != 200 { - conn.Close() - f := strings.SplitN(resp.Status, " ", 2) - return nil, errors.New(f[1]) - } - return conn, nil -} diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go deleted file mode 100644 index 887d558918..0000000000 --- a/vendor/github.com/gorilla/websocket/server.go +++ /dev/null @@ -1,363 +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 websocket - -import ( - "bufio" - "errors" - "io" - "net/http" - "net/url" - "strings" - "time" -) - -// HandshakeError describes an error with the handshake from the peer. -type HandshakeError struct { - message string -} - -func (e HandshakeError) Error() string { return e.message } - -// Upgrader specifies parameters for upgrading an HTTP connection to a -// WebSocket connection. -type Upgrader struct { - // HandshakeTimeout specifies the duration for the handshake to complete. - HandshakeTimeout time.Duration - - // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer - // size is zero, then buffers allocated by the HTTP server are used. The - // I/O buffer sizes do not limit the size of the messages that can be sent - // or received. - ReadBufferSize, WriteBufferSize int - - // WriteBufferPool is a pool of buffers for write operations. If the value - // is not set, then write buffers are allocated to the connection for the - // lifetime of the connection. - // - // A pool is most useful when the application has a modest volume of writes - // across a large number of connections. - // - // Applications should use a single pool for each unique value of - // WriteBufferSize. - WriteBufferPool BufferPool - - // Subprotocols specifies the server's supported protocols in order of - // preference. If this field is not nil, then the Upgrade method negotiates a - // subprotocol by selecting the first match in this list with a protocol - // requested by the client. If there's no match, then no protocol is - // negotiated (the Sec-Websocket-Protocol header is not included in the - // handshake response). - Subprotocols []string - - // Error specifies the function for generating HTTP error responses. If Error - // is nil, then http.Error is used to generate the HTTP response. - Error func(w http.ResponseWriter, r *http.Request, status int, reason error) - - // CheckOrigin returns true if the request Origin header is acceptable. If - // CheckOrigin is nil, then a safe default is used: return false if the - // Origin request header is present and the origin host is not equal to - // request Host header. - // - // A CheckOrigin function should carefully validate the request origin to - // prevent cross-site request forgery. - 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) { - err := HandshakeError{reason} - 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 -} - -// checkSameOrigin returns true if the origin is not set or is equal to the request host. -func checkSameOrigin(r *http.Request) bool { - origin := r.Header["Origin"] - if len(origin) == 0 { - return true - } - u, err := url.Parse(origin[0]) - if err != nil { - return false - } - return equalASCIIFold(u.Host, r.Host) -} - -func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { - if u.Subprotocols != nil { - clientProtocols := Subprotocols(r) - for _, serverProtocol := range u.Subprotocols { - for _, clientProtocol := range clientProtocols { - if clientProtocol == serverProtocol { - return clientProtocol - } - } - } - } else if responseHeader != nil { - return responseHeader.Get("Sec-Websocket-Protocol") - } - return "" -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie) and the -// application negotiated subprotocol (Sec-WebSocket-Protocol). -// -// If the upgrade fails, then Upgrade replies to the client with an HTTP error -// response. -func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { - const badHandshake = "websocket: the client is not using the websocket protocol: " - - if !tokenListContainsValue(r.Header, "Connection", "upgrade") { - return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header") - } - - if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { - return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header") - } - - if r.Method != "GET" { - return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET") - } - - if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { - return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") - } - - if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { - return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") - } - - checkOrigin := u.CheckOrigin - if checkOrigin == nil { - checkOrigin = checkSameOrigin - } - if !checkOrigin(r) { - return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin") - } - - challengeKey := r.Header.Get("Sec-Websocket-Key") - if challengeKey == "" { - return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") - } - - 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 - } - } - - h, ok := w.(http.Hijacker) - if !ok { - return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") - } - var brw *bufio.ReadWriter - netConn, brw, err := h.Hijack() - if err != nil { - return u.returnError(w, r, http.StatusInternalServerError, err.Error()) - } - - if brw.Reader.Buffered() > 0 { - netConn.Close() - return nil, errors.New("websocket: client sent data before handshake is complete") - } - - var br *bufio.Reader - if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 { - // Reuse hijacked buffered reader as connection reader. - br = brw.Reader - } - - buf := bufioWriterBuffer(netConn, brw.Writer) - - var writeBuf []byte - if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 { - // Reuse hijacked write buffer as connection buffer. - writeBuf = buf - } - - c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf) - c.subprotocol = subprotocol - - if compress { - c.newCompressionWriter = compressNoContextTakeover - c.newDecompressionReader = decompressNoContextTakeover - } - - // Use larger of hijacked buffer and connection write buffer for header. - p := buf - if len(c.writeBuf) > len(p) { - p = c.writeBuf - } - p = p[: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)...) - p = append(p, "\r\n"...) - if c.subprotocol != "" { - p = append(p, "Sec-WebSocket-Protocol: "...) - 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 - } - for _, v := range vs { - p = append(p, k...) - p = append(p, ": "...) - for i := 0; i < len(v); i++ { - b := v[i] - if b <= 31 { - // prevent response splitting. - b = ' ' - } - p = append(p, b) - } - p = append(p, "\r\n"...) - } - } - p = append(p, "\r\n"...) - - // Clear deadlines set by HTTP server. - netConn.SetDeadline(time.Time{}) - - if u.HandshakeTimeout > 0 { - netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) - } - if _, err = netConn.Write(p); err != nil { - netConn.Close() - return nil, err - } - if u.HandshakeTimeout > 0 { - netConn.SetWriteDeadline(time.Time{}) - } - - return c, nil -} - -// Upgrade upgrades the HTTP server connection to the WebSocket protocol. -// -// Deprecated: Use websocket.Upgrader instead. -// -// Upgrade does not perform origin checking. The application is responsible for -// checking the Origin header before calling Upgrade. An example implementation -// of the same origin policy check is: -// -// if req.Header.Get("Origin") != "http://"+req.Host { -// http.Error(w, "Origin not allowed", http.StatusForbidden) -// return -// } -// -// If the endpoint supports subprotocols, then the application is responsible -// for negotiating the protocol used on the connection. Use the Subprotocols() -// function to get the subprotocols requested by the client. Use the -// Sec-Websocket-Protocol response header to specify the subprotocol selected -// by the application. -// -// The responseHeader is included in the response to the client's upgrade -// request. Use the responseHeader to specify cookies (Set-Cookie) and the -// negotiated subprotocol (Sec-Websocket-Protocol). -// -// The connection buffers IO to the underlying network connection. The -// readBufSize and writeBufSize parameters specify the size of the buffers to -// use. Messages can be larger than the buffers. -// -// If the request is not a valid WebSocket handshake, then Upgrade returns an -// error of type HandshakeError. Applications should handle this error by -// replying to the client with an HTTP error response. -func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { - u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} - u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { - // don't return errors to maintain backwards compatibility - } - u.CheckOrigin = func(r *http.Request) bool { - // allow all connections by default - return true - } - return u.Upgrade(w, r, responseHeader) -} - -// Subprotocols returns the subprotocols requested by the client in the -// Sec-Websocket-Protocol header. -func Subprotocols(r *http.Request) []string { - h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) - if h == "" { - return nil - } - protocols := strings.Split(h, ",") - for i := range protocols { - protocols[i] = strings.TrimSpace(protocols[i]) - } - return protocols -} - -// IsWebSocketUpgrade returns true if the client requested upgrade to the -// WebSocket protocol. -func IsWebSocketUpgrade(r *http.Request) bool { - return tokenListContainsValue(r.Header, "Connection", "upgrade") && - tokenListContainsValue(r.Header, "Upgrade", "websocket") -} - -// bufioReaderSize size returns the size of a bufio.Reader. -func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int { - // This code assumes that peek on a reset reader returns - // bufio.Reader.buf[:0]. - // TODO: Use bufio.Reader.Size() after Go 1.10 - br.Reset(originalReader) - if p, err := br.Peek(0); err == nil { - return cap(p) - } - return 0 -} - -// writeHook is an io.Writer that records the last slice passed to it vio -// io.Writer.Write. -type writeHook struct { - p []byte -} - -func (wh *writeHook) Write(p []byte) (int, error) { - wh.p = p - return len(p), nil -} - -// bufioWriterBuffer grabs the buffer from a bufio.Writer. -func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte { - // This code assumes that bufio.Writer.buf[:1] is passed to the - // bufio.Writer's underlying writer. - var wh writeHook - bw.Reset(&wh) - bw.WriteByte(0) - bw.Flush() - - bw.Reset(originalWriter) - - return wh.p[:cap(wh.p)] -} diff --git a/vendor/github.com/gorilla/websocket/trace.go b/vendor/github.com/gorilla/websocket/trace.go deleted file mode 100644 index 834f122a00..0000000000 --- a/vendor/github.com/gorilla/websocket/trace.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build go1.8 - -package websocket - -import ( - "crypto/tls" - "net/http/httptrace" -) - -func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { - if trace.TLSHandshakeStart != nil { - trace.TLSHandshakeStart() - } - err := doHandshake(tlsConn, cfg) - if trace.TLSHandshakeDone != nil { - trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) - } - return err -} diff --git a/vendor/github.com/gorilla/websocket/trace_17.go b/vendor/github.com/gorilla/websocket/trace_17.go deleted file mode 100644 index 77d05a0b57..0000000000 --- a/vendor/github.com/gorilla/websocket/trace_17.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !go1.8 - -package websocket - -import ( - "crypto/tls" - "net/http/httptrace" -) - -func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { - return doHandshake(tlsConn, cfg) -} diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go deleted file mode 100644 index 7bf2f66c67..0000000000 --- a/vendor/github.com/gorilla/websocket/util.go +++ /dev/null @@ -1,283 +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 websocket - -import ( - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "io" - "net/http" - "strings" - "unicode/utf8" -) - -var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") - -func computeAcceptKey(challengeKey string) string { - h := sha1.New() - h.Write([]byte(challengeKey)) - h.Write(keyGUID) - return base64.StdEncoding.EncodeToString(h.Sum(nil)) -} - -func generateChallengeKey() (string, error) { - p := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, p); err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(p), nil -} - -// Token octets per RFC 2616. -var isTokenOctet = [256]bool{ - '!': true, - '#': true, - '$': true, - '%': true, - '&': true, - '\'': true, - '*': true, - '+': true, - '-': true, - '.': true, - '0': true, - '1': true, - '2': true, - '3': true, - '4': true, - '5': true, - '6': true, - '7': true, - '8': true, - '9': true, - 'A': true, - 'B': true, - 'C': true, - 'D': true, - 'E': true, - 'F': true, - 'G': true, - 'H': true, - 'I': true, - 'J': true, - 'K': true, - 'L': true, - 'M': true, - 'N': true, - 'O': true, - 'P': true, - 'Q': true, - 'R': true, - 'S': true, - 'T': true, - 'U': true, - 'W': true, - 'V': true, - 'X': true, - 'Y': true, - 'Z': true, - '^': true, - '_': true, - '`': true, - 'a': true, - 'b': true, - 'c': true, - 'd': true, - 'e': true, - 'f': true, - 'g': true, - 'h': true, - 'i': true, - 'j': true, - 'k': true, - 'l': true, - 'm': true, - 'n': true, - 'o': true, - 'p': true, - 'q': true, - 'r': true, - 's': true, - 't': true, - 'u': true, - 'v': true, - 'w': true, - 'x': true, - 'y': true, - 'z': true, - '|': true, - '~': true, -} - -// skipSpace returns a slice of the string s with all leading RFC 2616 linear -// whitespace removed. -func skipSpace(s string) (rest string) { - i := 0 - for ; i < len(s); i++ { - if b := s[i]; b != ' ' && b != '\t' { - break - } - } - return s[i:] -} - -// nextToken returns the leading RFC 2616 token of s and the string following -// the token. -func nextToken(s string) (token, rest string) { - i := 0 - for ; i < len(s); i++ { - if !isTokenOctet[s[i]] { - break - } - } - return s[:i], s[i:] -} - -// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 -// and the string following the token or quoted string. -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++ - case b == '\\': - escape = true - case b == '"': - return string(p[:j]), s[i+1:] - default: - p[j] = b - j++ - } - } - return "", "" - } - } - return "", "" -} - -// equalASCIIFold returns true if s is equal to t with ASCII case folding as -// defined in RFC 4790. -func equalASCIIFold(s, t string) bool { - for s != "" && t != "" { - sr, size := utf8.DecodeRuneInString(s) - s = s[size:] - tr, size := utf8.DecodeRuneInString(t) - t = t[size:] - if sr == tr { - continue - } - if 'A' <= sr && sr <= 'Z' { - sr = sr + 'a' - 'A' - } - if 'A' <= tr && tr <= 'Z' { - tr = tr + 'a' - 'A' - } - if sr != tr { - return false - } - } - return s == t -} - -// tokenListContainsValue returns true if the 1#token header with the given -// name contains a token equal to value with ASCII case folding. -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 equalASCIIFold(t, value) { - return true - } - if s == "" { - continue headers - } - s = s[1:] - } - } - return false -} - -// parseExtensions 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 -} diff --git a/vendor/github.com/gorilla/websocket/x_net_proxy.go b/vendor/github.com/gorilla/websocket/x_net_proxy.go deleted file mode 100644 index 2e668f6b88..0000000000 --- a/vendor/github.com/gorilla/websocket/x_net_proxy.go +++ /dev/null @@ -1,473 +0,0 @@ -// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. -//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy - -// Package proxy provides support for a variety of protocols to proxy network -// data. -// - -package websocket - -import ( - "errors" - "io" - "net" - "net/url" - "os" - "strconv" - "strings" - "sync" -) - -type proxy_direct struct{} - -// Direct is a direct proxy: one that makes network connections directly. -var proxy_Direct = proxy_direct{} - -func (proxy_direct) Dial(network, addr string) (net.Conn, error) { - return net.Dial(network, addr) -} - -// A PerHost directs connections to a default Dialer unless the host name -// requested matches one of a number of exceptions. -type proxy_PerHost struct { - def, bypass proxy_Dialer - - bypassNetworks []*net.IPNet - bypassIPs []net.IP - bypassZones []string - bypassHosts []string -} - -// NewPerHost returns a PerHost Dialer that directs connections to either -// defaultDialer or bypass, depending on whether the connection matches one of -// the configured rules. -func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost { - return &proxy_PerHost{ - def: defaultDialer, - bypass: bypass, - } -} - -// Dial connects to the address addr on the given network through either -// defaultDialer or bypass. -func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { - host, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - - return p.dialerForRequest(host).Dial(network, addr) -} - -func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { - if ip := net.ParseIP(host); ip != nil { - for _, net := range p.bypassNetworks { - if net.Contains(ip) { - return p.bypass - } - } - for _, bypassIP := range p.bypassIPs { - if bypassIP.Equal(ip) { - return p.bypass - } - } - return p.def - } - - for _, zone := range p.bypassZones { - if strings.HasSuffix(host, zone) { - return p.bypass - } - if host == zone[1:] { - // For a zone ".example.com", we match "example.com" - // too. - return p.bypass - } - } - for _, bypassHost := range p.bypassHosts { - if bypassHost == host { - return p.bypass - } - } - return p.def -} - -// AddFromString parses a string that contains comma-separated values -// specifying hosts that should use the bypass proxy. Each value is either an -// IP address, a CIDR range, a zone (*.example.com) or a host name -// (localhost). A best effort is made to parse the string and errors are -// ignored. -func (p *proxy_PerHost) AddFromString(s string) { - hosts := strings.Split(s, ",") - for _, host := range hosts { - host = strings.TrimSpace(host) - if len(host) == 0 { - continue - } - if strings.Contains(host, "/") { - // We assume that it's a CIDR address like 127.0.0.0/8 - if _, net, err := net.ParseCIDR(host); err == nil { - p.AddNetwork(net) - } - continue - } - if ip := net.ParseIP(host); ip != nil { - p.AddIP(ip) - continue - } - if strings.HasPrefix(host, "*.") { - p.AddZone(host[1:]) - continue - } - p.AddHost(host) - } -} - -// AddIP specifies an IP address that will use the bypass proxy. Note that -// this will only take effect if a literal IP address is dialed. A connection -// to a named host will never match an IP. -func (p *proxy_PerHost) AddIP(ip net.IP) { - p.bypassIPs = append(p.bypassIPs, ip) -} - -// AddNetwork specifies an IP range that will use the bypass proxy. Note that -// this will only take effect if a literal IP address is dialed. A connection -// to a named host will never match. -func (p *proxy_PerHost) AddNetwork(net *net.IPNet) { - p.bypassNetworks = append(p.bypassNetworks, net) -} - -// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of -// "example.com" matches "example.com" and all of its subdomains. -func (p *proxy_PerHost) AddZone(zone string) { - if strings.HasSuffix(zone, ".") { - zone = zone[:len(zone)-1] - } - if !strings.HasPrefix(zone, ".") { - zone = "." + zone - } - p.bypassZones = append(p.bypassZones, zone) -} - -// AddHost specifies a host name that will use the bypass proxy. -func (p *proxy_PerHost) AddHost(host string) { - if strings.HasSuffix(host, ".") { - host = host[:len(host)-1] - } - p.bypassHosts = append(p.bypassHosts, host) -} - -// A Dialer is a means to establish a connection. -type proxy_Dialer interface { - // Dial connects to the given address via the proxy. - Dial(network, addr string) (c net.Conn, err error) -} - -// Auth contains authentication parameters that specific Dialers may require. -type proxy_Auth struct { - User, Password string -} - -// FromEnvironment returns the dialer specified by the proxy related variables in -// the environment. -func proxy_FromEnvironment() proxy_Dialer { - allProxy := proxy_allProxyEnv.Get() - if len(allProxy) == 0 { - return proxy_Direct - } - - proxyURL, err := url.Parse(allProxy) - if err != nil { - return proxy_Direct - } - proxy, err := proxy_FromURL(proxyURL, proxy_Direct) - if err != nil { - return proxy_Direct - } - - noProxy := proxy_noProxyEnv.Get() - if len(noProxy) == 0 { - return proxy - } - - perHost := proxy_NewPerHost(proxy, proxy_Direct) - perHost.AddFromString(noProxy) - return perHost -} - -// proxySchemes is a map from URL schemes to a function that creates a Dialer -// from a URL with such a scheme. -var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error) - -// RegisterDialerType takes a URL scheme and a function to generate Dialers from -// a URL with that scheme and a forwarding Dialer. Registered schemes are used -// by FromURL. -func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) { - if proxy_proxySchemes == nil { - proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) - } - proxy_proxySchemes[scheme] = f -} - -// FromURL returns a Dialer given a URL specification and an underlying -// Dialer for it to make network requests. -func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) { - var auth *proxy_Auth - if u.User != nil { - auth = new(proxy_Auth) - auth.User = u.User.Username() - if p, ok := u.User.Password(); ok { - auth.Password = p - } - } - - switch u.Scheme { - case "socks5": - return proxy_SOCKS5("tcp", u.Host, auth, forward) - } - - // If the scheme doesn't match any of the built-in schemes, see if it - // was registered by another package. - if proxy_proxySchemes != nil { - if f, ok := proxy_proxySchemes[u.Scheme]; ok { - return f(u, forward) - } - } - - return nil, errors.New("proxy: unknown scheme: " + u.Scheme) -} - -var ( - proxy_allProxyEnv = &proxy_envOnce{ - names: []string{"ALL_PROXY", "all_proxy"}, - } - proxy_noProxyEnv = &proxy_envOnce{ - names: []string{"NO_PROXY", "no_proxy"}, - } -) - -// envOnce looks up an environment variable (optionally by multiple -// names) once. It mitigates expensive lookups on some platforms -// (e.g. Windows). -// (Borrowed from net/http/transport.go) -type proxy_envOnce struct { - names []string - once sync.Once - val string -} - -func (e *proxy_envOnce) Get() string { - e.once.Do(e.init) - return e.val -} - -func (e *proxy_envOnce) init() { - for _, n := range e.names { - e.val = os.Getenv(n) - if e.val != "" { - return - } - } -} - -// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address -// with an optional username and password. See RFC 1928 and RFC 1929. -func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) { - s := &proxy_socks5{ - network: network, - addr: addr, - forward: forward, - } - if auth != nil { - s.user = auth.User - s.password = auth.Password - } - - return s, nil -} - -type proxy_socks5 struct { - user, password string - network, addr string - forward proxy_Dialer -} - -const proxy_socks5Version = 5 - -const ( - proxy_socks5AuthNone = 0 - proxy_socks5AuthPassword = 2 -) - -const proxy_socks5Connect = 1 - -const ( - proxy_socks5IP4 = 1 - proxy_socks5Domain = 3 - proxy_socks5IP6 = 4 -) - -var proxy_socks5Errors = []string{ - "", - "general failure", - "connection forbidden", - "network unreachable", - "host unreachable", - "connection refused", - "TTL expired", - "command not supported", - "address type not supported", -} - -// Dial connects to the address addr on the given network via the SOCKS5 proxy. -func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { - switch network { - case "tcp", "tcp6", "tcp4": - default: - return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) - } - - conn, err := s.forward.Dial(s.network, s.addr) - if err != nil { - return nil, err - } - if err := s.connect(conn, addr); err != nil { - conn.Close() - return nil, err - } - return conn, nil -} - -// connect takes an existing connection to a socks5 proxy server, -// and commands the server to extend that connection to target, -// which must be a canonical address with a host and port. -func (s *proxy_socks5) connect(conn net.Conn, target string) error { - host, portStr, err := net.SplitHostPort(target) - if err != nil { - return err - } - - port, err := strconv.Atoi(portStr) - if err != nil { - return errors.New("proxy: failed to parse port number: " + portStr) - } - if port < 1 || port > 0xffff { - return errors.New("proxy: port number out of range: " + portStr) - } - - // the size here is just an estimate - buf := make([]byte, 0, 6+len(host)) - - buf = append(buf, proxy_socks5Version) - if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { - buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword) - } else { - buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone) - } - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - if buf[0] != 5 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) - } - if buf[1] == 0xff { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") - } - - // See RFC 1929 - if buf[1] == proxy_socks5AuthPassword { - buf = buf[:0] - buf = append(buf, 1 /* password protocol version */) - buf = append(buf, uint8(len(s.user))) - buf = append(buf, s.user...) - buf = append(buf, uint8(len(s.password))) - buf = append(buf, s.password...) - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if buf[1] != 0 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") - } - } - - buf = buf[:0] - buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */) - - if ip := net.ParseIP(host); ip != nil { - if ip4 := ip.To4(); ip4 != nil { - buf = append(buf, proxy_socks5IP4) - ip = ip4 - } else { - buf = append(buf, proxy_socks5IP6) - } - buf = append(buf, ip...) - } else { - if len(host) > 255 { - return errors.New("proxy: destination host name too long: " + host) - } - buf = append(buf, proxy_socks5Domain) - buf = append(buf, byte(len(host))) - buf = append(buf, host...) - } - buf = append(buf, byte(port>>8), byte(port)) - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:4]); err != nil { - return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - failure := "unknown error" - if int(buf[1]) < len(proxy_socks5Errors) { - failure = proxy_socks5Errors[buf[1]] - } - - if len(failure) > 0 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) - } - - bytesToDiscard := 0 - switch buf[3] { - case proxy_socks5IP4: - bytesToDiscard = net.IPv4len - case proxy_socks5IP6: - bytesToDiscard = net.IPv6len - case proxy_socks5Domain: - _, err := io.ReadFull(conn, buf[:1]) - if err != nil { - return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - bytesToDiscard = int(buf[0]) - default: - return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) - } - - if cap(buf) < bytesToDiscard { - buf = make([]byte, bytesToDiscard) - } else { - buf = buf[:bytesToDiscard] - } - if _, err := io.ReadFull(conn, buf); err != nil { - return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - // Also need to discard the port number - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - return nil -} diff --git a/vendor/golang.org/x/sys/execabs/execabs.go b/vendor/golang.org/x/sys/execabs/execabs.go index b981cfbb4a..3bf40fdfec 100644 --- a/vendor/golang.org/x/sys/execabs/execabs.go +++ b/vendor/golang.org/x/sys/execabs/execabs.go @@ -63,7 +63,7 @@ func LookPath(file string) (string, error) { } func fixCmd(name string, cmd *exec.Cmd) { - if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) { + if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) { // exec.Command was called with a bare binary name and // exec.LookPath returned a path which is not absolute. // Set cmd.lookPathErr and clear cmd.Path so that it diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go index 6ab5f50894..2000064a81 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go118.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go118.go @@ -7,6 +7,12 @@ package execabs +import "os/exec" + func isGo119ErrDot(err error) bool { return false } + +func isGo119ErrFieldSet(cmd *exec.Cmd) bool { + return false +} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go index 46c5b525e7..f364b34189 100644 --- a/vendor/golang.org/x/sys/execabs/execabs_go119.go +++ b/vendor/golang.org/x/sys/execabs/execabs_go119.go @@ -15,3 +15,7 @@ import ( func isGo119ErrDot(err error) bool { return errors.Is(err, exec.ErrDot) } + +func isGo119ErrFieldSet(cmd *exec.Cmd) bool { + return cmd.Err != nil +} diff --git a/vendor/golang.org/x/sys/unix/ioctl.go b/vendor/golang.org/x/sys/unix/ioctl.go index 1c51b0ec2b..7ce8dd406f 100644 --- a/vendor/golang.org/x/sys/unix/ioctl.go +++ b/vendor/golang.org/x/sys/unix/ioctl.go @@ -8,7 +8,6 @@ package unix import ( - "runtime" "unsafe" ) @@ -27,7 +26,7 @@ func IoctlSetInt(fd int, req uint, value int) error { // passing the integer value directly. func IoctlSetPointerInt(fd int, req uint, value int) error { v := int32(value) - return ioctl(fd, req, uintptr(unsafe.Pointer(&v))) + return ioctlPtr(fd, req, unsafe.Pointer(&v)) } // IoctlSetWinsize performs an ioctl on fd with a *Winsize argument. @@ -36,9 +35,7 @@ func IoctlSetPointerInt(fd int, req uint, value int) error { func IoctlSetWinsize(fd int, req uint, value *Winsize) error { // TODO: if we get the chance, remove the req parameter and // hardcode TIOCSWINSZ. - err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err + return ioctlPtr(fd, req, unsafe.Pointer(value)) } // IoctlSetTermios performs an ioctl on fd with a *Termios. @@ -46,9 +43,7 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error { // The req value will usually be TCSETA or TIOCSETA. func IoctlSetTermios(fd int, req uint, value *Termios) error { // TODO: if we get the chance, remove the req parameter. - err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err + return ioctlPtr(fd, req, unsafe.Pointer(value)) } // IoctlGetInt performs an ioctl operation which gets an integer value @@ -58,18 +53,18 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { // for those, IoctlRetInt should be used instead of this function. func IoctlGetInt(fd int, req uint) (int, error) { var value int - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + err := ioctlPtr(fd, req, unsafe.Pointer(&value)) return value, err } func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { var value Winsize - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + err := ioctlPtr(fd, req, unsafe.Pointer(&value)) return &value, err } func IoctlGetTermios(fd int, req uint) (*Termios, error) { var value Termios - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + err := ioctlPtr(fd, req, unsafe.Pointer(&value)) return &value, err } diff --git a/vendor/golang.org/x/sys/unix/ioctl_zos.go b/vendor/golang.org/x/sys/unix/ioctl_zos.go index 5384e7d91d..6532f09af2 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_zos.go +++ b/vendor/golang.org/x/sys/unix/ioctl_zos.go @@ -27,9 +27,7 @@ func IoctlSetInt(fd int, req uint, value int) error { func IoctlSetWinsize(fd int, req uint, value *Winsize) error { // TODO: if we get the chance, remove the req parameter and // hardcode TIOCSWINSZ. - err := ioctl(fd, req, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err + return ioctlPtr(fd, req, unsafe.Pointer(value)) } // IoctlSetTermios performs an ioctl on fd with a *Termios. @@ -51,13 +49,13 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { // for those, IoctlRetInt should be used instead of this function. func IoctlGetInt(fd int, req uint) (int, error) { var value int - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + err := ioctlPtr(fd, req, unsafe.Pointer(&value)) return value, err } func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { var value Winsize - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + err := ioctlPtr(fd, req, unsafe.Pointer(&value)) return &value, err } diff --git a/vendor/golang.org/x/sys/unix/ptrace_darwin.go b/vendor/golang.org/x/sys/unix/ptrace_darwin.go index 463c3eff7f..39dba6ca6a 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_darwin.go +++ b/vendor/golang.org/x/sys/unix/ptrace_darwin.go @@ -7,6 +7,12 @@ package unix +import "unsafe" + func ptrace(request int, pid int, addr uintptr, data uintptr) error { return ptrace1(request, pid, addr, data) } + +func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) error { + return ptrace1Ptr(request, pid, addr, data) +} diff --git a/vendor/golang.org/x/sys/unix/ptrace_ios.go b/vendor/golang.org/x/sys/unix/ptrace_ios.go index ed0509a011..9ea66330a9 100644 --- a/vendor/golang.org/x/sys/unix/ptrace_ios.go +++ b/vendor/golang.org/x/sys/unix/ptrace_ios.go @@ -7,6 +7,12 @@ package unix +import "unsafe" + func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { return ENOTSUP } + +func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) { + return ENOTSUP +} diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index 2db1b51e99..d9f5544ccf 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -292,9 +292,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { break } } - - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] - sa.Name = string(bytes) + sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) return sa, nil case AF_INET: @@ -411,6 +409,7 @@ func (w WaitStatus) CoreDump() bool { return w&0x80 == 0x80 } func (w WaitStatus) TrapCause() int { return -1 } //sys ioctl(fd int, req uint, arg uintptr) (err error) +//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = ioctl // fcntl must never be called with cmd=F_DUP2FD because it doesn't work on AIX // There is no way to create a custom fcntl and to keep //sys fcntl easily, diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index eda42671f1..7705c3270b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -245,8 +245,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { break } } - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] - sa.Name = string(bytes) + sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) return sa, nil case AF_INET: diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 192b071b3d..7064d6ebab 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -14,7 +14,6 @@ package unix import ( "fmt" - "runtime" "syscall" "unsafe" ) @@ -376,11 +375,10 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) { func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) } //sys ioctl(fd int, req uint, arg uintptr) (err error) +//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL func IoctlCtlInfo(fd int, ctlInfo *CtlInfo) error { - err := ioctl(fd, CTLIOCGINFO, uintptr(unsafe.Pointer(ctlInfo))) - runtime.KeepAlive(ctlInfo) - return err + return ioctlPtr(fd, CTLIOCGINFO, unsafe.Pointer(ctlInfo)) } // IfreqMTU is struct ifreq used to get or set a network device's MTU. @@ -394,16 +392,14 @@ type IfreqMTU struct { func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) { var ifreq IfreqMTU copy(ifreq.Name[:], ifname) - err := ioctl(fd, SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) + err := ioctlPtr(fd, SIOCGIFMTU, unsafe.Pointer(&ifreq)) return &ifreq, err } // IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU // of the network device specified by ifreq.Name. func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error { - err := ioctl(fd, SIOCSIFMTU, uintptr(unsafe.Pointer(ifreq))) - runtime.KeepAlive(ifreq) - return err + return ioctlPtr(fd, SIOCSIFMTU, unsafe.Pointer(ifreq)) } //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go index b37310ce9b..9fa879806b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go @@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT64 //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace +//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_STATFS64 diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go index d51ec99630..f17b8c526a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go @@ -47,5 +47,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sys getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) = SYS_GETFSSTAT //sys Lstat(path string, stat *Stat_t) (err error) //sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace +//sys ptrace1Ptr(request int, pid int, addr unsafe.Pointer, data uintptr) (err error) = SYS_ptrace //sys Stat(path string, stat *Stat_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go index a41111a794..221efc26bc 100644 --- a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go +++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go @@ -172,6 +172,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { } //sys ioctl(fd int, req uint, arg uintptr) (err error) +//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go index d50b9dc250..5bdde03e4a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -161,7 +161,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -//sys ioctl(fd int, req uint, arg uintptr) (err error) +//sys ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL +//sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL @@ -253,6 +254,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } //sys ptrace(request int, pid int, addr uintptr, data int) (err error) +//sys ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) = SYS_PTRACE func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) @@ -267,19 +269,36 @@ func PtraceDetach(pid int) (err error) { } func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) { - return ptrace(PT_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0) + return ptracePtr(PT_GETFPREGS, pid, unsafe.Pointer(fpregsout), 0) } func PtraceGetRegs(pid int, regsout *Reg) (err error) { - return ptrace(PT_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0) + return ptracePtr(PT_GETREGS, pid, unsafe.Pointer(regsout), 0) +} + +func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { + ioDesc := PtraceIoDesc{ + Op: int32(req), + Offs: offs, + } + if countin > 0 { + _ = out[:countin] // check bounds + ioDesc.Addr = &out[0] + } else if out != nil { + ioDesc.Addr = (*byte)(unsafe.Pointer(&_zero)) + } + ioDesc.SetLen(countin) + + err = ptracePtr(PT_IO, pid, unsafe.Pointer(&ioDesc), 0) + return int(ioDesc.Len), err } func PtraceLwpEvents(pid int, enable int) (err error) { return ptrace(PT_LWP_EVENTS, pid, 0, enable) } -func PtraceLwpInfo(pid int, info uintptr) (err error) { - return ptrace(PT_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{}))) +func PtraceLwpInfo(pid int, info *PtraceLwpInfoStruct) (err error) { + return ptracePtr(PT_LWPINFO, pid, unsafe.Pointer(info), int(unsafe.Sizeof(*info))) } func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { @@ -299,13 +318,25 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) { } func PtraceSetRegs(pid int, regs *Reg) (err error) { - return ptrace(PT_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0) + return ptracePtr(PT_SETREGS, pid, unsafe.Pointer(regs), 0) } func PtraceSingleStep(pid int) (err error) { return ptrace(PT_STEP, pid, 1, 0) } +func Dup3(oldfd, newfd, flags int) error { + if oldfd == newfd || flags&^O_CLOEXEC != 0 { + return EINVAL + } + how := F_DUP2FD + if flags&O_CLOEXEC != 0 { + how = F_DUP2FD_CLOEXEC + } + _, err := fcntl(oldfd, how, newfd) + return err +} + /* * Exposed directly */ diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go index 6a91d471d0..b8da510043 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go @@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func (d *PtraceIoDesc) SetLen(length int) { + d.Len = uint32(length) +} + func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) @@ -57,16 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func PtraceGetFsBase(pid int, fsbase *int64) (err error) { - return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) -} - -func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { - ioDesc := PtraceIoDesc{ - Op: int32(req), - Offs: offs, - Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe. - Len: uint32(countin), - } - err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) - return int(ioDesc.Len), err + return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0) } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go index 48110a0abb..47155c4839 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go @@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func (d *PtraceIoDesc) SetLen(length int) { + d.Len = uint64(length) +} + func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) @@ -57,16 +61,5 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func PtraceGetFsBase(pid int, fsbase *int64) (err error) { - return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) -} - -func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { - ioDesc := PtraceIoDesc{ - Op: int32(req), - Offs: offs, - Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe. - Len: uint64(countin), - } - err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) - return int(ioDesc.Len), err + return ptracePtr(PT_GETFSBASE, pid, unsafe.Pointer(fsbase), 0) } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go index 52f1d4b75a..08932093fa 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go @@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func (d *PtraceIoDesc) SetLen(length int) { + d.Len = uint32(length) +} + func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) @@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) - -func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { - ioDesc := PtraceIoDesc{ - Op: int32(req), - Offs: offs, - Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe. - Len: uint32(countin), - } - err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) - return int(ioDesc.Len), err -} diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go index 5537ee4f2e..d151a0d0e5 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go @@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func (d *PtraceIoDesc) SetLen(length int) { + d.Len = uint64(length) +} + func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) @@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) - -func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { - ioDesc := PtraceIoDesc{ - Op: int32(req), - Offs: offs, - Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe. - Len: uint64(countin), - } - err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) - return int(ioDesc.Len), err -} diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go index 164abd5d21..d5cd64b378 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go @@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func (d *PtraceIoDesc) SetLen(length int) { + d.Len = uint64(length) +} + func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) @@ -55,14 +59,3 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) - -func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) { - ioDesc := PtraceIoDesc{ - Op: int32(req), - Offs: offs, - Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe. - Len: uint64(countin), - } - err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) - return int(ioDesc.Len), err -} diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd.go b/vendor/golang.org/x/sys/unix/syscall_hurd.go index 4ffb64808d..381fd4673b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd.go @@ -20,3 +20,11 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { } return } + +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(uintptr(arg))) + if r0 == -1 && er != nil { + err = er + } + return +} diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 5443dddd48..9735331530 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1015,8 +1015,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { for n < len(pp.Path) && pp.Path[n] != 0 { n++ } - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] - sa.Name = string(bytes) + sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) return sa, nil case AF_INET: @@ -1365,6 +1364,10 @@ func SetsockoptTCPRepairOpt(fd, level, opt int, o []TCPRepairOpt) (err error) { return setsockopt(fd, level, opt, unsafe.Pointer(&o[0]), uintptr(SizeofTCPRepairOpt*len(o))) } +func SetsockoptTCPMD5Sig(fd, level, opt int, s *TCPMD5Sig) error { + return setsockopt(fd, level, opt, unsafe.Pointer(s), unsafe.Sizeof(*s)) +} + // Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html) // KeyctlInt calls keyctl commands in which each argument is an int. @@ -1579,6 +1582,7 @@ func BindToDevice(fd int, device string) (err error) { } //sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) +//sys ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) = SYS_PTRACE func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) { // The peek requests are machine-size oriented, so we wrap it @@ -1596,7 +1600,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro // boundary. n := 0 if addr%SizeofPtr != 0 { - err = ptrace(req, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + err = ptracePtr(req, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0])) if err != nil { return 0, err } @@ -1608,7 +1612,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro for len(out) > 0 { // We use an internal buffer to guarantee alignment. // It's not documented if this is necessary, but we're paranoid. - err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + err = ptracePtr(req, pid, addr+uintptr(n), unsafe.Pointer(&buf[0])) if err != nil { return n, err } @@ -1640,7 +1644,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c n := 0 if addr%SizeofPtr != 0 { var buf [SizeofPtr]byte - err = ptrace(peekReq, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + err = ptracePtr(peekReq, pid, addr-addr%SizeofPtr, unsafe.Pointer(&buf[0])) if err != nil { return 0, err } @@ -1667,7 +1671,7 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c // Trailing edge. if len(data) > 0 { var buf [SizeofPtr]byte - err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + err = ptracePtr(peekReq, pid, addr+uintptr(n), unsafe.Pointer(&buf[0])) if err != nil { return n, err } @@ -1696,11 +1700,11 @@ func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) { } func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { - return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) + return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout)) } func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { - return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) + return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs)) } func PtraceSetOptions(pid int, options int) (err error) { @@ -1709,7 +1713,7 @@ func PtraceSetOptions(pid int, options int) (err error) { func PtraceGetEventMsg(pid int) (msg uint, err error) { var data _C_long - err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) + err = ptracePtr(PTRACE_GETEVENTMSG, pid, 0, unsafe.Pointer(&data)) msg = uint(data) return } @@ -2154,6 +2158,14 @@ func isGroupMember(gid int) bool { return false } +func isCapDacOverrideSet() bool { + hdr := CapUserHeader{Version: LINUX_CAPABILITY_VERSION_3} + data := [2]CapUserData{} + err := Capget(&hdr, &data[0]) + + return err == nil && data[0].Effective&(1< 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index 77479d4581..1129065624 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -388,6 +388,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -414,6 +424,16 @@ func ptrace(request int, pid int, addr uintptr, data int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index 2e966d4d7a..55f5abfe59 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -388,6 +388,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -414,6 +424,16 @@ func ptrace(request int, pid int, addr uintptr, data int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index d65a7c0fa6..d39651c2b5 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -388,6 +388,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -414,6 +424,16 @@ func ptrace(request int, pid int, addr uintptr, data int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go index 6f0b97c6db..ddb7408680 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go @@ -388,6 +388,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -414,6 +424,16 @@ func ptrace(request int, pid int, addr uintptr, data int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go index e1c23b5272..09a53a616c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go @@ -388,6 +388,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -414,6 +424,16 @@ func ptrace(request int, pid int, addr uintptr, data int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 36ea3a55b7..430cb24de7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -379,6 +379,16 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(arg) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 79f7389963..8e1d9c8f66 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -405,6 +405,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index fb161f3a26..21c6950400 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -405,6 +405,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 4c8ac993a8..298168f90a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -405,6 +405,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go index 76dd8ec4fd..68b8bd492f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go @@ -405,6 +405,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index caeb807bd4..0b0f910e1a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index a05e5f4fff..48ff5de75b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index b2da8e50cc..2452a641da 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 048b2655e6..5e35600a60 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 6f33e37e72..b04cef1a19 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 330cf7f7ac..47a07ee0c2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 5f24de0d9d..573378fdb9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -527,6 +527,14 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + var libc_ioctl_trampoline_addr uintptr //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index 78d4a4240e..4873a1e5d3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -657,6 +657,17 @@ func ioctlRet(fd int, req uint, arg uintptr) (ret int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtrRet(fd int, req uint, arg unsafe.Pointer) (ret int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, uintptr(fd), uintptr(req), uintptr(arg), 0, 0, 0) + ret = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procpoll)), 3, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout), 0, 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go index f2079457c6..07bfe2ef9a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go @@ -267,6 +267,16 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index d9c78cdcbc..29dc483378 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -362,7 +362,7 @@ type FpExtendedPrecision struct{} type PtraceIoDesc struct { Op int32 Offs uintptr - Addr uintptr + Addr *byte Len uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 26991b1655..0a89b28906 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -367,7 +367,7 @@ type FpExtendedPrecision struct{} type PtraceIoDesc struct { Op int32 Offs uintptr - Addr uintptr + Addr *byte Len uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index f8324e7e7f..c8666bb152 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -350,7 +350,7 @@ type FpExtendedPrecision struct { type PtraceIoDesc struct { Op int32 Offs uintptr - Addr uintptr + Addr *byte Len uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 4220411f34..88fb48a887 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -347,7 +347,7 @@ type FpExtendedPrecision struct{} type PtraceIoDesc struct { Op int32 Offs uintptr - Addr uintptr + Addr *byte Len uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go index 0660fd45c7..698dc975e9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -348,7 +348,7 @@ type FpExtendedPrecision struct{} type PtraceIoDesc struct { Op int32 Offs uintptr - Addr uintptr + Addr *byte Len uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 7d9fc8f1c9..ca84727cfe 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -456,36 +456,60 @@ type Ucred struct { } type TCPInfo struct { - State uint8 - Ca_state uint8 - Retransmits uint8 - Probes uint8 - Backoff uint8 - Options uint8 - Rto uint32 - Ato uint32 - Snd_mss uint32 - Rcv_mss uint32 - Unacked uint32 - Sacked uint32 - Lost uint32 - Retrans uint32 - Fackets uint32 - Last_data_sent uint32 - Last_ack_sent uint32 - Last_data_recv uint32 - Last_ack_recv uint32 - Pmtu uint32 - Rcv_ssthresh uint32 - Rtt uint32 - Rttvar uint32 - Snd_ssthresh uint32 - Snd_cwnd uint32 - Advmss uint32 - Reordering uint32 - Rcv_rtt uint32 - Rcv_space uint32 - Total_retrans uint32 + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 + Pacing_rate uint64 + Max_pacing_rate uint64 + Bytes_acked uint64 + Bytes_received uint64 + Segs_out uint32 + Segs_in uint32 + Notsent_bytes uint32 + Min_rtt uint32 + Data_segs_in uint32 + Data_segs_out uint32 + Delivery_rate uint64 + Busy_time uint64 + Rwnd_limited uint64 + Sndbuf_limited uint64 + Delivered uint32 + Delivered_ce uint32 + Bytes_sent uint64 + Bytes_retrans uint64 + Dsack_dups uint32 + Reord_seen uint32 + Rcv_ooopack uint32 + Snd_wnd uint32 + Rcv_wnd uint32 + Rehash uint32 } type CanFilter struct { @@ -528,7 +552,7 @@ const ( SizeofIPv6MTUInfo = 0x20 SizeofICMPv6Filter = 0x20 SizeofUcred = 0xc - SizeofTCPInfo = 0x68 + SizeofTCPInfo = 0xf0 SizeofCanFilter = 0x8 SizeofTCPRepairOpt = 0x8 ) @@ -1043,6 +1067,7 @@ const ( PerfBitCommExec = CBitFieldMaskBit24 PerfBitUseClockID = CBitFieldMaskBit25 PerfBitContextSwitch = CBitFieldMaskBit26 + PerfBitWriteBackward = CBitFieldMaskBit27 ) const ( @@ -1239,7 +1264,7 @@ type TCPMD5Sig struct { Flags uint8 Prefixlen uint8 Keylen uint16 - _ uint32 + Ifindex int32 Key [80]uint8 } @@ -1939,7 +1964,11 @@ const ( NFT_MSG_GETOBJ = 0x13 NFT_MSG_DELOBJ = 0x14 NFT_MSG_GETOBJ_RESET = 0x15 - NFT_MSG_MAX = 0x19 + NFT_MSG_NEWFLOWTABLE = 0x16 + NFT_MSG_GETFLOWTABLE = 0x17 + NFT_MSG_DELFLOWTABLE = 0x18 + NFT_MSG_GETRULE_RESET = 0x19 + NFT_MSG_MAX = 0x1a NFTA_LIST_UNSPEC = 0x0 NFTA_LIST_ELEM = 0x1 NFTA_HOOK_UNSPEC = 0x0 @@ -2443,9 +2472,11 @@ const ( SOF_TIMESTAMPING_OPT_STATS = 0x1000 SOF_TIMESTAMPING_OPT_PKTINFO = 0x2000 SOF_TIMESTAMPING_OPT_TX_SWHW = 0x4000 + SOF_TIMESTAMPING_BIND_PHC = 0x8000 + SOF_TIMESTAMPING_OPT_ID_TCP = 0x10000 - SOF_TIMESTAMPING_LAST = 0x8000 - SOF_TIMESTAMPING_MASK = 0xffff + SOF_TIMESTAMPING_LAST = 0x10000 + SOF_TIMESTAMPING_MASK = 0x1ffff SCM_TSTAMP_SND = 0x0 SCM_TSTAMP_SCHED = 0x1 @@ -3265,7 +3296,7 @@ const ( DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES = 0xae DEVLINK_ATTR_NESTED_DEVLINK = 0xaf DEVLINK_ATTR_SELFTESTS = 0xb0 - DEVLINK_ATTR_MAX = 0xb0 + DEVLINK_ATTR_MAX = 0xb3 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0x0 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 0x1 DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0x0 @@ -3281,7 +3312,8 @@ const ( DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR = 0x1 DEVLINK_PORT_FN_ATTR_STATE = 0x2 DEVLINK_PORT_FN_ATTR_OPSTATE = 0x3 - DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x3 + DEVLINK_PORT_FN_ATTR_CAPS = 0x4 + DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x4 ) type FsverityDigest struct { @@ -3572,7 +3604,8 @@ const ( ETHTOOL_MSG_MODULE_SET = 0x23 ETHTOOL_MSG_PSE_GET = 0x24 ETHTOOL_MSG_PSE_SET = 0x25 - ETHTOOL_MSG_USER_MAX = 0x25 + ETHTOOL_MSG_RSS_GET = 0x26 + ETHTOOL_MSG_USER_MAX = 0x26 ETHTOOL_MSG_KERNEL_NONE = 0x0 ETHTOOL_MSG_STRSET_GET_REPLY = 0x1 ETHTOOL_MSG_LINKINFO_GET_REPLY = 0x2 @@ -3611,7 +3644,8 @@ const ( ETHTOOL_MSG_MODULE_GET_REPLY = 0x23 ETHTOOL_MSG_MODULE_NTF = 0x24 ETHTOOL_MSG_PSE_GET_REPLY = 0x25 - ETHTOOL_MSG_KERNEL_MAX = 0x25 + ETHTOOL_MSG_RSS_GET_REPLY = 0x26 + ETHTOOL_MSG_KERNEL_MAX = 0x26 ETHTOOL_A_HEADER_UNSPEC = 0x0 ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 @@ -3679,7 +3713,8 @@ const ( ETHTOOL_A_LINKSTATE_SQI_MAX = 0x4 ETHTOOL_A_LINKSTATE_EXT_STATE = 0x5 ETHTOOL_A_LINKSTATE_EXT_SUBSTATE = 0x6 - ETHTOOL_A_LINKSTATE_MAX = 0x6 + ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT = 0x7 + ETHTOOL_A_LINKSTATE_MAX = 0x7 ETHTOOL_A_DEBUG_UNSPEC = 0x0 ETHTOOL_A_DEBUG_HEADER = 0x1 ETHTOOL_A_DEBUG_MSGMASK = 0x2 @@ -4409,7 +4444,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x140 + NL80211_ATTR_MAX = 0x141 NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -4552,6 +4587,7 @@ const ( NL80211_ATTR_SUPPORT_MESH_AUTH = 0x73 NL80211_ATTR_SURVEY_INFO = 0x54 NL80211_ATTR_SURVEY_RADIO_STATS = 0xda + NL80211_ATTR_TD_BITMAP = 0x141 NL80211_ATTR_TDLS_ACTION = 0x88 NL80211_ATTR_TDLS_DIALOG_TOKEN = 0x89 NL80211_ATTR_TDLS_EXTERNAL_SETUP = 0x8c @@ -5752,3 +5788,25 @@ const ( AUDIT_NLGRP_NONE = 0x0 AUDIT_NLGRP_READLOG = 0x1 ) + +const ( + TUN_F_CSUM = 0x1 + TUN_F_TSO4 = 0x2 + TUN_F_TSO6 = 0x4 + TUN_F_TSO_ECN = 0x8 + TUN_F_UFO = 0x10 +) + +const ( + VIRTIO_NET_HDR_F_NEEDS_CSUM = 0x1 + VIRTIO_NET_HDR_F_DATA_VALID = 0x2 + VIRTIO_NET_HDR_F_RSC_INFO = 0x4 +) + +const ( + VIRTIO_NET_HDR_GSO_NONE = 0x0 + VIRTIO_NET_HDR_GSO_TCPV4 = 0x1 + VIRTIO_NET_HDR_GSO_UDP = 0x3 + VIRTIO_NET_HDR_GSO_TCPV6 = 0x4 + VIRTIO_NET_HDR_GSO_ECN = 0x80 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 89c516a29a..4ecc1495cd 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -414,7 +414,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [122]int8 + Data [122]byte _ uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index 62b4fb2699..34fddff964 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -427,7 +427,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index e86b35893e..3b14a6031f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -405,7 +405,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [122]uint8 + Data [122]byte _ uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 6c6be4c911..0517651ab3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -406,7 +406,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go index 4982ea355a..3b0c518134 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -407,7 +407,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 173141a670..fccdf4dd0f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -410,7 +410,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [122]int8 + Data [122]byte _ uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index 93ae4c5167..500de8fc07 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -409,7 +409,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 4e4e510ca5..d0434cd2c6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -409,7 +409,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 3f5ba013d9..84206ba534 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -410,7 +410,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [122]int8 + Data [122]byte _ uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index 71dfe7cdb4..ab078cf1f5 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -417,7 +417,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [122]uint8 + Data [122]byte _ uint32 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 3a2b7f0a66..42eb2c4cef 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -416,7 +416,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]uint8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index a52d627563..31304a4e8b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -416,7 +416,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]uint8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index dfc007d8a6..c311f9612d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -434,7 +434,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]uint8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index b53cb9103d..bba3cefac1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -429,7 +429,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index fe0aa35472..ad8a013804 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -411,7 +411,7 @@ const ( type SockaddrStorage struct { Family uint16 - _ [118]int8 + Data [118]byte _ uint64 } diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 41cb3c01fd..3723b2c224 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -824,6 +824,9 @@ const socket_error = uintptr(^uint32(0)) //sys WSAStartup(verreq uint32, data *WSAData) (sockerr error) = ws2_32.WSAStartup //sys WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup //sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSAIoctl +//sys WSALookupServiceBegin(querySet *WSAQUERYSET, flags uint32, handle *Handle) (err error) [failretval==socket_error] = ws2_32.WSALookupServiceBeginW +//sys WSALookupServiceNext(handle Handle, flags uint32, size *int32, querySet *WSAQUERYSET) (err error) [failretval==socket_error] = ws2_32.WSALookupServiceNextW +//sys WSALookupServiceEnd(handle Handle) (err error) [failretval==socket_error] = ws2_32.WSALookupServiceEnd //sys socket(af int32, typ int32, protocol int32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.socket //sys sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) (err error) [failretval==socket_error] = ws2_32.sendto //sys recvfrom(s Handle, buf []byte, flags int32, from *RawSockaddrAny, fromlen *int32) (n int32, err error) [failretval==-1] = ws2_32.recvfrom @@ -1019,8 +1022,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { for n < len(pp.Path) && pp.Path[n] != 0 { n++ } - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] - sa.Name = string(bytes) + sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n)) return sa, nil case AF_INET: diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 0c4add9741..857acf1032 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -1243,6 +1243,51 @@ const ( DnsSectionAdditional = 0x0003 ) +const ( + // flags of WSALookupService + LUP_DEEP = 0x0001 + LUP_CONTAINERS = 0x0002 + LUP_NOCONTAINERS = 0x0004 + LUP_NEAREST = 0x0008 + LUP_RETURN_NAME = 0x0010 + LUP_RETURN_TYPE = 0x0020 + LUP_RETURN_VERSION = 0x0040 + LUP_RETURN_COMMENT = 0x0080 + LUP_RETURN_ADDR = 0x0100 + LUP_RETURN_BLOB = 0x0200 + LUP_RETURN_ALIASES = 0x0400 + LUP_RETURN_QUERY_STRING = 0x0800 + LUP_RETURN_ALL = 0x0FF0 + LUP_RES_SERVICE = 0x8000 + + LUP_FLUSHCACHE = 0x1000 + LUP_FLUSHPREVIOUS = 0x2000 + + LUP_NON_AUTHORITATIVE = 0x4000 + LUP_SECURE = 0x8000 + LUP_RETURN_PREFERRED_NAMES = 0x10000 + LUP_DNS_ONLY = 0x20000 + + LUP_ADDRCONFIG = 0x100000 + LUP_DUAL_ADDR = 0x200000 + LUP_FILESERVER = 0x400000 + LUP_DISABLE_IDN_ENCODING = 0x00800000 + LUP_API_ANSI = 0x01000000 + + LUP_RESOLUTION_HANDLE = 0x80000000 +) + +const ( + // values of WSAQUERYSET's namespace + NS_ALL = 0 + NS_DNS = 12 + NS_NLA = 15 + NS_BTH = 16 + NS_EMAIL = 37 + NS_PNRPNAME = 38 + NS_PNRPCLOUD = 39 +) + type DNSSRVData struct { Target *uint16 Priority uint16 @@ -3258,3 +3303,43 @@ const ( DWMWA_TEXT_COLOR = 36 DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37 ) + +type WSAQUERYSET struct { + Size uint32 + ServiceInstanceName *uint16 + ServiceClassId *GUID + Version *WSAVersion + Comment *uint16 + NameSpace uint32 + NSProviderId *GUID + Context *uint16 + NumberOfProtocols uint32 + AfpProtocols *AFProtocols + QueryString *uint16 + NumberOfCsAddrs uint32 + SaBuffer *CSAddrInfo + OutputFlags uint32 + Blob *BLOB +} + +type WSAVersion struct { + Version uint32 + EnumerationOfComparison int32 +} + +type AFProtocols struct { + AddressFamily int32 + Protocol int32 +} + +type CSAddrInfo struct { + LocalAddr SocketAddress + RemoteAddr SocketAddress + SocketType int32 + Protocol int32 +} + +type BLOB struct { + Size uint32 + BlobData *byte +} diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index ac60052e44..6d2a268534 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -474,6 +474,9 @@ var ( procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW") procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") procWSAIoctl = modws2_32.NewProc("WSAIoctl") + procWSALookupServiceBeginW = modws2_32.NewProc("WSALookupServiceBeginW") + procWSALookupServiceEnd = modws2_32.NewProc("WSALookupServiceEnd") + procWSALookupServiceNextW = modws2_32.NewProc("WSALookupServiceNextW") procWSARecv = modws2_32.NewProc("WSARecv") procWSARecvFrom = modws2_32.NewProc("WSARecvFrom") procWSASend = modws2_32.NewProc("WSASend") @@ -4067,6 +4070,30 @@ func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbo return } +func WSALookupServiceBegin(querySet *WSAQUERYSET, flags uint32, handle *Handle) (err error) { + r1, _, e1 := syscall.Syscall(procWSALookupServiceBeginW.Addr(), 3, uintptr(unsafe.Pointer(querySet)), uintptr(flags), uintptr(unsafe.Pointer(handle))) + if r1 == socket_error { + err = errnoErr(e1) + } + return +} + +func WSALookupServiceEnd(handle Handle) (err error) { + r1, _, e1 := syscall.Syscall(procWSALookupServiceEnd.Addr(), 1, uintptr(handle), 0, 0) + if r1 == socket_error { + err = errnoErr(e1) + } + return +} + +func WSALookupServiceNext(handle Handle, flags uint32, size *int32, querySet *WSAQUERYSET) (err error) { + r1, _, e1 := syscall.Syscall6(procWSALookupServiceNextW.Addr(), 4, uintptr(handle), uintptr(flags), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(querySet)), 0, 0) + if r1 == socket_error { + err = errnoErr(e1) + } + return +} + func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := syscall.Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) if r1 == socket_error { diff --git a/vendor/modules.txt b/vendor/modules.txt index 5a3430c56c..fe163c8438 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -145,6 +145,19 @@ github.com/go-openapi/swag # github.com/gobuffalo/flect v0.2.4 ## explicit; go 1.13 github.com/gobuffalo/flect +# github.com/gobwas/httphead v0.1.0 +## explicit; go 1.15 +github.com/gobwas/httphead +# github.com/gobwas/pool v0.2.1 +## explicit +github.com/gobwas/pool +github.com/gobwas/pool/internal/pmath +github.com/gobwas/pool/pbufio +github.com/gobwas/pool/pbytes +# github.com/gobwas/ws v1.2.0 +## explicit; go 1.15 +github.com/gobwas/ws +github.com/gobwas/ws/wsutil # github.com/gogo/protobuf v1.3.2 ## explicit; go 1.15 github.com/gogo/protobuf/proto @@ -210,9 +223,6 @@ github.com/google/uuid github.com/googleapis/gax-go/v2 github.com/googleapis/gax-go/v2/apierror github.com/googleapis/gax-go/v2/apierror/internal/proto -# github.com/gorilla/websocket v1.4.2 -## explicit; go 1.12 -github.com/gorilla/websocket # github.com/grpc-ecosystem/grpc-gateway v1.16.0 ## explicit; go 1.14 github.com/grpc-ecosystem/grpc-gateway/internal @@ -379,7 +389,7 @@ golang.org/x/oauth2/jwt ## explicit golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.5.0 +# golang.org/x/sys v0.6.0 ## explicit; go 1.17 golang.org/x/sys/execabs golang.org/x/sys/internal/unsafeheader diff --git a/websocket/connection.go b/websocket/connection.go index 6aa1e61098..a22d79df53 100644 --- a/websocket/connection.go +++ b/websocket/connection.go @@ -18,11 +18,12 @@ package websocket import ( "bytes" + "context" "encoding/gob" "errors" "fmt" "io" - "net/http/httputil" + "net" "sync" "time" @@ -30,7 +31,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" - "github.com/gorilla/websocket" + "github.com/gobwas/ws" + "github.com/gobwas/ws/wsutil" ) var ( @@ -49,12 +51,40 @@ var ( // RawConnection is an interface defining the methods needed // from a websocket connection type rawConnection interface { - WriteMessage(messageType int, data []byte) error - NextReader() (int, io.Reader, error) Close() error - SetReadDeadline(deadline time.Time) error - SetPongHandler(func(string) error) + Read(p []byte) (n int, err error) + Write(p []byte) (n int, err error) + WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error + NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) +} + +type netConnExtension struct { + conn net.Conn +} + +func (nc *netConnExtension) Read(p []byte) (n int, err error) { + return nc.conn.Read(p) +} + +func (nc *netConnExtension) Write(p []byte) (n int, err error) { + return nc.conn.Write(p) +} + +func (nc *netConnExtension) Close() error { + return nc.conn.Close() +} + +func (nc *netConnExtension) SetReadDeadline(deadline time.Time) error { + return nc.conn.SetReadDeadline(deadline) +} + +func (nc *netConnExtension) WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error { + return wsutil.WriteClientMessage(w, op, p) +} + +func (nc *netConnExtension) NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) { + return wsutil.NextReader(r, s) } // ManagedConnection represents a websocket connection. @@ -128,23 +158,24 @@ func NewDurableSendingConnectionGuaranteed(target string, duration time.Duration // go func() {conn.Shutdown(); close(messageChan)} // go func() {for range messageChan {}} func NewDurableConnection(target string, messageChan chan []byte, logger *zap.SugaredLogger) *ManagedConnection { + ctx := context.TODO() websocketConnectionFactory := func() (rawConnection, error) { - dialer := &websocket.Dialer{ + dialer := &ws.Dialer{ // This needs to be relatively short to avoid the connection getting blackholed for a long time // by restarting the serving side of the connection behind a Kubernetes Service. - HandshakeTimeout: 3 * time.Second, + Timeout: 3 * time.Second, } - conn, resp, err := dialer.Dial(target, nil) + + conn, _, _, err := dialer.Dial(ctx, target) if err != nil { - if resp != nil { - dresp, _ := httputil.DumpResponse(resp, true /*body*/) // This is for logging so don't care if it fails. - logger.Errorw("Websocket connection could not be established", zap.Error(err), - zap.String("request", string(dresp))) - } else { + if err != nil { logger.Errorw("Websocket connection could not be established", zap.Error(err)) } } - return conn, err + nc := &netConnExtension{ + conn: conn, + } + return nc, err } c := newConnection(websocketConnectionFactory, messageChan) @@ -187,7 +218,7 @@ func NewDurableConnection(target string, messageChan chan []byte, logger *zap.Su for { select { case <-ticker.C: - if err := c.write(websocket.PingMessage, []byte{}); err != nil { + if err := c.write(ws.OpPing, []byte{}); err != nil { logger.Errorw("Failed to send ping message to "+target, zap.Error(err)) } case <-c.closeChan: @@ -232,10 +263,6 @@ func (c *ManagedConnection) connect() error { // time we receive a pong message so we know the connection // is still intact. conn.SetReadDeadline(time.Now().Add(pongTimeout)) - conn.SetPongHandler(func(string) error { - conn.SetReadDeadline(time.Now().Add(pongTimeout)) - return nil - }) c.connectionLock.Lock() defer c.connectionLock.Unlock() @@ -292,7 +319,10 @@ func (c *ManagedConnection) read() error { c.readerLock.Lock() defer c.readerLock.Unlock() - messageType, reader, err := c.connection.NextReader() + c.connection.SetReadDeadline(time.Now().Add(pongTimeout)) + + header, reader, err := c.connection.NextReader(c.connection, ws.StateClientSide) + messageType := header.OpCode if err != nil { return err } @@ -300,7 +330,7 @@ func (c *ManagedConnection) read() error { // Send the message to the channel if its an application level message // and if that channel is set. // TODO(markusthoemmes): Return the messageType along with the payload. - if c.messageChan != nil && (messageType == websocket.TextMessage || messageType == websocket.BinaryMessage) { + if c.messageChan != nil && (messageType == ws.OpText || messageType == ws.OpBinary) { if message, _ := io.ReadAll(reader); message != nil { c.messageChan <- message } @@ -309,7 +339,7 @@ func (c *ManagedConnection) read() error { return nil } -func (c *ManagedConnection) write(messageType int, body []byte) error { +func (c *ManagedConnection) write(messageType ws.OpCode, body []byte) error { c.connectionLock.RLock() defer c.connectionLock.RUnlock() @@ -319,8 +349,7 @@ func (c *ManagedConnection) write(messageType int, body []byte) error { c.writerLock.Lock() defer c.writerLock.Unlock() - - return c.connection.WriteMessage(messageType, body) + return c.connection.WriteClientMessage(c.connection, messageType, body) } // Status checks the connection status of the webhook. @@ -342,11 +371,11 @@ func (c *ManagedConnection) Send(msg interface{}) error { return err } - return c.write(websocket.BinaryMessage, b.Bytes()) + return c.write(ws.OpBinary, b.Bytes()) } // SendRaw sends a message over the websocket connection without performing any encoding. -func (c *ManagedConnection) SendRaw(messageType int, msg []byte) error { +func (c *ManagedConnection) SendRaw(messageType ws.OpCode, msg []byte) error { return c.write(messageType, msg) } diff --git a/websocket/connection_test.go b/websocket/connection_test.go index d8712c31f4..131996550c 100644 --- a/websocket/connection_test.go +++ b/websocket/connection_test.go @@ -29,33 +29,20 @@ import ( "k8s.io/apimachinery/pkg/util/wait" - "github.com/gorilla/websocket" + "github.com/gobwas/ws" ) const propagationTimeout = 5 * time.Second type inspectableConnection struct { - nextReaderCalls chan struct{} - writeMessageCalls chan struct{} - closeCalls chan struct{} - setReadDeadlineCalls chan struct{} - setPongHandlerCalls chan struct{} - - nextReaderFunc func() (int, io.Reader, error) -} - -func (c *inspectableConnection) WriteMessage(messageType int, data []byte) error { - if c.writeMessageCalls != nil { - c.writeMessageCalls <- struct{}{} - } - return nil -} - -func (c *inspectableConnection) NextReader() (int, io.Reader, error) { - if c.nextReaderCalls != nil { - c.nextReaderCalls <- struct{}{} - } - return c.nextReaderFunc() + closeCalls chan struct{} + setReadDeadlineCalls chan struct{} + writeClientMessageCalls chan struct{} + nextReaderCalls chan struct{} + + readFunc func() (int, error) + writeFunc func() (int, error) + nextReaderFunc func() (ws.Header, io.Reader, error) } func (c *inspectableConnection) Close() error { @@ -72,10 +59,26 @@ func (c *inspectableConnection) SetReadDeadline(deadline time.Time) error { return nil } -func (c *inspectableConnection) SetPongHandler(func(string) error) { - if c.setPongHandlerCalls != nil { - c.setPongHandlerCalls <- struct{}{} +func (c *inspectableConnection) Read(p []byte) (n int, err error) { + return c.readFunc() +} + +func (c *inspectableConnection) Write(p []byte) (n int, err error) { + return c.writeFunc() +} + +func (c *inspectableConnection) WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error { + if c.writeClientMessageCalls != nil { + c.writeClientMessageCalls <- struct{}{} } + return nil +} + +func (c *inspectableConnection) NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) { + if c.nextReaderCalls != nil { + c.nextReaderCalls <- struct{}{} + } + return c.nextReaderFunc() } // staticConnFactory returns a static connection, for example @@ -100,7 +103,6 @@ func TestRetriesWhileConnect(t *testing.T) { spy := &inspectableConnection{ closeCalls: make(chan struct{}, 1), setReadDeadlineCalls: make(chan struct{}, 1), - setPongHandlerCalls: make(chan struct{}, 1), } connFactory := func() (rawConnection, error) { @@ -123,9 +125,6 @@ func TestRetriesWhileConnect(t *testing.T) { if got, want := len(spy.setReadDeadlineCalls), 1; got != want { t.Fatalf("Got %d 'SetReadDeadline' calls, want %d", got, want) } - if got, want := len(spy.setPongHandlerCalls), 1; got != want { - t.Fatalf("Got %d 'SetPongHandler' calls, want %d", got, want) - } if len(spy.closeCalls) != 1 { t.Fatal("Wanted 'Close' to be called once, but got", len(spy.closeCalls)) @@ -156,7 +155,7 @@ func TestStatusOnNoConnection(t *testing.T) { func TestSendErrorOnEncode(t *testing.T) { spy := &inspectableConnection{ - writeMessageCalls: make(chan struct{}, 1), + writeClientMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -166,14 +165,14 @@ func TestSendErrorOnEncode(t *testing.T) { if got == nil { t.Fatal("Expected an error but got none") } - if len(spy.writeMessageCalls) != 0 { - t.Fatalf("Expected 'WriteMessage' not to be called, but was called %v times", spy.writeMessageCalls) + if len(spy.writeClientMessageCalls) != 0 { + t.Fatalf("Expected 'WriteClientMessage' not to be called, but was called %v times", spy.writeClientMessageCalls) } } func TestSendMessage(t *testing.T) { spy := &inspectableConnection{ - writeMessageCalls: make(chan struct{}, 1), + writeClientMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -185,14 +184,14 @@ func TestSendMessage(t *testing.T) { if got := conn.Send("test"); got != nil { t.Fatalf("Expected no error but got: %+v", got) } - if len(spy.writeMessageCalls) != 1 { - t.Fatalf("Expected 'WriteMessage' to be called once, but was called %v times", spy.writeMessageCalls) + if len(spy.writeClientMessageCalls) != 1 { + t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeClientMessageCalls) } } func TestSendRawMessage(t *testing.T) { spy := &inspectableConnection{ - writeMessageCalls: make(chan struct{}, 1), + writeClientMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -201,22 +200,24 @@ func TestSendRawMessage(t *testing.T) { t.Errorf("Status() = %v, wanted nil", got) } - if got := conn.SendRaw(websocket.BinaryMessage, []byte("test")); got != nil { + if got := conn.SendRaw(ws.OpBinary, []byte("test")); got != nil { t.Fatalf("Expected no error but got: %+v", got) } - if len(spy.writeMessageCalls) != 1 { - t.Fatalf("Expected 'WriteMessage' to be called once, but was called %v times", spy.writeMessageCalls) + if len(spy.writeClientMessageCalls) != 1 { + t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeClientMessageCalls) } } func TestReceiveMessage(t *testing.T) { testMessage := "testmessage" + header := ws.Header{ + OpCode: ws.OpText, + } spy := &inspectableConnection{ - writeMessageCalls: make(chan struct{}, 1), - nextReaderCalls: make(chan struct{}, 1), - nextReaderFunc: func() (int, io.Reader, error) { - return websocket.TextMessage, strings.NewReader(testMessage), nil + nextReaderCalls: make(chan struct{}, 1), + nextReaderFunc: func() (ws.Header, io.Reader, error) { + return header, strings.NewReader(testMessage), nil }, } @@ -299,9 +300,13 @@ func TestConnectLoopIsStopped(t *testing.T) { } func TestKeepaliveLoopIsStopped(t *testing.T) { + header := ws.Header{ + OpCode: ws.OpText, + } + spy := &inspectableConnection{ - nextReaderFunc: func() (int, io.Reader, error) { - return websocket.TextMessage, nil, nil + nextReaderFunc: func() (ws.Header, io.Reader, error) { + return header, nil, nil }, } conn := newConnection(staticConnFactory(spy), nil) @@ -342,9 +347,9 @@ func TestDurableConnectionWhenConnectionBreaksDown(t *testing.T) { const testPayload = "test" reconnectChan := make(chan struct{}) - upgrader := websocket.Upgrader{} + upgrader := ws.HTTPUpgrader{} s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c, err := upgrader.Upgrade(w, r, nil) + c, _, _, err := upgrader.Upgrade(r, w) if err != nil { return } @@ -385,25 +390,24 @@ func TestDurableConnectionSendsPingsRegularly(t *testing.T) { pongTimeout = pingTimeoutBackup }) - upgrader := websocket.Upgrader{} + upgrader := ws.HTTPUpgrader{} pingReceived := make(chan struct{}) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c, err := upgrader.Upgrade(w, r, nil) + c, _, _, err := upgrader.Upgrade(r, w) if err != nil { return } - c.SetPingHandler(func(_ string) error { - pingReceived <- struct{}{} - return c.WriteMessage(websocket.PongMessage, []byte{}) - }) - for { - _, _, err := c.ReadMessage() + frame, err := ws.ReadFrame(c) if err != nil { break } + if frame.Header.OpCode == ws.OpPing { + pingReceived <- struct{}{} + ws.WriteFrame(c, ws.NewPongFrame(frame.Payload)) + } } })) defer s.Close() @@ -430,9 +434,9 @@ func TestNewDurableSendingConnectionGuaranteed(t *testing.T) { // Happy case. const testPayload = "test" reconnectChan := make(chan struct{}) - upgrader := websocket.Upgrader{} + upgrader := ws.HTTPUpgrader{} s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c, err := upgrader.Upgrade(w, r, nil) + c, _, _, err := upgrader.Upgrade(r, w) if err != nil { return } From 68306cb7bd6f6ed7894f59e116fe59b691c14f7e Mon Sep 17 00:00:00 2001 From: moriya Date: Tue, 25 Apr 2023 22:08:59 +0900 Subject: [PATCH 2/4] lint --- .../github.com/gobwas/httphead/LICENSE | 21 ++++++++++++++++++ .../github.com/gobwas/pool/LICENSE | 21 ++++++++++++++++++ .../github.com/gobwas/ws/LICENSE | 21 ++++++++++++++++++ .../github.com/gorilla/websocket/LICENSE | 22 ------------------- websocket/connection_test.go | 10 ++++----- 5 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 third_party/VENDOR-LICENSE/github.com/gobwas/httphead/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/gobwas/pool/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/gobwas/ws/LICENSE delete mode 100644 third_party/VENDOR-LICENSE/github.com/gorilla/websocket/LICENSE diff --git a/third_party/VENDOR-LICENSE/github.com/gobwas/httphead/LICENSE b/third_party/VENDOR-LICENSE/github.com/gobwas/httphead/LICENSE new file mode 100644 index 0000000000..274431766f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/gobwas/httphead/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/gobwas/pool/LICENSE b/third_party/VENDOR-LICENSE/github.com/gobwas/pool/LICENSE new file mode 100644 index 0000000000..c41ffde6f7 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/gobwas/pool/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/gobwas/ws/LICENSE b/third_party/VENDOR-LICENSE/github.com/gobwas/ws/LICENSE new file mode 100644 index 0000000000..ca6dfd9e16 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/gobwas/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2021 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/gorilla/websocket/LICENSE b/third_party/VENDOR-LICENSE/github.com/gorilla/websocket/LICENSE deleted file mode 100644 index 9171c97225..0000000000 --- a/third_party/VENDOR-LICENSE/github.com/gorilla/websocket/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/websocket/connection_test.go b/websocket/connection_test.go index 131996550c..6ae27173b3 100644 --- a/websocket/connection_test.go +++ b/websocket/connection_test.go @@ -52,29 +52,29 @@ func (c *inspectableConnection) Close() error { return nil } -func (c *inspectableConnection) SetReadDeadline(deadline time.Time) error { +func (c *inspectableConnection) SetReadDeadline(_ time.Time) error { if c.setReadDeadlineCalls != nil { c.setReadDeadlineCalls <- struct{}{} } return nil } -func (c *inspectableConnection) Read(p []byte) (n int, err error) { +func (c *inspectableConnection) Read(_ []byte) (n int, err error) { return c.readFunc() } -func (c *inspectableConnection) Write(p []byte) (n int, err error) { +func (c *inspectableConnection) Write(_ []byte) (n int, err error) { return c.writeFunc() } -func (c *inspectableConnection) WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error { +func (c *inspectableConnection) WriteClientMessage(_ io.Writer, _ ws.OpCode, _ []byte) error { if c.writeClientMessageCalls != nil { c.writeClientMessageCalls <- struct{}{} } return nil } -func (c *inspectableConnection) NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) { +func (c *inspectableConnection) NextReader(_ io.Reader, _ ws.State) (ws.Header, io.Reader, error) { if c.nextReaderCalls != nil { c.nextReaderCalls <- struct{}{} } From a690d6ee623b85493e6272c727e66359954d9c52 Mon Sep 17 00:00:00 2001 From: moriya Date: Sat, 29 Apr 2023 22:39:33 +0900 Subject: [PATCH 3/4] change_for_knative_serving --- websocket/connection.go | 48 +++++++++++++++++++++++---------- websocket/connection_test.go | 51 +++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/websocket/connection.go b/websocket/connection.go index a22d79df53..4616e8380d 100644 --- a/websocket/connection.go +++ b/websocket/connection.go @@ -55,36 +55,56 @@ type rawConnection interface { SetReadDeadline(deadline time.Time) error Read(p []byte) (n int, err error) Write(p []byte) (n int, err error) - WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error - NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) + WriteMessage(op ws.OpCode, p []byte) error + NextReader() (ws.Header, io.Reader, error) + ReadMessage() (messageType ws.OpCode, p []byte, err error) } -type netConnExtension struct { +type NetConnExtension struct { conn net.Conn } -func (nc *netConnExtension) Read(p []byte) (n int, err error) { +func NewNetConnExtension(conn net.Conn) *NetConnExtension { + nc := &NetConnExtension{ + conn: conn, + } + return nc +} + +func (nc *NetConnExtension) Read(p []byte) (n int, err error) { return nc.conn.Read(p) } -func (nc *netConnExtension) Write(p []byte) (n int, err error) { +func (nc *NetConnExtension) Write(p []byte) (n int, err error) { return nc.conn.Write(p) } -func (nc *netConnExtension) Close() error { +func (nc *NetConnExtension) Close() error { return nc.conn.Close() } -func (nc *netConnExtension) SetReadDeadline(deadline time.Time) error { +func (nc *NetConnExtension) SetReadDeadline(deadline time.Time) error { return nc.conn.SetReadDeadline(deadline) } -func (nc *netConnExtension) WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error { - return wsutil.WriteClientMessage(w, op, p) +func (nc *NetConnExtension) WriteMessage(op ws.OpCode, p []byte) error { + return wsutil.WriteClientMessage(nc, op, p) } -func (nc *netConnExtension) NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) { - return wsutil.NextReader(r, s) +func (nc *NetConnExtension) NextReader() (ws.Header, io.Reader, error) { + return wsutil.NextReader(nc, ws.StateServerSide) +} + +func (nc *NetConnExtension) ReadMessage() (messageType ws.OpCode, p []byte, err error) { + var r io.Reader + var header ws.Header + header, r, err = nc.NextReader() + messageType = header.OpCode + if err != nil { + return messageType, nil, err + } + p, err = io.ReadAll(r) + return messageType, p, err } // ManagedConnection represents a websocket connection. @@ -172,7 +192,7 @@ func NewDurableConnection(target string, messageChan chan []byte, logger *zap.Su logger.Errorw("Websocket connection could not be established", zap.Error(err)) } } - nc := &netConnExtension{ + nc := &NetConnExtension{ conn: conn, } return nc, err @@ -321,7 +341,7 @@ func (c *ManagedConnection) read() error { c.connection.SetReadDeadline(time.Now().Add(pongTimeout)) - header, reader, err := c.connection.NextReader(c.connection, ws.StateClientSide) + header, reader, err := c.connection.NextReader() messageType := header.OpCode if err != nil { return err @@ -349,7 +369,7 @@ func (c *ManagedConnection) write(messageType ws.OpCode, body []byte) error { c.writerLock.Lock() defer c.writerLock.Unlock() - return c.connection.WriteClientMessage(c.connection, messageType, body) + return c.connection.WriteMessage(messageType, body) } // Status checks the connection status of the webhook. diff --git a/websocket/connection_test.go b/websocket/connection_test.go index 6ae27173b3..feef620c2e 100644 --- a/websocket/connection_test.go +++ b/websocket/connection_test.go @@ -35,14 +35,16 @@ import ( const propagationTimeout = 5 * time.Second type inspectableConnection struct { - closeCalls chan struct{} - setReadDeadlineCalls chan struct{} - writeClientMessageCalls chan struct{} - nextReaderCalls chan struct{} - - readFunc func() (int, error) - writeFunc func() (int, error) - nextReaderFunc func() (ws.Header, io.Reader, error) + closeCalls chan struct{} + setReadDeadlineCalls chan struct{} + writeMessageCalls chan struct{} + nextReaderCalls chan struct{} + readMessageCalls chan struct{} + + readFunc func() (int, error) + writeFunc func() (int, error) + nextReaderFunc func() (ws.Header, io.Reader, error) + readMessageFunc func() (messageType ws.OpCode, p []byte, err error) } func (c *inspectableConnection) Close() error { @@ -67,20 +69,27 @@ func (c *inspectableConnection) Write(_ []byte) (n int, err error) { return c.writeFunc() } -func (c *inspectableConnection) WriteClientMessage(_ io.Writer, _ ws.OpCode, _ []byte) error { - if c.writeClientMessageCalls != nil { - c.writeClientMessageCalls <- struct{}{} +func (c *inspectableConnection) WriteMessage(_ ws.OpCode, _ []byte) error { + if c.writeMessageCalls != nil { + c.writeMessageCalls <- struct{}{} } return nil } -func (c *inspectableConnection) NextReader(_ io.Reader, _ ws.State) (ws.Header, io.Reader, error) { +func (c *inspectableConnection) NextReader() (ws.Header, io.Reader, error) { if c.nextReaderCalls != nil { c.nextReaderCalls <- struct{}{} } return c.nextReaderFunc() } +func (c *inspectableConnection) ReadMessage() (messageType ws.OpCode, p []byte, err error) { + if c.readMessageCalls != nil { + c.readMessageCalls <- struct{}{} + } + return c.readMessageFunc() +} + // staticConnFactory returns a static connection, for example // an inspectable connection. func staticConnFactory(conn rawConnection) func() (rawConnection, error) { @@ -155,7 +164,7 @@ func TestStatusOnNoConnection(t *testing.T) { func TestSendErrorOnEncode(t *testing.T) { spy := &inspectableConnection{ - writeClientMessageCalls: make(chan struct{}, 1), + writeMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -165,14 +174,14 @@ func TestSendErrorOnEncode(t *testing.T) { if got == nil { t.Fatal("Expected an error but got none") } - if len(spy.writeClientMessageCalls) != 0 { - t.Fatalf("Expected 'WriteClientMessage' not to be called, but was called %v times", spy.writeClientMessageCalls) + if len(spy.writeMessageCalls) != 0 { + t.Fatalf("Expected 'WriteClientMessage' not to be called, but was called %v times", spy.writeMessageCalls) } } func TestSendMessage(t *testing.T) { spy := &inspectableConnection{ - writeClientMessageCalls: make(chan struct{}, 1), + writeMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -184,14 +193,14 @@ func TestSendMessage(t *testing.T) { if got := conn.Send("test"); got != nil { t.Fatalf("Expected no error but got: %+v", got) } - if len(spy.writeClientMessageCalls) != 1 { - t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeClientMessageCalls) + if len(spy.writeMessageCalls) != 1 { + t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeMessageCalls) } } func TestSendRawMessage(t *testing.T) { spy := &inspectableConnection{ - writeClientMessageCalls: make(chan struct{}, 1), + writeMessageCalls: make(chan struct{}, 1), } conn := newConnection(staticConnFactory(spy), nil) conn.connect() @@ -203,8 +212,8 @@ func TestSendRawMessage(t *testing.T) { if got := conn.SendRaw(ws.OpBinary, []byte("test")); got != nil { t.Fatalf("Expected no error but got: %+v", got) } - if len(spy.writeClientMessageCalls) != 1 { - t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeClientMessageCalls) + if len(spy.writeMessageCalls) != 1 { + t.Fatalf("Expected 'WriteClientMessage' to be called once, but was called %v times", spy.writeMessageCalls) } } From cf655682c57af2b7368ae1c7a2647a61c8469beb Mon Sep 17 00:00:00 2001 From: moriya Date: Mon, 1 May 2023 20:38:21 +0900 Subject: [PATCH 4/4] remove_some_functions --- go.mod | 2 +- go.sum | 4 ++-- .../k8s.io/code-generator/generate-groups.sh | 0 vendor/modules.txt | 2 +- websocket/connection.go | 22 +------------------ websocket/connection_test.go | 15 +++---------- 6 files changed, 8 insertions(+), 37 deletions(-) mode change 100644 => 100755 vendor/k8s.io/code-generator/generate-groups.sh diff --git a/go.mod b/go.mod index a81a900c9a..7c5af3b62e 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9 k8s.io/klog/v2 v2.80.2-0.20221028030830-9ae4992afb54 k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 - knative.dev/hack v0.0.0-20230417170854-f591fea109b3 + knative.dev/hack v0.0.0-20230501013555-7d81248b4638 sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index 7add89ed4d..caa89d3d5b 100644 --- a/go.sum +++ b/go.sum @@ -966,8 +966,8 @@ k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkI k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/hack v0.0.0-20230417170854-f591fea109b3 h1:+W4WBOq83tfGXKhtv8OB/uJeYqze3zh69GKiz1ucuqk= -knative.dev/hack v0.0.0-20230417170854-f591fea109b3/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q= +knative.dev/hack v0.0.0-20230501013555-7d81248b4638 h1:9IuXHdwp5jNmIg+0LVTQr8o4u0FYD99uCfynM9tS0XY= +knative.dev/hack v0.0.0-20230501013555-7d81248b4638/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q= pgregory.net/rapid v0.3.3 h1:jCjBsY4ln4Atz78QoBWxUEvAHaFyNDQg9+WU62aCn1U= pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/k8s.io/code-generator/generate-groups.sh b/vendor/k8s.io/code-generator/generate-groups.sh old mode 100644 new mode 100755 diff --git a/vendor/modules.txt b/vendor/modules.txt index fe163c8438..75a49667aa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1128,7 +1128,7 @@ k8s.io/utils/net k8s.io/utils/pointer k8s.io/utils/strings/slices k8s.io/utils/trace -# knative.dev/hack v0.0.0-20230417170854-f591fea109b3 +# knative.dev/hack v0.0.0-20230501013555-7d81248b4638 ## explicit; go 1.18 knative.dev/hack # sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 diff --git a/websocket/connection.go b/websocket/connection.go index 4616e8380d..4d6e833dc4 100644 --- a/websocket/connection.go +++ b/websocket/connection.go @@ -57,20 +57,12 @@ type rawConnection interface { Write(p []byte) (n int, err error) WriteMessage(op ws.OpCode, p []byte) error NextReader() (ws.Header, io.Reader, error) - ReadMessage() (messageType ws.OpCode, p []byte, err error) } type NetConnExtension struct { conn net.Conn } -func NewNetConnExtension(conn net.Conn) *NetConnExtension { - nc := &NetConnExtension{ - conn: conn, - } - return nc -} - func (nc *NetConnExtension) Read(p []byte) (n int, err error) { return nc.conn.Read(p) } @@ -92,19 +84,7 @@ func (nc *NetConnExtension) WriteMessage(op ws.OpCode, p []byte) error { } func (nc *NetConnExtension) NextReader() (ws.Header, io.Reader, error) { - return wsutil.NextReader(nc, ws.StateServerSide) -} - -func (nc *NetConnExtension) ReadMessage() (messageType ws.OpCode, p []byte, err error) { - var r io.Reader - var header ws.Header - header, r, err = nc.NextReader() - messageType = header.OpCode - if err != nil { - return messageType, nil, err - } - p, err = io.ReadAll(r) - return messageType, p, err + return wsutil.NextReader(nc, ws.StateClientSide) } // ManagedConnection represents a websocket connection. diff --git a/websocket/connection_test.go b/websocket/connection_test.go index feef620c2e..6b8ef7745f 100644 --- a/websocket/connection_test.go +++ b/websocket/connection_test.go @@ -39,12 +39,10 @@ type inspectableConnection struct { setReadDeadlineCalls chan struct{} writeMessageCalls chan struct{} nextReaderCalls chan struct{} - readMessageCalls chan struct{} - readFunc func() (int, error) - writeFunc func() (int, error) - nextReaderFunc func() (ws.Header, io.Reader, error) - readMessageFunc func() (messageType ws.OpCode, p []byte, err error) + readFunc func() (int, error) + writeFunc func() (int, error) + nextReaderFunc func() (ws.Header, io.Reader, error) } func (c *inspectableConnection) Close() error { @@ -83,13 +81,6 @@ func (c *inspectableConnection) NextReader() (ws.Header, io.Reader, error) { return c.nextReaderFunc() } -func (c *inspectableConnection) ReadMessage() (messageType ws.OpCode, p []byte, err error) { - if c.readMessageCalls != nil { - c.readMessageCalls <- struct{}{} - } - return c.readMessageFunc() -} - // staticConnFactory returns a static connection, for example // an inspectable connection. func staticConnFactory(conn rawConnection) func() (rawConnection, error) {