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