1
2
3
4
5 package http2
6
7 import (
8 "bytes"
9 "fmt"
10 "log"
11 "net/http"
12 "net/url"
13
14 "golang.org/x/net/http/httpguts"
15 "golang.org/x/net/http2/hpack"
16 )
17
18
19 type writeFramer interface {
20 writeFrame(writeContext) error
21
22
23
24
25 staysWithinBuffer(size int) bool
26 }
27
28
29
30
31
32
33
34
35
36
37
38 type writeContext interface {
39 Framer() *Framer
40 Flush() error
41 CloseConn() error
42
43
44 HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
45 }
46
47
48
49
50 func writeEndsStream(w writeFramer) bool {
51 switch v := w.(type) {
52 case *writeData:
53 return v.endStream
54 case *writeResHeaders:
55 return v.endStream
56 case nil:
57
58
59
60 panic("writeEndsStream called on nil writeFramer")
61 }
62 return false
63 }
64
65 type flushFrameWriter struct{}
66
67 func (flushFrameWriter) writeFrame(ctx writeContext) error {
68 return ctx.Flush()
69 }
70
71 func (flushFrameWriter) staysWithinBuffer(max int) bool { return false }
72
73 type writeSettings []Setting
74
75 func (s writeSettings) staysWithinBuffer(max int) bool {
76 const settingSize = 6
77 return frameHeaderLen+settingSize*len(s) <= max
78
79 }
80
81 func (s writeSettings) writeFrame(ctx writeContext) error {
82 return ctx.Framer().WriteSettings([]Setting(s)...)
83 }
84
85 type writeGoAway struct {
86 maxStreamID uint32
87 code ErrCode
88 }
89
90 func (p *writeGoAway) writeFrame(ctx writeContext) error {
91 err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
92 ctx.Flush()
93 return err
94 }
95
96 func (*writeGoAway) staysWithinBuffer(max int) bool { return false }
97
98 type writeData struct {
99 streamID uint32
100 p []byte
101 endStream bool
102 }
103
104 func (w *writeData) String() string {
105 return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
106 }
107
108 func (w *writeData) writeFrame(ctx writeContext) error {
109 return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
110 }
111
112 func (w *writeData) staysWithinBuffer(max int) bool {
113 return frameHeaderLen+len(w.p) <= max
114 }
115
116
117
118 type handlerPanicRST struct {
119 StreamID uint32
120 }
121
122 func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
123 return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
124 }
125
126 func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
127
128 func (se StreamError) writeFrame(ctx writeContext) error {
129 return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
130 }
131
132 func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
133
134 type writePing struct {
135 data [8]byte
136 }
137
138 func (w writePing) writeFrame(ctx writeContext) error {
139 return ctx.Framer().WritePing(false, w.data)
140 }
141
142 func (w writePing) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.data) <= max }
143
144 type writePingAck struct{ pf *PingFrame }
145
146 func (w writePingAck) writeFrame(ctx writeContext) error {
147 return ctx.Framer().WritePing(true, w.pf.Data)
148 }
149
150 func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max }
151
152 type writeSettingsAck struct{}
153
154 func (writeSettingsAck) writeFrame(ctx writeContext) error {
155 return ctx.Framer().WriteSettingsAck()
156 }
157
158 func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max }
159
160
161
162
163 func splitHeaderBlock(ctx writeContext, headerBlock []byte, fn func(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error) error {
164
165
166
167
168
169
170 const maxFrameSize = 16384
171
172 first := true
173 for len(headerBlock) > 0 {
174 frag := headerBlock
175 if len(frag) > maxFrameSize {
176 frag = frag[:maxFrameSize]
177 }
178 headerBlock = headerBlock[len(frag):]
179 if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil {
180 return err
181 }
182 first = false
183 }
184 return nil
185 }
186
187
188
189 type writeResHeaders struct {
190 streamID uint32
191 httpResCode int
192 h http.Header
193 trailers []string
194 endStream bool
195
196 date string
197 contentType string
198 contentLength string
199 }
200
201 func encKV(enc *hpack.Encoder, k, v string) {
202 if VerboseLogs {
203 log.Printf("http2: server encoding header %q = %q", k, v)
204 }
205 enc.WriteField(hpack.HeaderField{Name: k, Value: v})
206 }
207
208 func (w *writeResHeaders) staysWithinBuffer(max int) bool {
209
210
211
212
213
214
215
216 return false
217 }
218
219 func (w *writeResHeaders) writeFrame(ctx writeContext) error {
220 enc, buf := ctx.HeaderEncoder()
221 buf.Reset()
222
223 if w.httpResCode != 0 {
224 encKV(enc, ":status", httpCodeString(w.httpResCode))
225 }
226
227 encodeHeaders(enc, w.h, w.trailers)
228
229 if w.contentType != "" {
230 encKV(enc, "content-type", w.contentType)
231 }
232 if w.contentLength != "" {
233 encKV(enc, "content-length", w.contentLength)
234 }
235 if w.date != "" {
236 encKV(enc, "date", w.date)
237 }
238
239 headerBlock := buf.Bytes()
240 if len(headerBlock) == 0 && w.trailers == nil {
241 panic("unexpected empty hpack")
242 }
243
244 return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
245 }
246
247 func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
248 if firstFrag {
249 return ctx.Framer().WriteHeaders(HeadersFrameParam{
250 StreamID: w.streamID,
251 BlockFragment: frag,
252 EndStream: w.endStream,
253 EndHeaders: lastFrag,
254 })
255 } else {
256 return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
257 }
258 }
259
260
261 type writePushPromise struct {
262 streamID uint32
263 method string
264 url *url.URL
265 h http.Header
266
267
268
269 allocatePromisedID func() (uint32, error)
270 promisedID uint32
271 }
272
273 func (w *writePushPromise) staysWithinBuffer(max int) bool {
274
275 return false
276 }
277
278 func (w *writePushPromise) writeFrame(ctx writeContext) error {
279 enc, buf := ctx.HeaderEncoder()
280 buf.Reset()
281
282 encKV(enc, ":method", w.method)
283 encKV(enc, ":scheme", w.url.Scheme)
284 encKV(enc, ":authority", w.url.Host)
285 encKV(enc, ":path", w.url.RequestURI())
286 encodeHeaders(enc, w.h, nil)
287
288 headerBlock := buf.Bytes()
289 if len(headerBlock) == 0 {
290 panic("unexpected empty hpack")
291 }
292
293 return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
294 }
295
296 func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
297 if firstFrag {
298 return ctx.Framer().WritePushPromise(PushPromiseParam{
299 StreamID: w.streamID,
300 PromiseID: w.promisedID,
301 BlockFragment: frag,
302 EndHeaders: lastFrag,
303 })
304 } else {
305 return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
306 }
307 }
308
309 type write100ContinueHeadersFrame struct {
310 streamID uint32
311 }
312
313 func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
314 enc, buf := ctx.HeaderEncoder()
315 buf.Reset()
316 encKV(enc, ":status", "100")
317 return ctx.Framer().WriteHeaders(HeadersFrameParam{
318 StreamID: w.streamID,
319 BlockFragment: buf.Bytes(),
320 EndStream: false,
321 EndHeaders: true,
322 })
323 }
324
325 func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
326
327 return 9+2*(len(":status")+len("100")) <= max
328 }
329
330 type writeWindowUpdate struct {
331 streamID uint32
332 n uint32
333 }
334
335 func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
336
337 func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
338 return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
339 }
340
341
342
343 func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
344 if keys == nil {
345 sorter := sorterPool.Get().(*sorter)
346
347
348
349 defer sorterPool.Put(sorter)
350 keys = sorter.Keys(h)
351 }
352 for _, k := range keys {
353 vv := h[k]
354 k, ascii := lowerHeader(k)
355 if !ascii {
356
357
358 continue
359 }
360 if !validWireHeaderFieldName(k) {
361
362
363
364 continue
365 }
366 isTE := k == "transfer-encoding"
367 for _, v := range vv {
368 if !httpguts.ValidHeaderFieldValue(v) {
369
370
371 continue
372 }
373
374 if isTE && v != "trailers" {
375 continue
376 }
377 encKV(enc, k, v)
378 }
379 }
380 }
381
View as plain text