1
2
3
4
5 package http2
6
7 import (
8 "bytes"
9 "compress/gzip"
10 "crypto/tls"
11 "fmt"
12 "io"
13 "io/fs"
14 "reflect"
15 "strings"
16 "testing"
17 "time"
18 )
19
20 type panicReader struct{}
21
22 func (panicReader) Read([]byte) (int, error) { panic("unexpected Read") }
23 func (panicReader) Close() error { panic("unexpected Close") }
24
25 func TestActualContentLength(t *testing.T) {
26 tests := []struct {
27 req *ClientRequest
28 want int64
29 }{
30
31 0: {
32 req: &ClientRequest{Body: panicReader{}},
33 want: -1,
34 },
35
36 1: {
37 req: &ClientRequest{Body: nil, ContentLength: 5},
38 want: 0,
39 },
40
41 2: {
42 req: &ClientRequest{Body: panicReader{}, ContentLength: 5},
43 want: 5,
44 },
45
46 3: {
47 req: &ClientRequest{Body: NoBody},
48 want: 0,
49 },
50 }
51 for i, tt := range tests {
52 got := actualContentLength(tt.req)
53 if got != tt.want {
54 t.Errorf("test[%d]: got %d; want %d", i, got, tt.want)
55 }
56 }
57 }
58
59
60
61 func TestGzipReader_DoubleReadCrash(t *testing.T) {
62 gz := &gzipReader{
63 body: io.NopCloser(strings.NewReader("0123456789")),
64 }
65 var buf [1]byte
66 n, err1 := gz.Read(buf[:])
67 if n != 0 || !strings.Contains(fmt.Sprint(err1), "invalid header") {
68 t.Fatalf("Read = %v, %v; want 0, invalid header", n, err1)
69 }
70 n, err2 := gz.Read(buf[:])
71 if n != 0 || err2 != err1 {
72 t.Fatalf("second Read = %v, %v; want 0, %v", n, err2, err1)
73 }
74 }
75
76 func TestGzipReader_ReadAfterClose(t *testing.T) {
77 body := bytes.Buffer{}
78 w := gzip.NewWriter(&body)
79 w.Write([]byte("012345679"))
80 w.Close()
81 gz := &gzipReader{
82 body: io.NopCloser(&body),
83 }
84 var buf [1]byte
85 n, err := gz.Read(buf[:])
86 if n != 1 || err != nil {
87 t.Fatalf("first Read = %v, %v; want 1, nil", n, err)
88 }
89 if err := gz.Close(); err != nil {
90 t.Fatalf("gz Close error: %v", err)
91 }
92 n, err = gz.Read(buf[:])
93 if n != 0 || err != fs.ErrClosed {
94 t.Fatalf("Read after close = %v, %v; want 0, fs.ErrClosed", n, err)
95 }
96 }
97
98 func TestTransportNewTLSConfig(t *testing.T) {
99 tests := [...]struct {
100 conf *tls.Config
101 host string
102 want *tls.Config
103 }{
104
105 0: {
106 conf: nil,
107 host: "foo.com",
108 want: &tls.Config{
109 ServerName: "foo.com",
110 NextProtos: []string{NextProtoTLS},
111 },
112 },
113
114
115 1: {
116 conf: &tls.Config{
117 ServerName: "bar.com",
118 },
119 host: "foo.com",
120 want: &tls.Config{
121 ServerName: "bar.com",
122 NextProtos: []string{NextProtoTLS},
123 },
124 },
125
126
127 2: {
128 conf: &tls.Config{
129 NextProtos: []string{"foo", "bar"},
130 },
131 host: "example.com",
132 want: &tls.Config{
133 ServerName: "example.com",
134 NextProtos: []string{NextProtoTLS, "foo", "bar"},
135 },
136 },
137
138
139 3: {
140 conf: &tls.Config{
141 NextProtos: []string{"foo", "bar", NextProtoTLS},
142 },
143 host: "example.com",
144 want: &tls.Config{
145 ServerName: "example.com",
146 NextProtos: []string{"foo", "bar", NextProtoTLS},
147 },
148 },
149 }
150 for i, tt := range tests {
151
152
153 if tt.conf != nil {
154 tt.conf.SessionTicketsDisabled = true
155 }
156
157 tr := &Transport{TLSClientConfig: tt.conf}
158 got := tr.newTLSConfig(tt.host)
159
160 got.SessionTicketsDisabled = false
161
162 if !reflect.DeepEqual(got, tt.want) {
163 t.Errorf("%d. got %#v; want %#v", i, got, tt.want)
164 }
165 }
166 }
167
168 func TestAuthorityAddr(t *testing.T) {
169 tests := []struct {
170 scheme, authority string
171 want string
172 }{
173 {"http", "foo.com", "foo.com:80"},
174 {"https", "foo.com", "foo.com:443"},
175 {"https", "foo.com:", "foo.com:443"},
176 {"https", "foo.com:1234", "foo.com:1234"},
177 {"https", "1.2.3.4:1234", "1.2.3.4:1234"},
178 {"https", "1.2.3.4", "1.2.3.4:443"},
179 {"https", "1.2.3.4:", "1.2.3.4:443"},
180 {"https", "[::1]:1234", "[::1]:1234"},
181 {"https", "[::1]", "[::1]:443"},
182 {"https", "[::1]:", "[::1]:443"},
183 }
184 for _, tt := range tests {
185 got := authorityAddr(tt.scheme, tt.authority)
186 if got != tt.want {
187 t.Errorf("authorityAddr(%q, %q) = %q; want %q", tt.scheme, tt.authority, got, tt.want)
188 }
189 }
190 }
191
192
193
194
195
196
197 func TestTransportUsesGetBodyWhenPresent(t *testing.T) {
198 calls := 0
199 someBody := func() io.ReadCloser {
200 return struct{ io.ReadCloser }{io.NopCloser(bytes.NewReader(nil))}
201 }
202 req := &ClientRequest{
203 Body: someBody(),
204 GetBody: func() (io.ReadCloser, error) {
205 calls++
206 return someBody(), nil
207 },
208 }
209
210 req2, err := shouldRetryRequest(req, errClientConnUnusable)
211 if err != nil {
212 t.Fatal(err)
213 }
214 if calls != 1 {
215 t.Errorf("Calls = %d; want 1", calls)
216 }
217 if req2 == req {
218 t.Error("req2 changed")
219 }
220 if req2 == nil {
221 t.Fatal("req2 is nil")
222 }
223 if req2.Body == nil {
224 t.Fatal("req2.Body is nil")
225 }
226 if req2.GetBody == nil {
227 t.Fatal("req2.GetBody is nil")
228 }
229 if req2.Body == req.Body {
230 t.Error("req2.Body unchanged")
231 }
232 }
233
234 func TestClientConnTooIdle(t *testing.T) {
235 tests := []struct {
236 cc func() *ClientConn
237 want bool
238 }{
239 {
240 func() *ClientConn {
241 return &ClientConn{idleTimeout: 5 * time.Second, lastIdle: time.Now().Add(-10 * time.Second)}
242 },
243 true,
244 },
245 {
246 func() *ClientConn {
247 return &ClientConn{idleTimeout: 5 * time.Second, lastIdle: time.Time{}}
248 },
249 false,
250 },
251 {
252 func() *ClientConn {
253 return &ClientConn{idleTimeout: 60 * time.Second, lastIdle: time.Now().Add(-10 * time.Second)}
254 },
255 false,
256 },
257 {
258 func() *ClientConn {
259 return &ClientConn{idleTimeout: 0, lastIdle: time.Now().Add(-10 * time.Second)}
260 },
261 false,
262 },
263 }
264 for i, tt := range tests {
265 got := tt.cc().tooIdleLocked()
266 if got != tt.want {
267 t.Errorf("%d. got %v; want %v", i, got, tt.want)
268 }
269 }
270 }
271
View as plain text