|
|
@@ -1,7 +1,10 @@
|
|
|
package http2
|
|
|
|
|
|
import (
|
|
|
+ "encoding/binary"
|
|
|
"io"
|
|
|
+ "io/ioutil"
|
|
|
+ "log"
|
|
|
"sync"
|
|
|
)
|
|
|
|
|
|
@@ -11,27 +14,66 @@ type FrameType uint8
|
|
|
|
|
|
// Defined in http://http2.github.io/http2-spec/#rfc.section.11.2
|
|
|
const (
|
|
|
- FrameData FrameType = 0
|
|
|
- FrameHeaders FrameType = 1
|
|
|
- FramePriority FrameType = 2
|
|
|
- FrameRSTStream FrameType = 3
|
|
|
- FrameSettings FrameType = 4
|
|
|
- FramePushPromise FrameType = 5
|
|
|
- FramePing FrameType = 6
|
|
|
- FrameGoAway FrameType = 7
|
|
|
- FrameWindowUpdate FrameType = 8
|
|
|
- FrameContinuation FrameType = 9
|
|
|
+ FrameData FrameType = 0x0
|
|
|
+ FrameHeaders FrameType = 0x1
|
|
|
+ FramePriority FrameType = 0x2
|
|
|
+ FrameRSTStream FrameType = 0x3
|
|
|
+ FrameSettings FrameType = 0x4
|
|
|
+ FramePushPromise FrameType = 0x5
|
|
|
+ FramePing FrameType = 0x6
|
|
|
+ FrameGoAway FrameType = 0x7
|
|
|
+ FrameWindowUpdate FrameType = 0x8
|
|
|
+ FrameContinuation FrameType = 0x9
|
|
|
+
|
|
|
+ FlagSettingsAck Flags = 0x1
|
|
|
+)
|
|
|
+
|
|
|
+type SettingID uint16
|
|
|
+
|
|
|
+const (
|
|
|
+ SettingHeaderTableSize SettingID = 0x1
|
|
|
+ SettingEnablePush SettingID = 0x2
|
|
|
+ SettingMaxConcurrentStreams SettingID = 0x3
|
|
|
+ SettingInitialWindowSize SettingID = 0x4
|
|
|
)
|
|
|
|
|
|
+func knownSetting(id SettingID) bool {
|
|
|
+ // TODO: permit registration of custom settings values?
|
|
|
+ // Per server type?
|
|
|
+ return id >= 1 && id <= 4
|
|
|
+}
|
|
|
+
|
|
|
+type frameParser func(FrameHeader, io.Reader) (Frame, error)
|
|
|
+
|
|
|
+var FrameParsers = map[FrameType]frameParser{
|
|
|
+ FrameSettings: parseFrameSettings,
|
|
|
+}
|
|
|
+
|
|
|
+func typeFrameParser(t FrameType) frameParser {
|
|
|
+ if f, ok := FrameParsers[t]; ok {
|
|
|
+ return f
|
|
|
+ }
|
|
|
+ return parseUnknownFrame
|
|
|
+}
|
|
|
+
|
|
|
+type Flags uint8
|
|
|
+
|
|
|
+func (f Flags) Has(v Flags) bool {
|
|
|
+ return (f & v) == v
|
|
|
+}
|
|
|
+
|
|
|
// A FrameHeader is the 8 byte header of all HTTP/2 frames.
|
|
|
//
|
|
|
// See http://http2.github.io/http2-spec/#FrameHeader
|
|
|
type FrameHeader struct {
|
|
|
- Type FrameType
|
|
|
- Flags uint8
|
|
|
- Length uint16
|
|
|
+ Type FrameType
|
|
|
+ Flags Flags
|
|
|
+ Length uint16
|
|
|
+ StreamID uint32
|
|
|
}
|
|
|
|
|
|
+func (h FrameHeader) Header() FrameHeader { return h }
|
|
|
+
|
|
|
// frame header bytes
|
|
|
var fhBytes = sync.Pool{
|
|
|
New: func() interface{} {
|
|
|
@@ -49,13 +91,75 @@ func ReadFrameHeader(r io.Reader) (FrameHeader, error) {
|
|
|
return FrameHeader{}, err
|
|
|
}
|
|
|
return FrameHeader{
|
|
|
- Length: (uint16(buf[0])<<8 + uint16(buf[1])) & (1<<14 - 1),
|
|
|
- Flags: buf[3],
|
|
|
- Type: FrameType(buf[2]),
|
|
|
+ Length: (uint16(buf[0])<<8 + uint16(buf[1])) & (1<<14 - 1),
|
|
|
+ Flags: Flags(buf[3]),
|
|
|
+ Type: FrameType(buf[2]),
|
|
|
+ StreamID: binary.BigEndian.Uint32(buf[4:]) & (1<<31 - 1),
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+type Frame interface {
|
|
|
+ Header() FrameHeader
|
|
|
+}
|
|
|
+
|
|
|
+type SettingsFrame struct {
|
|
|
+ FrameHeader
|
|
|
+ Settings map[SettingID]uint32
|
|
|
+}
|
|
|
+
|
|
|
+func parseFrameSettings(fh FrameHeader, r io.Reader) (Frame, error) {
|
|
|
+ if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 {
|
|
|
+ // When this (ACK 0x1) bit is set, the payload of the
|
|
|
+ // SETTINGS frame MUST be empty. Receipt of a
|
|
|
+ // SETTINGS frame with the ACK flag set and a length
|
|
|
+ // field value other than 0 MUST be treated as a
|
|
|
+ // connection error (Section 5.4.1) of type
|
|
|
+ // FRAME_SIZE_ERROR.
|
|
|
+ return nil, ConnectionError(ErrCodeFrameSize)
|
|
|
+ }
|
|
|
+ if fh.StreamID != 0 {
|
|
|
+ // SETTINGS frames always apply to a connection,
|
|
|
+ // never a single stream. The stream identifier for a
|
|
|
+ // SETTINGS frame MUST be zero (0x0). If an endpoint
|
|
|
+ // receives a SETTINGS frame whose stream identifier
|
|
|
+ // field is anything other than 0x0, the endpoint MUST
|
|
|
+ // respond with a connection error (Section 5.4.1) of
|
|
|
+ // type PROTOCOL_ERROR.
|
|
|
+ log.Printf("Bogus StreamID in settings: %+v", fh)
|
|
|
+ return nil, ConnectionError(ErrCodeProtocol)
|
|
|
+ }
|
|
|
+ if fh.Length%6 != 0 {
|
|
|
+ // Expecting even number of 6 byte settings.
|
|
|
+ return nil, ConnectionError(ErrCodeFrameSize)
|
|
|
+ }
|
|
|
+ s := make(map[SettingID]uint32)
|
|
|
+ nSettings := int(fh.Length / 6)
|
|
|
+ var buf [4]byte
|
|
|
+ for i := 0; i < nSettings; i++ {
|
|
|
+ if _, err := io.ReadFull(r, buf[:2]); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ settingID := SettingID(binary.BigEndian.Uint16(buf[:2]))
|
|
|
+ if _, err := io.ReadFull(r, buf[:4]); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ value := binary.BigEndian.Uint32(buf[:4])
|
|
|
+ if knownSetting(settingID) {
|
|
|
+ s[settingID] = value
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return &SettingsFrame{
|
|
|
+ FrameHeader: fh,
|
|
|
+ Settings: s,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
-type Frame struct {
|
|
|
+type UnknownFrame struct {
|
|
|
FrameHeader
|
|
|
- data, unread []byte
|
|
|
+}
|
|
|
+
|
|
|
+func parseUnknownFrame(fh FrameHeader, r io.Reader) (Frame, error) {
|
|
|
+ _, err := io.CopyN(ioutil.Discard, r, int64(fh.Length))
|
|
|
+ return UnknownFrame{fh}, err
|
|
|
}
|