├── 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 | 
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 |
--------------------------------------------------------------------------------