├── http2 ├── .gitignore ├── h2demo │ ├── rootCA.srl │ ├── .gitignore │ ├── service.yaml │ ├── go.mod │ ├── deployment-prod.yaml │ ├── README │ ├── Dockerfile │ ├── server.crt │ ├── Makefile │ ├── rootCA.pem │ ├── rootCA.key │ ├── server.key │ └── go.sum ├── Makefile ├── not_go111.go ├── errors_test.go ├── README ├── go111.go ├── gotrack_test.go ├── flow.go ├── Dockerfile ├── writesched_random_test.go ├── headermap.go ├── flow_test.go ├── writesched_random.go ├── h2i │ ├── README.md │ └── h2i.go ├── h2c │ └── h2c_test.go ├── pipe_test.go ├── gotrack.go ├── writesched_test.go ├── databuffer.go ├── pipe.go ├── errors.go ├── databuffer_test.go ├── hpack │ ├── huffman.go │ ├── encode.go │ ├── tables_test.go │ ├── encode_test.go │ └── tables.go ├── http2_test.go ├── writesched.go ├── client_conn_pool.go ├── z_spec_test.go ├── http2.go ├── write.go └── ciphers_test.go ├── go.mod ├── .idea ├── .gitignore ├── modules.xml └── rushDetector.iml ├── README.md ├── main.go ├── go.sum ├── server.crt └── server.key /http2/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | h2i/h2i 3 | -------------------------------------------------------------------------------- /http2/h2demo/rootCA.srl: -------------------------------------------------------------------------------- 1 | E2CE26BF3285059C 2 | -------------------------------------------------------------------------------- /http2/Makefile: -------------------------------------------------------------------------------- 1 | curlimage: 2 | docker build -t gohttp2/curl . 3 | 4 | -------------------------------------------------------------------------------- /http2/h2demo/.gitignore: -------------------------------------------------------------------------------- 1 | h2demo 2 | h2demo.linux 3 | client-id.dat 4 | client-secret.dat 5 | token.dat 6 | ca-certificates.crt 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module rushDetector 2 | 3 | go 1.16 4 | 5 | require ( 6 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 7 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d 8 | ) 9 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /../../../../../../../:\Users\cc\go\src\rushDetector\.idea/dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rush-detector 2 | 3 | [the readme is basically in http2\server.go around line 1560.](https://github.com/x04/rush-detector/blob/main/http2/server.go#L1565-L1600) 4 | 5 | video: 6 | https://streamable.com/dfxze3 7 | 8 | ![](https://cdn.discordapp.com/attachments/552210716036300810/821761711425191956/unknown.png) 9 | -------------------------------------------------------------------------------- /.idea/rushDetector.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /http2/h2demo/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: h2demo 5 | spec: 6 | externalTrafficPolicy: Local 7 | ports: 8 | - port: 80 9 | targetPort: 80 10 | name: http 11 | - port: 443 12 | targetPort: 443 13 | name: https 14 | selector: 15 | app: h2demo 16 | type: LoadBalancer 17 | loadBalancerIP: 130.211.116.44 18 | -------------------------------------------------------------------------------- /http2/h2demo/go.mod: -------------------------------------------------------------------------------- 1 | module golang.org/x/net/http2/h2demo 2 | 3 | go 1.12 4 | 5 | require ( 6 | cloud.google.com/go v0.38.0 7 | go4.org v0.0.0-20190218023631-ce4c26f7be8e 8 | golang.org/x/build v0.0.0-20190509182522-45de920fc22c 9 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 10 | golang.org/x/net v0.0.0-20190502183928-7f726cade0ab 11 | ) 12 | 13 | replace golang.org/x/net => ../.. 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "rushDetector/http2" 7 | ) 8 | 9 | func main() { 10 | var srv http.Server 11 | srv.Addr = "0.0.0.0:443" 12 | http2.ConfigureServer(&srv, &http2.Server{}) 13 | 14 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 15 | _, _ = w.Write([]byte("hello not rush client!")) 16 | }) 17 | 18 | go func() { 19 | log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key")) 20 | }() 21 | select {} 22 | } 23 | -------------------------------------------------------------------------------- /http2/not_go111.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !go1.11 6 | // +build !go1.11 7 | 8 | package http2 9 | 10 | import ( 11 | "net/http/httptrace" 12 | "net/textproto" 13 | ) 14 | 15 | func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false } 16 | 17 | func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {} 18 | 19 | func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /http2/errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestErrCodeString(t *testing.T) { 10 | tests := []struct { 11 | err ErrCode 12 | want string 13 | }{ 14 | {ErrCodeProtocol, "PROTOCOL_ERROR"}, 15 | {0xd, "HTTP_1_1_REQUIRED"}, 16 | {0xf, "unknown error code 0xf"}, 17 | } 18 | for i, tt := range tests { 19 | got := tt.err.String() 20 | if got != tt.want { 21 | t.Errorf("%d. Error = %q; want %q", i, got, tt.want) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /http2/README: -------------------------------------------------------------------------------- 1 | This is a work-in-progress HTTP/2 implementation for Go. 2 | 3 | It will eventually live in the Go standard library and won't require 4 | any changes to your code to use. It will just be automatic. 5 | 6 | Status: 7 | 8 | * The server support is pretty good. A few things are missing 9 | but are being worked on. 10 | * The client work has just started but shares a lot of code 11 | is coming along much quicker. 12 | 13 | Docs are at https://godoc.org/golang.org/x/net/http2 14 | 15 | Demo test server at https://http2.golang.org/ 16 | 17 | Help & bug reports welcome! 18 | 19 | Contributing: https://golang.org/doc/contribute.html 20 | Bugs: https://golang.org/issue/new?title=x/net/http2:+ 21 | -------------------------------------------------------------------------------- /http2/go111.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build go1.11 6 | // +build go1.11 7 | 8 | package http2 9 | 10 | import ( 11 | "net/http/httptrace" 12 | "net/textproto" 13 | ) 14 | 15 | func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { 16 | return trace != nil && trace.WroteHeaderField != nil 17 | } 18 | 19 | func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { 20 | if trace != nil && trace.WroteHeaderField != nil { 21 | trace.WroteHeaderField(k, []string{v}) 22 | } 23 | } 24 | 25 | func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { 26 | if trace != nil { 27 | return trace.Got1xxResponse 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /http2/h2demo/deployment-prod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: h2demo-deployment 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: h2demo 11 | annotations: 12 | container.seccomp.security.alpha.kubernetes.io/h2demo: docker/default 13 | container.apparmor.security.beta.kubernetes.io/h2demo: runtime/default 14 | spec: 15 | containers: 16 | - name: h2demo 17 | image: gcr.io/symbolic-datum-552/h2demo:latest 18 | imagePullPolicy: Always 19 | command: ["/h2demo", "-prod"] 20 | ports: 21 | - containerPort: 80 22 | - containerPort: 443 23 | resources: 24 | requests: 25 | cpu: "1" 26 | memory: "1Gi" 27 | limits: 28 | memory: "2Gi" 29 | -------------------------------------------------------------------------------- /http2/h2demo/README: -------------------------------------------------------------------------------- 1 | This is a demo webserver that shows off Go's HTTP/2 support. 2 | 3 | It runs at https://http2.golang.org/ so people can hit our 4 | implementation with their HTTP/2 clients, etc. We intentionally do not 5 | run it behind any other HTTP implementation so clients (including 6 | people demonstrating attacks, etc) can hit our server directly. It 7 | just runs behind a TCP load balancer. 8 | 9 | When running locally, you'll need to click through TLS cert warnings. 10 | The dev cert was initially made like: 11 | 12 | Make CA: 13 | $ openssl genrsa -out rootCA.key 2048 14 | $ openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem 15 | 16 | Make cert: 17 | $ openssl genrsa -out server.key 2048 18 | $ openssl req -new -key server.key -out server.csr 19 | $ openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 20 | -------------------------------------------------------------------------------- /http2/gotrack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestGoroutineLock(t *testing.T) { 14 | oldDebug := DebugGoroutines 15 | DebugGoroutines = true 16 | defer func() { DebugGoroutines = oldDebug }() 17 | 18 | g := newGoroutineLock() 19 | g.check() 20 | 21 | sawPanic := make(chan interface{}) 22 | go func() { 23 | defer func() { sawPanic <- recover() }() 24 | g.check() // should panic 25 | }() 26 | e := <-sawPanic 27 | if e == nil { 28 | t.Fatal("did not see panic from check in other goroutine") 29 | } 30 | if !strings.Contains(fmt.Sprint(e), "wrong goroutine") { 31 | t.Errorf("expected on see panic about running on the wrong goroutine; got %v", e) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E= 2 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 3 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 4 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40= 5 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 6 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 7 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= 8 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 9 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 10 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 11 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 12 | -------------------------------------------------------------------------------- /http2/h2demo/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | FROM golang:1.13 AS build 6 | LABEL maintainer "golang-dev@googlegroups.com" 7 | 8 | ENV GO111MODULE=on 9 | 10 | RUN mkdir /gocache 11 | ENV GOCACHE /gocache 12 | 13 | COPY go.mod /go/src/golang.org/x/build/go.mod 14 | COPY go.sum /go/src/golang.org/x/build/go.sum 15 | 16 | # Optimization for iterative docker build speed, not necessary for correctness: 17 | # TODO: write a tool to make writing Go module-friendly Dockerfiles easier. 18 | RUN go install cloud.google.com/go/storage 19 | RUN go install golang.org/x/build/autocertcache 20 | RUN go install go4.org/syncutil/singleflight 21 | RUN go install golang.org/x/crypto/acme/autocert 22 | 23 | COPY . /go/src/golang.org/x/net 24 | 25 | # Install binary to /go/bin: 26 | WORKDIR /go/src/golang.org/x/net/http2/h2demo 27 | RUN go install -tags "netgo" 28 | 29 | 30 | FROM debian:stretch 31 | RUN apt-get update && apt-get install -y --no-install-recommends \ 32 | ca-certificates netbase \ 33 | && rm -rf /var/lib/apt/lists/* 34 | 35 | COPY --from=build /go/bin/h2demo /h2demo 36 | 37 | -------------------------------------------------------------------------------- /server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPjCCAiYCCQDizia/MoUFnDANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNVBAoT 4 | C0JyYWRmaXR6aW5jMRIwEAYDVQQDEwlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEW 5 | DmJyYWRAZGFuZ2EuY29tMB4XDTE0MDcxNTIwNTAyN1oXDTE1MTEyNzIwNTAyN1ow 6 | RzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQHEwJTRjEeMBwGA1UE 7 | ChMVYnJhZGZpdHogaHR0cDIgc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDifx2l 9 | gZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1LmJ4c2 10 | dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nefb3HL 11 | A7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55mjws 12 | /vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/fz88 13 | F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABMA0GCSqGSIb3DQEBBQUAA4IB 14 | AQC0zL+n/YpRZOdulSu9tS8FxrstXqGWoxfe+vIUgqfMZ5+0MkjJ/vW0FqlLDl2R 15 | rn4XaR3e7FmWkwdDVbq/UB6lPmoAaFkCgh9/5oapMaclNVNnfF3fjCJfRr+qj/iD 16 | EmJStTIN0ZuUjAlpiACmfnpEU55PafT5Zx+i1yE4FGjw8bJpFoyD4Hnm54nGjX19 17 | KeCuvcYFUPnBm3lcL0FalF2AjqV02WTHYNQk7YF/oeO7NKBoEgvGvKG3x+xaOeBI 18 | dwvdq175ZsGul30h+QjrRlXhH/twcuaT3GSdoysDl9cCYE8f1Mk8PD6gan3uBCJU 19 | 90p6/CbU71bGbfpM2PHot2fm 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /http2/h2demo/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPjCCAiYCCQDizia/MoUFnDANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNVBAoT 4 | C0JyYWRmaXR6aW5jMRIwEAYDVQQDEwlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEW 5 | DmJyYWRAZGFuZ2EuY29tMB4XDTE0MDcxNTIwNTAyN1oXDTE1MTEyNzIwNTAyN1ow 6 | RzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQHEwJTRjEeMBwGA1UE 7 | ChMVYnJhZGZpdHogaHR0cDIgc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDifx2l 9 | gZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1LmJ4c2 10 | dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nefb3HL 11 | A7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55mjws 12 | /vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/fz88 13 | F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABMA0GCSqGSIb3DQEBBQUAA4IB 14 | AQC0zL+n/YpRZOdulSu9tS8FxrstXqGWoxfe+vIUgqfMZ5+0MkjJ/vW0FqlLDl2R 15 | rn4XaR3e7FmWkwdDVbq/UB6lPmoAaFkCgh9/5oapMaclNVNnfF3fjCJfRr+qj/iD 16 | EmJStTIN0ZuUjAlpiACmfnpEU55PafT5Zx+i1yE4FGjw8bJpFoyD4Hnm54nGjX19 17 | KeCuvcYFUPnBm3lcL0FalF2AjqV02WTHYNQk7YF/oeO7NKBoEgvGvKG3x+xaOeBI 18 | dwvdq175ZsGul30h+QjrRlXhH/twcuaT3GSdoysDl9cCYE8f1Mk8PD6gan3uBCJU 19 | 90p6/CbU71bGbfpM2PHot2fm 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /http2/flow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Flow control 6 | 7 | package http2 8 | 9 | // flow is the flow control window's size. 10 | type flow struct { 11 | _ incomparable 12 | 13 | // n is the number of DATA bytes we're allowed to send. 14 | // A flow is kept both on a conn and a per-stream. 15 | n int32 16 | 17 | // conn points to the shared connection-level flow that is 18 | // shared by all streams on that conn. It is nil for the flow 19 | // that's on the conn directly. 20 | conn *flow 21 | } 22 | 23 | func (f *flow) setConnFlow(cf *flow) { f.conn = cf } 24 | 25 | func (f *flow) available() int32 { 26 | n := f.n 27 | if f.conn != nil && f.conn.n < n { 28 | n = f.conn.n 29 | } 30 | return n 31 | } 32 | 33 | func (f *flow) take(n int32) { 34 | if n > f.available() { 35 | panic("internal error: took too much") 36 | } 37 | f.n -= n 38 | if f.conn != nil { 39 | f.conn.n -= n 40 | } 41 | } 42 | 43 | // add adds n bytes (positive or negative) to the flow control window. 44 | // It returns false if the sum would exceed 2^31-1. 45 | func (f *flow) add(n int32) bool { 46 | sum := f.n + n 47 | if (sum > n) == (f.n > 0) { 48 | f.n = sum 49 | return true 50 | } 51 | return false 52 | } 53 | -------------------------------------------------------------------------------- /http2/h2demo/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | MUTABLE_VERSION ?= latest 6 | VERSION ?= $(shell git rev-parse --short HEAD) 7 | IMAGE_NAME=h2demo 8 | 9 | docker: Dockerfile 10 | go install golang.org/x/build/cmd/xb 11 | xb docker build -t golang/$(IMAGE_NAME) -f Dockerfile ../.. 12 | 13 | push-staging: docker 14 | go install golang.org/x/build/cmd/xb 15 | xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(VERSION) 16 | xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):latest 17 | xb --staging docker push REPO/$(IMAGE_NAME):$(VERSION) 18 | xb --staging docker push REPO/$(IMAGE_NAME):latest 19 | 20 | push-prod: docker 21 | go install golang.org/x/build/cmd/xb 22 | xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(VERSION) 23 | xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):latest 24 | xb --prod docker push REPO/$(IMAGE_NAME):$(VERSION) 25 | xb --prod docker push REPO/$(IMAGE_NAME):latest 26 | 27 | # TODO(bradfitz): add REPO subsitution in xb for the kubectl command. 28 | deploy-prod: push-prod 29 | xb --prod kubectl set image deployment/h2demo-deployment h2demo=gcr.io/symbolic-datum-552/h2demo:$(VERSION) 30 | deploy-staging: push-staging 31 | xb --staging kubectl set image deployment/h2demo-deployment h2demo=gcr.io/go-dashboard-dev/h2demo:$(VERSION) 32 | 33 | FORCE: 34 | -------------------------------------------------------------------------------- /http2/h2demo/rootCA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEWjCCA0KgAwIBAgIJALfRlWsI8YQHMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEUMBIG 4 | A1UEChMLQnJhZGZpdHppbmMxEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsGCSqGSIb3 5 | DQEJARYOYnJhZEBkYW5nYS5jb20wHhcNMTQwNzE1MjA0NjA1WhcNMTcwNTA0MjA0 6 | NjA1WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBG 7 | cmFuY2lzY28xFDASBgNVBAoTC0JyYWRmaXR6aW5jMRIwEAYDVQQDEwlsb2NhbGhv 8 | c3QxHTAbBgkqhkiG9w0BCQEWDmJyYWRAZGFuZ2EuY29tMIIBIjANBgkqhkiG9w0B 9 | AQEFAAOCAQ8AMIIBCgKCAQEAt5fAjp4fTcekWUTfzsp0kyih1OYbsGL0KX1eRbSS 10 | R8Od0+9Q62Hyny+GFwMTb4A/KU8mssoHvcceSAAbwfbxFK/+s51TobqUnORZrOoT 11 | ZjkUygbyXDSK99YBbcR1Pip8vwMTm4XKuLtCigeBBdjjAQdgUO28LENGlsMnmeYk 12 | JfODVGnVmr5Ltb9ANA8IKyTfsnHJ4iOCS/PlPbUj2q7YnoVLposUBMlgUb/CykX3 13 | mOoLb4yJJQyA/iST6ZxiIEj36D4yWZ5lg7YJl+UiiBQHGCnPdGyipqV06ex0heYW 14 | caiW8LWZSUQ93jQ+WVCH8hT7DQO1dmsvUmXlq/JeAlwQ/QIDAQABo4HgMIHdMB0G 15 | A1UdDgQWBBRcAROthS4P4U7vTfjByC569R7E6DCBrQYDVR0jBIGlMIGigBRcAROt 16 | hS4P4U7vTfjByC569R7E6KF/pH0wezELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNB 17 | MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRQwEgYDVQQKEwtCcmFkZml0emluYzES 18 | MBAGA1UEAxMJbG9jYWxob3N0MR0wGwYJKoZIhvcNAQkBFg5icmFkQGRhbmdhLmNv 19 | bYIJALfRlWsI8YQHMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAG6h 20 | U9f9sNH0/6oBbGGy2EVU0UgITUQIrFWo9rFkrW5k/XkDjQm+3lzjT0iGR4IxE/Ao 21 | eU6sQhua7wrWeFEn47GL98lnCsJdD7oZNhFmQ95Tb/LnDUjs5Yj9brP0NWzXfYU4 22 | UK2ZnINJRcJpB8iRCaCxE8DdcUF0XqIEq6pA272snoLmiXLMvNl3kYEdm+je6voD 23 | 58SNVEUsztzQyXmJEhCpwVI0A6QCjzXj+qvpmw3ZZHi8JwXei8ZZBLTSFBki8Z7n 24 | sH9BBH38/SzUmAN4QHSPy1gjqm00OAE8NaYDkh/bzE4d7mLGGMWp/WE3KPSu82HF 25 | kPe6XoSbiLm/kxk32T0= 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /http2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # This Dockerfile builds a recent curl with HTTP/2 client support, using 3 | # a recent nghttp2 build. 4 | # 5 | # See the Makefile for how to tag it. If Docker and that image is found, the 6 | # Go tests use this curl binary for integration tests. 7 | # 8 | 9 | FROM ubuntu:trusty 10 | 11 | RUN apt-get update && \ 12 | apt-get upgrade -y && \ 13 | apt-get install -y git-core build-essential wget 14 | 15 | RUN apt-get install -y --no-install-recommends \ 16 | autotools-dev libtool pkg-config zlib1g-dev \ 17 | libcunit1-dev libssl-dev libxml2-dev libevent-dev \ 18 | automake autoconf 19 | 20 | # The list of packages nghttp2 recommends for h2load: 21 | RUN apt-get install -y --no-install-recommends make binutils \ 22 | autoconf automake autotools-dev \ 23 | libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev \ 24 | libev-dev libevent-dev libjansson-dev libjemalloc-dev \ 25 | cython python3.4-dev python-setuptools 26 | 27 | # Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached: 28 | ENV NGHTTP2_VER 895da9a 29 | RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git 30 | 31 | WORKDIR /root/nghttp2 32 | RUN git reset --hard $NGHTTP2_VER 33 | RUN autoreconf -i 34 | RUN automake 35 | RUN autoconf 36 | RUN ./configure 37 | RUN make 38 | RUN make install 39 | 40 | WORKDIR /root 41 | RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz 42 | RUN tar -zxvf curl-7.45.0.tar.gz 43 | WORKDIR /root/curl-7.45.0 44 | RUN ./configure --with-ssl --with-nghttp2=/usr/local 45 | RUN make 46 | RUN make install 47 | RUN ldconfig 48 | 49 | CMD ["-h"] 50 | ENTRYPOINT ["/usr/local/bin/curl"] 51 | 52 | -------------------------------------------------------------------------------- /server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDi 3 | fx2lgZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1Lm 4 | J4c2dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nef 5 | b3HLA7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55 6 | mjws/vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/ 7 | fz88F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABAoIBADQ2spUwbY+bcz4p 8 | 3M66ECrNQTBggP40gYl2XyHxGGOu2xhZ94f9ELf1hjRWU2DUKWco1rJcdZClV6q3 9 | qwmXvcM2Q/SMS8JW0ImkNVl/0/NqPxGatEnj8zY30d/L8hGFb0orzFu/XYA5gCP4 10 | NbN2WrXgk3ZLeqwcNxHHtSiJWGJ/fPyeDWAu/apy75u9Xf2GlzBZmV6HYD9EfK80 11 | LTlI60f5FO487CrJnboL7ovPJrIHn+k05xRQqwma4orpz932rTXnTjs9Lg6KtbQN 12 | a7PrqfAntIISgr11a66Mng3IYH1lYqJsWJJwX/xHT4WLEy0EH4/0+PfYemJekz2+ 13 | Co62drECgYEA6O9zVJZXrLSDsIi54cfxA7nEZWm5CAtkYWeAHa4EJ+IlZ7gIf9sL 14 | W8oFcEfFGpvwVqWZ+AsQ70dsjXAv3zXaG0tmg9FtqWp7pzRSMPidifZcQwWkKeTO 15 | gJnFmnVyed8h6GfjTEu4gxo1/S5U0V+mYSha01z5NTnN6ltKx1Or3b0CgYEAxRgm 16 | S30nZxnyg/V7ys61AZhst1DG2tkZXEMcA7dYhabMoXPJAP/EfhlWwpWYYUs/u0gS 17 | Wwmf5IivX5TlYScgmkvb/NYz0u4ZmOXkLTnLPtdKKFXhjXJcHjUP67jYmOxNlJLp 18 | V4vLRnFxTpffAV+OszzRxsXX6fvruwZBANYJeXUCgYBVouLFsFgfWGYp2rpr9XP4 19 | KK25kvrBqF6JKOIDB1zjxNJ3pUMKrl8oqccCFoCyXa4oTM2kUX0yWxHfleUjrMq4 20 | yimwQKiOZmV7fVLSSjSw6e/VfBd0h3gb82ygcplZkN0IclkwTY5SNKqwn/3y07V5 21 | drqdhkrgdJXtmQ6O5YYECQKBgATERcDToQ1USlI4sKrB/wyv1AlG8dg/IebiVJ4e 22 | ZAyvcQmClFzq0qS+FiQUnB/WQw9TeeYrwGs1hxBHuJh16srwhLyDrbMvQP06qh8R 23 | 48F8UXXSRec22dV9MQphaROhu2qZdv1AC0WD3tqov6L33aqmEOi+xi8JgbT/PLk5 24 | c/c1AoGBAI1A/02ryksW6/wc7/6SP2M2rTy4m1sD/GnrTc67EHnRcVBdKO6qH2RY 25 | nqC8YcveC2ZghgPTDsA3VGuzuBXpwY6wTyV99q6jxQJ6/xcrD9/NUG6Uwv/xfCxl 26 | IJLeBYEqQundSSny3VtaAUK8Ul1nxpTvVRNwtcyWTo8RHAAyNPWd 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /http2/h2demo/rootCA.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAt5fAjp4fTcekWUTfzsp0kyih1OYbsGL0KX1eRbSSR8Od0+9Q 3 | 62Hyny+GFwMTb4A/KU8mssoHvcceSAAbwfbxFK/+s51TobqUnORZrOoTZjkUygby 4 | XDSK99YBbcR1Pip8vwMTm4XKuLtCigeBBdjjAQdgUO28LENGlsMnmeYkJfODVGnV 5 | mr5Ltb9ANA8IKyTfsnHJ4iOCS/PlPbUj2q7YnoVLposUBMlgUb/CykX3mOoLb4yJ 6 | JQyA/iST6ZxiIEj36D4yWZ5lg7YJl+UiiBQHGCnPdGyipqV06ex0heYWcaiW8LWZ 7 | SUQ93jQ+WVCH8hT7DQO1dmsvUmXlq/JeAlwQ/QIDAQABAoIBAFFHV7JMAqPWnMYA 8 | nezY6J81v9+XN+7xABNWM2Q8uv4WdksbigGLTXR3/680Z2hXqJ7LMeC5XJACFT/e 9 | /Gr0vmpgOCygnCPfjGehGKpavtfksXV3edikUlnCXsOP1C//c1bFL+sMYmFCVgTx 10 | qYdDK8yKzXNGrKYT6q5YG7IglyRNV1rsQa8lM/5taFYiD1Ck/3tQi3YIq8Lcuser 11 | hrxsMABcQ6mi+EIvG6Xr4mfJug0dGJMHG4RG1UGFQn6RXrQq2+q53fC8ZbVUSi0j 12 | NQ918aKFzktwv+DouKU0ME4I9toks03gM860bAL7zCbKGmwR3hfgX/TqzVCWpG9E 13 | LDVfvekCgYEA8fk9N53jbBRmULUGEf4qWypcLGiZnNU0OeXWpbPV9aa3H0VDytA7 14 | 8fCN2dPAVDPqlthMDdVe983NCNwp2Yo8ZimDgowyIAKhdC25s1kejuaiH9OAPj3c 15 | 0f8KbriYX4n8zNHxFwK6Ae3pQ6EqOLJVCUsziUaZX9nyKY5aZlyX6xcCgYEAwjws 16 | K62PjC64U5wYddNLp+kNdJ4edx+a7qBb3mEgPvSFT2RO3/xafJyG8kQB30Mfstjd 17 | bRxyUV6N0vtX1zA7VQtRUAvfGCecpMo+VQZzcHXKzoRTnQ7eZg4Lmj5fQ9tOAKAo 18 | QCVBoSW/DI4PZL26CAMDcAba4Pa22ooLapoRIQsCgYA6pIfkkbxLNkpxpt2YwLtt 19 | Kr/590O7UaR9n6k8sW/aQBRDXNsILR1KDl2ifAIxpf9lnXgZJiwE7HiTfCAcW7c1 20 | nzwDCI0hWuHcMTS/NYsFYPnLsstyyjVZI3FY0h4DkYKV9Q9z3zJLQ2hz/nwoD3gy 21 | b2pHC7giFcTts1VPV4Nt8wKBgHeFn4ihHJweg76vZz3Z78w7VNRWGFklUalVdDK7 22 | gaQ7w2y/ROn/146mo0OhJaXFIFRlrpvdzVrU3GDf2YXJYDlM5ZRkObwbZADjksev 23 | WInzcgDy3KDg7WnPasRXbTfMU4t/AkW2p1QKbi3DnSVYuokDkbH2Beo45vxDxhKr 24 | C69RAoGBAIyo3+OJenoZmoNzNJl2WPW5MeBUzSh8T/bgyjFTdqFHF5WiYRD/lfHj 25 | x9Glyw2nutuT4hlOqHvKhgTYdDMsF2oQ72fe3v8Q5FU7FuKndNPEAyvKNXZaShVA 26 | hnlhv5DjXKb0wFWnt5PCCiQLtzG0yyHaITrrEme7FikkIcTxaX/Y 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /http2/h2demo/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDi 3 | fx2lgZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1Lm 4 | J4c2dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nef 5 | b3HLA7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55 6 | mjws/vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/ 7 | fz88F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABAoIBADQ2spUwbY+bcz4p 8 | 3M66ECrNQTBggP40gYl2XyHxGGOu2xhZ94f9ELf1hjRWU2DUKWco1rJcdZClV6q3 9 | qwmXvcM2Q/SMS8JW0ImkNVl/0/NqPxGatEnj8zY30d/L8hGFb0orzFu/XYA5gCP4 10 | NbN2WrXgk3ZLeqwcNxHHtSiJWGJ/fPyeDWAu/apy75u9Xf2GlzBZmV6HYD9EfK80 11 | LTlI60f5FO487CrJnboL7ovPJrIHn+k05xRQqwma4orpz932rTXnTjs9Lg6KtbQN 12 | a7PrqfAntIISgr11a66Mng3IYH1lYqJsWJJwX/xHT4WLEy0EH4/0+PfYemJekz2+ 13 | Co62drECgYEA6O9zVJZXrLSDsIi54cfxA7nEZWm5CAtkYWeAHa4EJ+IlZ7gIf9sL 14 | W8oFcEfFGpvwVqWZ+AsQ70dsjXAv3zXaG0tmg9FtqWp7pzRSMPidifZcQwWkKeTO 15 | gJnFmnVyed8h6GfjTEu4gxo1/S5U0V+mYSha01z5NTnN6ltKx1Or3b0CgYEAxRgm 16 | S30nZxnyg/V7ys61AZhst1DG2tkZXEMcA7dYhabMoXPJAP/EfhlWwpWYYUs/u0gS 17 | Wwmf5IivX5TlYScgmkvb/NYz0u4ZmOXkLTnLPtdKKFXhjXJcHjUP67jYmOxNlJLp 18 | V4vLRnFxTpffAV+OszzRxsXX6fvruwZBANYJeXUCgYBVouLFsFgfWGYp2rpr9XP4 19 | KK25kvrBqF6JKOIDB1zjxNJ3pUMKrl8oqccCFoCyXa4oTM2kUX0yWxHfleUjrMq4 20 | yimwQKiOZmV7fVLSSjSw6e/VfBd0h3gb82ygcplZkN0IclkwTY5SNKqwn/3y07V5 21 | drqdhkrgdJXtmQ6O5YYECQKBgATERcDToQ1USlI4sKrB/wyv1AlG8dg/IebiVJ4e 22 | ZAyvcQmClFzq0qS+FiQUnB/WQw9TeeYrwGs1hxBHuJh16srwhLyDrbMvQP06qh8R 23 | 48F8UXXSRec22dV9MQphaROhu2qZdv1AC0WD3tqov6L33aqmEOi+xi8JgbT/PLk5 24 | c/c1AoGBAI1A/02ryksW6/wc7/6SP2M2rTy4m1sD/GnrTc67EHnRcVBdKO6qH2RY 25 | nqC8YcveC2ZghgPTDsA3VGuzuBXpwY6wTyV99q6jxQJ6/xcrD9/NUG6Uwv/xfCxl 26 | IJLeBYEqQundSSny3VtaAUK8Ul1nxpTvVRNwtcyWTo8RHAAyNPWd 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /http2/writesched_random_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestRandomScheduler(t *testing.T) { 10 | ws := NewRandomWriteScheduler() 11 | ws.Push(makeWriteHeadersRequest(3)) 12 | ws.Push(makeWriteHeadersRequest(4)) 13 | ws.Push(makeWriteHeadersRequest(1)) 14 | ws.Push(makeWriteHeadersRequest(2)) 15 | ws.Push(makeWriteNonStreamRequest()) 16 | ws.Push(makeWriteNonStreamRequest()) 17 | 18 | // Pop all frames. Should get the non-stream requests first, 19 | // followed by the stream requests in any order. 20 | var order []FrameWriteRequest 21 | for { 22 | wr, ok := ws.Pop() 23 | if !ok { 24 | break 25 | } 26 | order = append(order, wr) 27 | } 28 | t.Logf("got frames: %v", order) 29 | if len(order) != 6 { 30 | t.Fatalf("got %d frames, expected 6", len(order)) 31 | } 32 | if order[0].StreamID() != 0 || order[1].StreamID() != 0 { 33 | t.Fatal("expected non-stream frames first", order[0], order[1]) 34 | } 35 | got := make(map[uint32]bool) 36 | for _, wr := range order[2:] { 37 | got[wr.StreamID()] = true 38 | } 39 | for id := uint32(1); id <= 4; id++ { 40 | if !got[id] { 41 | t.Errorf("frame not found for stream %d", id) 42 | } 43 | } 44 | 45 | // Verify that we clean up maps for empty queues in all cases (golang.org/issue/33812) 46 | const arbitraryStreamID = 123 47 | ws.Push(makeHandlerPanicRST(arbitraryStreamID)) 48 | rws := ws.(*randomWriteScheduler) 49 | if got, want := len(rws.sq), 1; got != want { 50 | t.Fatalf("len of 123 stream = %v; want %v", got, want) 51 | } 52 | _, ok := ws.Pop() 53 | if !ok { 54 | t.Fatal("expected to be able to Pop") 55 | } 56 | if got, want := len(rws.sq), 0; got != want { 57 | t.Fatalf("len of 123 stream = %v; want %v", got, want) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /http2/headermap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "net/http" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | var ( 14 | commonBuildOnce sync.Once 15 | commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case 16 | commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case 17 | ) 18 | 19 | func buildCommonHeaderMapsOnce() { 20 | commonBuildOnce.Do(buildCommonHeaderMaps) 21 | } 22 | 23 | func buildCommonHeaderMaps() { 24 | common := []string{ 25 | "accept", 26 | "accept-charset", 27 | "accept-encoding", 28 | "accept-language", 29 | "accept-ranges", 30 | "age", 31 | "access-control-allow-origin", 32 | "allow", 33 | "authorization", 34 | "cache-control", 35 | "content-disposition", 36 | "content-encoding", 37 | "content-language", 38 | "content-length", 39 | "content-location", 40 | "content-range", 41 | "content-type", 42 | "cookie", 43 | "date", 44 | "etag", 45 | "expect", 46 | "expires", 47 | "from", 48 | "host", 49 | "if-match", 50 | "if-modified-since", 51 | "if-none-match", 52 | "if-unmodified-since", 53 | "last-modified", 54 | "link", 55 | "location", 56 | "max-forwards", 57 | "proxy-authenticate", 58 | "proxy-authorization", 59 | "range", 60 | "referer", 61 | "refresh", 62 | "retry-after", 63 | "server", 64 | "set-cookie", 65 | "strict-transport-security", 66 | "trailer", 67 | "transfer-encoding", 68 | "user-agent", 69 | "vary", 70 | "via", 71 | "www-authenticate", 72 | } 73 | commonLowerHeader = make(map[string]string, len(common)) 74 | commonCanonHeader = make(map[string]string, len(common)) 75 | for _, v := range common { 76 | chk := http.CanonicalHeaderKey(v) 77 | commonLowerHeader[chk] = v 78 | commonCanonHeader[v] = chk 79 | } 80 | } 81 | 82 | func lowerHeader(v string) string { 83 | buildCommonHeaderMapsOnce() 84 | if s, ok := commonLowerHeader[v]; ok { 85 | return s 86 | } 87 | return strings.ToLower(v) 88 | } 89 | -------------------------------------------------------------------------------- /http2/flow_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestFlow(t *testing.T) { 10 | var st flow 11 | var conn flow 12 | st.add(3) 13 | conn.add(2) 14 | 15 | if got, want := st.available(), int32(3); got != want { 16 | t.Errorf("available = %d; want %d", got, want) 17 | } 18 | st.setConnFlow(&conn) 19 | if got, want := st.available(), int32(2); got != want { 20 | t.Errorf("after parent setup, available = %d; want %d", got, want) 21 | } 22 | 23 | st.take(2) 24 | if got, want := conn.available(), int32(0); got != want { 25 | t.Errorf("after taking 2, conn = %d; want %d", got, want) 26 | } 27 | if got, want := st.available(), int32(0); got != want { 28 | t.Errorf("after taking 2, stream = %d; want %d", got, want) 29 | } 30 | } 31 | 32 | func TestFlowAdd(t *testing.T) { 33 | var f flow 34 | if !f.add(1) { 35 | t.Fatal("failed to add 1") 36 | } 37 | if !f.add(-1) { 38 | t.Fatal("failed to add -1") 39 | } 40 | if got, want := f.available(), int32(0); got != want { 41 | t.Fatalf("size = %d; want %d", got, want) 42 | } 43 | if !f.add(1<<31 - 1) { 44 | t.Fatal("failed to add 2^31-1") 45 | } 46 | if got, want := f.available(), int32(1<<31-1); got != want { 47 | t.Fatalf("size = %d; want %d", got, want) 48 | } 49 | if f.add(1) { 50 | t.Fatal("adding 1 to max shouldn't be allowed") 51 | } 52 | } 53 | 54 | func TestFlowAddOverflow(t *testing.T) { 55 | var f flow 56 | if !f.add(0) { 57 | t.Fatal("failed to add 0") 58 | } 59 | if !f.add(-1) { 60 | t.Fatal("failed to add -1") 61 | } 62 | if !f.add(0) { 63 | t.Fatal("failed to add 0") 64 | } 65 | if !f.add(1) { 66 | t.Fatal("failed to add 1") 67 | } 68 | if !f.add(1) { 69 | t.Fatal("failed to add 1") 70 | } 71 | if !f.add(0) { 72 | t.Fatal("failed to add 0") 73 | } 74 | if !f.add(-3) { 75 | t.Fatal("failed to add -3") 76 | } 77 | if got, want := f.available(), int32(-2); got != want { 78 | t.Fatalf("size = %d; want %d", got, want) 79 | } 80 | if !f.add(1<<31 - 1) { 81 | t.Fatal("failed to add 2^31-1") 82 | } 83 | if got, want := f.available(), int32(1+-3+(1<<31-1)); got != want { 84 | t.Fatalf("size = %d; want %d", got, want) 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /http2/writesched_random.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "math" 8 | 9 | // NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2 10 | // priorities. Control frames like SETTINGS and PING are written before DATA 11 | // frames, but if no control frames are queued and multiple streams have queued 12 | // HEADERS or DATA frames, Pop selects a ready stream arbitrarily. 13 | func NewRandomWriteScheduler() WriteScheduler { 14 | return &randomWriteScheduler{sq: make(map[uint32]*writeQueue)} 15 | } 16 | 17 | type randomWriteScheduler struct { 18 | // zero are frames not associated with a specific stream. 19 | zero writeQueue 20 | 21 | // sq contains the stream-specific queues, keyed by stream ID. 22 | // When a stream is idle, closed, or emptied, it's deleted 23 | // from the map. 24 | sq map[uint32]*writeQueue 25 | 26 | // pool of empty queues for reuse. 27 | queuePool writeQueuePool 28 | } 29 | 30 | func (ws *randomWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) { 31 | // no-op: idle streams are not tracked 32 | } 33 | 34 | func (ws *randomWriteScheduler) CloseStream(streamID uint32) { 35 | q, ok := ws.sq[streamID] 36 | if !ok { 37 | return 38 | } 39 | delete(ws.sq, streamID) 40 | ws.queuePool.put(q) 41 | } 42 | 43 | func (ws *randomWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) { 44 | // no-op: priorities are ignored 45 | } 46 | 47 | func (ws *randomWriteScheduler) Push(wr FrameWriteRequest) { 48 | id := wr.StreamID() 49 | if id == 0 { 50 | ws.zero.push(wr) 51 | return 52 | } 53 | q, ok := ws.sq[id] 54 | if !ok { 55 | q = ws.queuePool.get() 56 | ws.sq[id] = q 57 | } 58 | q.push(wr) 59 | } 60 | 61 | func (ws *randomWriteScheduler) Pop() (FrameWriteRequest, bool) { 62 | // Control frames first. 63 | if !ws.zero.empty() { 64 | return ws.zero.shift(), true 65 | } 66 | // Iterate over all non-idle streams until finding one that can be consumed. 67 | for streamID, q := range ws.sq { 68 | if wr, ok := q.consume(math.MaxInt32); ok { 69 | if q.empty() { 70 | delete(ws.sq, streamID) 71 | ws.queuePool.put(q) 72 | } 73 | return wr, true 74 | } 75 | } 76 | return FrameWriteRequest{}, false 77 | } 78 | -------------------------------------------------------------------------------- /http2/h2i/README.md: -------------------------------------------------------------------------------- 1 | # h2i 2 | 3 | **h2i** is an interactive HTTP/2 ("h2") console debugger. Miss the good ol' 4 | days of telnetting to your HTTP/1.n servers? We're bringing you 5 | back. 6 | 7 | Features: 8 | - send raw HTTP/2 frames 9 | - PING 10 | - SETTINGS 11 | - HEADERS 12 | - etc 13 | - type in HTTP/1.n and have it auto-HPACK/frame-ify it for HTTP/2 14 | - pretty print all received HTTP/2 frames from the peer (including HPACK decoding) 15 | - tab completion of commands, options 16 | 17 | Not yet features, but soon: 18 | - unnecessary CONTINUATION frames on short boundaries, to test peer implementations 19 | - request bodies (DATA frames) 20 | - send invalid frames for testing server implementations (supported by underlying Framer) 21 | 22 | Later: 23 | - act like a server 24 | 25 | ## Installation 26 | 27 | ``` 28 | $ go get golang.org/x/net/http2/h2i 29 | $ h2i 30 | ``` 31 | 32 | ## Demo 33 | 34 | ``` 35 | $ h2i 36 | Usage: h2i 37 | 38 | -insecure 39 | Whether to skip TLS cert validation 40 | -nextproto string 41 | Comma-separated list of NPN/ALPN protocol names to negotiate. (default "h2,h2-14") 42 | 43 | $ h2i google.com 44 | Connecting to google.com:443 ... 45 | Connected to 74.125.224.41:443 46 | Negotiated protocol "h2-14" 47 | [FrameHeader SETTINGS len=18] 48 | [MAX_CONCURRENT_STREAMS = 100] 49 | [INITIAL_WINDOW_SIZE = 1048576] 50 | [MAX_FRAME_SIZE = 16384] 51 | [FrameHeader WINDOW_UPDATE len=4] 52 | Window-Increment = 983041 53 | 54 | h2i> PING h2iSayHI 55 | [FrameHeader PING flags=ACK len=8] 56 | Data = "h2iSayHI" 57 | h2i> headers 58 | (as HTTP/1.1)> GET / HTTP/1.1 59 | (as HTTP/1.1)> Host: ip.appspot.com 60 | (as HTTP/1.1)> User-Agent: h2i/brad-n-blake 61 | (as HTTP/1.1)> 62 | Opening Stream-ID 1: 63 | :authority = ip.appspot.com 64 | :method = GET 65 | :path = / 66 | :scheme = https 67 | user-agent = h2i/brad-n-blake 68 | [FrameHeader HEADERS flags=END_HEADERS stream=1 len=77] 69 | :status = "200" 70 | alternate-protocol = "443:quic,p=1" 71 | content-length = "15" 72 | content-type = "text/html" 73 | date = "Fri, 01 May 2015 23:06:56 GMT" 74 | server = "Google Frontend" 75 | [FrameHeader DATA flags=END_STREAM stream=1 len=15] 76 | "173.164.155.78\n" 77 | [FrameHeader PING len=8] 78 | Data = "\x00\x00\x00\x00\x00\x00\x00\x00" 79 | h2i> ping 80 | [FrameHeader PING flags=ACK len=8] 81 | Data = "h2i_ping" 82 | h2i> ping 83 | [FrameHeader PING flags=ACK len=8] 84 | Data = "h2i_ping" 85 | h2i> ping 86 | [FrameHeader GOAWAY len=22] 87 | Last-Stream-ID = 1; Error-Code = PROTOCOL_ERROR (1) 88 | 89 | ReadFrame: EOF 90 | ``` 91 | 92 | ## Status 93 | 94 | Quick few hour hack. So much yet to do. Feel free to file issues for 95 | bugs or wishlist items, but [@bmizerany](https://github.com/bmizerany/) 96 | and I aren't yet accepting pull requests until things settle down. 97 | 98 | -------------------------------------------------------------------------------- /http2/h2c/h2c_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package h2c 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "context" 11 | "crypto/tls" 12 | "fmt" 13 | "io/ioutil" 14 | "log" 15 | "net" 16 | "net/http" 17 | "net/http/httptest" 18 | "testing" 19 | 20 | "golang.org/x/net/http2" 21 | ) 22 | 23 | func TestSettingsAckSwallowWriter(t *testing.T) { 24 | var buf bytes.Buffer 25 | swallower := newSettingsAckSwallowWriter(bufio.NewWriter(&buf)) 26 | fw := http2.NewFramer(swallower, nil) 27 | fw.WriteSettings(http2.Setting{http2.SettingMaxFrameSize, 2}) 28 | fw.WriteSettingsAck() 29 | fw.WriteData(1, true, []byte{}) 30 | swallower.Flush() 31 | 32 | fr := http2.NewFramer(nil, bufio.NewReader(&buf)) 33 | 34 | f, err := fr.ReadFrame() 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | if f.Header().Type != http2.FrameSettings { 39 | t.Fatalf("Expected first frame to be SETTINGS. Got: %v", f.Header().Type) 40 | } 41 | 42 | f, err = fr.ReadFrame() 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | if f.Header().Type != http2.FrameData { 47 | t.Fatalf("Expected first frame to be DATA. Got: %v", f.Header().Type) 48 | } 49 | } 50 | 51 | func ExampleNewHandler() { 52 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 53 | fmt.Fprint(w, "Hello world") 54 | }) 55 | h2s := &http2.Server{ 56 | // ... 57 | } 58 | h1s := &http.Server{ 59 | Addr: ":8080", 60 | Handler: NewHandler(handler, h2s), 61 | } 62 | log.Fatal(h1s.ListenAndServe()) 63 | } 64 | 65 | func TestContext(t *testing.T) { 66 | baseCtx := context.WithValue(context.Background(), "testkey", "testvalue") 67 | 68 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 | if r.ProtoMajor != 2 { 70 | t.Errorf("Request wasn't handled by h2c. Got ProtoMajor=%v", r.ProtoMajor) 71 | } 72 | if r.Context().Value("testkey") != "testvalue" { 73 | t.Errorf("Request doesn't have expected base context: %v", r.Context()) 74 | } 75 | fmt.Fprint(w, "Hello world") 76 | }) 77 | 78 | h2s := &http2.Server{} 79 | h1s := httptest.NewUnstartedServer(NewHandler(handler, h2s)) 80 | h1s.Config.BaseContext = func(_ net.Listener) context.Context { 81 | return baseCtx 82 | } 83 | h1s.Start() 84 | defer h1s.Close() 85 | 86 | client := &http.Client{ 87 | Transport: &http2.Transport{ 88 | AllowHTTP: true, 89 | DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { 90 | return net.Dial(network, addr) 91 | }, 92 | }, 93 | } 94 | 95 | resp, err := client.Get(h1s.URL) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | _, err = ioutil.ReadAll(resp.Body) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | if err := resp.Body.Close(); err != nil { 104 | t.Fatal(err) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /http2/pipe_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "io" 11 | "io/ioutil" 12 | "testing" 13 | ) 14 | 15 | func TestPipeClose(t *testing.T) { 16 | var p pipe 17 | p.b = new(bytes.Buffer) 18 | a := errors.New("a") 19 | b := errors.New("b") 20 | p.CloseWithError(a) 21 | p.CloseWithError(b) 22 | _, err := p.Read(make([]byte, 1)) 23 | if err != a { 24 | t.Errorf("err = %v want %v", err, a) 25 | } 26 | } 27 | 28 | func TestPipeDoneChan(t *testing.T) { 29 | var p pipe 30 | done := p.Done() 31 | select { 32 | case <-done: 33 | t.Fatal("done too soon") 34 | default: 35 | } 36 | p.CloseWithError(io.EOF) 37 | select { 38 | case <-done: 39 | default: 40 | t.Fatal("should be done") 41 | } 42 | } 43 | 44 | func TestPipeDoneChan_ErrFirst(t *testing.T) { 45 | var p pipe 46 | p.CloseWithError(io.EOF) 47 | done := p.Done() 48 | select { 49 | case <-done: 50 | default: 51 | t.Fatal("should be done") 52 | } 53 | } 54 | 55 | func TestPipeDoneChan_Break(t *testing.T) { 56 | var p pipe 57 | done := p.Done() 58 | select { 59 | case <-done: 60 | t.Fatal("done too soon") 61 | default: 62 | } 63 | p.BreakWithError(io.EOF) 64 | select { 65 | case <-done: 66 | default: 67 | t.Fatal("should be done") 68 | } 69 | } 70 | 71 | func TestPipeDoneChan_Break_ErrFirst(t *testing.T) { 72 | var p pipe 73 | p.BreakWithError(io.EOF) 74 | done := p.Done() 75 | select { 76 | case <-done: 77 | default: 78 | t.Fatal("should be done") 79 | } 80 | } 81 | 82 | func TestPipeCloseWithError(t *testing.T) { 83 | p := &pipe{b: new(bytes.Buffer)} 84 | const body = "foo" 85 | io.WriteString(p, body) 86 | a := errors.New("test error") 87 | p.CloseWithError(a) 88 | all, err := ioutil.ReadAll(p) 89 | if string(all) != body { 90 | t.Errorf("read bytes = %q; want %q", all, body) 91 | } 92 | if err != a { 93 | t.Logf("read error = %v, %v", err, a) 94 | } 95 | if p.Len() != 0 { 96 | t.Errorf("pipe should have 0 unread bytes") 97 | } 98 | // Read and Write should fail. 99 | if n, err := p.Write([]byte("abc")); err != errClosedPipeWrite || n != 0 { 100 | t.Errorf("Write(abc) after close\ngot %v, %v\nwant 0, %v", n, err, errClosedPipeWrite) 101 | } 102 | if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 { 103 | t.Errorf("Read() after close\ngot %v, nil\nwant 0, %v", n, errClosedPipeWrite) 104 | } 105 | if p.Len() != 0 { 106 | t.Errorf("pipe should have 0 unread bytes") 107 | } 108 | } 109 | 110 | func TestPipeBreakWithError(t *testing.T) { 111 | p := &pipe{b: new(bytes.Buffer)} 112 | io.WriteString(p, "foo") 113 | a := errors.New("test err") 114 | p.BreakWithError(a) 115 | all, err := ioutil.ReadAll(p) 116 | if string(all) != "" { 117 | t.Errorf("read bytes = %q; want empty string", all) 118 | } 119 | if err != a { 120 | t.Logf("read error = %v, %v", err, a) 121 | } 122 | if p.b != nil { 123 | t.Errorf("buffer should be nil after BreakWithError") 124 | } 125 | if p.Len() != 3 { 126 | t.Errorf("pipe should have 3 unread bytes") 127 | } 128 | // Write should succeed silently. 129 | if n, err := p.Write([]byte("abc")); err != nil || n != 3 { 130 | t.Errorf("Write(abc) after break\ngot %v, %v\nwant 0, nil", n, err) 131 | } 132 | if p.b != nil { 133 | t.Errorf("buffer should be nil after Write") 134 | } 135 | if p.Len() != 6 { 136 | t.Errorf("pipe should have 6 unread bytes") 137 | } 138 | // Read should fail. 139 | if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 { 140 | t.Errorf("Read() after close\ngot %v, nil\nwant 0, not nil", n) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /http2/gotrack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Defensive debug-only utility to track that functions run on the 6 | // goroutine that they're supposed to. 7 | 8 | package http2 9 | 10 | import ( 11 | "bytes" 12 | "errors" 13 | "fmt" 14 | "os" 15 | "runtime" 16 | "strconv" 17 | "sync" 18 | ) 19 | 20 | var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" 21 | 22 | type goroutineLock uint64 23 | 24 | func newGoroutineLock() goroutineLock { 25 | if !DebugGoroutines { 26 | return 0 27 | } 28 | return goroutineLock(curGoroutineID()) 29 | } 30 | 31 | func (g goroutineLock) check() { 32 | if !DebugGoroutines { 33 | return 34 | } 35 | if curGoroutineID() != uint64(g) { 36 | panic("running on the wrong goroutine") 37 | } 38 | } 39 | 40 | func (g goroutineLock) checkNotOn() { 41 | if !DebugGoroutines { 42 | return 43 | } 44 | if curGoroutineID() == uint64(g) { 45 | panic("running on the wrong goroutine") 46 | } 47 | } 48 | 49 | var goroutineSpace = []byte("goroutine ") 50 | 51 | func curGoroutineID() uint64 { 52 | bp := littleBuf.Get().(*[]byte) 53 | defer littleBuf.Put(bp) 54 | b := *bp 55 | b = b[:runtime.Stack(b, false)] 56 | // Parse the 4707 out of "goroutine 4707 [" 57 | b = bytes.TrimPrefix(b, goroutineSpace) 58 | i := bytes.IndexByte(b, ' ') 59 | if i < 0 { 60 | panic(fmt.Sprintf("No space found in %q", b)) 61 | } 62 | b = b[:i] 63 | n, err := parseUintBytes(b, 10, 64) 64 | if err != nil { 65 | panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 66 | } 67 | return n 68 | } 69 | 70 | var littleBuf = sync.Pool{ 71 | New: func() interface{} { 72 | buf := make([]byte, 64) 73 | return &buf 74 | }, 75 | } 76 | 77 | // parseUintBytes is like strconv.ParseUint, but using a []byte. 78 | func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { 79 | var cutoff, maxVal uint64 80 | 81 | if bitSize == 0 { 82 | bitSize = int(strconv.IntSize) 83 | } 84 | 85 | s0 := s 86 | switch { 87 | case len(s) < 1: 88 | err = strconv.ErrSyntax 89 | goto Error 90 | 91 | case 2 <= base && base <= 36: 92 | // valid base; nothing to do 93 | 94 | case base == 0: 95 | // Look for octal, hex prefix. 96 | switch { 97 | case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 98 | base = 16 99 | s = s[2:] 100 | if len(s) < 1 { 101 | err = strconv.ErrSyntax 102 | goto Error 103 | } 104 | case s[0] == '0': 105 | base = 8 106 | default: 107 | base = 10 108 | } 109 | 110 | default: 111 | err = errors.New("invalid base " + strconv.Itoa(base)) 112 | goto Error 113 | } 114 | 115 | n = 0 116 | cutoff = cutoff64(base) 117 | maxVal = 1<= base { 135 | n = 0 136 | err = strconv.ErrSyntax 137 | goto Error 138 | } 139 | 140 | if n >= cutoff { 141 | // n*base overflows 142 | n = 1<<64 - 1 143 | err = strconv.ErrRange 144 | goto Error 145 | } 146 | n *= uint64(base) 147 | 148 | n1 := n + uint64(v) 149 | if n1 < n || n1 > maxVal { 150 | // n+v overflows 151 | n = 1<<64 - 1 152 | err = strconv.ErrRange 153 | goto Error 154 | } 155 | n = n1 156 | } 157 | 158 | return n, nil 159 | 160 | Error: 161 | return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} 162 | } 163 | 164 | // Return the first number n such that n*base >= 1<<64. 165 | func cutoff64(base int) uint64 { 166 | if base < 2 { 167 | return 0 168 | } 169 | return (1<<64-1)/uint64(base) + 1 170 | } 171 | -------------------------------------------------------------------------------- /http2/writesched_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | func makeWriteNonStreamRequest() FrameWriteRequest { 15 | return FrameWriteRequest{writeSettingsAck{}, nil, nil} 16 | } 17 | 18 | func makeWriteHeadersRequest(streamID uint32) FrameWriteRequest { 19 | st := &stream{id: streamID} 20 | return FrameWriteRequest{&writeResHeaders{streamID: streamID, httpResCode: 200}, st, nil} 21 | } 22 | 23 | func makeHandlerPanicRST(streamID uint32) FrameWriteRequest { 24 | st := &stream{id: streamID} 25 | return FrameWriteRequest{&handlerPanicRST{StreamID: streamID}, st, nil} 26 | } 27 | 28 | func checkConsume(wr FrameWriteRequest, nbytes int32, want []FrameWriteRequest) error { 29 | consumed, rest, n := wr.Consume(nbytes) 30 | var wantConsumed, wantRest FrameWriteRequest 31 | switch len(want) { 32 | case 0: 33 | case 1: 34 | wantConsumed = want[0] 35 | case 2: 36 | wantConsumed = want[0] 37 | wantRest = want[1] 38 | } 39 | if !reflect.DeepEqual(consumed, wantConsumed) || !reflect.DeepEqual(rest, wantRest) || n != len(want) { 40 | return fmt.Errorf("got %v, %v, %v\nwant %v, %v, %v", consumed, rest, n, wantConsumed, wantRest, len(want)) 41 | } 42 | return nil 43 | } 44 | 45 | func TestFrameWriteRequestNonData(t *testing.T) { 46 | wr := makeWriteNonStreamRequest() 47 | if got, want := wr.DataSize(), 0; got != want { 48 | t.Errorf("DataSize: got %v, want %v", got, want) 49 | } 50 | 51 | // Non-DATA frames are always consumed whole. 52 | if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil { 53 | t.Errorf("Consume:\n%v", err) 54 | } 55 | } 56 | 57 | func TestFrameWriteRequestData(t *testing.T) { 58 | st := &stream{ 59 | id: 1, 60 | sc: &serverConn{maxFrameSize: 16}, 61 | } 62 | const size = 32 63 | wr := FrameWriteRequest{&writeData{st.id, make([]byte, size), true}, st, make(chan error)} 64 | if got, want := wr.DataSize(), size; got != want { 65 | t.Errorf("DataSize: got %v, want %v", got, want) 66 | } 67 | 68 | // No flow-control bytes available: cannot consume anything. 69 | if err := checkConsume(wr, math.MaxInt32, []FrameWriteRequest{}); err != nil { 70 | t.Errorf("Consume(limited by flow control):\n%v", err) 71 | } 72 | 73 | // Add enough flow-control bytes to consume the entire frame, 74 | // but we're now restricted by st.sc.maxFrameSize. 75 | st.flow.add(size) 76 | want := []FrameWriteRequest{ 77 | { 78 | write: &writeData{st.id, make([]byte, st.sc.maxFrameSize), false}, 79 | stream: st, 80 | done: nil, 81 | }, 82 | { 83 | write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize), true}, 84 | stream: st, 85 | done: wr.done, 86 | }, 87 | } 88 | if err := checkConsume(wr, math.MaxInt32, want); err != nil { 89 | t.Errorf("Consume(limited by maxFrameSize):\n%v", err) 90 | } 91 | rest := want[1] 92 | 93 | // Consume 8 bytes from the remaining frame. 94 | want = []FrameWriteRequest{ 95 | { 96 | write: &writeData{st.id, make([]byte, 8), false}, 97 | stream: st, 98 | done: nil, 99 | }, 100 | { 101 | write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true}, 102 | stream: st, 103 | done: wr.done, 104 | }, 105 | } 106 | if err := checkConsume(rest, 8, want); err != nil { 107 | t.Errorf("Consume(8):\n%v", err) 108 | } 109 | rest = want[1] 110 | 111 | // Consume all remaining bytes. 112 | want = []FrameWriteRequest{ 113 | { 114 | write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true}, 115 | stream: st, 116 | done: wr.done, 117 | }, 118 | } 119 | if err := checkConsume(rest, math.MaxInt32, want); err != nil { 120 | t.Errorf("Consume(remainder):\n%v", err) 121 | } 122 | } 123 | 124 | func TestFrameWriteRequest_StreamID(t *testing.T) { 125 | const streamID = 123 126 | wr := FrameWriteRequest{write: streamError(streamID, ErrCodeNo)} 127 | if got := wr.StreamID(); got != streamID { 128 | t.Errorf("FrameWriteRequest(StreamError) = %v; want %v", got, streamID) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /http2/databuffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "sync" 11 | ) 12 | 13 | // Buffer chunks are allocated from a pool to reduce pressure on GC. 14 | // The maximum wasted space per dataBuffer is 2x the largest size class, 15 | // which happens when the dataBuffer has multiple chunks and there is 16 | // one unread byte in both the first and last chunks. We use a few size 17 | // classes to minimize overheads for servers that typically receive very 18 | // small request bodies. 19 | // 20 | // TODO: Benchmark to determine if the pools are necessary. The GC may have 21 | // improved enough that we can instead allocate chunks like this: 22 | // make([]byte, max(16<<10, expectedBytesRemaining)) 23 | var ( 24 | dataChunkSizeClasses = []int{ 25 | 1 << 10, 26 | 2 << 10, 27 | 4 << 10, 28 | 8 << 10, 29 | 16 << 10, 30 | } 31 | dataChunkPools = [...]sync.Pool{ 32 | {New: func() interface{} { return make([]byte, 1<<10) }}, 33 | {New: func() interface{} { return make([]byte, 2<<10) }}, 34 | {New: func() interface{} { return make([]byte, 4<<10) }}, 35 | {New: func() interface{} { return make([]byte, 8<<10) }}, 36 | {New: func() interface{} { return make([]byte, 16<<10) }}, 37 | } 38 | ) 39 | 40 | func getDataBufferChunk(size int64) []byte { 41 | i := 0 42 | for ; i < len(dataChunkSizeClasses)-1; i++ { 43 | if size <= int64(dataChunkSizeClasses[i]) { 44 | break 45 | } 46 | } 47 | return dataChunkPools[i].Get().([]byte) 48 | } 49 | 50 | func putDataBufferChunk(p []byte) { 51 | for i, n := range dataChunkSizeClasses { 52 | if len(p) == n { 53 | dataChunkPools[i].Put(p) 54 | return 55 | } 56 | } 57 | panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) 58 | } 59 | 60 | // dataBuffer is an io.ReadWriter backed by a list of data chunks. 61 | // Each dataBuffer is used to read DATA frames on a single stream. 62 | // The buffer is divided into chunks so the server can limit the 63 | // total memory used by a single connection without limiting the 64 | // request body size on any single stream. 65 | type dataBuffer struct { 66 | chunks [][]byte 67 | r int // next byte to read is chunks[0][r] 68 | w int // next byte to write is chunks[len(chunks)-1][w] 69 | size int // total buffered bytes 70 | expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0) 71 | } 72 | 73 | var errReadEmpty = errors.New("read from empty dataBuffer") 74 | 75 | // Read copies bytes from the buffer into p. 76 | // It is an error to read when no data is available. 77 | func (b *dataBuffer) Read(p []byte) (int, error) { 78 | if b.size == 0 { 79 | return 0, errReadEmpty 80 | } 81 | var ntotal int 82 | for len(p) > 0 && b.size > 0 { 83 | readFrom := b.bytesFromFirstChunk() 84 | n := copy(p, readFrom) 85 | p = p[n:] 86 | ntotal += n 87 | b.r += n 88 | b.size -= n 89 | // If the first chunk has been consumed, advance to the next chunk. 90 | if b.r == len(b.chunks[0]) { 91 | putDataBufferChunk(b.chunks[0]) 92 | end := len(b.chunks) - 1 93 | copy(b.chunks[:end], b.chunks[1:]) 94 | b.chunks[end] = nil 95 | b.chunks = b.chunks[:end] 96 | b.r = 0 97 | } 98 | } 99 | return ntotal, nil 100 | } 101 | 102 | func (b *dataBuffer) bytesFromFirstChunk() []byte { 103 | if len(b.chunks) == 1 { 104 | return b.chunks[0][b.r:b.w] 105 | } 106 | return b.chunks[0][b.r:] 107 | } 108 | 109 | // Len returns the number of bytes of the unread portion of the buffer. 110 | func (b *dataBuffer) Len() int { 111 | return b.size 112 | } 113 | 114 | // Write appends p to the buffer. 115 | func (b *dataBuffer) Write(p []byte) (int, error) { 116 | ntotal := len(p) 117 | for len(p) > 0 { 118 | // If the last chunk is empty, allocate a new chunk. Try to allocate 119 | // enough to fully copy p plus any additional bytes we expect to 120 | // receive. However, this may allocate less than len(p). 121 | want := int64(len(p)) 122 | if b.expected > want { 123 | want = b.expected 124 | } 125 | chunk := b.lastChunkOrAlloc(want) 126 | n := copy(chunk[b.w:], p) 127 | p = p[n:] 128 | b.w += n 129 | b.size += n 130 | b.expected -= int64(n) 131 | } 132 | return ntotal, nil 133 | } 134 | 135 | func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte { 136 | if len(b.chunks) != 0 { 137 | last := b.chunks[len(b.chunks)-1] 138 | if b.w < len(last) { 139 | return last 140 | } 141 | } 142 | chunk := getDataBufferChunk(want) 143 | b.chunks = append(b.chunks, chunk) 144 | b.w = 0 145 | return chunk 146 | } 147 | -------------------------------------------------------------------------------- /http2/pipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "errors" 9 | "io" 10 | "sync" 11 | ) 12 | 13 | // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like 14 | // io.Pipe except there are no PipeReader/PipeWriter halves, and the 15 | // underlying buffer is an interface. (io.Pipe is always unbuffered) 16 | type pipe struct { 17 | mu sync.Mutex 18 | c sync.Cond // c.L lazily initialized to &p.mu 19 | b pipeBuffer // nil when done reading 20 | unread int // bytes unread when done 21 | err error // read error once empty. non-nil means closed. 22 | breakErr error // immediate read error (caller doesn't see rest of b) 23 | donec chan struct{} // closed on error 24 | readFn func() // optional code to run in Read before error 25 | } 26 | 27 | type pipeBuffer interface { 28 | Len() int 29 | io.Writer 30 | io.Reader 31 | } 32 | 33 | func (p *pipe) Len() int { 34 | p.mu.Lock() 35 | defer p.mu.Unlock() 36 | if p.b == nil { 37 | return p.unread 38 | } 39 | return p.b.Len() 40 | } 41 | 42 | // Read waits until data is available and copies bytes 43 | // from the buffer into p. 44 | func (p *pipe) Read(d []byte) (n int, err error) { 45 | p.mu.Lock() 46 | defer p.mu.Unlock() 47 | if p.c.L == nil { 48 | p.c.L = &p.mu 49 | } 50 | for { 51 | if p.breakErr != nil { 52 | return 0, p.breakErr 53 | } 54 | if p.b != nil && p.b.Len() > 0 { 55 | return p.b.Read(d) 56 | } 57 | if p.err != nil { 58 | if p.readFn != nil { 59 | p.readFn() // e.g. copy trailers 60 | p.readFn = nil // not sticky like p.err 61 | } 62 | p.b = nil 63 | return 0, p.err 64 | } 65 | p.c.Wait() 66 | } 67 | } 68 | 69 | var errClosedPipeWrite = errors.New("write on closed buffer") 70 | 71 | // Write copies bytes from p into the buffer and wakes a reader. 72 | // It is an error to write more data than the buffer can hold. 73 | func (p *pipe) Write(d []byte) (n int, err error) { 74 | p.mu.Lock() 75 | defer p.mu.Unlock() 76 | if p.c.L == nil { 77 | p.c.L = &p.mu 78 | } 79 | defer p.c.Signal() 80 | if p.err != nil { 81 | return 0, errClosedPipeWrite 82 | } 83 | if p.breakErr != nil { 84 | p.unread += len(d) 85 | return len(d), nil // discard when there is no reader 86 | } 87 | return p.b.Write(d) 88 | } 89 | 90 | // CloseWithError causes the next Read (waking up a current blocked 91 | // Read if needed) to return the provided err after all data has been 92 | // read. 93 | // 94 | // The error must be non-nil. 95 | func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) } 96 | 97 | // BreakWithError causes the next Read (waking up a current blocked 98 | // Read if needed) to return the provided err immediately, without 99 | // waiting for unread data. 100 | func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) } 101 | 102 | // closeWithErrorAndCode is like CloseWithError but also sets some code to run 103 | // in the caller's goroutine before returning the error. 104 | func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) } 105 | 106 | func (p *pipe) closeWithError(dst *error, err error, fn func()) { 107 | if err == nil { 108 | panic("err must be non-nil") 109 | } 110 | p.mu.Lock() 111 | defer p.mu.Unlock() 112 | if p.c.L == nil { 113 | p.c.L = &p.mu 114 | } 115 | defer p.c.Signal() 116 | if *dst != nil { 117 | // Already been done. 118 | return 119 | } 120 | p.readFn = fn 121 | if dst == &p.breakErr { 122 | if p.b != nil { 123 | p.unread += p.b.Len() 124 | } 125 | p.b = nil 126 | } 127 | *dst = err 128 | p.closeDoneLocked() 129 | } 130 | 131 | // requires p.mu be held. 132 | func (p *pipe) closeDoneLocked() { 133 | if p.donec == nil { 134 | return 135 | } 136 | // Close if unclosed. This isn't racy since we always 137 | // hold p.mu while closing. 138 | select { 139 | case <-p.donec: 140 | default: 141 | close(p.donec) 142 | } 143 | } 144 | 145 | // Err returns the error (if any) first set by BreakWithError or CloseWithError. 146 | func (p *pipe) Err() error { 147 | p.mu.Lock() 148 | defer p.mu.Unlock() 149 | if p.breakErr != nil { 150 | return p.breakErr 151 | } 152 | return p.err 153 | } 154 | 155 | // Done returns a channel which is closed if and when this pipe is closed 156 | // with CloseWithError. 157 | func (p *pipe) Done() <-chan struct{} { 158 | p.mu.Lock() 159 | defer p.mu.Unlock() 160 | if p.donec == nil { 161 | p.donec = make(chan struct{}) 162 | if p.err != nil || p.breakErr != nil { 163 | // Already hit an error. 164 | p.closeDoneLocked() 165 | } 166 | } 167 | return p.donec 168 | } 169 | -------------------------------------------------------------------------------- /http2/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | // An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec. 13 | type ErrCode uint32 14 | 15 | const ( 16 | ErrCodeNo ErrCode = 0x0 17 | ErrCodeProtocol ErrCode = 0x1 18 | ErrCodeInternal ErrCode = 0x2 19 | ErrCodeFlowControl ErrCode = 0x3 20 | ErrCodeSettingsTimeout ErrCode = 0x4 21 | ErrCodeStreamClosed ErrCode = 0x5 22 | ErrCodeFrameSize ErrCode = 0x6 23 | ErrCodeRefusedStream ErrCode = 0x7 24 | ErrCodeCancel ErrCode = 0x8 25 | ErrCodeCompression ErrCode = 0x9 26 | ErrCodeConnect ErrCode = 0xa 27 | ErrCodeEnhanceYourCalm ErrCode = 0xb 28 | ErrCodeInadequateSecurity ErrCode = 0xc 29 | ErrCodeHTTP11Required ErrCode = 0xd 30 | ) 31 | 32 | var errCodeName = map[ErrCode]string{ 33 | ErrCodeNo: "NO_ERROR", 34 | ErrCodeProtocol: "PROTOCOL_ERROR", 35 | ErrCodeInternal: "INTERNAL_ERROR", 36 | ErrCodeFlowControl: "FLOW_CONTROL_ERROR", 37 | ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT", 38 | ErrCodeStreamClosed: "STREAM_CLOSED", 39 | ErrCodeFrameSize: "FRAME_SIZE_ERROR", 40 | ErrCodeRefusedStream: "REFUSED_STREAM", 41 | ErrCodeCancel: "CANCEL", 42 | ErrCodeCompression: "COMPRESSION_ERROR", 43 | ErrCodeConnect: "CONNECT_ERROR", 44 | ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM", 45 | ErrCodeInadequateSecurity: "INADEQUATE_SECURITY", 46 | ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED", 47 | } 48 | 49 | func (e ErrCode) String() string { 50 | if s, ok := errCodeName[e]; ok { 51 | return s 52 | } 53 | return fmt.Sprintf("unknown error code 0x%x", uint32(e)) 54 | } 55 | 56 | // ConnectionError is an error that results in the termination of the 57 | // entire connection. 58 | type ConnectionError ErrCode 59 | 60 | func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: %s", ErrCode(e)) } 61 | 62 | // StreamError is an error that only affects one stream within an 63 | // HTTP/2 connection. 64 | type StreamError struct { 65 | StreamID uint32 66 | Code ErrCode 67 | Cause error // optional additional detail 68 | } 69 | 70 | func streamError(id uint32, code ErrCode) StreamError { 71 | return StreamError{StreamID: id, Code: code} 72 | } 73 | 74 | func (e StreamError) Error() string { 75 | if e.Cause != nil { 76 | return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause) 77 | } 78 | return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code) 79 | } 80 | 81 | // 6.9.1 The Flow Control Window 82 | // "If a sender receives a WINDOW_UPDATE that causes a flow control 83 | // window to exceed this maximum it MUST terminate either the stream 84 | // or the connection, as appropriate. For streams, [...]; for the 85 | // connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code." 86 | type goAwayFlowError struct{} 87 | 88 | func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" } 89 | 90 | // connError represents an HTTP/2 ConnectionError error code, along 91 | // with a string (for debugging) explaining why. 92 | // 93 | // Errors of this type are only returned by the frame parser functions 94 | // and converted into ConnectionError(Code), after stashing away 95 | // the Reason into the Framer's errDetail field, accessible via 96 | // the (*Framer).ErrorDetail method. 97 | type connError struct { 98 | Code ErrCode // the ConnectionError error code 99 | Reason string // additional reason 100 | } 101 | 102 | func (e connError) Error() string { 103 | return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason) 104 | } 105 | 106 | type pseudoHeaderError string 107 | 108 | func (e pseudoHeaderError) Error() string { 109 | return fmt.Sprintf("invalid pseudo-header %q", string(e)) 110 | } 111 | 112 | type duplicatePseudoHeaderError string 113 | 114 | func (e duplicatePseudoHeaderError) Error() string { 115 | return fmt.Sprintf("duplicate pseudo-header %q", string(e)) 116 | } 117 | 118 | type headerFieldNameError string 119 | 120 | func (e headerFieldNameError) Error() string { 121 | return fmt.Sprintf("invalid header field name %q", string(e)) 122 | } 123 | 124 | type headerFieldValueError string 125 | 126 | func (e headerFieldValueError) Error() string { 127 | return fmt.Sprintf("invalid header field value %q", string(e)) 128 | } 129 | 130 | var ( 131 | errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers") 132 | errPseudoAfterRegular = errors.New("pseudo header field after regular") 133 | ) 134 | -------------------------------------------------------------------------------- /http2/databuffer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | func fmtDataChunk(chunk []byte) string { 15 | out := "" 16 | var last byte 17 | var count int 18 | for _, c := range chunk { 19 | if c != last { 20 | if count > 0 { 21 | out += fmt.Sprintf(" x %d ", count) 22 | count = 0 23 | } 24 | out += string([]byte{c}) 25 | last = c 26 | } 27 | count++ 28 | } 29 | if count > 0 { 30 | out += fmt.Sprintf(" x %d", count) 31 | } 32 | return out 33 | } 34 | 35 | func fmtDataChunks(chunks [][]byte) string { 36 | var out string 37 | for _, chunk := range chunks { 38 | out += fmt.Sprintf("{%q}", fmtDataChunk(chunk)) 39 | } 40 | return out 41 | } 42 | 43 | func testDataBuffer(t *testing.T, wantBytes []byte, setup func(t *testing.T) *dataBuffer) { 44 | // Run setup, then read the remaining bytes from the dataBuffer and check 45 | // that they match wantBytes. We use different read sizes to check corner 46 | // cases in Read. 47 | for _, readSize := range []int{1, 2, 1 * 1024, 32 * 1024} { 48 | t.Run(fmt.Sprintf("ReadSize=%d", readSize), func(t *testing.T) { 49 | b := setup(t) 50 | buf := make([]byte, readSize) 51 | var gotRead bytes.Buffer 52 | for { 53 | n, err := b.Read(buf) 54 | gotRead.Write(buf[:n]) 55 | if err == errReadEmpty { 56 | break 57 | } 58 | if err != nil { 59 | t.Fatalf("error after %v bytes: %v", gotRead.Len(), err) 60 | } 61 | } 62 | if got, want := gotRead.Bytes(), wantBytes; !bytes.Equal(got, want) { 63 | t.Errorf("FinalRead=%q, want %q", fmtDataChunk(got), fmtDataChunk(want)) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestDataBufferAllocation(t *testing.T) { 70 | writes := [][]byte{ 71 | bytes.Repeat([]byte("a"), 1*1024-1), 72 | []byte("a"), 73 | bytes.Repeat([]byte("b"), 4*1024-1), 74 | []byte("b"), 75 | bytes.Repeat([]byte("c"), 8*1024-1), 76 | []byte("c"), 77 | bytes.Repeat([]byte("d"), 16*1024-1), 78 | []byte("d"), 79 | bytes.Repeat([]byte("e"), 32*1024), 80 | } 81 | var wantRead bytes.Buffer 82 | for _, p := range writes { 83 | wantRead.Write(p) 84 | } 85 | 86 | testDataBuffer(t, wantRead.Bytes(), func(t *testing.T) *dataBuffer { 87 | b := &dataBuffer{} 88 | for _, p := range writes { 89 | if n, err := b.Write(p); n != len(p) || err != nil { 90 | t.Fatalf("Write(%q x %d)=%v,%v want %v,nil", p[:1], len(p), n, err, len(p)) 91 | } 92 | } 93 | want := [][]byte{ 94 | bytes.Repeat([]byte("a"), 1*1024), 95 | bytes.Repeat([]byte("b"), 4*1024), 96 | bytes.Repeat([]byte("c"), 8*1024), 97 | bytes.Repeat([]byte("d"), 16*1024), 98 | bytes.Repeat([]byte("e"), 16*1024), 99 | bytes.Repeat([]byte("e"), 16*1024), 100 | } 101 | if !reflect.DeepEqual(b.chunks, want) { 102 | t.Errorf("dataBuffer.chunks\ngot: %s\nwant: %s", fmtDataChunks(b.chunks), fmtDataChunks(want)) 103 | } 104 | return b 105 | }) 106 | } 107 | 108 | func TestDataBufferAllocationWithExpected(t *testing.T) { 109 | writes := [][]byte{ 110 | bytes.Repeat([]byte("a"), 1*1024), // allocates 16KB 111 | bytes.Repeat([]byte("b"), 14*1024), 112 | bytes.Repeat([]byte("c"), 15*1024), // allocates 16KB more 113 | bytes.Repeat([]byte("d"), 2*1024), 114 | bytes.Repeat([]byte("e"), 1*1024), // overflows 32KB expectation, allocates just 1KB 115 | } 116 | var wantRead bytes.Buffer 117 | for _, p := range writes { 118 | wantRead.Write(p) 119 | } 120 | 121 | testDataBuffer(t, wantRead.Bytes(), func(t *testing.T) *dataBuffer { 122 | b := &dataBuffer{expected: 32 * 1024} 123 | for _, p := range writes { 124 | if n, err := b.Write(p); n != len(p) || err != nil { 125 | t.Fatalf("Write(%q x %d)=%v,%v want %v,nil", p[:1], len(p), n, err, len(p)) 126 | } 127 | } 128 | want := [][]byte{ 129 | append(bytes.Repeat([]byte("a"), 1*1024), append(bytes.Repeat([]byte("b"), 14*1024), bytes.Repeat([]byte("c"), 1*1024)...)...), 130 | append(bytes.Repeat([]byte("c"), 14*1024), bytes.Repeat([]byte("d"), 2*1024)...), 131 | bytes.Repeat([]byte("e"), 1*1024), 132 | } 133 | if !reflect.DeepEqual(b.chunks, want) { 134 | t.Errorf("dataBuffer.chunks\ngot: %s\nwant: %s", fmtDataChunks(b.chunks), fmtDataChunks(want)) 135 | } 136 | return b 137 | }) 138 | } 139 | 140 | func TestDataBufferWriteAfterPartialRead(t *testing.T) { 141 | testDataBuffer(t, []byte("cdxyz"), func(t *testing.T) *dataBuffer { 142 | b := &dataBuffer{} 143 | if n, err := b.Write([]byte("abcd")); n != 4 || err != nil { 144 | t.Fatalf("Write(\"abcd\")=%v,%v want 4,nil", n, err) 145 | } 146 | p := make([]byte, 2) 147 | if n, err := b.Read(p); n != 2 || err != nil || !bytes.Equal(p, []byte("ab")) { 148 | t.Fatalf("Read()=%q,%v,%v want \"ab\",2,nil", p, n, err) 149 | } 150 | if n, err := b.Write([]byte("xyz")); n != 3 || err != nil { 151 | t.Fatalf("Write(\"xyz\")=%v,%v want 3,nil", n, err) 152 | } 153 | return b 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /http2/hpack/huffman.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hpack 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "io" 11 | "sync" 12 | ) 13 | 14 | var bufPool = sync.Pool{ 15 | New: func() interface{} { return new(bytes.Buffer) }, 16 | } 17 | 18 | // HuffmanDecode decodes the string in v and writes the expanded 19 | // result to w, returning the number of bytes written to w and the 20 | // Write call's return value. At most one Write call is made. 21 | func HuffmanDecode(w io.Writer, v []byte) (int, error) { 22 | buf := bufPool.Get().(*bytes.Buffer) 23 | buf.Reset() 24 | defer bufPool.Put(buf) 25 | if err := huffmanDecode(buf, 0, v); err != nil { 26 | return 0, err 27 | } 28 | return w.Write(buf.Bytes()) 29 | } 30 | 31 | // HuffmanDecodeToString decodes the string in v. 32 | func HuffmanDecodeToString(v []byte) (string, error) { 33 | buf := bufPool.Get().(*bytes.Buffer) 34 | buf.Reset() 35 | defer bufPool.Put(buf) 36 | if err := huffmanDecode(buf, 0, v); err != nil { 37 | return "", err 38 | } 39 | return buf.String(), nil 40 | } 41 | 42 | // ErrInvalidHuffman is returned for errors found decoding 43 | // Huffman-encoded strings. 44 | var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data") 45 | 46 | // huffmanDecode decodes v to buf. 47 | // If maxLen is greater than 0, attempts to write more to buf than 48 | // maxLen bytes will return ErrStringLength. 49 | func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { 50 | rootHuffmanNode := getRootHuffmanNode() 51 | n := rootHuffmanNode 52 | // cur is the bit buffer that has not been fed into n. 53 | // cbits is the number of low order bits in cur that are valid. 54 | // sbits is the number of bits of the symbol prefix being decoded. 55 | cur, cbits, sbits := uint(0), uint8(0), uint8(0) 56 | for _, b := range v { 57 | cur = cur<<8 | uint(b) 58 | cbits += 8 59 | sbits += 8 60 | for cbits >= 8 { 61 | idx := byte(cur >> (cbits - 8)) 62 | n = n.children[idx] 63 | if n == nil { 64 | return ErrInvalidHuffman 65 | } 66 | if n.children == nil { 67 | if maxLen != 0 && buf.Len() == maxLen { 68 | return ErrStringLength 69 | } 70 | buf.WriteByte(n.sym) 71 | cbits -= n.codeLen 72 | n = rootHuffmanNode 73 | sbits = cbits 74 | } else { 75 | cbits -= 8 76 | } 77 | } 78 | } 79 | for cbits > 0 { 80 | n = n.children[byte(cur<<(8-cbits))] 81 | if n == nil { 82 | return ErrInvalidHuffman 83 | } 84 | if n.children != nil || n.codeLen > cbits { 85 | break 86 | } 87 | if maxLen != 0 && buf.Len() == maxLen { 88 | return ErrStringLength 89 | } 90 | buf.WriteByte(n.sym) 91 | cbits -= n.codeLen 92 | n = rootHuffmanNode 93 | sbits = cbits 94 | } 95 | if sbits > 7 { 96 | // Either there was an incomplete symbol, or overlong padding. 97 | // Both are decoding errors per RFC 7541 section 5.2. 98 | return ErrInvalidHuffman 99 | } 100 | if mask := uint(1< 8 { 151 | codeLen -= 8 152 | i := uint8(code >> codeLen) 153 | if cur.children[i] == nil { 154 | cur.children[i] = newInternalNode() 155 | } 156 | cur = cur.children[i] 157 | } 158 | shift := 8 - codeLen 159 | start, end := int(uint8(code<> (nbits - rembits)) 183 | dst[len(dst)-1] |= t 184 | } 185 | 186 | return dst 187 | } 188 | 189 | // HuffmanEncodeLength returns the number of bytes required to encode 190 | // s in Huffman codes. The result is round up to byte boundary. 191 | func HuffmanEncodeLength(s string) uint64 { 192 | n := uint64(0) 193 | for i := 0; i < len(s); i++ { 194 | n += uint64(huffmanCodeLen[s[i]]) 195 | } 196 | return (n + 7) / 8 197 | } 198 | 199 | // appendByteToHuffmanCode appends Huffman code for c to dst and 200 | // returns the extended buffer and the remaining bits in the last 201 | // element. The appending is not byte aligned and the remaining bits 202 | // in the last element of dst is given in rembits. 203 | func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) { 204 | code := huffmanCodes[c] 205 | nbits := huffmanCodeLen[c] 206 | 207 | for { 208 | if rembits > nbits { 209 | t := uint8(code << (rembits - nbits)) 210 | dst[len(dst)-1] |= t 211 | rembits -= nbits 212 | break 213 | } 214 | 215 | t := uint8(code >> (nbits - rembits)) 216 | dst[len(dst)-1] |= t 217 | 218 | nbits -= rembits 219 | rembits = 8 220 | 221 | if nbits == 0 { 222 | break 223 | } 224 | 225 | dst = append(dst, 0) 226 | } 227 | 228 | return dst, rembits 229 | } 230 | -------------------------------------------------------------------------------- /http2/hpack/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hpack 6 | 7 | import ( 8 | "io" 9 | ) 10 | 11 | const ( 12 | uint32Max = ^uint32(0) 13 | initialHeaderTableSize = 4096 14 | ) 15 | 16 | type Encoder struct { 17 | dynTab dynamicTable 18 | // minSize is the minimum table size set by 19 | // SetMaxDynamicTableSize after the previous Header Table Size 20 | // Update. 21 | minSize uint32 22 | // maxSizeLimit is the maximum table size this encoder 23 | // supports. This will protect the encoder from too large 24 | // size. 25 | maxSizeLimit uint32 26 | // tableSizeUpdate indicates whether "Header Table Size 27 | // Update" is required. 28 | tableSizeUpdate bool 29 | w io.Writer 30 | buf []byte 31 | } 32 | 33 | // NewEncoder returns a new Encoder which performs HPACK encoding. An 34 | // encoded data is written to w. 35 | func NewEncoder(w io.Writer) *Encoder { 36 | e := &Encoder{ 37 | minSize: uint32Max, 38 | maxSizeLimit: initialHeaderTableSize, 39 | tableSizeUpdate: false, 40 | w: w, 41 | } 42 | e.dynTab.table.init() 43 | e.dynTab.setMaxSize(initialHeaderTableSize) 44 | return e 45 | } 46 | 47 | // WriteField encodes f into a single Write to e's underlying Writer. 48 | // This function may also produce bytes for "Header Table Size Update" 49 | // if necessary. If produced, it is done before encoding f. 50 | func (e *Encoder) WriteField(f HeaderField) error { 51 | e.buf = e.buf[:0] 52 | 53 | if e.tableSizeUpdate { 54 | e.tableSizeUpdate = false 55 | if e.minSize < e.dynTab.maxSize { 56 | e.buf = appendTableSize(e.buf, e.minSize) 57 | } 58 | e.minSize = uint32Max 59 | e.buf = appendTableSize(e.buf, e.dynTab.maxSize) 60 | } 61 | 62 | idx, nameValueMatch := e.searchTable(f) 63 | if nameValueMatch { 64 | e.buf = appendIndexed(e.buf, idx) 65 | } else { 66 | indexing := e.shouldIndex(f) 67 | if indexing { 68 | e.dynTab.add(f) 69 | } 70 | 71 | if idx == 0 { 72 | e.buf = appendNewName(e.buf, f, indexing) 73 | } else { 74 | e.buf = appendIndexedName(e.buf, f, idx, indexing) 75 | } 76 | } 77 | n, err := e.w.Write(e.buf) 78 | if err == nil && n != len(e.buf) { 79 | err = io.ErrShortWrite 80 | } 81 | return err 82 | } 83 | 84 | // searchTable searches f in both stable and dynamic header tables. 85 | // The static header table is searched first. Only when there is no 86 | // exact match for both name and value, the dynamic header table is 87 | // then searched. If there is no match, i is 0. If both name and value 88 | // match, i is the matched index and nameValueMatch becomes true. If 89 | // only name matches, i points to that index and nameValueMatch 90 | // becomes false. 91 | func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) { 92 | i, nameValueMatch = staticTable.search(f) 93 | if nameValueMatch { 94 | return i, true 95 | } 96 | 97 | j, nameValueMatch := e.dynTab.table.search(f) 98 | if nameValueMatch || (i == 0 && j != 0) { 99 | return j + uint64(staticTable.len()), nameValueMatch 100 | } 101 | 102 | return i, false 103 | } 104 | 105 | // SetMaxDynamicTableSize changes the dynamic header table size to v. 106 | // The actual size is bounded by the value passed to 107 | // SetMaxDynamicTableSizeLimit. 108 | func (e *Encoder) SetMaxDynamicTableSize(v uint32) { 109 | if v > e.maxSizeLimit { 110 | v = e.maxSizeLimit 111 | } 112 | if v < e.minSize { 113 | e.minSize = v 114 | } 115 | e.tableSizeUpdate = true 116 | e.dynTab.setMaxSize(v) 117 | } 118 | 119 | // SetMaxDynamicTableSizeLimit changes the maximum value that can be 120 | // specified in SetMaxDynamicTableSize to v. By default, it is set to 121 | // 4096, which is the same size of the default dynamic header table 122 | // size described in HPACK specification. If the current maximum 123 | // dynamic header table size is strictly greater than v, "Header Table 124 | // Size Update" will be done in the next WriteField call and the 125 | // maximum dynamic header table size is truncated to v. 126 | func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) { 127 | e.maxSizeLimit = v 128 | if e.dynTab.maxSize > v { 129 | e.tableSizeUpdate = true 130 | e.dynTab.setMaxSize(v) 131 | } 132 | } 133 | 134 | // shouldIndex reports whether f should be indexed. 135 | func (e *Encoder) shouldIndex(f HeaderField) bool { 136 | return !f.Sensitive && f.Size() <= e.dynTab.maxSize 137 | } 138 | 139 | // appendIndexed appends index i, as encoded in "Indexed Header Field" 140 | // representation, to dst and returns the extended buffer. 141 | func appendIndexed(dst []byte, i uint64) []byte { 142 | first := len(dst) 143 | dst = appendVarInt(dst, 7, i) 144 | dst[first] |= 0x80 145 | return dst 146 | } 147 | 148 | // appendNewName appends f, as encoded in one of "Literal Header field 149 | // - New Name" representation variants, to dst and returns the 150 | // extended buffer. 151 | // 152 | // If f.Sensitive is true, "Never Indexed" representation is used. If 153 | // f.Sensitive is false and indexing is true, "Incremental Indexing" 154 | // representation is used. 155 | func appendNewName(dst []byte, f HeaderField, indexing bool) []byte { 156 | dst = append(dst, encodeTypeByte(indexing, f.Sensitive)) 157 | dst = appendHpackString(dst, f.Name) 158 | return appendHpackString(dst, f.Value) 159 | } 160 | 161 | // appendIndexedName appends f and index i referring indexed name 162 | // entry, as encoded in one of "Literal Header field - Indexed Name" 163 | // representation variants, to dst and returns the extended buffer. 164 | // 165 | // If f.Sensitive is true, "Never Indexed" representation is used. If 166 | // f.Sensitive is false and indexing is true, "Incremental Indexing" 167 | // representation is used. 168 | func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte { 169 | first := len(dst) 170 | var n byte 171 | if indexing { 172 | n = 6 173 | } else { 174 | n = 4 175 | } 176 | dst = appendVarInt(dst, n, i) 177 | dst[first] |= encodeTypeByte(indexing, f.Sensitive) 178 | return appendHpackString(dst, f.Value) 179 | } 180 | 181 | // appendTableSize appends v, as encoded in "Header Table Size Update" 182 | // representation, to dst and returns the extended buffer. 183 | func appendTableSize(dst []byte, v uint32) []byte { 184 | first := len(dst) 185 | dst = appendVarInt(dst, 5, uint64(v)) 186 | dst[first] |= 0x20 187 | return dst 188 | } 189 | 190 | // appendVarInt appends i, as encoded in variable integer form using n 191 | // bit prefix, to dst and returns the extended buffer. 192 | // 193 | // See 194 | // http://http2.github.io/http2-spec/compression.html#integer.representation 195 | func appendVarInt(dst []byte, n byte, i uint64) []byte { 196 | k := uint64((1 << n) - 1) 197 | if i < k { 198 | return append(dst, byte(i)) 199 | } 200 | dst = append(dst, byte(k)) 201 | i -= k 202 | for ; i >= 128; i >>= 7 { 203 | dst = append(dst, byte(0x80|(i&0x7f))) 204 | } 205 | return append(dst, byte(i)) 206 | } 207 | 208 | // appendHpackString appends s, as encoded in "String Literal" 209 | // representation, to dst and returns the extended buffer. 210 | // 211 | // s will be encoded in Huffman codes only when it produces strictly 212 | // shorter byte string. 213 | func appendHpackString(dst []byte, s string) []byte { 214 | huffmanLength := HuffmanEncodeLength(s) 215 | if huffmanLength < uint64(len(s)) { 216 | first := len(dst) 217 | dst = appendVarInt(dst, 7, huffmanLength) 218 | dst = AppendHuffmanString(dst, s) 219 | dst[first] |= 0x80 220 | } else { 221 | dst = appendVarInt(dst, 7, uint64(len(s))) 222 | dst = append(dst, s...) 223 | } 224 | return dst 225 | } 226 | 227 | // encodeTypeByte returns type byte. If sensitive is true, type byte 228 | // for "Never Indexed" representation is returned. If sensitive is 229 | // false and indexing is true, type byte for "Incremental Indexing" 230 | // representation is returned. Otherwise, type byte for "Without 231 | // Indexing" is returned. 232 | func encodeTypeByte(indexing, sensitive bool) byte { 233 | if sensitive { 234 | return 0x10 235 | } 236 | if indexing { 237 | return 0x40 238 | } 239 | return 0 240 | } 241 | -------------------------------------------------------------------------------- /http2/http2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "flag" 11 | "fmt" 12 | "net/http" 13 | "os/exec" 14 | "strconv" 15 | "strings" 16 | "testing" 17 | "time" 18 | 19 | "golang.org/x/net/http2/hpack" 20 | ) 21 | 22 | var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.") 23 | 24 | func condSkipFailingTest(t *testing.T) { 25 | if !*knownFailing { 26 | t.Skip("Skipping known-failing test without --known_failing") 27 | } 28 | } 29 | 30 | func init() { 31 | inTests = true 32 | DebugGoroutines = true 33 | flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging") 34 | } 35 | 36 | func TestSettingString(t *testing.T) { 37 | tests := []struct { 38 | s Setting 39 | want string 40 | }{ 41 | {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"}, 42 | {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"}, 43 | } 44 | for i, tt := range tests { 45 | got := fmt.Sprint(tt.s) 46 | if got != tt.want { 47 | t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want) 48 | } 49 | } 50 | } 51 | 52 | type twriter struct { 53 | t testing.TB 54 | st *serverTester // optional 55 | } 56 | 57 | func (w twriter) Write(p []byte) (n int, err error) { 58 | if w.st != nil { 59 | ps := string(p) 60 | for _, phrase := range w.st.logFilter { 61 | if strings.Contains(ps, phrase) { 62 | return len(p), nil // no logging 63 | } 64 | } 65 | } 66 | w.t.Logf("%s", p) 67 | return len(p), nil 68 | } 69 | 70 | // like encodeHeader, but don't add implicit pseudo headers. 71 | func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte { 72 | var buf bytes.Buffer 73 | enc := hpack.NewEncoder(&buf) 74 | for len(headers) > 0 { 75 | k, v := headers[0], headers[1] 76 | headers = headers[2:] 77 | if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil { 78 | t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err) 79 | } 80 | } 81 | return buf.Bytes() 82 | } 83 | 84 | // Verify that curl has http2. 85 | func requireCurl(t *testing.T) { 86 | out, err := dockerLogs(curl(t, "--version")) 87 | if err != nil { 88 | t.Skipf("failed to determine curl features; skipping test") 89 | } 90 | if !strings.Contains(string(out), "HTTP2") { 91 | t.Skip("curl doesn't support HTTP2; skipping test") 92 | } 93 | } 94 | 95 | func curl(t *testing.T, args ...string) (container string) { 96 | out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).Output() 97 | if err != nil { 98 | t.Skipf("Failed to run curl in docker: %v, %s", err, out) 99 | } 100 | return strings.TrimSpace(string(out)) 101 | } 102 | 103 | // Verify that h2load exists. 104 | func requireH2load(t *testing.T) { 105 | out, err := dockerLogs(h2load(t, "--version")) 106 | if err != nil { 107 | t.Skipf("failed to probe h2load; skipping test: %s", out) 108 | } 109 | if !strings.Contains(string(out), "h2load nghttp2/") { 110 | t.Skipf("h2load not present; skipping test. (Output=%q)", out) 111 | } 112 | } 113 | 114 | func h2load(t *testing.T, args ...string) (container string) { 115 | out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl"}, args...)...).Output() 116 | if err != nil { 117 | t.Skipf("Failed to run h2load in docker: %v, %s", err, out) 118 | } 119 | return strings.TrimSpace(string(out)) 120 | } 121 | 122 | type puppetCommand struct { 123 | fn func(w http.ResponseWriter, r *http.Request) 124 | done chan<- bool 125 | } 126 | 127 | type handlerPuppet struct { 128 | ch chan puppetCommand 129 | } 130 | 131 | func newHandlerPuppet() *handlerPuppet { 132 | return &handlerPuppet{ 133 | ch: make(chan puppetCommand), 134 | } 135 | } 136 | 137 | func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) { 138 | for cmd := range p.ch { 139 | cmd.fn(w, r) 140 | cmd.done <- true 141 | } 142 | } 143 | 144 | func (p *handlerPuppet) done() { close(p.ch) } 145 | func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) { 146 | done := make(chan bool) 147 | p.ch <- puppetCommand{fn, done} 148 | <-done 149 | } 150 | func dockerLogs(container string) ([]byte, error) { 151 | out, err := exec.Command("docker", "wait", container).CombinedOutput() 152 | if err != nil { 153 | return out, err 154 | } 155 | exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out))) 156 | if err != nil { 157 | return out, errors.New("unexpected exit status from docker wait") 158 | } 159 | out, err = exec.Command("docker", "logs", container).CombinedOutput() 160 | exec.Command("docker", "rm", container).Run() 161 | if err == nil && exitStatus != 0 { 162 | err = fmt.Errorf("exit status %d: %s", exitStatus, out) 163 | } 164 | return out, err 165 | } 166 | 167 | func kill(container string) { 168 | exec.Command("docker", "kill", container).Run() 169 | exec.Command("docker", "rm", container).Run() 170 | } 171 | 172 | func cleanDate(res *http.Response) { 173 | if d := res.Header["Date"]; len(d) == 1 { 174 | d[0] = "XXX" 175 | } 176 | } 177 | 178 | func TestSorterPoolAllocs(t *testing.T) { 179 | ss := []string{"a", "b", "c"} 180 | h := http.Header{ 181 | "a": nil, 182 | "b": nil, 183 | "c": nil, 184 | } 185 | sorter := new(sorter) 186 | 187 | if allocs := testing.AllocsPerRun(100, func() { 188 | sorter.SortStrings(ss) 189 | }); allocs >= 1 { 190 | t.Logf("SortStrings allocs = %v; want <1", allocs) 191 | } 192 | 193 | if allocs := testing.AllocsPerRun(5, func() { 194 | if len(sorter.Keys(h)) != 3 { 195 | t.Fatal("wrong result") 196 | } 197 | }); allocs > 0 { 198 | t.Logf("Keys allocs = %v; want <1", allocs) 199 | } 200 | } 201 | 202 | // waitCondition reports whether fn eventually returned true, 203 | // checking immediately and then every checkEvery amount, 204 | // until waitFor has elapsed, at which point it returns false. 205 | func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 206 | deadline := time.Now().Add(waitFor) 207 | for time.Now().Before(deadline) { 208 | if fn() { 209 | return true 210 | } 211 | time.Sleep(checkEvery) 212 | } 213 | return false 214 | } 215 | 216 | // waitErrCondition is like waitCondition but with errors instead of bools. 217 | func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 218 | deadline := time.Now().Add(waitFor) 219 | var err error 220 | for time.Now().Before(deadline) { 221 | if err = fn(); err == nil { 222 | return nil 223 | } 224 | time.Sleep(checkEvery) 225 | } 226 | return err 227 | } 228 | 229 | func equalError(a, b error) bool { 230 | if a == nil { 231 | return b == nil 232 | } 233 | if b == nil { 234 | return a == nil 235 | } 236 | return a.Error() == b.Error() 237 | } 238 | 239 | // Tests that http2.Server.IdleTimeout is initialized from 240 | // http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was 241 | // added in Go 1.8. 242 | func TestConfigureServerIdleTimeout_Go18(t *testing.T) { 243 | const timeout = 5 * time.Second 244 | const notThisOne = 1 * time.Second 245 | 246 | // With a zero http2.Server, verify that it copies IdleTimeout: 247 | { 248 | s1 := &http.Server{ 249 | IdleTimeout: timeout, 250 | ReadTimeout: notThisOne, 251 | } 252 | s2 := &Server{} 253 | if err := ConfigureServer(s1, s2); err != nil { 254 | t.Fatal(err) 255 | } 256 | if s2.IdleTimeout != timeout { 257 | t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 258 | } 259 | } 260 | 261 | // And that it falls back to ReadTimeout: 262 | { 263 | s1 := &http.Server{ 264 | ReadTimeout: timeout, 265 | } 266 | s2 := &Server{} 267 | if err := ConfigureServer(s1, s2); err != nil { 268 | t.Fatal(err) 269 | } 270 | if s2.IdleTimeout != timeout { 271 | t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 272 | } 273 | } 274 | 275 | // Verify that s1's IdleTimeout doesn't overwrite an existing setting: 276 | { 277 | s1 := &http.Server{ 278 | IdleTimeout: notThisOne, 279 | } 280 | s2 := &Server{ 281 | IdleTimeout: timeout, 282 | } 283 | if err := ConfigureServer(s1, s2); err != nil { 284 | t.Fatal(err) 285 | } 286 | if s2.IdleTimeout != timeout { 287 | t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /http2/writesched.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "fmt" 8 | 9 | // WriteScheduler is the interface implemented by HTTP/2 write schedulers. 10 | // Methods are never called concurrently. 11 | type WriteScheduler interface { 12 | // OpenStream opens a new stream in the write scheduler. 13 | // It is illegal to call this with streamID=0 or with a streamID that is 14 | // already open -- the call may panic. 15 | OpenStream(streamID uint32, options OpenStreamOptions) 16 | 17 | // CloseStream closes a stream in the write scheduler. Any frames queued on 18 | // this stream should be discarded. It is illegal to call this on a stream 19 | // that is not open -- the call may panic. 20 | CloseStream(streamID uint32) 21 | 22 | // AdjustStream adjusts the priority of the given stream. This may be called 23 | // on a stream that has not yet been opened or has been closed. Note that 24 | // RFC 7540 allows PRIORITY frames to be sent on streams in any state. See: 25 | // https://tools.ietf.org/html/rfc7540#section-5.1 26 | AdjustStream(streamID uint32, priority PriorityParam) 27 | 28 | // Push queues a frame in the scheduler. In most cases, this will not be 29 | // called with wr.StreamID()!=0 unless that stream is currently open. The one 30 | // exception is RST_STREAM frames, which may be sent on idle or closed streams. 31 | Push(wr FrameWriteRequest) 32 | 33 | // Pop dequeues the next frame to write. Returns false if no frames can 34 | // be written. Frames with a given wr.StreamID() are Pop'd in the same 35 | // order they are Push'd. No frames should be discarded except by CloseStream. 36 | Pop() (wr FrameWriteRequest, ok bool) 37 | } 38 | 39 | // OpenStreamOptions specifies extra options for WriteScheduler.OpenStream. 40 | type OpenStreamOptions struct { 41 | // PusherID is zero if the stream was initiated by the client. Otherwise, 42 | // PusherID names the stream that pushed the newly opened stream. 43 | PusherID uint32 44 | } 45 | 46 | // FrameWriteRequest is a request to write a frame. 47 | type FrameWriteRequest struct { 48 | // write is the interface value that does the writing, once the 49 | // WriteScheduler has selected this frame to write. The write 50 | // functions are all defined in write.go. 51 | write writeFramer 52 | 53 | // stream is the stream on which this frame will be written. 54 | // nil for non-stream frames like PING and SETTINGS. 55 | stream *stream 56 | 57 | // done, if non-nil, must be a buffered channel with space for 58 | // 1 message and is sent the return value from write (or an 59 | // earlier error) when the frame has been written. 60 | done chan error 61 | } 62 | 63 | // StreamID returns the id of the stream this frame will be written to. 64 | // 0 is used for non-stream frames such as PING and SETTINGS. 65 | func (wr FrameWriteRequest) StreamID() uint32 { 66 | if wr.stream == nil { 67 | if se, ok := wr.write.(StreamError); ok { 68 | // (*serverConn).resetStream doesn't set 69 | // stream because it doesn't necessarily have 70 | // one. So special case this type of write 71 | // message. 72 | return se.StreamID 73 | } 74 | return 0 75 | } 76 | return wr.stream.id 77 | } 78 | 79 | // isControl reports whether wr is a control frame for MaxQueuedControlFrames 80 | // purposes. That includes non-stream frames and RST_STREAM frames. 81 | func (wr FrameWriteRequest) isControl() bool { 82 | return wr.stream == nil 83 | } 84 | 85 | // DataSize returns the number of flow control bytes that must be consumed 86 | // to write this entire frame. This is 0 for non-DATA frames. 87 | func (wr FrameWriteRequest) DataSize() int { 88 | if wd, ok := wr.write.(*writeData); ok { 89 | return len(wd.p) 90 | } 91 | return 0 92 | } 93 | 94 | // Consume consumes min(n, available) bytes from this frame, where available 95 | // is the number of flow control bytes available on the stream. Consume returns 96 | // 0, 1, or 2 frames, where the integer return value gives the number of frames 97 | // returned. 98 | // 99 | // If flow control prevents consuming any bytes, this returns (_, _, 0). If 100 | // the entire frame was consumed, this returns (wr, _, 1). Otherwise, this 101 | // returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and 102 | // 'rest' contains the remaining bytes. The consumed bytes are deducted from the 103 | // underlying stream's flow control budget. 104 | func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) { 105 | var empty FrameWriteRequest 106 | 107 | // Non-DATA frames are always consumed whole. 108 | wd, ok := wr.write.(*writeData) 109 | if !ok || len(wd.p) == 0 { 110 | return wr, empty, 1 111 | } 112 | 113 | // Might need to split after applying limits. 114 | allowed := wr.stream.flow.available() 115 | if n < allowed { 116 | allowed = n 117 | } 118 | if wr.stream.sc.maxFrameSize < allowed { 119 | allowed = wr.stream.sc.maxFrameSize 120 | } 121 | if allowed <= 0 { 122 | return empty, empty, 0 123 | } 124 | if len(wd.p) > int(allowed) { 125 | wr.stream.flow.take(allowed) 126 | consumed := FrameWriteRequest{ 127 | stream: wr.stream, 128 | write: &writeData{ 129 | streamID: wd.streamID, 130 | p: wd.p[:allowed], 131 | // Even if the original had endStream set, there 132 | // are bytes remaining because len(wd.p) > allowed, 133 | // so we know endStream is false. 134 | endStream: false, 135 | }, 136 | // Our caller is blocking on the final DATA frame, not 137 | // this intermediate frame, so no need to wait. 138 | done: nil, 139 | } 140 | rest := FrameWriteRequest{ 141 | stream: wr.stream, 142 | write: &writeData{ 143 | streamID: wd.streamID, 144 | p: wd.p[allowed:], 145 | endStream: wd.endStream, 146 | }, 147 | done: wr.done, 148 | } 149 | return consumed, rest, 2 150 | } 151 | 152 | // The frame is consumed whole. 153 | // NB: This cast cannot overflow because allowed is <= math.MaxInt32. 154 | wr.stream.flow.take(int32(len(wd.p))) 155 | return wr, empty, 1 156 | } 157 | 158 | // String is for debugging only. 159 | func (wr FrameWriteRequest) String() string { 160 | var des string 161 | if s, ok := wr.write.(fmt.Stringer); ok { 162 | des = s.String() 163 | } else { 164 | des = fmt.Sprintf("%T", wr.write) 165 | } 166 | return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des) 167 | } 168 | 169 | // replyToWriter sends err to wr.done and panics if the send must block 170 | // This does nothing if wr.done is nil. 171 | func (wr *FrameWriteRequest) replyToWriter(err error) { 172 | if wr.done == nil { 173 | return 174 | } 175 | select { 176 | case wr.done <- err: 177 | default: 178 | panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write)) 179 | } 180 | wr.write = nil // prevent use (assume it's tainted after wr.done send) 181 | } 182 | 183 | // writeQueue is used by implementations of WriteScheduler. 184 | type writeQueue struct { 185 | s []FrameWriteRequest 186 | } 187 | 188 | func (q *writeQueue) empty() bool { return len(q.s) == 0 } 189 | 190 | func (q *writeQueue) push(wr FrameWriteRequest) { 191 | q.s = append(q.s, wr) 192 | } 193 | 194 | func (q *writeQueue) shift() FrameWriteRequest { 195 | if len(q.s) == 0 { 196 | panic("invalid use of queue") 197 | } 198 | wr := q.s[0] 199 | // TODO: less copy-happy queue. 200 | copy(q.s, q.s[1:]) 201 | q.s[len(q.s)-1] = FrameWriteRequest{} 202 | q.s = q.s[:len(q.s)-1] 203 | return wr 204 | } 205 | 206 | // consume consumes up to n bytes from q.s[0]. If the frame is 207 | // entirely consumed, it is removed from the queue. If the frame 208 | // is partially consumed, the frame is kept with the consumed 209 | // bytes removed. Returns true iff any bytes were consumed. 210 | func (q *writeQueue) consume(n int32) (FrameWriteRequest, bool) { 211 | if len(q.s) == 0 { 212 | return FrameWriteRequest{}, false 213 | } 214 | consumed, rest, numresult := q.s[0].Consume(n) 215 | switch numresult { 216 | case 0: 217 | return FrameWriteRequest{}, false 218 | case 1: 219 | q.shift() 220 | case 2: 221 | q.s[0] = rest 222 | } 223 | return consumed, true 224 | } 225 | 226 | type writeQueuePool []*writeQueue 227 | 228 | // put inserts an unused writeQueue into the pool. 229 | func (p *writeQueuePool) put(q *writeQueue) { 230 | for i := range q.s { 231 | q.s[i] = FrameWriteRequest{} 232 | } 233 | q.s = q.s[:0] 234 | *p = append(*p, q) 235 | } 236 | 237 | // get returns an empty writeQueue. 238 | func (p *writeQueuePool) get() *writeQueue { 239 | ln := len(*p) 240 | if ln == 0 { 241 | return new(writeQueue) 242 | } 243 | x := ln - 1 244 | q := (*p)[x] 245 | (*p)[x] = nil 246 | *p = (*p)[:x] 247 | return q 248 | } 249 | -------------------------------------------------------------------------------- /http2/client_conn_pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Transport code's client connection pooling. 6 | 7 | package http2 8 | 9 | import ( 10 | "crypto/tls" 11 | "net/http" 12 | "sync" 13 | ) 14 | 15 | // ClientConnPool manages a pool of HTTP/2 client connections. 16 | type ClientConnPool interface { 17 | GetClientConn(req *http.Request, addr string) (*ClientConn, error) 18 | MarkDead(*ClientConn) 19 | } 20 | 21 | // clientConnPoolIdleCloser is the interface implemented by ClientConnPool 22 | // implementations which can close their idle connections. 23 | type clientConnPoolIdleCloser interface { 24 | ClientConnPool 25 | closeIdleConnections() 26 | } 27 | 28 | var ( 29 | _ clientConnPoolIdleCloser = (*clientConnPool)(nil) 30 | _ clientConnPoolIdleCloser = noDialClientConnPool{} 31 | ) 32 | 33 | // TODO: use singleflight for dialing and addConnCalls? 34 | type clientConnPool struct { 35 | t *Transport 36 | 37 | mu sync.Mutex // TODO: maybe switch to RWMutex 38 | // TODO: add support for sharing conns based on cert names 39 | // (e.g. share conn for googleapis.com and appspot.com) 40 | conns map[string][]*ClientConn // key is host:port 41 | dialing map[string]*dialCall // currently in-flight dials 42 | keys map[*ClientConn][]string 43 | addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls 44 | } 45 | 46 | func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) { 47 | return p.getClientConn(req, addr, dialOnMiss) 48 | } 49 | 50 | const ( 51 | dialOnMiss = true 52 | noDialOnMiss = false 53 | ) 54 | 55 | // shouldTraceGetConn reports whether getClientConn should call any 56 | // ClientTrace.GetConn hook associated with the http.Request. 57 | // 58 | // This complexity is needed to avoid double calls of the GetConn hook 59 | // during the back-and-forth between net/http and x/net/http2 (when the 60 | // net/http.Transport is upgraded to also speak http2), as well as support 61 | // the case where x/net/http2 is being used directly. 62 | func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool { 63 | // If our Transport wasn't made via ConfigureTransport, always 64 | // trace the GetConn hook if provided, because that means the 65 | // http2 package is being used directly and it's the one 66 | // dialing, as opposed to net/http. 67 | if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok { 68 | return true 69 | } 70 | // Otherwise, only use the GetConn hook if this connection has 71 | // been used previously for other requests. For fresh 72 | // connections, the net/http package does the dialing. 73 | return !st.freshConn 74 | } 75 | 76 | func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) { 77 | if isConnectionCloseRequest(req) && dialOnMiss { 78 | // It gets its own connection. 79 | traceGetConn(req, addr) 80 | const singleUse = true 81 | cc, err := p.t.dialClientConn(addr, singleUse) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return cc, nil 86 | } 87 | p.mu.Lock() 88 | for _, cc := range p.conns[addr] { 89 | if st := cc.idleState(); st.canTakeNewRequest { 90 | if p.shouldTraceGetConn(st) { 91 | traceGetConn(req, addr) 92 | } 93 | p.mu.Unlock() 94 | return cc, nil 95 | } 96 | } 97 | if !dialOnMiss { 98 | p.mu.Unlock() 99 | return nil, ErrNoCachedConn 100 | } 101 | traceGetConn(req, addr) 102 | call := p.getStartDialLocked(addr) 103 | p.mu.Unlock() 104 | <-call.done 105 | return call.res, call.err 106 | } 107 | 108 | // dialCall is an in-flight Transport dial call to a host. 109 | type dialCall struct { 110 | _ incomparable 111 | p *clientConnPool 112 | done chan struct{} // closed when done 113 | res *ClientConn // valid after done is closed 114 | err error // valid after done is closed 115 | } 116 | 117 | // requires p.mu is held. 118 | func (p *clientConnPool) getStartDialLocked(addr string) *dialCall { 119 | if call, ok := p.dialing[addr]; ok { 120 | // A dial is already in-flight. Don't start another. 121 | return call 122 | } 123 | call := &dialCall{p: p, done: make(chan struct{})} 124 | if p.dialing == nil { 125 | p.dialing = make(map[string]*dialCall) 126 | } 127 | p.dialing[addr] = call 128 | go call.dial(addr) 129 | return call 130 | } 131 | 132 | // run in its own goroutine. 133 | func (c *dialCall) dial(addr string) { 134 | const singleUse = false // shared conn 135 | c.res, c.err = c.p.t.dialClientConn(addr, singleUse) 136 | close(c.done) 137 | 138 | c.p.mu.Lock() 139 | delete(c.p.dialing, addr) 140 | if c.err == nil { 141 | c.p.addConnLocked(addr, c.res) 142 | } 143 | c.p.mu.Unlock() 144 | } 145 | 146 | // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't 147 | // already exist. It coalesces concurrent calls with the same key. 148 | // This is used by the http1 Transport code when it creates a new connection. Because 149 | // the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know 150 | // the protocol), it can get into a situation where it has multiple TLS connections. 151 | // This code decides which ones live or die. 152 | // The return value used is whether c was used. 153 | // c is never closed. 154 | func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) { 155 | p.mu.Lock() 156 | for _, cc := range p.conns[key] { 157 | if cc.CanTakeNewRequest() { 158 | p.mu.Unlock() 159 | return false, nil 160 | } 161 | } 162 | call, dup := p.addConnCalls[key] 163 | if !dup { 164 | if p.addConnCalls == nil { 165 | p.addConnCalls = make(map[string]*addConnCall) 166 | } 167 | call = &addConnCall{ 168 | p: p, 169 | done: make(chan struct{}), 170 | } 171 | p.addConnCalls[key] = call 172 | go call.run(t, key, c) 173 | } 174 | p.mu.Unlock() 175 | 176 | <-call.done 177 | if call.err != nil { 178 | return false, call.err 179 | } 180 | return !dup, nil 181 | } 182 | 183 | type addConnCall struct { 184 | _ incomparable 185 | p *clientConnPool 186 | done chan struct{} // closed when done 187 | err error 188 | } 189 | 190 | func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) { 191 | cc, err := t.NewClientConn(tc) 192 | 193 | p := c.p 194 | p.mu.Lock() 195 | if err != nil { 196 | c.err = err 197 | } else { 198 | p.addConnLocked(key, cc) 199 | } 200 | delete(p.addConnCalls, key) 201 | p.mu.Unlock() 202 | close(c.done) 203 | } 204 | 205 | // p.mu must be held 206 | func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) { 207 | for _, v := range p.conns[key] { 208 | if v == cc { 209 | return 210 | } 211 | } 212 | if p.conns == nil { 213 | p.conns = make(map[string][]*ClientConn) 214 | } 215 | if p.keys == nil { 216 | p.keys = make(map[*ClientConn][]string) 217 | } 218 | p.conns[key] = append(p.conns[key], cc) 219 | p.keys[cc] = append(p.keys[cc], key) 220 | } 221 | 222 | func (p *clientConnPool) MarkDead(cc *ClientConn) { 223 | p.mu.Lock() 224 | defer p.mu.Unlock() 225 | for _, key := range p.keys[cc] { 226 | vv, ok := p.conns[key] 227 | if !ok { 228 | continue 229 | } 230 | newList := filterOutClientConn(vv, cc) 231 | if len(newList) > 0 { 232 | p.conns[key] = newList 233 | } else { 234 | delete(p.conns, key) 235 | } 236 | } 237 | delete(p.keys, cc) 238 | } 239 | 240 | func (p *clientConnPool) closeIdleConnections() { 241 | p.mu.Lock() 242 | defer p.mu.Unlock() 243 | // TODO: don't close a cc if it was just added to the pool 244 | // milliseconds ago and has never been used. There's currently 245 | // a small race window with the HTTP/1 Transport's integration 246 | // where it can add an idle conn just before using it, and 247 | // somebody else can concurrently call CloseIdleConns and 248 | // break some caller's RoundTrip. 249 | for _, vv := range p.conns { 250 | for _, cc := range vv { 251 | cc.closeIfIdle() 252 | } 253 | } 254 | } 255 | 256 | func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn { 257 | out := in[:0] 258 | for _, v := range in { 259 | if v != exclude { 260 | out = append(out, v) 261 | } 262 | } 263 | // If we filtered it out, zero out the last item to prevent 264 | // the GC from seeing it. 265 | if len(in) != len(out) { 266 | in[len(in)-1] = nil 267 | } 268 | return out 269 | } 270 | 271 | // noDialClientConnPool is an implementation of http2.ClientConnPool 272 | // which never dials. We let the HTTP/1.1 client dial and use its TLS 273 | // connection instead. 274 | type noDialClientConnPool struct{ *clientConnPool } 275 | 276 | func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) { 277 | return p.getClientConn(req, addr, noDialOnMiss) 278 | } 279 | -------------------------------------------------------------------------------- /http2/z_spec_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "bytes" 9 | "encoding/xml" 10 | "flag" 11 | "fmt" 12 | "io" 13 | "os" 14 | "reflect" 15 | "regexp" 16 | "sort" 17 | "strconv" 18 | "strings" 19 | "sync" 20 | "testing" 21 | ) 22 | 23 | var coverSpec = flag.Bool("coverspec", false, "Run spec coverage tests") 24 | 25 | // The global map of sentence coverage for the http2 spec. 26 | var defaultSpecCoverage specCoverage 27 | 28 | var loadSpecOnce sync.Once 29 | 30 | func loadSpec() { 31 | if f, err := os.Open("testdata/draft-ietf-httpbis-http2.xml"); err != nil { 32 | panic(err) 33 | } else { 34 | defaultSpecCoverage = readSpecCov(f) 35 | f.Close() 36 | } 37 | } 38 | 39 | // covers marks all sentences for section sec in defaultSpecCoverage. Sentences not 40 | // "covered" will be included in report outputted by TestSpecCoverage. 41 | func covers(sec, sentences string) { 42 | loadSpecOnce.Do(loadSpec) 43 | defaultSpecCoverage.cover(sec, sentences) 44 | } 45 | 46 | type specPart struct { 47 | section string 48 | sentence string 49 | } 50 | 51 | func (ss specPart) Less(oo specPart) bool { 52 | atoi := func(s string) int { 53 | n, err := strconv.Atoi(s) 54 | if err != nil { 55 | panic(err) 56 | } 57 | return n 58 | } 59 | a := strings.Split(ss.section, ".") 60 | b := strings.Split(oo.section, ".") 61 | for len(a) > 0 { 62 | if len(b) == 0 { 63 | return false 64 | } 65 | x, y := atoi(a[0]), atoi(b[0]) 66 | if x == y { 67 | a, b = a[1:], b[1:] 68 | continue 69 | } 70 | return x < y 71 | } 72 | if len(b) > 0 { 73 | return true 74 | } 75 | return false 76 | } 77 | 78 | type bySpecSection []specPart 79 | 80 | func (a bySpecSection) Len() int { return len(a) } 81 | func (a bySpecSection) Less(i, j int) bool { return a[i].Less(a[j]) } 82 | func (a bySpecSection) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 83 | 84 | type specCoverage struct { 85 | coverage map[specPart]bool 86 | d *xml.Decoder 87 | } 88 | 89 | func joinSection(sec []int) string { 90 | s := fmt.Sprintf("%d", sec[0]) 91 | for _, n := range sec[1:] { 92 | s = fmt.Sprintf("%s.%d", s, n) 93 | } 94 | return s 95 | } 96 | 97 | func (sc specCoverage) readSection(sec []int) { 98 | var ( 99 | buf = new(bytes.Buffer) 100 | sub = 0 101 | ) 102 | for { 103 | tk, err := sc.d.Token() 104 | if err != nil { 105 | if err == io.EOF { 106 | return 107 | } 108 | panic(err) 109 | } 110 | switch v := tk.(type) { 111 | case xml.StartElement: 112 | if skipElement(v) { 113 | if err := sc.d.Skip(); err != nil { 114 | panic(err) 115 | } 116 | if v.Name.Local == "section" { 117 | sub++ 118 | } 119 | break 120 | } 121 | switch v.Name.Local { 122 | case "section": 123 | sub++ 124 | sc.readSection(append(sec, sub)) 125 | case "xref": 126 | buf.Write(sc.readXRef(v)) 127 | } 128 | case xml.CharData: 129 | if len(sec) == 0 { 130 | break 131 | } 132 | buf.Write(v) 133 | case xml.EndElement: 134 | if v.Name.Local == "section" { 135 | sc.addSentences(joinSection(sec), buf.String()) 136 | return 137 | } 138 | } 139 | } 140 | } 141 | 142 | func (sc specCoverage) readXRef(se xml.StartElement) []byte { 143 | var b []byte 144 | for { 145 | tk, err := sc.d.Token() 146 | if err != nil { 147 | panic(err) 148 | } 149 | switch v := tk.(type) { 150 | case xml.CharData: 151 | if b != nil { 152 | panic("unexpected CharData") 153 | } 154 | b = []byte(string(v)) 155 | case xml.EndElement: 156 | if v.Name.Local != "xref" { 157 | panic("expected ") 158 | } 159 | if b != nil { 160 | return b 161 | } 162 | sig := attrSig(se) 163 | switch sig { 164 | case "target": 165 | return []byte(fmt.Sprintf("[%s]", attrValue(se, "target"))) 166 | case "fmt-of,rel,target", "fmt-,,rel,target": 167 | return []byte(fmt.Sprintf("[%s, %s]", attrValue(se, "target"), attrValue(se, "rel"))) 168 | case "fmt-of,sec,target", "fmt-,,sec,target": 169 | return []byte(fmt.Sprintf("[section %s of %s]", attrValue(se, "sec"), attrValue(se, "target"))) 170 | case "fmt-of,rel,sec,target": 171 | return []byte(fmt.Sprintf("[section %s of %s, %s]", attrValue(se, "sec"), attrValue(se, "target"), attrValue(se, "rel"))) 172 | default: 173 | panic(fmt.Sprintf("unknown attribute signature %q in %#v", sig, fmt.Sprintf("%#v", se))) 174 | } 175 | default: 176 | panic(fmt.Sprintf("unexpected tag %q", v)) 177 | } 178 | } 179 | } 180 | 181 | var skipAnchor = map[string]bool{ 182 | "intro": true, 183 | "Overview": true, 184 | } 185 | 186 | var skipTitle = map[string]bool{ 187 | "Acknowledgements": true, 188 | "Change Log": true, 189 | "Document Organization": true, 190 | "Conventions and Terminology": true, 191 | } 192 | 193 | func skipElement(s xml.StartElement) bool { 194 | switch s.Name.Local { 195 | case "artwork": 196 | return true 197 | case "section": 198 | for _, attr := range s.Attr { 199 | switch attr.Name.Local { 200 | case "anchor": 201 | if skipAnchor[attr.Value] || strings.HasPrefix(attr.Value, "changes.since.") { 202 | return true 203 | } 204 | case "title": 205 | if skipTitle[attr.Value] { 206 | return true 207 | } 208 | } 209 | } 210 | } 211 | return false 212 | } 213 | 214 | func readSpecCov(r io.Reader) specCoverage { 215 | sc := specCoverage{ 216 | coverage: map[specPart]bool{}, 217 | d: xml.NewDecoder(r)} 218 | sc.readSection(nil) 219 | return sc 220 | } 221 | 222 | func (sc specCoverage) addSentences(sec string, sentence string) { 223 | for _, s := range parseSentences(sentence) { 224 | sc.coverage[specPart{sec, s}] = false 225 | } 226 | } 227 | 228 | func (sc specCoverage) cover(sec string, sentence string) { 229 | for _, s := range parseSentences(sentence) { 230 | p := specPart{sec, s} 231 | if _, ok := sc.coverage[p]; !ok { 232 | panic(fmt.Sprintf("Not found in spec: %q, %q", sec, s)) 233 | } 234 | sc.coverage[specPart{sec, s}] = true 235 | } 236 | 237 | } 238 | 239 | var whitespaceRx = regexp.MustCompile(`\s+`) 240 | 241 | func parseSentences(sens string) []string { 242 | sens = strings.TrimSpace(sens) 243 | if sens == "" { 244 | return nil 245 | } 246 | ss := strings.Split(whitespaceRx.ReplaceAllString(sens, " "), ". ") 247 | for i, s := range ss { 248 | s = strings.TrimSpace(s) 249 | if !strings.HasSuffix(s, ".") { 250 | s += "." 251 | } 252 | ss[i] = s 253 | } 254 | return ss 255 | } 256 | 257 | func TestSpecParseSentences(t *testing.T) { 258 | tests := []struct { 259 | ss string 260 | want []string 261 | }{ 262 | {"Sentence 1. Sentence 2.", 263 | []string{ 264 | "Sentence 1.", 265 | "Sentence 2.", 266 | }}, 267 | {"Sentence 1. \nSentence 2.\tSentence 3.", 268 | []string{ 269 | "Sentence 1.", 270 | "Sentence 2.", 271 | "Sentence 3.", 272 | }}, 273 | } 274 | 275 | for i, tt := range tests { 276 | got := parseSentences(tt.ss) 277 | if !reflect.DeepEqual(got, tt.want) { 278 | t.Errorf("%d: got = %q, want %q", i, got, tt.want) 279 | } 280 | } 281 | } 282 | 283 | func TestSpecCoverage(t *testing.T) { 284 | if !*coverSpec { 285 | t.Skip() 286 | } 287 | 288 | loadSpecOnce.Do(loadSpec) 289 | 290 | var ( 291 | list []specPart 292 | cv = defaultSpecCoverage.coverage 293 | total = len(cv) 294 | complete = 0 295 | ) 296 | 297 | for sp, touched := range defaultSpecCoverage.coverage { 298 | if touched { 299 | complete++ 300 | } else { 301 | list = append(list, sp) 302 | } 303 | } 304 | sort.Stable(bySpecSection(list)) 305 | 306 | if testing.Short() && len(list) > 5 { 307 | list = list[:5] 308 | } 309 | 310 | for _, p := range list { 311 | t.Errorf("\tSECTION %s: %s", p.section, p.sentence) 312 | } 313 | 314 | t.Logf("%d/%d (%d%%) sentences covered", complete, total, (complete/total)*100) 315 | } 316 | 317 | func attrSig(se xml.StartElement) string { 318 | var names []string 319 | for _, attr := range se.Attr { 320 | if attr.Name.Local == "fmt" { 321 | names = append(names, "fmt-"+attr.Value) 322 | } else { 323 | names = append(names, attr.Name.Local) 324 | } 325 | } 326 | sort.Strings(names) 327 | return strings.Join(names, ",") 328 | } 329 | 330 | func attrValue(se xml.StartElement, attr string) string { 331 | for _, a := range se.Attr { 332 | if a.Name.Local == attr { 333 | return a.Value 334 | } 335 | } 336 | panic("unknown attribute " + attr) 337 | } 338 | 339 | func TestSpecPartLess(t *testing.T) { 340 | tests := []struct { 341 | sec1, sec2 string 342 | want bool 343 | }{ 344 | {"6.2.1", "6.2", false}, 345 | {"6.2", "6.2.1", true}, 346 | {"6.10", "6.10.1", true}, 347 | {"6.10", "6.1.1", false}, // 10, not 1 348 | {"6.1", "6.1", false}, // equal, so not less 349 | } 350 | for _, tt := range tests { 351 | got := (specPart{tt.sec1, "foo"}).Less(specPart{tt.sec2, "foo"}) 352 | if got != tt.want { 353 | t.Errorf("Less(%q, %q) = %v; want %v", tt.sec1, tt.sec2, got, tt.want) 354 | } 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /http2/hpack/tables_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hpack 6 | 7 | import ( 8 | "bufio" 9 | "regexp" 10 | "strconv" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func TestHeaderFieldTable(t *testing.T) { 16 | table := &headerFieldTable{} 17 | table.init() 18 | table.addEntry(pair("key1", "value1-1")) 19 | table.addEntry(pair("key2", "value2-1")) 20 | table.addEntry(pair("key1", "value1-2")) 21 | table.addEntry(pair("key3", "value3-1")) 22 | table.addEntry(pair("key4", "value4-1")) 23 | table.addEntry(pair("key2", "value2-2")) 24 | 25 | // Tests will be run twice: once before evicting anything, and 26 | // again after evicting the three oldest entries. 27 | tests := []struct { 28 | f HeaderField 29 | beforeWantStaticI uint64 30 | beforeWantMatch bool 31 | afterWantStaticI uint64 32 | afterWantMatch bool 33 | }{ 34 | {HeaderField{"key1", "value1-1", false}, 1, true, 0, false}, 35 | {HeaderField{"key1", "value1-2", false}, 3, true, 0, false}, 36 | {HeaderField{"key1", "value1-3", false}, 3, false, 0, false}, 37 | {HeaderField{"key2", "value2-1", false}, 2, true, 3, false}, 38 | {HeaderField{"key2", "value2-2", false}, 6, true, 3, true}, 39 | {HeaderField{"key2", "value2-3", false}, 6, false, 3, false}, 40 | {HeaderField{"key4", "value4-1", false}, 5, true, 2, true}, 41 | // Name match only, because sensitive. 42 | {HeaderField{"key4", "value4-1", true}, 5, false, 2, false}, 43 | // Key not found. 44 | {HeaderField{"key5", "value5-x", false}, 0, false, 0, false}, 45 | } 46 | 47 | staticToDynamic := func(i uint64) uint64 { 48 | if i == 0 { 49 | return 0 50 | } 51 | return uint64(table.len()) - i + 1 // dynamic is the reversed table 52 | } 53 | 54 | searchStatic := func(f HeaderField) (uint64, bool) { 55 | old := staticTable 56 | staticTable = table 57 | defer func() { staticTable = old }() 58 | return staticTable.search(f) 59 | } 60 | 61 | searchDynamic := func(f HeaderField) (uint64, bool) { 62 | return table.search(f) 63 | } 64 | 65 | for _, test := range tests { 66 | gotI, gotMatch := searchStatic(test.f) 67 | if wantI, wantMatch := test.beforeWantStaticI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch { 68 | t.Errorf("before evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch) 69 | } 70 | gotI, gotMatch = searchDynamic(test.f) 71 | wantDynamicI := staticToDynamic(test.beforeWantStaticI) 72 | if wantI, wantMatch := wantDynamicI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch { 73 | t.Errorf("before evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch) 74 | } 75 | } 76 | 77 | table.evictOldest(3) 78 | 79 | for _, test := range tests { 80 | gotI, gotMatch := searchStatic(test.f) 81 | if wantI, wantMatch := test.afterWantStaticI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch { 82 | t.Errorf("after evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch) 83 | } 84 | gotI, gotMatch = searchDynamic(test.f) 85 | wantDynamicI := staticToDynamic(test.afterWantStaticI) 86 | if wantI, wantMatch := wantDynamicI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch { 87 | t.Errorf("after evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch) 88 | } 89 | } 90 | } 91 | 92 | func TestHeaderFieldTable_LookupMapEviction(t *testing.T) { 93 | table := &headerFieldTable{} 94 | table.init() 95 | table.addEntry(pair("key1", "value1-1")) 96 | table.addEntry(pair("key2", "value2-1")) 97 | table.addEntry(pair("key1", "value1-2")) 98 | table.addEntry(pair("key3", "value3-1")) 99 | table.addEntry(pair("key4", "value4-1")) 100 | table.addEntry(pair("key2", "value2-2")) 101 | 102 | // evict all pairs 103 | table.evictOldest(table.len()) 104 | 105 | if l := table.len(); l > 0 { 106 | t.Errorf("table.len() = %d, want 0", l) 107 | } 108 | 109 | if l := len(table.byName); l > 0 { 110 | t.Errorf("len(table.byName) = %d, want 0", l) 111 | } 112 | 113 | if l := len(table.byNameValue); l > 0 { 114 | t.Errorf("len(table.byNameValue) = %d, want 0", l) 115 | } 116 | } 117 | 118 | func TestStaticTable(t *testing.T) { 119 | fromSpec := ` 120 | +-------+-----------------------------+---------------+ 121 | | 1 | :authority | | 122 | | 2 | :method | GET | 123 | | 3 | :method | POST | 124 | | 4 | :path | / | 125 | | 5 | :path | /index.html | 126 | | 6 | :scheme | http | 127 | | 7 | :scheme | https | 128 | | 8 | :status | 200 | 129 | | 9 | :status | 204 | 130 | | 10 | :status | 206 | 131 | | 11 | :status | 304 | 132 | | 12 | :status | 400 | 133 | | 13 | :status | 404 | 134 | | 14 | :status | 500 | 135 | | 15 | accept-charset | | 136 | | 16 | accept-encoding | gzip, deflate | 137 | | 17 | accept-language | | 138 | | 18 | accept-ranges | | 139 | | 19 | accept | | 140 | | 20 | access-control-allow-origin | | 141 | | 21 | age | | 142 | | 22 | allow | | 143 | | 23 | authorization | | 144 | | 24 | cache-control | | 145 | | 25 | content-disposition | | 146 | | 26 | content-encoding | | 147 | | 27 | content-language | | 148 | | 28 | content-length | | 149 | | 29 | content-location | | 150 | | 30 | content-range | | 151 | | 31 | content-type | | 152 | | 32 | cookie | | 153 | | 33 | date | | 154 | | 34 | etag | | 155 | | 35 | expect | | 156 | | 36 | expires | | 157 | | 37 | from | | 158 | | 38 | host | | 159 | | 39 | if-match | | 160 | | 40 | if-modified-since | | 161 | | 41 | if-none-match | | 162 | | 42 | if-range | | 163 | | 43 | if-unmodified-since | | 164 | | 44 | last-modified | | 165 | | 45 | link | | 166 | | 46 | location | | 167 | | 47 | max-forwards | | 168 | | 48 | proxy-authenticate | | 169 | | 49 | proxy-authorization | | 170 | | 50 | range | | 171 | | 51 | referer | | 172 | | 52 | refresh | | 173 | | 53 | retry-after | | 174 | | 54 | server | | 175 | | 55 | set-cookie | | 176 | | 56 | strict-transport-security | | 177 | | 57 | transfer-encoding | | 178 | | 58 | user-agent | | 179 | | 59 | vary | | 180 | | 60 | via | | 181 | | 61 | www-authenticate | | 182 | +-------+-----------------------------+---------------+ 183 | ` 184 | bs := bufio.NewScanner(strings.NewReader(fromSpec)) 185 | re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`) 186 | for bs.Scan() { 187 | l := bs.Text() 188 | if !strings.Contains(l, "|") { 189 | continue 190 | } 191 | m := re.FindStringSubmatch(l) 192 | if m == nil { 193 | continue 194 | } 195 | i, err := strconv.Atoi(m[1]) 196 | if err != nil { 197 | t.Errorf("Bogus integer on line %q", l) 198 | continue 199 | } 200 | if i < 1 || i > staticTable.len() { 201 | t.Errorf("Bogus index %d on line %q", i, l) 202 | continue 203 | } 204 | if got, want := staticTable.ents[i-1].Name, m[2]; got != want { 205 | t.Errorf("header index %d name = %q; want %q", i, got, want) 206 | } 207 | if got, want := staticTable.ents[i-1].Value, m[3]; got != want { 208 | t.Errorf("header index %d value = %q; want %q", i, got, want) 209 | } 210 | } 211 | if err := bs.Err(); err != nil { 212 | t.Error(err) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /http2/h2demo/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= 4 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 7 | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 8 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 9 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 12 | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 13 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 15 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 16 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 19 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 21 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 22 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 23 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 24 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 25 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 26 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 27 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 28 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 29 | github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc= 30 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 31 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 32 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 33 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 34 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 35 | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= 36 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 37 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 38 | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 39 | go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= 40 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 41 | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 42 | go4.org v0.0.0-20190218023631-ce4c26f7be8e h1:m9LfARr2VIOW0vsV19kEKp/sWQvZnGobA8JHui/XJoY= 43 | go4.org v0.0.0-20190218023631-ce4c26f7be8e/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 44 | golang.org/x/build v0.0.0-20190509182522-45de920fc22c h1:lgCiAYQNZHWRJOB1au5nVtnBZS1ZmKN1DpViRg23ZZc= 45 | golang.org/x/build v0.0.0-20190509182522-45de920fc22c/go.mod h1:fYw7AShPAhGMdXqA9gRadk/CcMsvLlClpE5oBwnS3dM= 46 | golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 47 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 48 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 49 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 50 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 51 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 52 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 53 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 54 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 55 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 56 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= 57 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 58 | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 59 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 60 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 61 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 63 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40= 70 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 74 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 75 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 76 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 77 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 78 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 79 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 80 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 81 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 82 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 83 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 84 | google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= 85 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 86 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 87 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 88 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= 89 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 90 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 91 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 92 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 93 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= 94 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 95 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 96 | google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= 97 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 98 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 99 | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 100 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 101 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 102 | -------------------------------------------------------------------------------- /http2/http2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package http2 implements the HTTP/2 protocol. 6 | // 7 | // This package is low-level and intended to be used directly by very 8 | // few people. Most users will use it indirectly through the automatic 9 | // use by the net/http package (from Go 1.6 and later). 10 | // For use in earlier Go versions see ConfigureServer. (Transport support 11 | // requires Go 1.6 or later) 12 | // 13 | // See https://http2.github.io/ for more information on HTTP/2. 14 | // 15 | // See https://http2.golang.org/ for a test server running this code. 16 | // 17 | package http2 // import "golang.org/x/net/http2" 18 | 19 | import ( 20 | "bufio" 21 | "crypto/tls" 22 | "fmt" 23 | "io" 24 | "net/http" 25 | "os" 26 | "sort" 27 | "strconv" 28 | "strings" 29 | "sync" 30 | 31 | "golang.org/x/net/http/httpguts" 32 | ) 33 | 34 | var ( 35 | VerboseLogs bool 36 | logFrameWrites bool 37 | logFrameReads bool 38 | inTests bool 39 | ) 40 | 41 | func init() { 42 | e := os.Getenv("GODEBUG") 43 | if strings.Contains(e, "http2debug=1") { 44 | VerboseLogs = true 45 | } 46 | if strings.Contains(e, "http2debug=2") { 47 | VerboseLogs = true 48 | logFrameWrites = true 49 | logFrameReads = true 50 | } 51 | } 52 | 53 | const ( 54 | // ClientPreface is the string that must be sent by new 55 | // connections from clients. 56 | ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 57 | 58 | // SETTINGS_MAX_FRAME_SIZE default 59 | // http://http2.github.io/http2-spec/#rfc.section.6.5.2 60 | initialMaxFrameSize = 16384 61 | 62 | // NextProtoTLS is the NPN/ALPN protocol negotiated during 63 | // HTTP/2's TLS setup. 64 | NextProtoTLS = "h2" 65 | 66 | // http://http2.github.io/http2-spec/#SettingValues 67 | initialHeaderTableSize = 4096 68 | 69 | initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size 70 | 71 | defaultMaxReadFrameSize = 1 << 20 72 | ) 73 | 74 | var ( 75 | clientPreface = []byte(ClientPreface) 76 | ) 77 | 78 | type streamState int 79 | 80 | // HTTP/2 stream states. 81 | // 82 | // See http://tools.ietf.org/html/rfc7540#section-5.1. 83 | // 84 | // For simplicity, the server code merges "reserved (local)" into 85 | // "half-closed (remote)". This is one less state transition to track. 86 | // The only downside is that we send PUSH_PROMISEs slightly less 87 | // liberally than allowable. More discussion here: 88 | // https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html 89 | // 90 | // "reserved (remote)" is omitted since the client code does not 91 | // support server push. 92 | const ( 93 | stateIdle streamState = iota 94 | stateOpen 95 | stateHalfClosedLocal 96 | stateHalfClosedRemote 97 | stateClosed 98 | ) 99 | 100 | var stateName = [...]string{ 101 | stateIdle: "Idle", 102 | stateOpen: "Open", 103 | stateHalfClosedLocal: "HalfClosedLocal", 104 | stateHalfClosedRemote: "HalfClosedRemote", 105 | stateClosed: "Closed", 106 | } 107 | 108 | func (st streamState) String() string { 109 | return stateName[st] 110 | } 111 | 112 | // Setting is a setting parameter: which setting it is, and its value. 113 | type Setting struct { 114 | // ID is which setting is being set. 115 | // See http://http2.github.io/http2-spec/#SettingValues 116 | ID SettingID 117 | 118 | // Val is the value. 119 | Val uint32 120 | } 121 | 122 | func (s Setting) String() string { 123 | return fmt.Sprintf("[%v = %d]", s.ID, s.Val) 124 | } 125 | 126 | // Valid reports whether the setting is valid. 127 | func (s Setting) Valid() error { 128 | // Limits and error codes from 6.5.2 Defined SETTINGS Parameters 129 | switch s.ID { 130 | case SettingEnablePush: 131 | if s.Val != 1 && s.Val != 0 { 132 | return ConnectionError(ErrCodeProtocol) 133 | } 134 | case SettingInitialWindowSize: 135 | if s.Val > 1<<31-1 { 136 | return ConnectionError(ErrCodeFlowControl) 137 | } 138 | case SettingMaxFrameSize: 139 | if s.Val < 16384 || s.Val > 1<<24-1 { 140 | return ConnectionError(ErrCodeProtocol) 141 | } 142 | } 143 | return nil 144 | } 145 | 146 | // A SettingID is an HTTP/2 setting as defined in 147 | // http://http2.github.io/http2-spec/#iana-settings 148 | type SettingID uint16 149 | 150 | const ( 151 | SettingHeaderTableSize SettingID = 0x1 152 | SettingEnablePush SettingID = 0x2 153 | SettingMaxConcurrentStreams SettingID = 0x3 154 | SettingInitialWindowSize SettingID = 0x4 155 | SettingMaxFrameSize SettingID = 0x5 156 | SettingMaxHeaderListSize SettingID = 0x6 157 | ) 158 | 159 | var settingName = map[SettingID]string{ 160 | SettingHeaderTableSize: "HEADER_TABLE_SIZE", 161 | SettingEnablePush: "ENABLE_PUSH", 162 | SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", 163 | SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", 164 | SettingMaxFrameSize: "MAX_FRAME_SIZE", 165 | SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", 166 | } 167 | 168 | func (s SettingID) String() string { 169 | if v, ok := settingName[s]; ok { 170 | return v 171 | } 172 | return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s)) 173 | } 174 | 175 | // validWireHeaderFieldName reports whether v is a valid header field 176 | // name (key). See httpguts.ValidHeaderName for the base rules. 177 | // 178 | // Further, http2 says: 179 | // "Just as in HTTP/1.x, header field names are strings of ASCII 180 | // characters that are compared in a case-insensitive 181 | // fashion. However, header field names MUST be converted to 182 | // lowercase prior to their encoding in HTTP/2. " 183 | func validWireHeaderFieldName(v string) bool { 184 | if len(v) == 0 { 185 | return false 186 | } 187 | for _, r := range v { 188 | if !httpguts.IsTokenRune(r) { 189 | return false 190 | } 191 | if 'A' <= r && r <= 'Z' { 192 | return false 193 | } 194 | } 195 | return true 196 | } 197 | 198 | func httpCodeString(code int) string { 199 | switch code { 200 | case 200: 201 | return "200" 202 | case 404: 203 | return "404" 204 | } 205 | return strconv.Itoa(code) 206 | } 207 | 208 | // from pkg io 209 | type stringWriter interface { 210 | WriteString(s string) (n int, err error) 211 | } 212 | 213 | // A gate lets two goroutines coordinate their activities. 214 | type gate chan struct{} 215 | 216 | func (g gate) Done() { g <- struct{}{} } 217 | func (g gate) Wait() { <-g } 218 | 219 | // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). 220 | type closeWaiter chan struct{} 221 | 222 | // Init makes a closeWaiter usable. 223 | // It exists because so a closeWaiter value can be placed inside a 224 | // larger struct and have the Mutex and Cond's memory in the same 225 | // allocation. 226 | func (cw *closeWaiter) Init() { 227 | *cw = make(chan struct{}) 228 | } 229 | 230 | // Close marks the closeWaiter as closed and unblocks any waiters. 231 | func (cw closeWaiter) Close() { 232 | close(cw) 233 | } 234 | 235 | // Wait waits for the closeWaiter to become closed. 236 | func (cw closeWaiter) Wait() { 237 | <-cw 238 | } 239 | 240 | // bufferedWriter is a buffered writer that writes to w. 241 | // Its buffered writer is lazily allocated as needed, to minimize 242 | // idle memory usage with many connections. 243 | type bufferedWriter struct { 244 | _ incomparable 245 | w io.Writer // immutable 246 | bw *bufio.Writer // non-nil when data is buffered 247 | } 248 | 249 | func newBufferedWriter(w io.Writer) *bufferedWriter { 250 | return &bufferedWriter{w: w} 251 | } 252 | 253 | // bufWriterPoolBufferSize is the size of bufio.Writer's 254 | // buffers created using bufWriterPool. 255 | // 256 | // TODO: pick a less arbitrary value? this is a bit under 257 | // (3 x typical 1500 byte MTU) at least. Other than that, 258 | // not much thought went into it. 259 | const bufWriterPoolBufferSize = 4 << 10 260 | 261 | var bufWriterPool = sync.Pool{ 262 | New: func() interface{} { 263 | return bufio.NewWriterSize(nil, bufWriterPoolBufferSize) 264 | }, 265 | } 266 | 267 | func (w *bufferedWriter) Available() int { 268 | if w.bw == nil { 269 | return bufWriterPoolBufferSize 270 | } 271 | return w.bw.Available() 272 | } 273 | 274 | func (w *bufferedWriter) Write(p []byte) (n int, err error) { 275 | if w.bw == nil { 276 | bw := bufWriterPool.Get().(*bufio.Writer) 277 | bw.Reset(w.w) 278 | w.bw = bw 279 | } 280 | return w.bw.Write(p) 281 | } 282 | 283 | func (w *bufferedWriter) Flush() error { 284 | bw := w.bw 285 | if bw == nil { 286 | return nil 287 | } 288 | err := bw.Flush() 289 | bw.Reset(nil) 290 | bufWriterPool.Put(bw) 291 | w.bw = nil 292 | return err 293 | } 294 | 295 | func mustUint31(v int32) uint32 { 296 | if v < 0 || v > 2147483647 { 297 | panic("out of range") 298 | } 299 | return uint32(v) 300 | } 301 | 302 | // bodyAllowedForStatus reports whether a given response status code 303 | // permits a body. See RFC 7230, section 3.3. 304 | func bodyAllowedForStatus(status int) bool { 305 | switch { 306 | case status >= 100 && status <= 199: 307 | return false 308 | case status == 204: 309 | return false 310 | case status == 304: 311 | return false 312 | } 313 | return true 314 | } 315 | 316 | type httpError struct { 317 | _ incomparable 318 | msg string 319 | timeout bool 320 | } 321 | 322 | func (e *httpError) Error() string { return e.msg } 323 | func (e *httpError) Timeout() bool { return e.timeout } 324 | func (e *httpError) Temporary() bool { return true } 325 | 326 | var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true} 327 | 328 | type connectionStater interface { 329 | ConnectionState() tls.ConnectionState 330 | } 331 | 332 | var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }} 333 | 334 | type sorter struct { 335 | v []string // owned by sorter 336 | } 337 | 338 | func (s *sorter) Len() int { return len(s.v) } 339 | func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] } 340 | func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] } 341 | 342 | // Keys returns the sorted keys of h. 343 | // 344 | // The returned slice is only valid until s used again or returned to 345 | // its pool. 346 | func (s *sorter) Keys(h http.Header) []string { 347 | keys := s.v[:0] 348 | for k := range h { 349 | keys = append(keys, k) 350 | } 351 | s.v = keys 352 | sort.Sort(s) 353 | return keys 354 | } 355 | 356 | func (s *sorter) SortStrings(ss []string) { 357 | // Our sorter works on s.v, which sorter owns, so 358 | // stash it away while we sort the user's buffer. 359 | save := s.v 360 | s.v = ss 361 | sort.Sort(s) 362 | s.v = save 363 | } 364 | 365 | // validPseudoPath reports whether v is a valid :path pseudo-header 366 | // value. It must be either: 367 | // 368 | // *) a non-empty string starting with '/' 369 | // *) the string '*', for OPTIONS requests. 370 | // 371 | // For now this is only used a quick check for deciding when to clean 372 | // up Opaque URLs before sending requests from the Transport. 373 | // See golang.org/issue/16847 374 | // 375 | // We used to enforce that the path also didn't start with "//", but 376 | // Google's GFE accepts such paths and Chrome sends them, so ignore 377 | // that part of the spec. See golang.org/issue/19103. 378 | func validPseudoPath(v string) bool { 379 | return (len(v) > 0 && v[0] == '/') || v == "*" 380 | } 381 | 382 | // incomparable is a zero-width, non-comparable type. Adding it to a struct 383 | // makes that struct also non-comparable, and generally doesn't add 384 | // any size (as long as it's first). 385 | type incomparable [0]func() 386 | -------------------------------------------------------------------------------- /http2/hpack/encode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hpack 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "fmt" 11 | "math/rand" 12 | "reflect" 13 | "strings" 14 | "testing" 15 | ) 16 | 17 | func TestEncoderTableSizeUpdate(t *testing.T) { 18 | tests := []struct { 19 | size1, size2 uint32 20 | wantHex string 21 | }{ 22 | // Should emit 2 table size updates (2048 and 4096) 23 | {2048, 4096, "3fe10f 3fe11f 82"}, 24 | 25 | // Should emit 1 table size update (2048) 26 | {16384, 2048, "3fe10f 82"}, 27 | } 28 | for _, tt := range tests { 29 | var buf bytes.Buffer 30 | e := NewEncoder(&buf) 31 | e.SetMaxDynamicTableSize(tt.size1) 32 | e.SetMaxDynamicTableSize(tt.size2) 33 | if err := e.WriteField(pair(":method", "GET")); err != nil { 34 | t.Fatal(err) 35 | } 36 | want := removeSpace(tt.wantHex) 37 | if got := hex.EncodeToString(buf.Bytes()); got != want { 38 | t.Errorf("e.SetDynamicTableSize %v, %v = %q; want %q", tt.size1, tt.size2, got, want) 39 | } 40 | } 41 | } 42 | 43 | func TestEncoderWriteField(t *testing.T) { 44 | var buf bytes.Buffer 45 | e := NewEncoder(&buf) 46 | var got []HeaderField 47 | d := NewDecoder(4<<10, func(f HeaderField) { 48 | got = append(got, f) 49 | }) 50 | 51 | tests := []struct { 52 | hdrs []HeaderField 53 | }{ 54 | {[]HeaderField{ 55 | pair(":method", "GET"), 56 | pair(":scheme", "http"), 57 | pair(":path", "/"), 58 | pair(":authority", "www.example.com"), 59 | }}, 60 | {[]HeaderField{ 61 | pair(":method", "GET"), 62 | pair(":scheme", "http"), 63 | pair(":path", "/"), 64 | pair(":authority", "www.example.com"), 65 | pair("cache-control", "no-cache"), 66 | }}, 67 | {[]HeaderField{ 68 | pair(":method", "GET"), 69 | pair(":scheme", "https"), 70 | pair(":path", "/index.html"), 71 | pair(":authority", "www.example.com"), 72 | pair("custom-key", "custom-value"), 73 | }}, 74 | } 75 | for i, tt := range tests { 76 | buf.Reset() 77 | got = got[:0] 78 | for _, hf := range tt.hdrs { 79 | if err := e.WriteField(hf); err != nil { 80 | t.Fatal(err) 81 | } 82 | } 83 | _, err := d.Write(buf.Bytes()) 84 | if err != nil { 85 | t.Errorf("%d. Decoder Write = %v", i, err) 86 | } 87 | if !reflect.DeepEqual(got, tt.hdrs) { 88 | t.Errorf("%d. Decoded %+v; want %+v", i, got, tt.hdrs) 89 | } 90 | } 91 | } 92 | 93 | func TestEncoderSearchTable(t *testing.T) { 94 | e := NewEncoder(nil) 95 | 96 | e.dynTab.add(pair("foo", "bar")) 97 | e.dynTab.add(pair("blake", "miz")) 98 | e.dynTab.add(pair(":method", "GET")) 99 | 100 | tests := []struct { 101 | hf HeaderField 102 | wantI uint64 103 | wantMatch bool 104 | }{ 105 | // Name and Value match 106 | {pair("foo", "bar"), uint64(staticTable.len()) + 3, true}, 107 | {pair("blake", "miz"), uint64(staticTable.len()) + 2, true}, 108 | {pair(":method", "GET"), 2, true}, 109 | 110 | // Only name match because Sensitive == true. This is allowed to match 111 | // any ":method" entry. The current implementation uses the last entry 112 | // added in newStaticTable. 113 | {HeaderField{":method", "GET", true}, 3, false}, 114 | 115 | // Only Name matches 116 | {pair("foo", "..."), uint64(staticTable.len()) + 3, false}, 117 | {pair("blake", "..."), uint64(staticTable.len()) + 2, false}, 118 | // As before, this is allowed to match any ":method" entry. 119 | {pair(":method", "..."), 3, false}, 120 | 121 | // None match 122 | {pair("foo-", "bar"), 0, false}, 123 | } 124 | for _, tt := range tests { 125 | if gotI, gotMatch := e.searchTable(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch { 126 | t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch) 127 | } 128 | } 129 | } 130 | 131 | func TestAppendVarInt(t *testing.T) { 132 | tests := []struct { 133 | n byte 134 | i uint64 135 | want []byte 136 | }{ 137 | // Fits in a byte: 138 | {1, 0, []byte{0}}, 139 | {2, 2, []byte{2}}, 140 | {3, 6, []byte{6}}, 141 | {4, 14, []byte{14}}, 142 | {5, 30, []byte{30}}, 143 | {6, 62, []byte{62}}, 144 | {7, 126, []byte{126}}, 145 | {8, 254, []byte{254}}, 146 | 147 | // Multiple bytes: 148 | {5, 1337, []byte{31, 154, 10}}, 149 | } 150 | for _, tt := range tests { 151 | got := appendVarInt(nil, tt.n, tt.i) 152 | if !bytes.Equal(got, tt.want) { 153 | t.Errorf("appendVarInt(nil, %v, %v) = %v; want %v", tt.n, tt.i, got, tt.want) 154 | } 155 | } 156 | } 157 | 158 | func TestAppendHpackString(t *testing.T) { 159 | tests := []struct { 160 | s, wantHex string 161 | }{ 162 | // Huffman encoded 163 | {"www.example.com", "8c f1e3 c2e5 f23a 6ba0 ab90 f4ff"}, 164 | 165 | // Not Huffman encoded 166 | {"a", "01 61"}, 167 | 168 | // zero length 169 | {"", "00"}, 170 | } 171 | for _, tt := range tests { 172 | want := removeSpace(tt.wantHex) 173 | buf := appendHpackString(nil, tt.s) 174 | if got := hex.EncodeToString(buf); want != got { 175 | t.Errorf("appendHpackString(nil, %q) = %q; want %q", tt.s, got, want) 176 | } 177 | } 178 | } 179 | 180 | func TestAppendIndexed(t *testing.T) { 181 | tests := []struct { 182 | i uint64 183 | wantHex string 184 | }{ 185 | // 1 byte 186 | {1, "81"}, 187 | {126, "fe"}, 188 | 189 | // 2 bytes 190 | {127, "ff00"}, 191 | {128, "ff01"}, 192 | } 193 | for _, tt := range tests { 194 | want := removeSpace(tt.wantHex) 195 | buf := appendIndexed(nil, tt.i) 196 | if got := hex.EncodeToString(buf); want != got { 197 | t.Errorf("appendIndex(nil, %v) = %q; want %q", tt.i, got, want) 198 | } 199 | } 200 | } 201 | 202 | func TestAppendNewName(t *testing.T) { 203 | tests := []struct { 204 | f HeaderField 205 | indexing bool 206 | wantHex string 207 | }{ 208 | // Incremental indexing 209 | {HeaderField{"custom-key", "custom-value", false}, true, "40 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"}, 210 | 211 | // Without indexing 212 | {HeaderField{"custom-key", "custom-value", false}, false, "00 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"}, 213 | 214 | // Never indexed 215 | {HeaderField{"custom-key", "custom-value", true}, true, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"}, 216 | {HeaderField{"custom-key", "custom-value", true}, false, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"}, 217 | } 218 | for _, tt := range tests { 219 | want := removeSpace(tt.wantHex) 220 | buf := appendNewName(nil, tt.f, tt.indexing) 221 | if got := hex.EncodeToString(buf); want != got { 222 | t.Errorf("appendNewName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want) 223 | } 224 | } 225 | } 226 | 227 | func TestAppendIndexedName(t *testing.T) { 228 | tests := []struct { 229 | f HeaderField 230 | i uint64 231 | indexing bool 232 | wantHex string 233 | }{ 234 | // Incremental indexing 235 | {HeaderField{":status", "302", false}, 8, true, "48 82 6402"}, 236 | 237 | // Without indexing 238 | {HeaderField{":status", "302", false}, 8, false, "08 82 6402"}, 239 | 240 | // Never indexed 241 | {HeaderField{":status", "302", true}, 8, true, "18 82 6402"}, 242 | {HeaderField{":status", "302", true}, 8, false, "18 82 6402"}, 243 | } 244 | for _, tt := range tests { 245 | want := removeSpace(tt.wantHex) 246 | buf := appendIndexedName(nil, tt.f, tt.i, tt.indexing) 247 | if got := hex.EncodeToString(buf); want != got { 248 | t.Errorf("appendIndexedName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want) 249 | } 250 | } 251 | } 252 | 253 | func TestAppendTableSize(t *testing.T) { 254 | tests := []struct { 255 | i uint32 256 | wantHex string 257 | }{ 258 | // Fits into 1 byte 259 | {30, "3e"}, 260 | 261 | // Extra byte 262 | {31, "3f00"}, 263 | {32, "3f01"}, 264 | } 265 | for _, tt := range tests { 266 | want := removeSpace(tt.wantHex) 267 | buf := appendTableSize(nil, tt.i) 268 | if got := hex.EncodeToString(buf); want != got { 269 | t.Errorf("appendTableSize(nil, %v) = %q; want %q", tt.i, got, want) 270 | } 271 | } 272 | } 273 | 274 | func TestEncoderSetMaxDynamicTableSize(t *testing.T) { 275 | var buf bytes.Buffer 276 | e := NewEncoder(&buf) 277 | tests := []struct { 278 | v uint32 279 | wantUpdate bool 280 | wantMinSize uint32 281 | wantMaxSize uint32 282 | }{ 283 | // Set new table size to 2048 284 | {2048, true, 2048, 2048}, 285 | 286 | // Set new table size to 16384, but still limited to 287 | // 4096 288 | {16384, true, 2048, 4096}, 289 | } 290 | for _, tt := range tests { 291 | e.SetMaxDynamicTableSize(tt.v) 292 | if got := e.tableSizeUpdate; tt.wantUpdate != got { 293 | t.Errorf("e.tableSizeUpdate = %v; want %v", got, tt.wantUpdate) 294 | } 295 | if got := e.minSize; tt.wantMinSize != got { 296 | t.Errorf("e.minSize = %v; want %v", got, tt.wantMinSize) 297 | } 298 | if got := e.dynTab.maxSize; tt.wantMaxSize != got { 299 | t.Errorf("e.maxSize = %v; want %v", got, tt.wantMaxSize) 300 | } 301 | } 302 | } 303 | 304 | func TestEncoderSetMaxDynamicTableSizeLimit(t *testing.T) { 305 | e := NewEncoder(nil) 306 | // 4095 < initialHeaderTableSize means maxSize is truncated to 307 | // 4095. 308 | e.SetMaxDynamicTableSizeLimit(4095) 309 | if got, want := e.dynTab.maxSize, uint32(4095); got != want { 310 | t.Errorf("e.dynTab.maxSize = %v; want %v", got, want) 311 | } 312 | if got, want := e.maxSizeLimit, uint32(4095); got != want { 313 | t.Errorf("e.maxSizeLimit = %v; want %v", got, want) 314 | } 315 | if got, want := e.tableSizeUpdate, true; got != want { 316 | t.Errorf("e.tableSizeUpdate = %v; want %v", got, want) 317 | } 318 | // maxSize will be truncated to maxSizeLimit 319 | e.SetMaxDynamicTableSize(16384) 320 | if got, want := e.dynTab.maxSize, uint32(4095); got != want { 321 | t.Errorf("e.dynTab.maxSize = %v; want %v", got, want) 322 | } 323 | // 8192 > current maxSizeLimit, so maxSize does not change. 324 | e.SetMaxDynamicTableSizeLimit(8192) 325 | if got, want := e.dynTab.maxSize, uint32(4095); got != want { 326 | t.Errorf("e.dynTab.maxSize = %v; want %v", got, want) 327 | } 328 | if got, want := e.maxSizeLimit, uint32(8192); got != want { 329 | t.Errorf("e.maxSizeLimit = %v; want %v", got, want) 330 | } 331 | } 332 | 333 | func removeSpace(s string) string { 334 | return strings.Replace(s, " ", "", -1) 335 | } 336 | 337 | func BenchmarkEncoderSearchTable(b *testing.B) { 338 | e := NewEncoder(nil) 339 | 340 | // A sample of possible header fields. 341 | // This is not based on any actual data from HTTP/2 traces. 342 | var possible []HeaderField 343 | for _, f := range staticTable.ents { 344 | if f.Value == "" { 345 | possible = append(possible, f) 346 | continue 347 | } 348 | // Generate 5 random values, except for cookie and set-cookie, 349 | // which we know can have many values in practice. 350 | num := 5 351 | if f.Name == "cookie" || f.Name == "set-cookie" { 352 | num = 25 353 | } 354 | for i := 0; i < num; i++ { 355 | f.Value = fmt.Sprintf("%s-%d", f.Name, i) 356 | possible = append(possible, f) 357 | } 358 | } 359 | for k := 0; k < 10; k++ { 360 | f := HeaderField{ 361 | Name: fmt.Sprintf("x-header-%d", k), 362 | Sensitive: rand.Int()%2 == 0, 363 | } 364 | for i := 0; i < 5; i++ { 365 | f.Value = fmt.Sprintf("%s-%d", f.Name, i) 366 | possible = append(possible, f) 367 | } 368 | } 369 | 370 | // Add a random sample to the dynamic table. This very loosely simulates 371 | // a history of 100 requests with 20 header fields per request. 372 | for r := 0; r < 100*20; r++ { 373 | f := possible[rand.Int31n(int32(len(possible)))] 374 | // Skip if this is in the staticTable verbatim. 375 | if _, has := staticTable.search(f); !has { 376 | e.dynTab.add(f) 377 | } 378 | } 379 | 380 | b.ResetTimer() 381 | for n := 0; n < b.N; n++ { 382 | for _, f := range possible { 383 | e.searchTable(f) 384 | } 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /http2/hpack/tables.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hpack 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // headerFieldTable implements a list of HeaderFields. 12 | // This is used to implement the static and dynamic tables. 13 | type headerFieldTable struct { 14 | // For static tables, entries are never evicted. 15 | // 16 | // For dynamic tables, entries are evicted from ents[0] and added to the end. 17 | // Each entry has a unique id that starts at one and increments for each 18 | // entry that is added. This unique id is stable across evictions, meaning 19 | // it can be used as a pointer to a specific entry. As in hpack, unique ids 20 | // are 1-based. The unique id for ents[k] is k + evictCount + 1. 21 | // 22 | // Zero is not a valid unique id. 23 | // 24 | // evictCount should not overflow in any remotely practical situation. In 25 | // practice, we will have one dynamic table per HTTP/2 connection. If we 26 | // assume a very powerful server that handles 1M QPS per connection and each 27 | // request adds (then evicts) 100 entries from the table, it would still take 28 | // 2M years for evictCount to overflow. 29 | ents []HeaderField 30 | evictCount uint64 31 | 32 | // byName maps a HeaderField name to the unique id of the newest entry with 33 | // the same name. See above for a definition of "unique id". 34 | byName map[string]uint64 35 | 36 | // byNameValue maps a HeaderField name/value pair to the unique id of the newest 37 | // entry with the same name and value. See above for a definition of "unique id". 38 | byNameValue map[pairNameValue]uint64 39 | } 40 | 41 | type pairNameValue struct { 42 | name, value string 43 | } 44 | 45 | func (t *headerFieldTable) init() { 46 | t.byName = make(map[string]uint64) 47 | t.byNameValue = make(map[pairNameValue]uint64) 48 | } 49 | 50 | // len reports the number of entries in the table. 51 | func (t *headerFieldTable) len() int { 52 | return len(t.ents) 53 | } 54 | 55 | // addEntry adds a new entry. 56 | func (t *headerFieldTable) addEntry(f HeaderField) { 57 | id := uint64(t.len()) + t.evictCount + 1 58 | t.byName[f.Name] = id 59 | t.byNameValue[pairNameValue{f.Name, f.Value}] = id 60 | t.ents = append(t.ents, f) 61 | } 62 | 63 | // evictOldest evicts the n oldest entries in the table. 64 | func (t *headerFieldTable) evictOldest(n int) { 65 | if n > t.len() { 66 | panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len())) 67 | } 68 | for k := 0; k < n; k++ { 69 | f := t.ents[k] 70 | id := t.evictCount + uint64(k) + 1 71 | if t.byName[f.Name] == id { 72 | delete(t.byName, f.Name) 73 | } 74 | if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id { 75 | delete(t.byNameValue, p) 76 | } 77 | } 78 | copy(t.ents, t.ents[n:]) 79 | for k := t.len() - n; k < t.len(); k++ { 80 | t.ents[k] = HeaderField{} // so strings can be garbage collected 81 | } 82 | t.ents = t.ents[:t.len()-n] 83 | if t.evictCount+uint64(n) < t.evictCount { 84 | panic("evictCount overflow") 85 | } 86 | t.evictCount += uint64(n) 87 | } 88 | 89 | // search finds f in the table. If there is no match, i is 0. 90 | // If both name and value match, i is the matched index and nameValueMatch 91 | // becomes true. If only name matches, i points to that index and 92 | // nameValueMatch becomes false. 93 | // 94 | // The returned index is a 1-based HPACK index. For dynamic tables, HPACK says 95 | // that index 1 should be the newest entry, but t.ents[0] is the oldest entry, 96 | // meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic 97 | // table, the return value i actually refers to the entry t.ents[t.len()-i]. 98 | // 99 | // All tables are assumed to be a dynamic tables except for the global 100 | // staticTable pointer. 101 | // 102 | // See Section 2.3.3. 103 | func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) { 104 | if !f.Sensitive { 105 | if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 { 106 | return t.idToIndex(id), true 107 | } 108 | } 109 | if id := t.byName[f.Name]; id != 0 { 110 | return t.idToIndex(id), false 111 | } 112 | return 0, false 113 | } 114 | 115 | // idToIndex converts a unique id to an HPACK index. 116 | // See Section 2.3.3. 117 | func (t *headerFieldTable) idToIndex(id uint64) uint64 { 118 | if id <= t.evictCount { 119 | panic(fmt.Sprintf("id (%v) <= evictCount (%v)", id, t.evictCount)) 120 | } 121 | k := id - t.evictCount - 1 // convert id to an index t.ents[k] 122 | if t != staticTable { 123 | return uint64(t.len()) - k // dynamic table 124 | } 125 | return k + 1 126 | } 127 | 128 | // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B 129 | var staticTable = newStaticTable() 130 | var staticTableEntries = [...]HeaderField{ 131 | {Name: ":authority"}, 132 | {Name: ":method", Value: "GET"}, 133 | {Name: ":method", Value: "POST"}, 134 | {Name: ":path", Value: "/"}, 135 | {Name: ":path", Value: "/index.html"}, 136 | {Name: ":scheme", Value: "http"}, 137 | {Name: ":scheme", Value: "https"}, 138 | {Name: ":status", Value: "200"}, 139 | {Name: ":status", Value: "204"}, 140 | {Name: ":status", Value: "206"}, 141 | {Name: ":status", Value: "304"}, 142 | {Name: ":status", Value: "400"}, 143 | {Name: ":status", Value: "404"}, 144 | {Name: ":status", Value: "500"}, 145 | {Name: "accept-charset"}, 146 | {Name: "accept-encoding", Value: "gzip, deflate"}, 147 | {Name: "accept-language"}, 148 | {Name: "accept-ranges"}, 149 | {Name: "accept"}, 150 | {Name: "access-control-allow-origin"}, 151 | {Name: "age"}, 152 | {Name: "allow"}, 153 | {Name: "authorization"}, 154 | {Name: "cache-control"}, 155 | {Name: "content-disposition"}, 156 | {Name: "content-encoding"}, 157 | {Name: "content-language"}, 158 | {Name: "content-length"}, 159 | {Name: "content-location"}, 160 | {Name: "content-range"}, 161 | {Name: "content-type"}, 162 | {Name: "cookie"}, 163 | {Name: "date"}, 164 | {Name: "etag"}, 165 | {Name: "expect"}, 166 | {Name: "expires"}, 167 | {Name: "from"}, 168 | {Name: "host"}, 169 | {Name: "if-match"}, 170 | {Name: "if-modified-since"}, 171 | {Name: "if-none-match"}, 172 | {Name: "if-range"}, 173 | {Name: "if-unmodified-since"}, 174 | {Name: "last-modified"}, 175 | {Name: "link"}, 176 | {Name: "location"}, 177 | {Name: "max-forwards"}, 178 | {Name: "proxy-authenticate"}, 179 | {Name: "proxy-authorization"}, 180 | {Name: "range"}, 181 | {Name: "referer"}, 182 | {Name: "refresh"}, 183 | {Name: "retry-after"}, 184 | {Name: "server"}, 185 | {Name: "set-cookie"}, 186 | {Name: "strict-transport-security"}, 187 | {Name: "transfer-encoding"}, 188 | {Name: "user-agent"}, 189 | {Name: "vary"}, 190 | {Name: "via"}, 191 | {Name: "www-authenticate"}, 192 | } 193 | 194 | func newStaticTable() *headerFieldTable { 195 | t := &headerFieldTable{} 196 | t.init() 197 | for _, e := range staticTableEntries[:] { 198 | t.addEntry(e) 199 | } 200 | return t 201 | } 202 | 203 | var huffmanCodes = [256]uint32{ 204 | 0x1ff8, 205 | 0x7fffd8, 206 | 0xfffffe2, 207 | 0xfffffe3, 208 | 0xfffffe4, 209 | 0xfffffe5, 210 | 0xfffffe6, 211 | 0xfffffe7, 212 | 0xfffffe8, 213 | 0xffffea, 214 | 0x3ffffffc, 215 | 0xfffffe9, 216 | 0xfffffea, 217 | 0x3ffffffd, 218 | 0xfffffeb, 219 | 0xfffffec, 220 | 0xfffffed, 221 | 0xfffffee, 222 | 0xfffffef, 223 | 0xffffff0, 224 | 0xffffff1, 225 | 0xffffff2, 226 | 0x3ffffffe, 227 | 0xffffff3, 228 | 0xffffff4, 229 | 0xffffff5, 230 | 0xffffff6, 231 | 0xffffff7, 232 | 0xffffff8, 233 | 0xffffff9, 234 | 0xffffffa, 235 | 0xffffffb, 236 | 0x14, 237 | 0x3f8, 238 | 0x3f9, 239 | 0xffa, 240 | 0x1ff9, 241 | 0x15, 242 | 0xf8, 243 | 0x7fa, 244 | 0x3fa, 245 | 0x3fb, 246 | 0xf9, 247 | 0x7fb, 248 | 0xfa, 249 | 0x16, 250 | 0x17, 251 | 0x18, 252 | 0x0, 253 | 0x1, 254 | 0x2, 255 | 0x19, 256 | 0x1a, 257 | 0x1b, 258 | 0x1c, 259 | 0x1d, 260 | 0x1e, 261 | 0x1f, 262 | 0x5c, 263 | 0xfb, 264 | 0x7ffc, 265 | 0x20, 266 | 0xffb, 267 | 0x3fc, 268 | 0x1ffa, 269 | 0x21, 270 | 0x5d, 271 | 0x5e, 272 | 0x5f, 273 | 0x60, 274 | 0x61, 275 | 0x62, 276 | 0x63, 277 | 0x64, 278 | 0x65, 279 | 0x66, 280 | 0x67, 281 | 0x68, 282 | 0x69, 283 | 0x6a, 284 | 0x6b, 285 | 0x6c, 286 | 0x6d, 287 | 0x6e, 288 | 0x6f, 289 | 0x70, 290 | 0x71, 291 | 0x72, 292 | 0xfc, 293 | 0x73, 294 | 0xfd, 295 | 0x1ffb, 296 | 0x7fff0, 297 | 0x1ffc, 298 | 0x3ffc, 299 | 0x22, 300 | 0x7ffd, 301 | 0x3, 302 | 0x23, 303 | 0x4, 304 | 0x24, 305 | 0x5, 306 | 0x25, 307 | 0x26, 308 | 0x27, 309 | 0x6, 310 | 0x74, 311 | 0x75, 312 | 0x28, 313 | 0x29, 314 | 0x2a, 315 | 0x7, 316 | 0x2b, 317 | 0x76, 318 | 0x2c, 319 | 0x8, 320 | 0x9, 321 | 0x2d, 322 | 0x77, 323 | 0x78, 324 | 0x79, 325 | 0x7a, 326 | 0x7b, 327 | 0x7ffe, 328 | 0x7fc, 329 | 0x3ffd, 330 | 0x1ffd, 331 | 0xffffffc, 332 | 0xfffe6, 333 | 0x3fffd2, 334 | 0xfffe7, 335 | 0xfffe8, 336 | 0x3fffd3, 337 | 0x3fffd4, 338 | 0x3fffd5, 339 | 0x7fffd9, 340 | 0x3fffd6, 341 | 0x7fffda, 342 | 0x7fffdb, 343 | 0x7fffdc, 344 | 0x7fffdd, 345 | 0x7fffde, 346 | 0xffffeb, 347 | 0x7fffdf, 348 | 0xffffec, 349 | 0xffffed, 350 | 0x3fffd7, 351 | 0x7fffe0, 352 | 0xffffee, 353 | 0x7fffe1, 354 | 0x7fffe2, 355 | 0x7fffe3, 356 | 0x7fffe4, 357 | 0x1fffdc, 358 | 0x3fffd8, 359 | 0x7fffe5, 360 | 0x3fffd9, 361 | 0x7fffe6, 362 | 0x7fffe7, 363 | 0xffffef, 364 | 0x3fffda, 365 | 0x1fffdd, 366 | 0xfffe9, 367 | 0x3fffdb, 368 | 0x3fffdc, 369 | 0x7fffe8, 370 | 0x7fffe9, 371 | 0x1fffde, 372 | 0x7fffea, 373 | 0x3fffdd, 374 | 0x3fffde, 375 | 0xfffff0, 376 | 0x1fffdf, 377 | 0x3fffdf, 378 | 0x7fffeb, 379 | 0x7fffec, 380 | 0x1fffe0, 381 | 0x1fffe1, 382 | 0x3fffe0, 383 | 0x1fffe2, 384 | 0x7fffed, 385 | 0x3fffe1, 386 | 0x7fffee, 387 | 0x7fffef, 388 | 0xfffea, 389 | 0x3fffe2, 390 | 0x3fffe3, 391 | 0x3fffe4, 392 | 0x7ffff0, 393 | 0x3fffe5, 394 | 0x3fffe6, 395 | 0x7ffff1, 396 | 0x3ffffe0, 397 | 0x3ffffe1, 398 | 0xfffeb, 399 | 0x7fff1, 400 | 0x3fffe7, 401 | 0x7ffff2, 402 | 0x3fffe8, 403 | 0x1ffffec, 404 | 0x3ffffe2, 405 | 0x3ffffe3, 406 | 0x3ffffe4, 407 | 0x7ffffde, 408 | 0x7ffffdf, 409 | 0x3ffffe5, 410 | 0xfffff1, 411 | 0x1ffffed, 412 | 0x7fff2, 413 | 0x1fffe3, 414 | 0x3ffffe6, 415 | 0x7ffffe0, 416 | 0x7ffffe1, 417 | 0x3ffffe7, 418 | 0x7ffffe2, 419 | 0xfffff2, 420 | 0x1fffe4, 421 | 0x1fffe5, 422 | 0x3ffffe8, 423 | 0x3ffffe9, 424 | 0xffffffd, 425 | 0x7ffffe3, 426 | 0x7ffffe4, 427 | 0x7ffffe5, 428 | 0xfffec, 429 | 0xfffff3, 430 | 0xfffed, 431 | 0x1fffe6, 432 | 0x3fffe9, 433 | 0x1fffe7, 434 | 0x1fffe8, 435 | 0x7ffff3, 436 | 0x3fffea, 437 | 0x3fffeb, 438 | 0x1ffffee, 439 | 0x1ffffef, 440 | 0xfffff4, 441 | 0xfffff5, 442 | 0x3ffffea, 443 | 0x7ffff4, 444 | 0x3ffffeb, 445 | 0x7ffffe6, 446 | 0x3ffffec, 447 | 0x3ffffed, 448 | 0x7ffffe7, 449 | 0x7ffffe8, 450 | 0x7ffffe9, 451 | 0x7ffffea, 452 | 0x7ffffeb, 453 | 0xffffffe, 454 | 0x7ffffec, 455 | 0x7ffffed, 456 | 0x7ffffee, 457 | 0x7ffffef, 458 | 0x7fffff0, 459 | 0x3ffffee, 460 | } 461 | 462 | var huffmanCodeLen = [256]uint8{ 463 | 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 464 | 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 465 | 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 466 | 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, 467 | 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 468 | 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 469 | 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 470 | 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, 471 | 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, 472 | 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, 473 | 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, 474 | 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, 475 | 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, 476 | 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, 477 | 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, 478 | 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, 479 | } 480 | -------------------------------------------------------------------------------- /http2/write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "log" 11 | "net/http" 12 | "net/url" 13 | 14 | "golang.org/x/net/http/httpguts" 15 | "golang.org/x/net/http2/hpack" 16 | ) 17 | 18 | // writeFramer is implemented by any type that is used to write frames. 19 | type writeFramer interface { 20 | writeFrame(writeContext) error 21 | 22 | // staysWithinBuffer reports whether this writer promises that 23 | // it will only write less than or equal to size bytes, and it 24 | // won't Flush the write context. 25 | staysWithinBuffer(size int) bool 26 | } 27 | 28 | // writeContext is the interface needed by the various frame writer 29 | // types below. All the writeFrame methods below are scheduled via the 30 | // frame writing scheduler (see writeScheduler in writesched.go). 31 | // 32 | // This interface is implemented by *serverConn. 33 | // 34 | // TODO: decide whether to a) use this in the client code (which didn't 35 | // end up using this yet, because it has a simpler design, not 36 | // currently implementing priorities), or b) delete this and 37 | // make the server code a bit more concrete. 38 | type writeContext interface { 39 | Framer() *Framer 40 | Flush() error 41 | CloseConn() error 42 | // HeaderEncoder returns an HPACK encoder that writes to the 43 | // returned buffer. 44 | HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) 45 | } 46 | 47 | // writeEndsStream reports whether w writes a frame that will transition 48 | // the stream to a half-closed local state. This returns false for RST_STREAM, 49 | // which closes the entire stream (not just the local half). 50 | func writeEndsStream(w writeFramer) bool { 51 | switch v := w.(type) { 52 | case *writeData: 53 | return v.endStream 54 | case *writeResHeaders: 55 | return v.endStream 56 | case nil: 57 | // This can only happen if the caller reuses w after it's 58 | // been intentionally nil'ed out to prevent use. Keep this 59 | // here to catch future refactoring breaking it. 60 | panic("writeEndsStream called on nil writeFramer") 61 | } 62 | return false 63 | } 64 | 65 | type flushFrameWriter struct{} 66 | 67 | func (flushFrameWriter) writeFrame(ctx writeContext) error { 68 | return ctx.Flush() 69 | } 70 | 71 | func (flushFrameWriter) staysWithinBuffer(max int) bool { return false } 72 | 73 | type writeSettings []Setting 74 | 75 | func (s writeSettings) staysWithinBuffer(max int) bool { 76 | const settingSize = 6 // uint16 + uint32 77 | return frameHeaderLen+settingSize*len(s) <= max 78 | 79 | } 80 | 81 | func (s writeSettings) writeFrame(ctx writeContext) error { 82 | return ctx.Framer().WriteSettings([]Setting(s)...) 83 | } 84 | 85 | type writeGoAway struct { 86 | maxStreamID uint32 87 | code ErrCode 88 | } 89 | 90 | func (p *writeGoAway) writeFrame(ctx writeContext) error { 91 | err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) 92 | ctx.Flush() // ignore error: we're hanging up on them anyway 93 | return err 94 | } 95 | 96 | func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes 97 | 98 | type writeData struct { 99 | streamID uint32 100 | p []byte 101 | endStream bool 102 | } 103 | 104 | func (w *writeData) String() string { 105 | return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream) 106 | } 107 | 108 | func (w *writeData) writeFrame(ctx writeContext) error { 109 | return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) 110 | } 111 | 112 | func (w *writeData) staysWithinBuffer(max int) bool { 113 | return frameHeaderLen+len(w.p) <= max 114 | } 115 | 116 | // handlerPanicRST is the message sent from handler goroutines when 117 | // the handler panics. 118 | type handlerPanicRST struct { 119 | StreamID uint32 120 | } 121 | 122 | func (hp handlerPanicRST) writeFrame(ctx writeContext) error { 123 | return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal) 124 | } 125 | 126 | func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } 127 | 128 | func (se StreamError) writeFrame(ctx writeContext) error { 129 | return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) 130 | } 131 | 132 | func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } 133 | 134 | type writePingAck struct{ pf *PingFrame } 135 | 136 | func (w writePingAck) writeFrame(ctx writeContext) error { 137 | return ctx.Framer().WritePing(true, w.pf.Data) 138 | } 139 | 140 | func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max } 141 | 142 | type writeSettingsAck struct{} 143 | 144 | func (writeSettingsAck) writeFrame(ctx writeContext) error { 145 | return ctx.Framer().WriteSettingsAck() 146 | } 147 | 148 | func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max } 149 | 150 | // splitHeaderBlock splits headerBlock into fragments so that each fragment fits 151 | // in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true 152 | // for the first/last fragment, respectively. 153 | func splitHeaderBlock(ctx writeContext, headerBlock []byte, fn func(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error) error { 154 | // For now we're lazy and just pick the minimum MAX_FRAME_SIZE 155 | // that all peers must support (16KB). Later we could care 156 | // more and send larger frames if the peer advertised it, but 157 | // there's little point. Most headers are small anyway (so we 158 | // generally won't have CONTINUATION frames), and extra frames 159 | // only waste 9 bytes anyway. 160 | const maxFrameSize = 16384 161 | 162 | first := true 163 | for len(headerBlock) > 0 { 164 | frag := headerBlock 165 | if len(frag) > maxFrameSize { 166 | frag = frag[:maxFrameSize] 167 | } 168 | headerBlock = headerBlock[len(frag):] 169 | if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil { 170 | return err 171 | } 172 | first = false 173 | } 174 | return nil 175 | } 176 | 177 | // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames 178 | // for HTTP response headers or trailers from a server handler. 179 | type writeResHeaders struct { 180 | streamID uint32 181 | httpResCode int // 0 means no ":status" line 182 | h http.Header // may be nil 183 | trailers []string // if non-nil, which keys of h to write. nil means all. 184 | endStream bool 185 | 186 | date string 187 | contentType string 188 | contentLength string 189 | } 190 | 191 | func encKV(enc *hpack.Encoder, k, v string) { 192 | if VerboseLogs { 193 | log.Printf("http2: server encoding header %q = %q", k, v) 194 | } 195 | enc.WriteField(hpack.HeaderField{Name: k, Value: v}) 196 | } 197 | 198 | func (w *writeResHeaders) staysWithinBuffer(max int) bool { 199 | // TODO: this is a common one. It'd be nice to return true 200 | // here and get into the fast path if we could be clever and 201 | // calculate the size fast enough, or at least a conservative 202 | // upper bound that usually fires. (Maybe if w.h and 203 | // w.trailers are nil, so we don't need to enumerate it.) 204 | // Otherwise I'm afraid that just calculating the length to 205 | // answer this question would be slower than the ~2µs benefit. 206 | return false 207 | } 208 | 209 | func (w *writeResHeaders) writeFrame(ctx writeContext) error { 210 | enc, buf := ctx.HeaderEncoder() 211 | buf.Reset() 212 | 213 | if w.httpResCode != 0 { 214 | encKV(enc, ":status", httpCodeString(w.httpResCode)) 215 | } 216 | 217 | encodeHeaders(enc, w.h, w.trailers) 218 | 219 | if w.contentType != "" { 220 | encKV(enc, "content-type", w.contentType) 221 | } 222 | if w.contentLength != "" { 223 | encKV(enc, "content-length", w.contentLength) 224 | } 225 | if w.date != "" { 226 | encKV(enc, "date", w.date) 227 | } 228 | 229 | headerBlock := buf.Bytes() 230 | if len(headerBlock) == 0 && w.trailers == nil { 231 | panic("unexpected empty hpack") 232 | } 233 | 234 | return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) 235 | } 236 | 237 | func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error { 238 | if firstFrag { 239 | return ctx.Framer().WriteHeaders(HeadersFrameParam{ 240 | StreamID: w.streamID, 241 | BlockFragment: frag, 242 | EndStream: w.endStream, 243 | EndHeaders: lastFrag, 244 | }) 245 | } else { 246 | return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) 247 | } 248 | } 249 | 250 | // writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames. 251 | type writePushPromise struct { 252 | streamID uint32 // pusher stream 253 | method string // for :method 254 | url *url.URL // for :scheme, :authority, :path 255 | h http.Header 256 | 257 | // Creates an ID for a pushed stream. This runs on serveG just before 258 | // the frame is written. The returned ID is copied to promisedID. 259 | allocatePromisedID func() (uint32, error) 260 | promisedID uint32 261 | } 262 | 263 | func (w *writePushPromise) staysWithinBuffer(max int) bool { 264 | // TODO: see writeResHeaders.staysWithinBuffer 265 | return false 266 | } 267 | 268 | func (w *writePushPromise) writeFrame(ctx writeContext) error { 269 | enc, buf := ctx.HeaderEncoder() 270 | buf.Reset() 271 | 272 | encKV(enc, ":method", w.method) 273 | encKV(enc, ":scheme", w.url.Scheme) 274 | encKV(enc, ":authority", w.url.Host) 275 | encKV(enc, ":path", w.url.RequestURI()) 276 | encodeHeaders(enc, w.h, nil) 277 | 278 | headerBlock := buf.Bytes() 279 | if len(headerBlock) == 0 { 280 | panic("unexpected empty hpack") 281 | } 282 | 283 | return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) 284 | } 285 | 286 | func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error { 287 | if firstFrag { 288 | return ctx.Framer().WritePushPromise(PushPromiseParam{ 289 | StreamID: w.streamID, 290 | PromiseID: w.promisedID, 291 | BlockFragment: frag, 292 | EndHeaders: lastFrag, 293 | }) 294 | } else { 295 | return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) 296 | } 297 | } 298 | 299 | type write100ContinueHeadersFrame struct { 300 | streamID uint32 301 | } 302 | 303 | func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error { 304 | enc, buf := ctx.HeaderEncoder() 305 | buf.Reset() 306 | encKV(enc, ":status", "100") 307 | return ctx.Framer().WriteHeaders(HeadersFrameParam{ 308 | StreamID: w.streamID, 309 | BlockFragment: buf.Bytes(), 310 | EndStream: false, 311 | EndHeaders: true, 312 | }) 313 | } 314 | 315 | func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool { 316 | // Sloppy but conservative: 317 | return 9+2*(len(":status")+len("100")) <= max 318 | } 319 | 320 | type writeWindowUpdate struct { 321 | streamID uint32 // or 0 for conn-level 322 | n uint32 323 | } 324 | 325 | func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } 326 | 327 | func (wu writeWindowUpdate) writeFrame(ctx writeContext) error { 328 | return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) 329 | } 330 | 331 | // encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k]) 332 | // is encoded only if k is in keys. 333 | func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) { 334 | if keys == nil { 335 | sorter := sorterPool.Get().(*sorter) 336 | // Using defer here, since the returned keys from the 337 | // sorter.Keys method is only valid until the sorter 338 | // is returned: 339 | defer sorterPool.Put(sorter) 340 | keys = sorter.Keys(h) 341 | } 342 | for _, k := range keys { 343 | vv := h[k] 344 | k = lowerHeader(k) 345 | if !validWireHeaderFieldName(k) { 346 | // Skip it as backup paranoia. Per 347 | // golang.org/issue/14048, these should 348 | // already be rejected at a higher level. 349 | continue 350 | } 351 | isTE := k == "transfer-encoding" 352 | for _, v := range vv { 353 | if !httpguts.ValidHeaderFieldValue(v) { 354 | // TODO: return an error? golang.org/issue/14048 355 | // For now just omit it. 356 | continue 357 | } 358 | // TODO: more of "8.1.2.2 Connection-Specific Header Fields" 359 | if isTE && v != "trailers" { 360 | continue 361 | } 362 | encKV(enc, k, v) 363 | } 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /http2/ciphers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestIsBadCipherBad(t *testing.T) { 10 | for _, c := range badCiphers { 11 | if !isBadCipher(c) { 12 | t.Errorf("Wrong result for isBadCipher(%d), want true", c) 13 | } 14 | } 15 | } 16 | 17 | // verify we don't give false positives on ciphers not on blacklist 18 | func TestIsBadCipherGood(t *testing.T) { 19 | goodCiphers := map[uint16]string{ 20 | cipher_TLS_DHE_RSA_WITH_AES_256_CCM: "cipher_TLS_DHE_RSA_WITH_AES_256_CCM", 21 | cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: "cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM", 22 | cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256: "cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", 23 | } 24 | for c, name := range goodCiphers { 25 | if isBadCipher(c) { 26 | t.Errorf("Wrong result for isBadCipher(%d) %s, want false", c, name) 27 | } 28 | } 29 | } 30 | 31 | // copied from https://http2.github.io/http2-spec/#BadCipherSuites, 32 | var badCiphers = []uint16{ 33 | cipher_TLS_NULL_WITH_NULL_NULL, 34 | cipher_TLS_RSA_WITH_NULL_MD5, 35 | cipher_TLS_RSA_WITH_NULL_SHA, 36 | cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5, 37 | cipher_TLS_RSA_WITH_RC4_128_MD5, 38 | cipher_TLS_RSA_WITH_RC4_128_SHA, 39 | cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, 40 | cipher_TLS_RSA_WITH_IDEA_CBC_SHA, 41 | cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, 42 | cipher_TLS_RSA_WITH_DES_CBC_SHA, 43 | cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA, 44 | cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, 45 | cipher_TLS_DH_DSS_WITH_DES_CBC_SHA, 46 | cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, 47 | cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, 48 | cipher_TLS_DH_RSA_WITH_DES_CBC_SHA, 49 | cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, 50 | cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, 51 | cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA, 52 | cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 53 | cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, 54 | cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA, 55 | cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 56 | cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, 57 | cipher_TLS_DH_anon_WITH_RC4_128_MD5, 58 | cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, 59 | cipher_TLS_DH_anon_WITH_DES_CBC_SHA, 60 | cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, 61 | cipher_TLS_KRB5_WITH_DES_CBC_SHA, 62 | cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA, 63 | cipher_TLS_KRB5_WITH_RC4_128_SHA, 64 | cipher_TLS_KRB5_WITH_IDEA_CBC_SHA, 65 | cipher_TLS_KRB5_WITH_DES_CBC_MD5, 66 | cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5, 67 | cipher_TLS_KRB5_WITH_RC4_128_MD5, 68 | cipher_TLS_KRB5_WITH_IDEA_CBC_MD5, 69 | cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, 70 | cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, 71 | cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA, 72 | cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, 73 | cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, 74 | cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5, 75 | cipher_TLS_PSK_WITH_NULL_SHA, 76 | cipher_TLS_DHE_PSK_WITH_NULL_SHA, 77 | cipher_TLS_RSA_PSK_WITH_NULL_SHA, 78 | cipher_TLS_RSA_WITH_AES_128_CBC_SHA, 79 | cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA, 80 | cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA, 81 | cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 82 | cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 83 | cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA, 84 | cipher_TLS_RSA_WITH_AES_256_CBC_SHA, 85 | cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA, 86 | cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA, 87 | cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 88 | cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 89 | cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA, 90 | cipher_TLS_RSA_WITH_NULL_SHA256, 91 | cipher_TLS_RSA_WITH_AES_128_CBC_SHA256, 92 | cipher_TLS_RSA_WITH_AES_256_CBC_SHA256, 93 | cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, 94 | cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, 95 | cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 96 | cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, 97 | cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA, 98 | cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA, 99 | cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, 100 | cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, 101 | cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, 102 | cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 103 | cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, 104 | cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, 105 | cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 106 | cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 107 | cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256, 108 | cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256, 109 | cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, 110 | cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA, 111 | cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA, 112 | cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, 113 | cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, 114 | cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA, 115 | cipher_TLS_PSK_WITH_RC4_128_SHA, 116 | cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA, 117 | cipher_TLS_PSK_WITH_AES_128_CBC_SHA, 118 | cipher_TLS_PSK_WITH_AES_256_CBC_SHA, 119 | cipher_TLS_DHE_PSK_WITH_RC4_128_SHA, 120 | cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, 121 | cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, 122 | cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, 123 | cipher_TLS_RSA_PSK_WITH_RC4_128_SHA, 124 | cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, 125 | cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, 126 | cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, 127 | cipher_TLS_RSA_WITH_SEED_CBC_SHA, 128 | cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA, 129 | cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA, 130 | cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA, 131 | cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA, 132 | cipher_TLS_DH_anon_WITH_SEED_CBC_SHA, 133 | cipher_TLS_RSA_WITH_AES_128_GCM_SHA256, 134 | cipher_TLS_RSA_WITH_AES_256_GCM_SHA384, 135 | cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256, 136 | cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384, 137 | cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256, 138 | cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384, 139 | cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256, 140 | cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384, 141 | cipher_TLS_PSK_WITH_AES_128_GCM_SHA256, 142 | cipher_TLS_PSK_WITH_AES_256_GCM_SHA384, 143 | cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, 144 | cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, 145 | cipher_TLS_PSK_WITH_AES_128_CBC_SHA256, 146 | cipher_TLS_PSK_WITH_AES_256_CBC_SHA384, 147 | cipher_TLS_PSK_WITH_NULL_SHA256, 148 | cipher_TLS_PSK_WITH_NULL_SHA384, 149 | cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, 150 | cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, 151 | cipher_TLS_DHE_PSK_WITH_NULL_SHA256, 152 | cipher_TLS_DHE_PSK_WITH_NULL_SHA384, 153 | cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, 154 | cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, 155 | cipher_TLS_RSA_PSK_WITH_NULL_SHA256, 156 | cipher_TLS_RSA_PSK_WITH_NULL_SHA384, 157 | cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, 158 | cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256, 159 | cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256, 160 | cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, 161 | cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, 162 | cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256, 163 | cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, 164 | cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256, 165 | cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256, 166 | cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, 167 | cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, 168 | cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, 169 | cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV, 170 | cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA, 171 | cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA, 172 | cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 173 | cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, 174 | cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, 175 | cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA, 176 | cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 177 | cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 178 | cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 179 | cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 180 | cipher_TLS_ECDH_RSA_WITH_NULL_SHA, 181 | cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA, 182 | cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 183 | cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, 184 | cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, 185 | cipher_TLS_ECDHE_RSA_WITH_NULL_SHA, 186 | cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA, 187 | cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 188 | cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 189 | cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 190 | cipher_TLS_ECDH_anon_WITH_NULL_SHA, 191 | cipher_TLS_ECDH_anon_WITH_RC4_128_SHA, 192 | cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, 193 | cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA, 194 | cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA, 195 | cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, 196 | cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, 197 | cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, 198 | cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA, 199 | cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, 200 | cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, 201 | cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA, 202 | cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, 203 | cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, 204 | cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 205 | cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 206 | cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 207 | cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 208 | cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 209 | cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 210 | cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, 211 | cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, 212 | cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 213 | cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 214 | cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, 215 | cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, 216 | cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA, 217 | cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, 218 | cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 219 | cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 220 | cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, 221 | cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, 222 | cipher_TLS_ECDHE_PSK_WITH_NULL_SHA, 223 | cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256, 224 | cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384, 225 | cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256, 226 | cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384, 227 | cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256, 228 | cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384, 229 | cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256, 230 | cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384, 231 | cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, 232 | cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, 233 | cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, 234 | cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, 235 | cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256, 236 | cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384, 237 | cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, 238 | cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, 239 | cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, 240 | cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, 241 | cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, 242 | cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, 243 | cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, 244 | cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, 245 | cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256, 246 | cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384, 247 | cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256, 248 | cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384, 249 | cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256, 250 | cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384, 251 | cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256, 252 | cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384, 253 | cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, 254 | cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, 255 | cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, 256 | cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, 257 | cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256, 258 | cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384, 259 | cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, 260 | cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, 261 | cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, 262 | cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, 263 | cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256, 264 | cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384, 265 | cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, 266 | cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, 267 | cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, 268 | cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, 269 | cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, 270 | cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, 271 | cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, 272 | cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, 273 | cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, 274 | cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, 275 | cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, 276 | cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, 277 | cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, 278 | cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, 279 | cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256, 280 | cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384, 281 | cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256, 282 | cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384, 283 | cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256, 284 | cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384, 285 | cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, 286 | cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, 287 | cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, 288 | cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, 289 | cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, 290 | cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, 291 | cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, 292 | cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, 293 | cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, 294 | cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, 295 | cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, 296 | cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, 297 | cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, 298 | cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, 299 | cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, 300 | cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, 301 | cipher_TLS_RSA_WITH_AES_128_CCM, 302 | cipher_TLS_RSA_WITH_AES_256_CCM, 303 | cipher_TLS_RSA_WITH_AES_128_CCM_8, 304 | cipher_TLS_RSA_WITH_AES_256_CCM_8, 305 | cipher_TLS_PSK_WITH_AES_128_CCM, 306 | cipher_TLS_PSK_WITH_AES_256_CCM, 307 | cipher_TLS_PSK_WITH_AES_128_CCM_8, 308 | cipher_TLS_PSK_WITH_AES_256_CCM_8, 309 | } 310 | -------------------------------------------------------------------------------- /http2/h2i/h2i.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows 6 | // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows 7 | 8 | /* 9 | The h2i command is an interactive HTTP/2 console. 10 | 11 | Usage: 12 | $ h2i [flags] 13 | 14 | Interactive commands in the console: (all parts case-insensitive) 15 | 16 | ping [data] 17 | settings ack 18 | settings FOO=n BAR=z 19 | headers (open a new stream by typing HTTP/1.1) 20 | */ 21 | package main 22 | 23 | import ( 24 | "bufio" 25 | "bytes" 26 | "crypto/tls" 27 | "errors" 28 | "flag" 29 | "fmt" 30 | "io" 31 | "log" 32 | "net" 33 | "net/http" 34 | "os" 35 | "regexp" 36 | "strconv" 37 | "strings" 38 | 39 | "golang.org/x/net/http2" 40 | "golang.org/x/net/http2/hpack" 41 | "golang.org/x/term" 42 | ) 43 | 44 | // Flags 45 | var ( 46 | flagNextProto = flag.String("nextproto", "h2,h2-14", "Comma-separated list of NPN/ALPN protocol names to negotiate.") 47 | flagInsecure = flag.Bool("insecure", false, "Whether to skip TLS cert validation") 48 | flagSettings = flag.String("settings", "empty", "comma-separated list of KEY=value settings for the initial SETTINGS frame. The magic value 'empty' sends an empty initial settings frame, and the magic value 'omit' causes no initial settings frame to be sent.") 49 | flagDial = flag.String("dial", "", "optional ip:port to dial, to connect to a host:port but use a different SNI name (including a SNI name without DNS)") 50 | ) 51 | 52 | type command struct { 53 | run func(*h2i, []string) error // required 54 | 55 | // complete optionally specifies tokens (case-insensitive) which are 56 | // valid for this subcommand. 57 | complete func() []string 58 | } 59 | 60 | var commands = map[string]command{ 61 | "ping": {run: (*h2i).cmdPing}, 62 | "settings": { 63 | run: (*h2i).cmdSettings, 64 | complete: func() []string { 65 | return []string{ 66 | "ACK", 67 | http2.SettingHeaderTableSize.String(), 68 | http2.SettingEnablePush.String(), 69 | http2.SettingMaxConcurrentStreams.String(), 70 | http2.SettingInitialWindowSize.String(), 71 | http2.SettingMaxFrameSize.String(), 72 | http2.SettingMaxHeaderListSize.String(), 73 | } 74 | }, 75 | }, 76 | "quit": {run: (*h2i).cmdQuit}, 77 | "headers": {run: (*h2i).cmdHeaders}, 78 | } 79 | 80 | func usage() { 81 | fmt.Fprintf(os.Stderr, "Usage: h2i \n\n") 82 | flag.PrintDefaults() 83 | } 84 | 85 | // withPort adds ":443" if another port isn't already present. 86 | func withPort(host string) string { 87 | if _, _, err := net.SplitHostPort(host); err != nil { 88 | return net.JoinHostPort(host, "443") 89 | } 90 | return host 91 | } 92 | 93 | // withoutPort strips the port from addr if present. 94 | func withoutPort(addr string) string { 95 | if h, _, err := net.SplitHostPort(addr); err == nil { 96 | return h 97 | } 98 | return addr 99 | } 100 | 101 | // h2i is the app's state. 102 | type h2i struct { 103 | host string 104 | tc *tls.Conn 105 | framer *http2.Framer 106 | term *term.Terminal 107 | 108 | // owned by the command loop: 109 | streamID uint32 110 | hbuf bytes.Buffer 111 | henc *hpack.Encoder 112 | 113 | // owned by the readFrames loop: 114 | peerSetting map[http2.SettingID]uint32 115 | hdec *hpack.Decoder 116 | } 117 | 118 | func main() { 119 | flag.Usage = usage 120 | flag.Parse() 121 | if flag.NArg() != 1 { 122 | usage() 123 | os.Exit(2) 124 | } 125 | log.SetFlags(0) 126 | 127 | host := flag.Arg(0) 128 | app := &h2i{ 129 | host: host, 130 | peerSetting: make(map[http2.SettingID]uint32), 131 | } 132 | app.henc = hpack.NewEncoder(&app.hbuf) 133 | 134 | if err := app.Main(); err != nil { 135 | if app.term != nil { 136 | app.logf("%v\n", err) 137 | } else { 138 | fmt.Fprintf(os.Stderr, "%v\n", err) 139 | } 140 | os.Exit(1) 141 | } 142 | fmt.Fprintf(os.Stdout, "\n") 143 | } 144 | 145 | func (app *h2i) Main() error { 146 | cfg := &tls.Config{ 147 | ServerName: withoutPort(app.host), 148 | NextProtos: strings.Split(*flagNextProto, ","), 149 | InsecureSkipVerify: *flagInsecure, 150 | } 151 | 152 | hostAndPort := *flagDial 153 | if hostAndPort == "" { 154 | hostAndPort = withPort(app.host) 155 | } 156 | log.Printf("Connecting to %s ...", hostAndPort) 157 | tc, err := tls.Dial("tcp", hostAndPort, cfg) 158 | if err != nil { 159 | return fmt.Errorf("Error dialing %s: %v", hostAndPort, err) 160 | } 161 | log.Printf("Connected to %v", tc.RemoteAddr()) 162 | defer tc.Close() 163 | 164 | if err := tc.Handshake(); err != nil { 165 | return fmt.Errorf("TLS handshake: %v", err) 166 | } 167 | if !*flagInsecure { 168 | if err := tc.VerifyHostname(app.host); err != nil { 169 | return fmt.Errorf("VerifyHostname: %v", err) 170 | } 171 | } 172 | state := tc.ConnectionState() 173 | log.Printf("Negotiated protocol %q", state.NegotiatedProtocol) 174 | if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" { 175 | return fmt.Errorf("Could not negotiate protocol mutually") 176 | } 177 | 178 | if _, err := io.WriteString(tc, http2.ClientPreface); err != nil { 179 | return err 180 | } 181 | 182 | app.framer = http2.NewFramer(tc, tc) 183 | 184 | oldState, err := term.MakeRaw(int(os.Stdin.Fd())) 185 | if err != nil { 186 | return err 187 | } 188 | defer term.Restore(0, oldState) 189 | 190 | var screen = struct { 191 | io.Reader 192 | io.Writer 193 | }{os.Stdin, os.Stdout} 194 | 195 | app.term = term.NewTerminal(screen, "h2i> ") 196 | lastWord := regexp.MustCompile(`.+\W(\w+)$`) 197 | app.term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) { 198 | if key != '\t' { 199 | return 200 | } 201 | if pos != len(line) { 202 | // TODO: we're being lazy for now, only supporting tab completion at the end. 203 | return 204 | } 205 | // Auto-complete for the command itself. 206 | if !strings.Contains(line, " ") { 207 | var name string 208 | name, _, ok = lookupCommand(line) 209 | if !ok { 210 | return 211 | } 212 | return name, len(name), true 213 | } 214 | _, c, ok := lookupCommand(line[:strings.IndexByte(line, ' ')]) 215 | if !ok || c.complete == nil { 216 | return 217 | } 218 | if strings.HasSuffix(line, " ") { 219 | app.logf("%s", strings.Join(c.complete(), " ")) 220 | return line, pos, true 221 | } 222 | m := lastWord.FindStringSubmatch(line) 223 | if m == nil { 224 | return line, len(line), true 225 | } 226 | soFar := m[1] 227 | var match []string 228 | for _, cand := range c.complete() { 229 | if len(soFar) > len(cand) || !strings.EqualFold(cand[:len(soFar)], soFar) { 230 | continue 231 | } 232 | match = append(match, cand) 233 | } 234 | if len(match) == 0 { 235 | return 236 | } 237 | if len(match) > 1 { 238 | // TODO: auto-complete any common prefix 239 | app.logf("%s", strings.Join(match, " ")) 240 | return line, pos, true 241 | } 242 | newLine = line[:len(line)-len(soFar)] + match[0] 243 | return newLine, len(newLine), true 244 | 245 | } 246 | 247 | errc := make(chan error, 2) 248 | go func() { errc <- app.readFrames() }() 249 | go func() { errc <- app.readConsole() }() 250 | return <-errc 251 | } 252 | 253 | func (app *h2i) logf(format string, args ...interface{}) { 254 | fmt.Fprintf(app.term, format+"\r\n", args...) 255 | } 256 | 257 | func (app *h2i) readConsole() error { 258 | if s := *flagSettings; s != "omit" { 259 | var args []string 260 | if s != "empty" { 261 | args = strings.Split(s, ",") 262 | } 263 | _, c, ok := lookupCommand("settings") 264 | if !ok { 265 | panic("settings command not found") 266 | } 267 | c.run(app, args) 268 | } 269 | 270 | for { 271 | line, err := app.term.ReadLine() 272 | if err == io.EOF { 273 | return nil 274 | } 275 | if err != nil { 276 | return fmt.Errorf("term.ReadLine: %v", err) 277 | } 278 | f := strings.Fields(line) 279 | if len(f) == 0 { 280 | continue 281 | } 282 | cmd, args := f[0], f[1:] 283 | if _, c, ok := lookupCommand(cmd); ok { 284 | err = c.run(app, args) 285 | } else { 286 | app.logf("Unknown command %q", line) 287 | } 288 | if err == errExitApp { 289 | return nil 290 | } 291 | if err != nil { 292 | return err 293 | } 294 | } 295 | } 296 | 297 | func lookupCommand(prefix string) (name string, c command, ok bool) { 298 | prefix = strings.ToLower(prefix) 299 | if c, ok = commands[prefix]; ok { 300 | return prefix, c, ok 301 | } 302 | 303 | for full, candidate := range commands { 304 | if strings.HasPrefix(full, prefix) { 305 | if c.run != nil { 306 | return "", command{}, false // ambiguous 307 | } 308 | c = candidate 309 | name = full 310 | } 311 | } 312 | return name, c, c.run != nil 313 | } 314 | 315 | var errExitApp = errors.New("internal sentinel error value to quit the console reading loop") 316 | 317 | func (a *h2i) cmdQuit(args []string) error { 318 | if len(args) > 0 { 319 | a.logf("the QUIT command takes no argument") 320 | return nil 321 | } 322 | return errExitApp 323 | } 324 | 325 | func (a *h2i) cmdSettings(args []string) error { 326 | if len(args) == 1 && strings.EqualFold(args[0], "ACK") { 327 | return a.framer.WriteSettingsAck() 328 | } 329 | var settings []http2.Setting 330 | for _, arg := range args { 331 | if strings.EqualFold(arg, "ACK") { 332 | a.logf("Error: ACK must be only argument with the SETTINGS command") 333 | return nil 334 | } 335 | eq := strings.Index(arg, "=") 336 | if eq == -1 { 337 | a.logf("Error: invalid argument %q (expected SETTING_NAME=nnnn)", arg) 338 | return nil 339 | } 340 | sid, ok := settingByName(arg[:eq]) 341 | if !ok { 342 | a.logf("Error: unknown setting name %q", arg[:eq]) 343 | return nil 344 | } 345 | val, err := strconv.ParseUint(arg[eq+1:], 10, 32) 346 | if err != nil { 347 | a.logf("Error: invalid argument %q (expected SETTING_NAME=nnnn)", arg) 348 | return nil 349 | } 350 | settings = append(settings, http2.Setting{ 351 | ID: sid, 352 | Val: uint32(val), 353 | }) 354 | } 355 | a.logf("Sending: %v", settings) 356 | return a.framer.WriteSettings(settings...) 357 | } 358 | 359 | func settingByName(name string) (http2.SettingID, bool) { 360 | for _, sid := range [...]http2.SettingID{ 361 | http2.SettingHeaderTableSize, 362 | http2.SettingEnablePush, 363 | http2.SettingMaxConcurrentStreams, 364 | http2.SettingInitialWindowSize, 365 | http2.SettingMaxFrameSize, 366 | http2.SettingMaxHeaderListSize, 367 | } { 368 | if strings.EqualFold(sid.String(), name) { 369 | return sid, true 370 | } 371 | } 372 | return 0, false 373 | } 374 | 375 | func (app *h2i) cmdPing(args []string) error { 376 | if len(args) > 1 { 377 | app.logf("invalid PING usage: only accepts 0 or 1 args") 378 | return nil // nil means don't end the program 379 | } 380 | var data [8]byte 381 | if len(args) == 1 { 382 | copy(data[:], args[0]) 383 | } else { 384 | copy(data[:], "h2i_ping") 385 | } 386 | return app.framer.WritePing(false, data) 387 | } 388 | 389 | func (app *h2i) cmdHeaders(args []string) error { 390 | if len(args) > 0 { 391 | app.logf("Error: HEADERS doesn't yet take arguments.") 392 | // TODO: flags for restricting window size, to force CONTINUATION 393 | // frames. 394 | return nil 395 | } 396 | var h1req bytes.Buffer 397 | app.term.SetPrompt("(as HTTP/1.1)> ") 398 | defer app.term.SetPrompt("h2i> ") 399 | for { 400 | line, err := app.term.ReadLine() 401 | if err != nil { 402 | return err 403 | } 404 | h1req.WriteString(line) 405 | h1req.WriteString("\r\n") 406 | if line == "" { 407 | break 408 | } 409 | } 410 | req, err := http.ReadRequest(bufio.NewReader(&h1req)) 411 | if err != nil { 412 | app.logf("Invalid HTTP/1.1 request: %v", err) 413 | return nil 414 | } 415 | if app.streamID == 0 { 416 | app.streamID = 1 417 | } else { 418 | app.streamID += 2 419 | } 420 | app.logf("Opening Stream-ID %d:", app.streamID) 421 | hbf := app.encodeHeaders(req) 422 | if len(hbf) > 16<<10 { 423 | app.logf("TODO: h2i doesn't yet write CONTINUATION frames. Copy it from transport.go") 424 | return nil 425 | } 426 | return app.framer.WriteHeaders(http2.HeadersFrameParam{ 427 | StreamID: app.streamID, 428 | BlockFragment: hbf, 429 | EndStream: req.Method == "GET" || req.Method == "HEAD", // good enough for now 430 | EndHeaders: true, // for now 431 | }) 432 | } 433 | 434 | func (app *h2i) readFrames() error { 435 | for { 436 | f, err := app.framer.ReadFrame() 437 | if err != nil { 438 | return fmt.Errorf("ReadFrame: %v", err) 439 | } 440 | app.logf("%v", f) 441 | switch f := f.(type) { 442 | case *http2.PingFrame: 443 | app.logf(" Data = %q", f.Data) 444 | case *http2.SettingsFrame: 445 | f.ForeachSetting(func(s http2.Setting) error { 446 | app.logf(" %v", s) 447 | app.peerSetting[s.ID] = s.Val 448 | return nil 449 | }) 450 | case *http2.WindowUpdateFrame: 451 | app.logf(" Window-Increment = %v", f.Increment) 452 | case *http2.GoAwayFrame: 453 | app.logf(" Last-Stream-ID = %d; Error-Code = %v (%d)", f.LastStreamID, f.ErrCode, f.ErrCode) 454 | case *http2.DataFrame: 455 | app.logf(" %q", f.Data()) 456 | case *http2.HeadersFrame: 457 | if f.HasPriority() { 458 | app.logf(" PRIORITY = %v", f.Priority) 459 | } 460 | if app.hdec == nil { 461 | // TODO: if the user uses h2i to send a SETTINGS frame advertising 462 | // something larger, we'll need to respect SETTINGS_HEADER_TABLE_SIZE 463 | // and stuff here instead of using the 4k default. But for now: 464 | tableSize := uint32(4 << 10) 465 | app.hdec = hpack.NewDecoder(tableSize, app.onNewHeaderField) 466 | } 467 | app.hdec.Write(f.HeaderBlockFragment()) 468 | case *http2.PushPromiseFrame: 469 | if app.hdec == nil { 470 | // TODO: if the user uses h2i to send a SETTINGS frame advertising 471 | // something larger, we'll need to respect SETTINGS_HEADER_TABLE_SIZE 472 | // and stuff here instead of using the 4k default. But for now: 473 | tableSize := uint32(4 << 10) 474 | app.hdec = hpack.NewDecoder(tableSize, app.onNewHeaderField) 475 | } 476 | app.hdec.Write(f.HeaderBlockFragment()) 477 | } 478 | } 479 | } 480 | 481 | // called from readLoop 482 | func (app *h2i) onNewHeaderField(f hpack.HeaderField) { 483 | if f.Sensitive { 484 | app.logf(" %s = %q (SENSITIVE)", f.Name, f.Value) 485 | } 486 | app.logf(" %s = %q", f.Name, f.Value) 487 | } 488 | 489 | func (app *h2i) encodeHeaders(req *http.Request) []byte { 490 | app.hbuf.Reset() 491 | 492 | // TODO(bradfitz): figure out :authority-vs-Host stuff between http2 and Go 493 | host := req.Host 494 | if host == "" { 495 | host = req.URL.Host 496 | } 497 | 498 | path := req.RequestURI 499 | if path == "" { 500 | path = "/" 501 | } 502 | 503 | app.writeHeader(":authority", host) // probably not right for all sites 504 | app.writeHeader(":method", req.Method) 505 | app.writeHeader(":path", path) 506 | app.writeHeader(":scheme", "https") 507 | 508 | for k, vv := range req.Header { 509 | lowKey := strings.ToLower(k) 510 | if lowKey == "host" { 511 | continue 512 | } 513 | for _, v := range vv { 514 | app.writeHeader(lowKey, v) 515 | } 516 | } 517 | return app.hbuf.Bytes() 518 | } 519 | 520 | func (app *h2i) writeHeader(name, value string) { 521 | app.henc.WriteField(hpack.HeaderField{Name: name, Value: value}) 522 | app.logf(" %s = %s", name, value) 523 | } 524 | --------------------------------------------------------------------------------