aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/http2_interop/frameheader.go
blob: 84f6fa5c55822277ff51d100679ab0a07042f5a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package http2interop

import (
	"encoding/binary"
	"fmt"
	"io"
)

type FrameHeader struct {
	Length   int
	Type     FrameType
	Flags    byte
	Reserved Reserved
	StreamID
}

type Reserved bool

func (r Reserved) String() string {
	if r {
		return "R"
	}
	return ""
}

func (fh *FrameHeader) Parse(r io.Reader) error {
	buf := make([]byte, 9)
	if _, err := io.ReadFull(r, buf); err != nil {
		return err
	}
	return fh.UnmarshalBinary(buf)
}

func (fh *FrameHeader) UnmarshalBinary(b []byte) error {
	if len(b) != 9 {
		return fmt.Errorf("Invalid frame header length %d", len(b))
	}
	*fh = FrameHeader{
		Length:   int(b[0])<<16 | int(b[1])<<8 | int(b[2]),
		Type:     FrameType(b[3]),
		Flags:    b[4],
		Reserved: Reserved(b[5]>>7 == 1),
		StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff),
	}
	return nil
}

func (fh *FrameHeader) MarshalBinary() ([]byte, error) {
	buf := make([]byte, 9, 9+fh.Length)

	if fh.Length > 0xFFFFFF || fh.Length < 0 {
		return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length)
	}
	if fh.StreamID < 0 {
		return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID)
	}

	buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length)
	buf[3] = byte(fh.Type)
	buf[4] = fh.Flags
	var res uint32
	if fh.Reserved {
		res = 0x80000000
	}
	binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res)

	return buf, nil
}

type StreamID int32

type FrameType byte

func (ft FrameType) String() string {
	switch ft {
	case DataFrameType:
		return "DATA"
	case HeadersFrameType:
		return "HEADERS"
	case PriorityFrameType:
		return "PRIORITY"
	case ResetStreamFrameType:
		return "RST_STREAM"
	case SettingsFrameType:
		return "SETTINGS"
	case PushPromiseFrameType:
		return "PUSH_PROMISE"
	case PingFrameType:
		return "PING"
	case GoAwayFrameType:
		return "GOAWAY"
	case WindowUpdateFrameType:
		return "WINDOW_UPDATE"
	case ContinuationFrameType:
		return "CONTINUATION"
	case HTTP1FrameType:
		return "HTTP/1.? (Bad)"
	default:
		return fmt.Sprintf("UNKNOWN(%d)", byte(ft))
	}
}

// Types
const (
	DataFrameType         FrameType = 0
	HeadersFrameType      FrameType = 1
	PriorityFrameType     FrameType = 2
	ResetStreamFrameType  FrameType = 3
	SettingsFrameType     FrameType = 4
	PushPromiseFrameType  FrameType = 5
	PingFrameType         FrameType = 6
	GoAwayFrameType       FrameType = 7
	WindowUpdateFrameType FrameType = 8
	ContinuationFrameType FrameType = 9

	// HTTP1FrameType is not a real type, but rather a convenient way to check if the response
	// is an http response.  The type of a frame header is the 4th byte, which in an http1
	// response will be "HTTP/1.1 200 OK" or something like that.  The character for "P" is 80.
	HTTP1FrameType FrameType = 80
)