forked from chinawebeye/http2
/
upgrade_test.go
130 lines (119 loc) · 3.26 KB
/
upgrade_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// unit tests for http/1.1 -> http/2 upgrading (Section 3.2)
package http2
import (
"io"
"net/http"
"net/http/httptest"
"runtime"
"strings"
"sync/atomic"
"testing"
"time"
)
type testUpgradeMode int
const (
testUpgradeNoTLS testUpgradeMode = iota
testUpgradeTLSOnly
testUpgradeDualTLS
)
func TestUpgradeWithCurl(t *testing.T) { testUpgradeWithCurl(t, testUpgradeNoTLS, false) }
func TestUpgradeTLSWithCurl(t *testing.T) { testUpgradeWithCurl(t, testUpgradeTLSOnly, true) }
func TestUpgradeDualWithCurl(t *testing.T) { testUpgradeWithCurl(t, testUpgradeDualTLS, true) }
func testUpgradeWithCurl(t *testing.T, mode testUpgradeMode, lenient bool) {
if runtime.GOOS != "linux" {
t.Skip("skipping Docker test when not on Linux; requires --net which won't work with boot2docker anyway")
}
requireCurl(t)
const msg = "Hello from curl!\n"
var ts2 *httptest.Server
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Foo", "Bar")
w.Header().Set("Client-Proto", r.Proto)
io.WriteString(w, msg)
})
ts := httptest.NewUnstartedServer(handler)
switch mode {
case testUpgradeNoTLS:
ts.TLS = nil
default:
ts2 = httptest.NewUnstartedServer(handler)
ts2.TLS = nil
}
wrapped := UpgradeServer(ts.Config, &Server{
PermitProhibitedCipherSuites: lenient,
})
modes := make([]testUpgradeMode, 0, 2)
switch mode {
case testUpgradeNoTLS:
modes = append(modes, mode)
ts.Config = wrapped
ts.Start()
t.Logf("Running test server for curl to hit at: %s", ts.URL)
case testUpgradeDualTLS:
modes = append(modes, testUpgradeNoTLS)
fallthrough
default:
modes = append(modes, testUpgradeTLSOnly)
ts2.Config = wrapped
ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
ts.StartTLS()
ts2.Start()
defer ts2.Close()
t.Logf("Running test server for curl to hit at: %s and %s", ts.URL, ts2.URL)
}
defer ts.Close()
defer func() { testHookOnConn = nil }()
for _, mode := range modes {
var gotConn int32
testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
T := ts
if mode == testUpgradeNoTLS && ts2 != nil {
T = ts2
}
container := curl(t, "-D-", "--silent", "--http2", "--insecure", T.URL)
defer kill(container)
resc := make(chan interface{}, 1)
go func() {
res, err := dockerLogs(container)
if err != nil {
resc <- err
} else {
resc <- res
}
}()
select {
case res := <-resc:
if err, ok := res.(error); ok {
t.Fatal(err)
}
testDualUpgradeOutput(t, string(res.([]byte)), (T.TLS == nil),
"HTTP/2.0 200",
"foo:Bar",
"client-proto:HTTP/2",
msg)
case <-time.After(3 * time.Second):
t.Errorf("timeout waiting for curl")
}
if atomic.LoadInt32(&gotConn) == 0 {
t.Error("never saw an http2 connection")
}
}
}
func testDualUpgradeOutput(t *testing.T, out string, isUpgrade bool, must ...string) {
if isUpgrade {
if !strings.HasPrefix(out, "HTTP/1.1 101 Switching Protocols") {
t.Error("didn't see prefix 'HTTP/1.1 101 Switching Protocols'")
t.Logf("Got: %s", out)
}
if !strings.Contains(out, "Upgrade: h2c") {
t.Error("didn't see HTTP/1.1 Upgrade header")
t.Logf("Got: %s", out)
}
}
for _, s := range must {
if !strings.Contains(out, s) {
t.Errorf("didn't see %q", s)
t.Logf("Got: %s", out)
}
}
}