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.
20 func TestServer_Push_Success(t *testing.T) {
22 mainBody = "<html>index page</html>"
23 pushedBody = "<html>pushed page</html>"
24 userAgent = "testagent"
29 checkPromisedReq := func(r *http.Request, wantMethod string, wantH http.Header) error {
30 if got, want := r.Method, wantMethod; got != want {
31 return fmt.Errorf("promised Req.Method=%q, want %q", got, want)
33 if got, want := r.Header, wantH; !reflect.DeepEqual(got, want) {
34 return fmt.Errorf("promised Req.Header=%q, want %q", got, want)
36 if got, want := "https://"+r.Host, stURL; got != want {
37 return fmt.Errorf("promised Req.Host=%q, want %q", got, want)
40 return fmt.Errorf("nil Body")
42 if buf, err := ioutil.ReadAll(r.Body); err != nil || len(buf) != 0 {
43 return fmt.Errorf("ReadAll(Body)=%q,%v, want '',nil", buf, err)
48 errc := make(chan error, 3)
49 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
50 switch r.URL.RequestURI() {
52 // Push "/pushed?get" as a GET request, using an absolute URL.
53 opt := &http.PushOptions{
55 "User-Agent": {userAgent},
58 if err := w.(http.Pusher).Push(stURL+"/pushed?get", opt); err != nil {
59 errc <- fmt.Errorf("error pushing /pushed?get: %v", err)
62 // Push "/pushed?head" as a HEAD request, using a path.
63 opt = &http.PushOptions{
66 "User-Agent": {userAgent},
70 if err := w.(http.Pusher).Push("/pushed?head", opt); err != nil {
71 errc <- fmt.Errorf("error pushing /pushed?head: %v", err)
74 w.Header().Set("Content-Type", "text/html")
75 w.Header().Set("Content-Length", strconv.Itoa(len(mainBody)))
77 io.WriteString(w, mainBody)
81 wantH := http.Header{}
82 wantH.Set("User-Agent", userAgent)
83 if err := checkPromisedReq(r, "GET", wantH); err != nil {
84 errc <- fmt.Errorf("/pushed?get: %v", err)
87 w.Header().Set("Content-Type", "text/html")
88 w.Header().Set("Content-Length", strconv.Itoa(len(pushedBody)))
90 io.WriteString(w, pushedBody)
94 wantH := http.Header{}
95 wantH.Set("User-Agent", userAgent)
96 wantH.Set("Cookie", cookie)
97 if err := checkPromisedReq(r, "HEAD", wantH); err != nil {
98 errc <- fmt.Errorf("/pushed?head: %v", err)
105 errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
110 // Send one request, which should push two responses.
113 for k := 0; k < 3; k++ {
115 case <-time.After(2 * time.Second):
116 t.Errorf("timeout waiting for handler %d to finish", k)
124 checkPushPromise := func(f Frame, promiseID uint32, wantH [][2]string) error {
125 pp, ok := f.(*PushPromiseFrame)
127 return fmt.Errorf("got a %T; want *PushPromiseFrame", f)
129 if !pp.HeadersEnded() {
130 return fmt.Errorf("want END_HEADERS flag in PushPromiseFrame")
132 if got, want := pp.PromiseID, promiseID; got != want {
133 return fmt.Errorf("got PromiseID %v; want %v", got, want)
135 gotH := st.decodeHeader(pp.HeaderBlockFragment())
136 if !reflect.DeepEqual(gotH, wantH) {
137 return fmt.Errorf("got promised headers %v; want %v", gotH, wantH)
141 checkHeaders := func(f Frame, wantH [][2]string) error {
142 hf, ok := f.(*HeadersFrame)
144 return fmt.Errorf("got a %T; want *HeadersFrame", f)
146 gotH := st.decodeHeader(hf.HeaderBlockFragment())
147 if !reflect.DeepEqual(gotH, wantH) {
148 return fmt.Errorf("got response headers %v; want %v", gotH, wantH)
152 checkData := func(f Frame, wantData string) error {
153 df, ok := f.(*DataFrame)
155 return fmt.Errorf("got a %T; want *DataFrame", f)
157 if gotData := string(df.Data()); gotData != wantData {
158 return fmt.Errorf("got response data %q; want %q", gotData, wantData)
163 // Stream 1 has 2 PUSH_PROMISE + HEADERS + DATA
164 // Stream 2 has HEADERS + DATA
165 // Stream 4 has HEADERS
166 expected := map[uint32][]func(Frame) error{
168 func(f Frame) error {
169 return checkPushPromise(f, 2, [][2]string{
171 {":scheme", "https"},
172 {":authority", st.ts.Listener.Addr().String()},
173 {":path", "/pushed?get"},
174 {"user-agent", userAgent},
177 func(f Frame) error {
178 return checkPushPromise(f, 4, [][2]string{
180 {":scheme", "https"},
181 {":authority", st.ts.Listener.Addr().String()},
182 {":path", "/pushed?head"},
184 {"user-agent", userAgent},
187 func(f Frame) error {
188 return checkHeaders(f, [][2]string{
190 {"content-type", "text/html"},
191 {"content-length", strconv.Itoa(len(mainBody))},
194 func(f Frame) error {
195 return checkData(f, mainBody)
199 func(f Frame) error {
200 return checkHeaders(f, [][2]string{
202 {"content-type", "text/html"},
203 {"content-length", strconv.Itoa(len(pushedBody))},
206 func(f Frame) error {
207 return checkData(f, pushedBody)
211 func(f Frame) error {
212 return checkHeaders(f, [][2]string{
219 consumed := map[uint32]int{}
220 for k := 0; len(expected) > 0; k++ {
221 f, err := st.readFrame()
223 for id, left := range expected {
224 t.Errorf("stream %d: missing %d frames", id, len(left))
226 t.Fatalf("readFrame %d: %v", k, err)
228 id := f.Header().StreamID
229 label := fmt.Sprintf("stream %d, frame %d", id, consumed[id])
230 if len(expected[id]) == 0 {
231 t.Fatalf("%s: unexpected frame %#+v", label, f)
233 check := expected[id][0]
234 expected[id] = expected[id][1:]
235 if len(expected[id]) == 0 {
238 if err := check(f); err != nil {
239 t.Fatalf("%s: %v", label, err)
245 func TestServer_Push_SuccessNoRace(t *testing.T) {
246 // Regression test for issue #18326. Ensure the request handler can mutate
247 // pushed request headers without racing with the PUSH_PROMISE write.
248 errc := make(chan error, 2)
249 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
250 switch r.URL.RequestURI() {
252 opt := &http.PushOptions{
253 Header: http.Header{"User-Agent": {"testagent"}},
255 if err := w.(http.Pusher).Push("/pushed", opt); err != nil {
256 errc <- fmt.Errorf("error pushing: %v", err)
263 // Update request header, ensure there is no race.
264 r.Header.Set("User-Agent", "newagent")
265 r.Header.Set("Cookie", "cookie")
270 errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
274 // Send one request, which should push one response.
277 for k := 0; k < 2; k++ {
279 case <-time.After(2 * time.Second):
280 t.Errorf("timeout waiting for handler %d to finish", k)
289 func TestServer_Push_RejectRecursivePush(t *testing.T) {
290 // Expect two requests, but might get three if there's a bug and the second push succeeds.
291 errc := make(chan error, 3)
292 handler := func(w http.ResponseWriter, r *http.Request) error {
293 baseURL := "https://" + r.Host
296 if err := w.(http.Pusher).Push(baseURL+"/push1", nil); err != nil {
297 return fmt.Errorf("first Push()=%v, want nil", err)
302 if got, want := w.(http.Pusher).Push(baseURL+"/push2", nil), ErrRecursivePush; got != want {
303 return fmt.Errorf("Push()=%v, want %v", got, want)
308 return fmt.Errorf("unexpected path: %q", r.URL.Path)
311 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
312 errc <- handler(w, r)
317 if err := <-errc; err != nil {
318 t.Errorf("First request failed: %v", err)
320 if err := <-errc; err != nil {
321 t.Errorf("Second request failed: %v", err)
325 func testServer_Push_RejectSingleRequest(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) {
326 // Expect one request, but might get two if there's a bug and the push succeeds.
327 errc := make(chan error, 2)
328 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
329 errc <- doPush(w.(http.Pusher), r)
333 if err := st.fr.WriteSettings(settings...); err != nil {
334 st.t.Fatalf("WriteSettings: %v", err)
338 if err := <-errc; err != nil {
341 // Should not get a PUSH_PROMISE frame.
342 hf := st.wantHeaders()
343 if !hf.StreamEnded() {
344 t.Error("stream should end after headers")
348 func TestServer_Push_RejectIfDisabled(t *testing.T) {
349 testServer_Push_RejectSingleRequest(t,
350 func(p http.Pusher, r *http.Request) error {
351 if got, want := p.Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
352 return fmt.Errorf("Push()=%v, want %v", got, want)
356 Setting{SettingEnablePush, 0})
359 func TestServer_Push_RejectWhenNoConcurrentStreams(t *testing.T) {
360 testServer_Push_RejectSingleRequest(t,
361 func(p http.Pusher, r *http.Request) error {
362 if got, want := p.Push("https://"+r.Host+"/pushed", nil), ErrPushLimitReached; got != want {
363 return fmt.Errorf("Push()=%v, want %v", got, want)
367 Setting{SettingMaxConcurrentStreams, 0})
370 func TestServer_Push_RejectWrongScheme(t *testing.T) {
371 testServer_Push_RejectSingleRequest(t,
372 func(p http.Pusher, r *http.Request) error {
373 if err := p.Push("http://"+r.Host+"/pushed", nil); err == nil {
374 return errors.New("Push() should have failed (push target URL is http)")
380 func TestServer_Push_RejectMissingHost(t *testing.T) {
381 testServer_Push_RejectSingleRequest(t,
382 func(p http.Pusher, r *http.Request) error {
383 if err := p.Push("https:pushed", nil); err == nil {
384 return errors.New("Push() should have failed (push target URL missing host)")
390 func TestServer_Push_RejectRelativePath(t *testing.T) {
391 testServer_Push_RejectSingleRequest(t,
392 func(p http.Pusher, r *http.Request) error {
393 if err := p.Push("../test", nil); err == nil {
394 return errors.New("Push() should have failed (push target is a relative path)")
400 func TestServer_Push_RejectForbiddenMethod(t *testing.T) {
401 testServer_Push_RejectSingleRequest(t,
402 func(p http.Pusher, r *http.Request) error {
403 if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Method: "POST"}); err == nil {
404 return errors.New("Push() should have failed (cannot promise a POST)")
410 func TestServer_Push_RejectForbiddenHeader(t *testing.T) {
411 testServer_Push_RejectSingleRequest(t,
412 func(p http.Pusher, r *http.Request) error {
413 header := http.Header{
414 "Content-Length": {"10"},
415 "Content-Encoding": {"gzip"},
418 "Host": {"test.com"},
419 ":authority": {"test.com"},
421 if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Header: header}); err == nil {
422 return errors.New("Push() should have failed (forbidden headers)")
428 func TestServer_Push_StateTransitions(t *testing.T) {
431 gotPromise := make(chan bool)
432 finishedPush := make(chan bool)
434 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
435 switch r.URL.RequestURI() {
437 if err := w.(http.Pusher).Push("/pushed", nil); err != nil {
438 t.Errorf("Push error: %v", err)
440 // Don't finish this request until the push finishes so we don't
441 // nondeterministically interleave output frames with the push.
446 w.Header().Set("Content-Type", "text/html")
447 w.Header().Set("Content-Length", strconv.Itoa(len(body)))
449 io.WriteString(w, body)
454 if st.stream(2) != nil {
455 t.Fatal("stream 2 should be empty")
457 if got, want := st.streamState(2), stateIdle; got != want {
458 t.Fatalf("streamState(2)=%v, want %v", got, want)
461 // After the PUSH_PROMISE is sent, the stream should be stateHalfClosedRemote.
463 if got, want := st.streamState(2), stateHalfClosedRemote; got != want {
464 t.Fatalf("streamState(2)=%v, want %v", got, want)
466 // We stall the HTTP handler for "/pushed" until the above check. If we don't
467 // stall the handler, then the handler might write HEADERS and DATA and finish
468 // the stream before we check st.streamState(2) -- should that happen, we'll
469 // see stateClosed and fail the above check.
472 if df := st.wantData(); !df.StreamEnded() {
473 t.Fatal("expected END_STREAM flag on DATA")
475 if got, want := st.streamState(2), stateClosed; got != want {
476 t.Fatalf("streamState(2)=%v, want %v", got, want)
481 func TestServer_Push_RejectAfterGoAway(t *testing.T) {
482 var readyOnce sync.Once
483 ready := make(chan struct{})
484 errc := make(chan error, 2)
485 st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
488 case <-time.After(5 * time.Second):
489 errc <- fmt.Errorf("timeout waiting for GOAWAY to be processed")
491 if got, want := w.(http.Pusher).Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
492 errc <- fmt.Errorf("Push()=%v, want %v", got, want)
500 // Send GOAWAY and wait for it to be processed.
501 st.fr.WriteGoAway(1, ErrCodeNo, nil)
509 st.sc.serveMsgCh <- func(loopNum int) {
510 if !st.sc.pushEnabled {
511 readyOnce.Do(func() { close(ready) })
516 if err := <-errc; err != nil {