1package http2interop 2 3import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7) 8 9type FrameHeader struct { 10 Length int 11 Type FrameType 12 Flags byte 13 Reserved Reserved 14 StreamID 15} 16 17type Reserved bool 18 19func (r Reserved) String() string { 20 if r { 21 return "R" 22 } 23 return "" 24} 25 26func (fh *FrameHeader) Parse(r io.Reader) error { 27 buf := make([]byte, 9) 28 if _, err := io.ReadFull(r, buf); err != nil { 29 return err 30 } 31 return fh.UnmarshalBinary(buf) 32} 33 34func (fh *FrameHeader) UnmarshalBinary(b []byte) error { 35 if len(b) != 9 { 36 return fmt.Errorf("Invalid frame header length %d", len(b)) 37 } 38 *fh = FrameHeader{ 39 Length: int(b[0])<<16 | int(b[1])<<8 | int(b[2]), 40 Type: FrameType(b[3]), 41 Flags: b[4], 42 Reserved: Reserved(b[5]>>7 == 1), 43 StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff), 44 } 45 return nil 46} 47 48func (fh *FrameHeader) MarshalBinary() ([]byte, error) { 49 buf := make([]byte, 9, 9+fh.Length) 50 51 if fh.Length > 0xFFFFFF || fh.Length < 0 { 52 return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length) 53 } 54 if fh.StreamID < 0 { 55 return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID) 56 } 57 58 buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length) 59 buf[3] = byte(fh.Type) 60 buf[4] = fh.Flags 61 var res uint32 62 if fh.Reserved { 63 res = 0x80000000 64 } 65 binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res) 66 67 return buf, nil 68} 69 70type StreamID int32 71 72type FrameType byte 73 74func (ft FrameType) String() string { 75 switch ft { 76 case DataFrameType: 77 return "DATA" 78 case HeadersFrameType: 79 return "HEADERS" 80 case PriorityFrameType: 81 return "PRIORITY" 82 case ResetStreamFrameType: 83 return "RST_STREAM" 84 case SettingsFrameType: 85 return "SETTINGS" 86 case PushPromiseFrameType: 87 return "PUSH_PROMISE" 88 case PingFrameType: 89 return "PING" 90 case GoAwayFrameType: 91 return "GOAWAY" 92 case WindowUpdateFrameType: 93 return "WINDOW_UPDATE" 94 case ContinuationFrameType: 95 return "CONTINUATION" 96 case HTTP1FrameType: 97 return "HTTP/1.? (Bad)" 98 default: 99 return fmt.Sprintf("UNKNOWN(%d)", byte(ft)) 100 } 101} 102 103// Types 104const ( 105 DataFrameType FrameType = 0 106 HeadersFrameType FrameType = 1 107 PriorityFrameType FrameType = 2 108 ResetStreamFrameType FrameType = 3 109 SettingsFrameType FrameType = 4 110 PushPromiseFrameType FrameType = 5 111 PingFrameType FrameType = 6 112 GoAwayFrameType FrameType = 7 113 WindowUpdateFrameType FrameType = 8 114 ContinuationFrameType FrameType = 9 115 116 // HTTP1FrameType is not a real type, but rather a convenient way to check if the response 117 // is an http response. The type of a frame header is the 4th byte, which in an http1 118 // response will be "HTTP/1.1 200 OK" or something like that. The character for "P" is 80. 119 HTTP1FrameType FrameType = 80 120) 121