blob: 84f6fa5c55822277ff51d100679ab0a07042f5a6 [file] [log] [blame]
Carl Mastrangelo4aca7962015-10-05 16:17:47 -07001package 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
Carl Mastrangelo945836e2015-11-20 17:43:15 -080061 var res uint32
62 if fh.Reserved {
63 res = 0x80000000
64 }
65 binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res)
Carl Mastrangelo4aca7962015-10-05 16:17:47 -070066
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"
Carl Mastrangelo945836e2015-11-20 17:43:15 -080096 case HTTP1FrameType:
97 return "HTTP/1.? (Bad)"
Carl Mastrangelo4aca7962015-10-05 16:17:47 -070098 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
Carl Mastrangelo945836e2015-11-20 17:43:15 -0800115
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
Carl Mastrangelo4aca7962015-10-05 16:17:47 -0700120)