فهرست منبع

Move each frame type to its own processing method. Add some rules from spec.

Brad Fitzpatrick 11 سال پیش
والد
کامیت
6b0e3ccaf4
1فایلهای تغییر یافته به همراه74 افزوده شده و 37 حذف شده
  1. 74 37
      http2.go

+ 74 - 37
http2.go

@@ -63,11 +63,12 @@ type serverConn struct {
 
 	hpackDecoder *hpack.Decoder
 
-	// midHeaderStreamID is non-zero if we're in the middle
+	// curHeaderStreamID is non-zero if we're in the middle
 	// of parsing headers that span multiple frames.
-	midHeaderStreamID uint32
+	curHeaderStreamID uint32
 
-	streams map[uint32]*stream
+	maxStreamID uint32 // max ever seen
+	streams     map[uint32]*stream
 }
 
 type streamState int
@@ -93,21 +94,6 @@ func (sc *serverConn) logf(format string, args ...interface{}) {
 	}
 }
 
-func (sc *serverConn) frameAcceptable(f Frame) error {
-	if hf, ok := f.(*HeadersFrame); ok && hf.Header().StreamID%2 != 1 {
-		// TODO: all of http://http2.github.io/http2-spec/#rfc.section.5.1.1
-
-	}
-	if s := sc.midHeaderStreamID; s != 0 {
-		if cf, ok := f.(*ContinuationFrame); !ok {
-			return ConnectionError(ErrCodeProtocol)
-		} else if cf.Header().StreamID != s {
-			return ConnectionError(ErrCodeProtocol)
-		}
-	}
-	return nil
-}
-
 func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 	log.Printf("Header field: +%v", f)
 }
@@ -131,7 +117,8 @@ func (sc *serverConn) serve() {
 
 		f, err := sc.framer.ReadFrame()
 		if err == nil {
-			err = sc.frameAcceptable(f)
+			log.Printf("got %v: %#v", f.Header(), f)
+			err = sc.processFrame(f)
 		}
 		if h2e, ok := err.(Error); ok {
 			if h2e.IsConnectionError() {
@@ -144,27 +131,77 @@ func (sc *serverConn) serve() {
 			sc.logf("Disconnection due to other error: %v", err)
 			return
 		}
+	}
+}
 
-		log.Printf("got %v: %#v", f.Header(), f)
-		switch f := f.(type) {
-		case *SettingsFrame:
-			f.ForeachSetting(func(s SettingID, v uint32) {
-				log.Printf("  setting %s = %v", s, v)
-			})
-		case *HeadersFrame:
-			sc.hpackDecoder.Write(f.HeaderBlockFragment())
-			if f.HeadersEnded() {
-				sc.midHeaderStreamID = 0
-				// TODO: transition state
-			}
-		case *ContinuationFrame:
-			sc.hpackDecoder.Write(f.HeaderBlockFragment())
-			if f.HeadersEnded() {
-				sc.midHeaderStreamID = 0
-				// TODO: transition state
-			}
+func (sc *serverConn) processFrame(f Frame) error {
+	if s := sc.curHeaderStreamID; s != 0 {
+		if cf, ok := f.(*ContinuationFrame); !ok {
+			return ConnectionError(ErrCodeProtocol)
+		} else if cf.Header().StreamID != s {
+			return ConnectionError(ErrCodeProtocol)
 		}
 	}
+
+	switch f := f.(type) {
+	case *SettingsFrame:
+		return sc.processSettings(f)
+	case *HeadersFrame:
+		return sc.processHeaders(f)
+	case *ContinuationFrame:
+		return sc.processContinuation(f)
+	default:
+		log.Printf("Ignoring unknown %v", f.Header)
+		return nil
+	}
+}
+
+func (sc *serverConn) processSettings(f *SettingsFrame) error {
+	f.ForeachSetting(func(s SettingID, v uint32) {
+		log.Printf("  setting %s = %v", s, v)
+	})
+	return nil
+}
+
+func (sc *serverConn) processHeaders(f *HeadersFrame) error {
+	id := f.Header().StreamID
+
+	// http://http2.github.io/http2-spec/#rfc.section.5.1.1
+	if id%2 != 1 || id <= sc.maxStreamID {
+		// Streams initiated by a client MUST use odd-numbered
+		// stream identifiers. [...] The identifier of a newly
+		// established stream MUST be numerically greater than all
+		// streams that the initiating endpoint has opened or
+		// reserved. [...]  An endpoint that receives an unexpected
+		// stream identifier MUST respond with a connection error
+		// (Section 5.4.1) of type PROTOCOL_ERROR.
+		return ConnectionError(ErrCodeProtocol)
+	}
+	if id > sc.maxStreamID {
+		sc.maxStreamID = id
+	}
+
+	sc.curHeaderStreamID = id
+	return sc.processHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
+func (sc *serverConn) processHeaderBlockFragment(frag []byte, end bool) error {
+	if _, err := sc.hpackDecoder.Write(frag); err != nil {
+		// TODO: convert to stream error I assume?
+	}
+	if end {
+		if err := sc.hpackDecoder.Close(); err != nil {
+			// TODO: convert to stream error I assume?
+			return err
+		}
+		sc.curHeaderStreamID = 0
+		// TODO: transition state
+	}
+	return nil
+}
+
+func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
+	return sc.processHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
 }
 
 // ConfigureServer adds HTTP2 support to s as configured by the HTTP/2