Browse Source

Feed HeadersFrame data into hpack. Add ContinuationFrame.

Brad Fitzpatrick 11 years ago
parent
commit
9ae9e719b1
2 changed files with 85 additions and 12 deletions
  1. 34 6
      frame.go
  2. 51 6
      http2.go

+ 34 - 6
frame.go

@@ -71,6 +71,8 @@ const (
 	FlagHeadersEndHeaders Flags = 0x4
 	FlagHeadersPadded     Flags = 0x8
 	FlagHeadersPriority   Flags = 0x20
+
+	FlagContinuationEndHeaders = FlagHeadersEndHeaders
 )
 
 // A SettingID is an HTTP/2 setting as defined in
@@ -111,6 +113,7 @@ var frameParsers = map[FrameType]frameParser{
 	FrameSettings:     parseSettingsFrame,
 	FrameWindowUpdate: parseWindowUpdateFrame,
 	FrameHeaders:      parseHeadersFrame,
+	FrameContinuation: parseContinuationFrame,
 }
 
 func typeFrameParser(t FrameType) frameParser {
@@ -182,7 +185,7 @@ func readFrameHeader(buf []byte, r io.Reader) (FrameHeader, error) {
 
 // A Frame is the base interface implemented by all frame types.
 // Callers will generally type-assert the specific frame type:
-// *HeaderFrame, *SettingsFrame, *WindowUpdateFrame, etc.
+// *HeadersFrame, *SettingsFrame, *WindowUpdateFrame, etc.
 //
 // Frames are only valid until the next call to Framer.ReadFrame.
 type Frame interface {
@@ -341,7 +344,9 @@ func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
 	}, nil
 }
 
-type HeaderFrame struct {
+// A HeadersFrame is used to open a stream and additionally carries a
+// header block fragment.
+type HeadersFrame struct {
 	FrameHeader
 
 	// If FlagHeadersPriority:
@@ -351,18 +356,21 @@ type HeaderFrame struct {
 	// Weight is [0,255]. Only valid if FrameHeader.Flags has the
 	// FlagHeadersPriority bit set, in which case the caller must
 	// also add 1 to get to spec-defined [1,256] range.
-	Weight uint8
-
+	Weight        uint8
 	headerFragBuf []byte // not owned
 }
 
-func (f *HeaderFrame) HeaderBlockFragment() []byte {
+func (f *HeadersFrame) HeaderBlockFragment() []byte {
 	f.checkValid()
 	return f.headerFragBuf
 }
 
+func (f *HeadersFrame) HeadersEnded() bool {
+	return f.FrameHeader.Flags.Has(FlagHeadersEndHeaders)
+}
+
 func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
-	hf := &HeaderFrame{
+	hf := &HeadersFrame{
 		FrameHeader: fh,
 	}
 	if fh.StreamID == 0 {
@@ -398,6 +406,26 @@ func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
 	return hf, nil
 }
 
+// A ContinuationFrame is used to continue a sequence of header block fragments.
+// See http://http2.github.io/http2-spec/#rfc.section.6.10
+type ContinuationFrame struct {
+	FrameHeader
+	headerFragBuf []byte
+}
+
+func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
+	return &ContinuationFrame{fh, p}, nil
+}
+
+func (f *ContinuationFrame) HeaderBlockFragment() []byte {
+	f.checkValid()
+	return f.headerFragBuf
+}
+
+func (f *ContinuationFrame) HeadersEnded() bool {
+	return f.FrameHeader.Flags.Has(FlagContinuationEndHeaders)
+}
+
 func readByte(p []byte) (remain []byte, b byte, err error) {
 	if len(p) == 0 {
 		return nil, 0, io.ErrUnexpectedEOF

+ 51 - 6
http2.go

@@ -16,6 +16,8 @@ import (
 	"io"
 	"log"
 	"net/http"
+
+	"github.com/bradfitz/http2/hpack"
 )
 
 const (
@@ -28,7 +30,12 @@ var (
 	clientPreface = []byte(ClientPreface)
 )
 
-const npnProto = "h2-14"
+const (
+	npnProto = "h2-14"
+
+	// http://http2.github.io/http2-spec/#SettingValues
+	initialHeaderTableSize = 4096
+)
 
 // Server is an HTTP2 server.
 type Server struct {
@@ -43,6 +50,7 @@ func (srv *Server) handleClientConn(hs *http.Server, c *tls.Conn, h http.Handler
 		handler: h,
 		framer:  NewFramer(c, c),
 	}
+	cc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField)
 	cc.serve()
 }
 
@@ -51,6 +59,12 @@ type clientConn struct {
 	conn    *tls.Conn
 	handler http.Handler
 	framer  *Framer
+
+	hpackDecoder *hpack.Decoder
+
+	// midHeaderStreamID is non-zero if we're in the middle
+	// of parsing headers that span multiple frames.
+	midHeaderStreamID uint32
 }
 
 func (cc *clientConn) logf(format string, args ...interface{}) {
@@ -61,6 +75,21 @@ func (cc *clientConn) logf(format string, args ...interface{}) {
 	}
 }
 
+func (cc *clientConn) frameAcceptable(f Frame) error {
+	if s := cc.midHeaderStreamID; s != 0 {
+		if cf, ok := f.(*ContinuationFrame); !ok {
+			return ConnectionError(ErrCodeProtocol)
+		} else if cf.Header().StreamID != s {
+			return ConnectionError(ErrCodeProtocol)
+		}
+	}
+	return nil
+}
+
+func (cc *clientConn) onNewHeaderField(f hpack.HeaderField) {
+	log.Printf("Header field: +%v", f)
+}
+
 func (cc *clientConn) serve() {
 	defer cc.conn.Close()
 	log.Printf("HTTP/2 connection from %v on %p", cc.conn.RemoteAddr(), cc.hs)
@@ -79,23 +108,39 @@ func (cc *clientConn) serve() {
 	for {
 
 		f, err := cc.framer.ReadFrame()
+		if err == nil {
+			err = cc.frameAcceptable(f)
+		}
 		if h2e, ok := err.(Error); ok {
 			if h2e.IsConnectionError() {
-				log.Printf("Disconnection; connection error: %v", err)
+				cc.logf("Disconnection; connection error: %v", err)
 				return
 			}
 			// TODO: stream errors, etc
 		}
 		if err != nil {
-			log.Printf("Disconnection due to other error: %v", err)
+			cc.logf("Disconnection due to other error: %v", err)
 			return
 		}
+
 		log.Printf("got %v: %#v", f.Header(), f)
-		if sf, ok := f.(*SettingsFrame); ok {
-			log.Printf("Is a settings frame")
-			sf.ForeachSetting(func(s SettingID, v uint32) {
+		switch f := f.(type) {
+		case *SettingsFrame:
+			f.ForeachSetting(func(s SettingID, v uint32) {
 				log.Printf("  setting %s = %v", s, v)
 			})
+		case *HeadersFrame:
+			cc.hpackDecoder.Write(f.HeaderBlockFragment())
+			if f.HeadersEnded() {
+				cc.midHeaderStreamID = 0
+				// TODO: transition state
+			}
+		case *ContinuationFrame:
+			cc.hpackDecoder.Write(f.HeaderBlockFragment())
+			if f.HeadersEnded() {
+				cc.midHeaderStreamID = 0
+				// TODO: transition state
+			}
 		}
 	}
 }