-
Notifications
You must be signed in to change notification settings - Fork 1
/
frame.go
181 lines (159 loc) · 3.96 KB
/
frame.go
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package minhq
import (
"bytes"
"errors"
"io"
bitio "github.com/martinthomson/minhq/io"
)
// FrameType is the type of an HTTP/QUIC frame.
type FrameType byte
const (
frameData = FrameType(0)
frameHeaders = FrameType(1)
framePriority = FrameType(2)
frameCancelPush = FrameType(3)
frameSettings = FrameType(4)
framePushPromise = FrameType(5)
frameGoaway = FrameType(7)
frameMaxPushID = FrameType(13)
)
// String produces the strings from the spec.
func (ft FrameType) String() string {
switch ft {
case frameData:
return "DATA"
case frameHeaders:
return "HEADERS"
case framePriority:
return "PRIORITY"
case frameCancelPush:
return "CANCEL_PUSH"
case frameSettings:
return "SETTINGS"
case framePushPromise:
return "PUSH_PROMISE"
case frameGoaway:
return "GOAWAY"
case frameMaxPushID:
return "MAX_PUSH_ID"
}
return "UNKNOWN!"
}
// ErrUnsupportedFrame signals that an unsupported frame was received.
var ErrUnsupportedFrame = errors.New("Unsupported frame type received")
// ErrTooLarge signals that a value was too large.
var ErrTooLarge = errors.New("Value too large for the field")
// FrameReader wraps a reader with helper functions specific to HTTP/QUIC.
type FrameReader interface {
bitio.BitReader
ReadVarint() (uint64, error)
ReadFrame() (FrameType, FrameReader, error)
Limited(n uint64) FrameReader
CheckForEOF() error
}
type frameReader struct {
bitio.BitReader
}
// NewFrameReader wraps the given io.Reader.
func NewFrameReader(r io.Reader) FrameReader {
return &frameReader{bitio.NewBitReader(r)}
}
// ReadVarint reads a variable length integer.
func (fr *frameReader) ReadVarint() (uint64, error) {
len, err := fr.ReadBits(2)
if err != nil {
return 0, err
}
return fr.ReadBits((8 << len) - 2)
}
// ReadFrame reads a frame header and returns the different pieces of the frame.
func (fr *frameReader) ReadFrame() (FrameType, FrameReader, error) {
len, err := fr.ReadVarint()
if err != nil {
return 0, nil, err
}
t, err := fr.ReadByte()
if err != nil {
return 0, nil, err
}
return FrameType(t), fr.Limited(len), nil
}
// Limited makes an io.LimitedReader that reads the next `n` bytes from this reader.
func (fr *frameReader) Limited(n uint64) FrameReader {
return NewFrameReader(&io.LimitedReader{R: fr, N: int64(n)})
}
// CheckForEOF returns an error if the reader has remaining data.
func (fr *frameReader) CheckForEOF() error {
var p [1]byte
n, err := fr.Read(p[:])
if err != nil && err != io.EOF {
return err
}
if n > 0 {
return ErrExtraData
}
return nil
}
// FrameWriter wraps the io.Writer interface with HTTP/QUIC helper functions.
type FrameWriter interface {
bitio.BitWriter
WriteVarint(v uint64) (int, error)
WriteFrame(t FrameType, p []byte) (int, error)
}
type frameWriter struct {
bitio.BitWriter
}
// NewFrameWriter makes a FrameWriter.
func NewFrameWriter(w io.Writer) FrameWriter {
return &frameWriter{bitio.NewBitWriter(w)}
}
func (fw *frameWriter) WriteVarint(v uint64) (int, error) {
var size byte
switch {
case v >= 1<<62:
return 0, ErrTooLarge
case v >= 1<<30:
size = 3
case v >= 1<<14:
size = 2
case v >= 1<<6:
size = 1
default:
size = 0
}
err := fw.WriteBits(uint64(size), 2)
if err != nil {
return 0, err
}
n := byte(1) << size
err = fw.WriteBits(v, n*8-2)
if err != nil {
return 0, nil
}
return int(n), nil
}
func (fw *frameWriter) WriteFrame(t FrameType, p []byte) (int, error) {
written, err := fw.WriteVarint(uint64(len(p)))
if err != nil {
return written, err
}
err = fw.WriteByte(byte(t))
if err != nil {
return written, err
}
n, err := io.Copy(fw, bytes.NewReader(p))
return written + int(n) + 2, err
}
// FrameWriteCloser adds io.Closer to the FrameWriter interface.
type FrameWriteCloser interface {
FrameWriter
io.Closer
}
type frameWriteCloser struct {
FrameWriter
io.Closer
}
// NewFrameWriteCloser makes a FrameWriteCloser.
func NewFrameWriteCloser(w io.WriteCloser) FrameWriteCloser {
return &frameWriteCloser{NewFrameWriter(w), w}
}