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.
22 func TestServer_Push_Success(t *testing.T) {
24 mainBody = "<html>index page</html>"
25 pushedBody = "<html>pushed page</html>"
26 userAgent = "testagent"
31 checkPromisedReq := func(r *http.Request, wantMethod string, wantH http.Header) error {
32 if got, want := r.Method, wantMethod; got != want {
33 return fmt.Errorf("promised Req.Method=%q, want %q", got, want)
35 if got, want := r.Header, wantH; !reflect.DeepEqual(got, want) {
36 return fmt.Errorf("promised Req.Header=%q, want %q", got, want)
38 if got, want := "https://"+r.Host, stURL; got != want {
39 return fmt.Errorf("promised Req.Host=%q, want %q", got, want)
42 return fmt.Errorf("nil Body")
44 if buf, err := ioutil.ReadAll(r.Body); err != nil || len(buf) != 0 {
45 return fmt.Errorf("ReadAll(Body)=%q,%v, want '',nil", buf, err)
50 errc := make(chan error, 3)
51 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
52 switch r.URL.RequestURI() {
54 // Push "/pushed?get" as a GET request, using an absolute URL.
55 opt := &http.PushOptions{
57 "User-Agent": {userAgent},
60 if err := w.(http.Pusher).Push(stURL+"/pushed?get", opt); err != nil {
61 errc <- fmt.Errorf("error pushing /pushed?get: %v", err)
64 // Push "/pushed?head" as a HEAD request, using a path.
65 opt = &http.PushOptions{
68 "User-Agent": {userAgent},
72 if err := w.(http.Pusher).Push("/pushed?head", opt); err != nil {
73 errc <- fmt.Errorf("error pushing /pushed?head: %v", err)
76 w.Header().Set("Content-Type", "text/html")
77 w.Header().Set("Content-Length", strconv.Itoa(len(mainBody)))
79 io.WriteString(w, mainBody)
83 wantH := http.Header{}
84 wantH.Set("User-Agent", userAgent)
85 if err := checkPromisedReq(r, "GET", wantH); err != nil {
86 errc <- fmt.Errorf("/pushed?get: %v", err)
89 w.Header().Set("Content-Type", "text/html")
90 w.Header().Set("Content-Length", strconv.Itoa(len(pushedBody)))
92 io.WriteString(w, pushedBody)
96 wantH := http.Header{}
97 wantH.Set("User-Agent", userAgent)
98 wantH.Set("Cookie", cookie)
99 if err := checkPromisedReq(r, "HEAD", wantH); err != nil {
100 errc <- fmt.Errorf("/pushed?head: %v", err)
107 errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
112 // Send one request, which should push two responses.
115 for k := 0; k < 3; k++ {
117 case <-time.After(2 * time.Second):
118 t.Errorf("timeout waiting for handler %d to finish", k)
126 checkPushPromise := func(f Frame, promiseID uint32, wantH [][2]string) error {
127 pp, ok := f.(*PushPromiseFrame)
129 return fmt.Errorf("got a %T; want *PushPromiseFrame", f)
131 if !pp.HeadersEnded() {
132 return fmt.Errorf("want END_HEADERS flag in PushPromiseFrame")
134 if got, want := pp.PromiseID, promiseID; got != want {
135 return fmt.Errorf("got PromiseID %v; want %v", got, want)
137 gotH := st.decodeHeader(pp.HeaderBlockFragment())
138 if !reflect.DeepEqual(gotH, wantH) {
139 return fmt.Errorf("got promised headers %v; want %v", gotH, wantH)
143 checkHeaders := func(f Frame, wantH [][2]string) error {
144 hf, ok := f.(*HeadersFrame)
146 return fmt.Errorf("got a %T; want *HeadersFrame", f)
148 gotH := st.decodeHeader(hf.HeaderBlockFragment())
149 if !reflect.DeepEqual(gotH, wantH) {
150 return fmt.Errorf("got response headers %v; want %v", gotH, wantH)
154 checkData := func(f Frame, wantData string) error {
155 df, ok := f.(*DataFrame)
157 return fmt.Errorf("got a %T; want *DataFrame", f)
159 if gotData := string(df.Data()); gotData != wantData {
160 return fmt.Errorf("got response data %q; want %q", gotData, wantData)
165 // Stream 1 has 2 PUSH_PROMISE + HEADERS + DATA
166 // Stream 2 has HEADERS + DATA
167 // Stream 4 has HEADERS
168 expected := map[uint32][]func(Frame) error{
170 func(f Frame) error {
171 return checkPushPromise(f, 2, [][2]string{
173 {":scheme", "https"},
174 {":authority", st.ts.Listener.Addr().String()},
175 {":path", "/pushed?get"},
176 {"user-agent", userAgent},
179 func(f Frame) error {
180 return checkPushPromise(f, 4, [][2]string{
182 {":scheme", "https"},
183 {":authority", st.ts.Listener.Addr().String()},
184 {":path", "/pushed?head"},
186 {"user-agent", userAgent},
189 func(f Frame) error {
190 return checkHeaders(f, [][2]string{
192 {"content-type", "text/html"},
193 {"content-length", strconv.Itoa(len(mainBody))},
196 func(f Frame) error {
197 return checkData(f, mainBody)
201 func(f Frame) error {
202 return checkHeaders(f, [][2]string{
204 {"content-type", "text/html"},
205 {"content-length", strconv.Itoa(len(pushedBody))},
208 func(f Frame) error {
209 return checkData(f, pushedBody)
213 func(f Frame) error {
214 return checkHeaders(f, [][2]string{
221 consumed := map[uint32]int{}
222 for k := 0; len(expected) > 0; k++ {
223 f, err := st.readFrame()
225 for id, left := range expected {
226 t.Errorf("stream %d: missing %d frames", id, len(left))
228 t.Fatalf("readFrame %d: %v", k, err)
230 id := f.Header().StreamID
231 label := fmt.Sprintf("stream %d, frame %d", id, consumed[id])
232 if len(expected[id]) == 0 {
233 t.Fatalf("%s: unexpected frame %#+v", label, f)
235 check := expected[id][0]
236 expected[id] = expected[id][1:]
237 if len(expected[id]) == 0 {
240 if err := check(f); err != nil {
241 t.Fatalf("%s: %v", label, err)
247 func TestServer_Push_SuccessNoRace(t *testing.T) {
248 // Regression test for issue #18326. Ensure the request handler can mutate
249 // pushed request headers without racing with the PUSH_PROMISE write.
250 errc := make(chan error, 2)
251 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
252 switch r.URL.RequestURI() {
254 opt := &http.PushOptions{
255 Header: http.Header{"User-Agent": {"testagent"}},
257 if err := w.(http.Pusher).Push("/pushed", opt); err != nil {
258 errc <- fmt.Errorf("error pushing: %v", err)
265 // Update request header, ensure there is no race.
266 r.Header.Set("User-Agent", "newagent")
267 r.Header.Set("Cookie", "cookie")
272 errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
276 // Send one request, which should push one response.
279 for k := 0; k < 2; k++ {
281 case <-time.After(2 * time.Second):
282 t.Errorf("timeout waiting for handler %d to finish", k)
291 func TestServer_Push_RejectRecursivePush(t *testing.T) {
292 // Expect two requests, but might get three if there's a bug and the second push succeeds.
293 errc := make(chan error, 3)
294 handler := func(w http.ResponseWriter, r *http.Request) error {
295 baseURL := "https://" + r.Host
298 if err := w.(http.Pusher).Push(baseURL+"/push1", nil); err != nil {
299 return fmt.Errorf("first Push()=%v, want nil", err)
304 if got, want := w.(http.Pusher).Push(baseURL+"/push2", nil), ErrRecursivePush; got != want {
305 return fmt.Errorf("Push()=%v, want %v", got, want)
310 return fmt.Errorf("unexpected path: %q", r.URL.Path)
313 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
314 errc <- handler(w, r)
319 if err := <-errc; err != nil {
320 t.Errorf("First request failed: %v", err)
322 if err := <-errc; err != nil {
323 t.Errorf("Second request failed: %v", err)
327 func testServer_Push_RejectSingleRequest(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) {
328 // Expect one request, but might get two if there's a bug and the push succeeds.
329 errc := make(chan error, 2)
330 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
331 errc <- doPush(w.(http.Pusher), r)
335 if err := st.fr.WriteSettings(settings...); err != nil {
336 st.t.Fatalf("WriteSettings: %v", err)
340 if err := <-errc; err != nil {
343 // Should not get a PUSH_PROMISE frame.
344 hf := st.wantHeaders()
345 if !hf.StreamEnded() {
346 t.Error("stream should end after headers")
350 func TestServer_Push_RejectIfDisabled(t *testing.T) {
351 testServer_Push_RejectSingleRequest(t,
352 func(p http.Pusher, r *http.Request) error {
353 if got, want := p.Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
354 return fmt.Errorf("Push()=%v, want %v", got, want)
358 Setting{SettingEnablePush, 0})
361 func TestServer_Push_RejectWhenNoConcurrentStreams(t *testing.T) {
362 testServer_Push_RejectSingleRequest(t,
363 func(p http.Pusher, r *http.Request) error {
364 if got, want := p.Push("https://"+r.Host+"/pushed", nil), ErrPushLimitReached; got != want {
365 return fmt.Errorf("Push()=%v, want %v", got, want)
369 Setting{SettingMaxConcurrentStreams, 0})
372 func TestServer_Push_RejectWrongScheme(t *testing.T) {
373 testServer_Push_RejectSingleRequest(t,
374 func(p http.Pusher, r *http.Request) error {
375 if err := p.Push("http://"+r.Host+"/pushed", nil); err == nil {
376 return errors.New("Push() should have failed (push target URL is http)")
382 func TestServer_Push_RejectMissingHost(t *testing.T) {
383 testServer_Push_RejectSingleRequest(t,
384 func(p http.Pusher, r *http.Request) error {
385 if err := p.Push("https:pushed", nil); err == nil {
386 return errors.New("Push() should have failed (push target URL missing host)")
392 func TestServer_Push_RejectRelativePath(t *testing.T) {
393 testServer_Push_RejectSingleRequest(t,
394 func(p http.Pusher, r *http.Request) error {
395 if err := p.Push("../test", nil); err == nil {
396 return errors.New("Push() should have failed (push target is a relative path)")
402 func TestServer_Push_RejectForbiddenMethod(t *testing.T) {
403 testServer_Push_RejectSingleRequest(t,
404 func(p http.Pusher, r *http.Request) error {
405 if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Method: "POST"}); err == nil {
406 return errors.New("Push() should have failed (cannot promise a POST)")
412 func TestServer_Push_RejectForbiddenHeader(t *testing.T) {
413 testServer_Push_RejectSingleRequest(t,
414 func(p http.Pusher, r *http.Request) error {
415 header := http.Header{
416 "Content-Length": {"10"},
417 "Content-Encoding": {"gzip"},
420 "Host": {"test.com"},
421 ":authority": {"test.com"},
423 if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Header: header}); err == nil {
424 return errors.New("Push() should have failed (forbidden headers)")
430 func TestServer_Push_StateTransitions(t *testing.T) {
433 gotPromise := make(chan bool)
434 finishedPush := make(chan bool)
436 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
437 switch r.URL.RequestURI() {
439 if err := w.(http.Pusher).Push("/pushed", nil); err != nil {
440 t.Errorf("Push error: %v", err)
442 // Don't finish this request until the push finishes so we don't
443 // nondeterministically interleave output frames with the push.
448 w.Header().Set("Content-Type", "text/html")
449 w.Header().Set("Content-Length", strconv.Itoa(len(body)))
451 io.WriteString(w, body)
456 if st.stream(2) != nil {
457 t.Fatal("stream 2 should be empty")
459 if got, want := st.streamState(2), stateIdle; got != want {
460 t.Fatalf("streamState(2)=%v, want %v", got, want)
463 // After the PUSH_PROMISE is sent, the stream should be stateHalfClosedRemote.
465 if got, want := st.streamState(2), stateHalfClosedRemote; got != want {
466 t.Fatalf("streamState(2)=%v, want %v", got, want)
468 // We stall the HTTP handler for "/pushed" until the above check. If we don't
469 // stall the handler, then the handler might write HEADERS and DATA and finish
470 // the stream before we check st.streamState(2) -- should that happen, we'll
471 // see stateClosed and fail the above check.
474 if df := st.wantData(); !df.StreamEnded() {
475 t.Fatal("expected END_STREAM flag on DATA")
477 if got, want := st.streamState(2), stateClosed; got != want {
478 t.Fatalf("streamState(2)=%v, want %v", got, want)
483 func TestServer_Push_RejectAfterGoAway(t *testing.T) {
484 var readyOnce sync.Once
485 ready := make(chan struct{})
486 errc := make(chan error, 2)
487 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
490 case <-time.After(5 * time.Second):
491 errc <- fmt.Errorf("timeout waiting for GOAWAY to be processed")
493 if got, want := w.(http.Pusher).Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
494 errc <- fmt.Errorf("Push()=%v, want %v", got, want)
502 // Send GOAWAY and wait for it to be processed.
503 st.fr.WriteGoAway(1, ErrCodeNo, nil)
511 st.sc.serveMsgCh <- func(loopNum int) {
512 if !st.sc.pushEnabled {
513 readyOnce.Do(func() { close(ready) })
518 if err := <-errc; err != nil {