Browse Source

Send CONTINUATION frames from server.

Brad Fitzpatrick 11 years ago
parent
commit
7ded6e2215
2 changed files with 67 additions and 9 deletions
  1. 27 9
      server.go
  2. 40 0
      server_test.go

+ 27 - 9
server.go

@@ -1351,16 +1351,34 @@ func (sc *serverConn) writeHeadersFrame(streamID uint32, v interface{}) error {
 	}
 
 	headerBlock := sc.headerWriteBuf.Bytes()
-	if len(headerBlock) > int(sc.maxWriteFrameSize) {
-		// we'll need continuation ones.
-		panic("TODO")
+	if len(headerBlock) == 0 {
+		panic("unexpected empty hpack")
+	}
+	first := true
+	for len(headerBlock) > 0 {
+		frag := headerBlock
+		if len(frag) > int(sc.maxWriteFrameSize) {
+			frag = frag[:sc.maxWriteFrameSize]
+		}
+		headerBlock = headerBlock[len(frag):]
+		endHeaders := len(headerBlock) == 0
+		var err error
+		if first {
+			first = false
+			err = sc.framer.WriteHeaders(HeadersFrameParam{
+				StreamID:      req.stream.id,
+				BlockFragment: frag,
+				EndStream:     req.endStream,
+				EndHeaders:    endHeaders,
+			})
+		} else {
+			err = sc.framer.WriteContinuation(req.stream.id, endHeaders, frag)
+		}
+		if err != nil {
+			return err
+		}
 	}
-	return sc.framer.WriteHeaders(HeadersFrameParam{
-		StreamID:      req.stream.id,
-		BlockFragment: headerBlock,
-		EndStream:     req.endStream,
-		EndHeaders:    true, // no continuation yet
-	})
+	return nil
 }
 
 // called from handler goroutines.

+ 40 - 0
server_test.go

@@ -202,6 +202,18 @@ func (st *serverTester) wantHeaders() *HeadersFrame {
 	return hf
 }
 
+func (st *serverTester) wantContinuation() *ContinuationFrame {
+	f, err := st.readFrame()
+	if err != nil {
+		st.t.Fatalf("Error while expecting a CONTINUATION frame: %v", err)
+	}
+	cf, ok := f.(*ContinuationFrame)
+	if !ok {
+		st.t.Fatalf("got a %T; want *ContinuationFrame", f)
+	}
+	return cf
+}
+
 func (st *serverTester) wantData() *DataFrame {
 	f, err := st.readFrame()
 	if err != nil {
@@ -1659,6 +1671,34 @@ func TestServer_Rejects_Too_Many_Streams(t *testing.T) {
 	}
 }
 
+// So many response headers that the server needs to use CONTINUATION frames:
+func TestServer_Response_ManyHeaders_With_Continuation(t *testing.T) {
+	testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+		h := w.Header()
+		for i := 0; i < 5000; i++ {
+			h.Set(fmt.Sprintf("x-header-%d", i), fmt.Sprintf("x-value-%d", i))
+		}
+		return nil
+	}, func(st *serverTester) {
+		getSlash(st)
+		hf := st.wantHeaders()
+		if hf.HeadersEnded() {
+			t.Fatal("got unwanted END_HEADERS flag")
+		}
+		n := 0
+		for {
+			n++
+			cf := st.wantContinuation()
+			if cf.HeadersEnded() {
+				break
+			}
+		}
+		if n < 5 {
+			t.Errorf("Only got %d CONTINUATION frames; expected 5+ (currently 6)")
+		}
+	})
+}
+
 func decodeHeader(t *testing.T, headerBlock []byte) (pairs [][2]string) {
 	d := hpack.NewDecoder(initialHeaderTableSize, func(f hpack.HeaderField) {
 		pairs = append(pairs, [2]string{f.Name, f.Value})