diff options
Diffstat (limited to 'vendor/golang.org/x/net/http2/write.go')
-rw-r--r-- | vendor/golang.org/x/net/http2/write.go | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/vendor/golang.org/x/net/http2/write.go b/vendor/golang.org/x/net/http2/write.go new file mode 100644 index 0000000..6b0dfae --- /dev/null +++ b/vendor/golang.org/x/net/http2/write.go | |||
@@ -0,0 +1,370 @@ | |||
1 | // Copyright 2014 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package http2 | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "fmt" | ||
10 | "log" | ||
11 | "net/http" | ||
12 | "net/url" | ||
13 | "time" | ||
14 | |||
15 | "golang.org/x/net/http2/hpack" | ||
16 | "golang.org/x/net/lex/httplex" | ||
17 | ) | ||
18 | |||
19 | // writeFramer is implemented by any type that is used to write frames. | ||
20 | type writeFramer interface { | ||
21 | writeFrame(writeContext) error | ||
22 | |||
23 | // staysWithinBuffer reports whether this writer promises that | ||
24 | // it will only write less than or equal to size bytes, and it | ||
25 | // won't Flush the write context. | ||
26 | staysWithinBuffer(size int) bool | ||
27 | } | ||
28 | |||
29 | // writeContext is the interface needed by the various frame writer | ||
30 | // types below. All the writeFrame methods below are scheduled via the | ||
31 | // frame writing scheduler (see writeScheduler in writesched.go). | ||
32 | // | ||
33 | // This interface is implemented by *serverConn. | ||
34 | // | ||
35 | // TODO: decide whether to a) use this in the client code (which didn't | ||
36 | // end up using this yet, because it has a simpler design, not | ||
37 | // currently implementing priorities), or b) delete this and | ||
38 | // make the server code a bit more concrete. | ||
39 | type writeContext interface { | ||
40 | Framer() *Framer | ||
41 | Flush() error | ||
42 | CloseConn() error | ||
43 | // HeaderEncoder returns an HPACK encoder that writes to the | ||
44 | // returned buffer. | ||
45 | HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) | ||
46 | } | ||
47 | |||
48 | // writeEndsStream reports whether w writes a frame that will transition | ||
49 | // the stream to a half-closed local state. This returns false for RST_STREAM, | ||
50 | // which closes the entire stream (not just the local half). | ||
51 | func writeEndsStream(w writeFramer) bool { | ||
52 | switch v := w.(type) { | ||
53 | case *writeData: | ||
54 | return v.endStream | ||
55 | case *writeResHeaders: | ||
56 | return v.endStream | ||
57 | case nil: | ||
58 | // This can only happen if the caller reuses w after it's | ||
59 | // been intentionally nil'ed out to prevent use. Keep this | ||
60 | // here to catch future refactoring breaking it. | ||
61 | panic("writeEndsStream called on nil writeFramer") | ||
62 | } | ||
63 | return false | ||
64 | } | ||
65 | |||
66 | type flushFrameWriter struct{} | ||
67 | |||
68 | func (flushFrameWriter) writeFrame(ctx writeContext) error { | ||
69 | return ctx.Flush() | ||
70 | } | ||
71 | |||
72 | func (flushFrameWriter) staysWithinBuffer(max int) bool { return false } | ||
73 | |||
74 | type writeSettings []Setting | ||
75 | |||
76 | func (s writeSettings) staysWithinBuffer(max int) bool { | ||
77 | const settingSize = 6 // uint16 + uint32 | ||
78 | return frameHeaderLen+settingSize*len(s) <= max | ||
79 | |||
80 | } | ||
81 | |||
82 | func (s writeSettings) writeFrame(ctx writeContext) error { | ||
83 | return ctx.Framer().WriteSettings([]Setting(s)...) | ||
84 | } | ||
85 | |||
86 | type writeGoAway struct { | ||
87 | maxStreamID uint32 | ||
88 | code ErrCode | ||
89 | } | ||
90 | |||
91 | func (p *writeGoAway) writeFrame(ctx writeContext) error { | ||
92 | err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) | ||
93 | if p.code != 0 { | ||
94 | ctx.Flush() // ignore error: we're hanging up on them anyway | ||
95 | time.Sleep(50 * time.Millisecond) | ||
96 | ctx.CloseConn() | ||
97 | } | ||
98 | return err | ||
99 | } | ||
100 | |||
101 | func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes | ||
102 | |||
103 | type writeData struct { | ||
104 | streamID uint32 | ||
105 | p []byte | ||
106 | endStream bool | ||
107 | } | ||
108 | |||
109 | func (w *writeData) String() string { | ||
110 | return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream) | ||
111 | } | ||
112 | |||
113 | func (w *writeData) writeFrame(ctx writeContext) error { | ||
114 | return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) | ||
115 | } | ||
116 | |||
117 | func (w *writeData) staysWithinBuffer(max int) bool { | ||
118 | return frameHeaderLen+len(w.p) <= max | ||
119 | } | ||
120 | |||
121 | // handlerPanicRST is the message sent from handler goroutines when | ||
122 | // the handler panics. | ||
123 | type handlerPanicRST struct { | ||
124 | StreamID uint32 | ||
125 | } | ||
126 | |||
127 | func (hp handlerPanicRST) writeFrame(ctx writeContext) error { | ||
128 | return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal) | ||
129 | } | ||
130 | |||
131 | func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } | ||
132 | |||
133 | func (se StreamError) writeFrame(ctx writeContext) error { | ||
134 | return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) | ||
135 | } | ||
136 | |||
137 | func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } | ||
138 | |||
139 | type writePingAck struct{ pf *PingFrame } | ||
140 | |||
141 | func (w writePingAck) writeFrame(ctx writeContext) error { | ||
142 | return ctx.Framer().WritePing(true, w.pf.Data) | ||
143 | } | ||
144 | |||
145 | func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max } | ||
146 | |||
147 | type writeSettingsAck struct{} | ||
148 | |||
149 | func (writeSettingsAck) writeFrame(ctx writeContext) error { | ||
150 | return ctx.Framer().WriteSettingsAck() | ||
151 | } | ||
152 | |||
153 | func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max } | ||
154 | |||
155 | // splitHeaderBlock splits headerBlock into fragments so that each fragment fits | ||
156 | // in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true | ||
157 | // for the first/last fragment, respectively. | ||
158 | func splitHeaderBlock(ctx writeContext, headerBlock []byte, fn func(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error) error { | ||
159 | // For now we're lazy and just pick the minimum MAX_FRAME_SIZE | ||
160 | // that all peers must support (16KB). Later we could care | ||
161 | // more and send larger frames if the peer advertised it, but | ||
162 | // there's little point. Most headers are small anyway (so we | ||
163 | // generally won't have CONTINUATION frames), and extra frames | ||
164 | // only waste 9 bytes anyway. | ||
165 | const maxFrameSize = 16384 | ||
166 | |||
167 | first := true | ||
168 | for len(headerBlock) > 0 { | ||
169 | frag := headerBlock | ||
170 | if len(frag) > maxFrameSize { | ||
171 | frag = frag[:maxFrameSize] | ||
172 | } | ||
173 | headerBlock = headerBlock[len(frag):] | ||
174 | if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil { | ||
175 | return err | ||
176 | } | ||
177 | first = false | ||
178 | } | ||
179 | return nil | ||
180 | } | ||
181 | |||
182 | // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames | ||
183 | // for HTTP response headers or trailers from a server handler. | ||
184 | type writeResHeaders struct { | ||
185 | streamID uint32 | ||
186 | httpResCode int // 0 means no ":status" line | ||
187 | h http.Header // may be nil | ||
188 | trailers []string // if non-nil, which keys of h to write. nil means all. | ||
189 | endStream bool | ||
190 | |||
191 | date string | ||
192 | contentType string | ||
193 | contentLength string | ||
194 | } | ||
195 | |||
196 | func encKV(enc *hpack.Encoder, k, v string) { | ||
197 | if VerboseLogs { | ||
198 | log.Printf("http2: server encoding header %q = %q", k, v) | ||
199 | } | ||
200 | enc.WriteField(hpack.HeaderField{Name: k, Value: v}) | ||
201 | } | ||
202 | |||
203 | func (w *writeResHeaders) staysWithinBuffer(max int) bool { | ||
204 | // TODO: this is a common one. It'd be nice to return true | ||
205 | // here and get into the fast path if we could be clever and | ||
206 | // calculate the size fast enough, or at least a conservative | ||
207 | // uppper bound that usually fires. (Maybe if w.h and | ||
208 | // w.trailers are nil, so we don't need to enumerate it.) | ||
209 | // Otherwise I'm afraid that just calculating the length to | ||
210 | // answer this question would be slower than the ~2µs benefit. | ||
211 | return false | ||
212 | } | ||
213 | |||
214 | func (w *writeResHeaders) writeFrame(ctx writeContext) error { | ||
215 | enc, buf := ctx.HeaderEncoder() | ||
216 | buf.Reset() | ||
217 | |||
218 | if w.httpResCode != 0 { | ||
219 | encKV(enc, ":status", httpCodeString(w.httpResCode)) | ||
220 | } | ||
221 | |||
222 | encodeHeaders(enc, w.h, w.trailers) | ||
223 | |||
224 | if w.contentType != "" { | ||
225 | encKV(enc, "content-type", w.contentType) | ||
226 | } | ||
227 | if w.contentLength != "" { | ||
228 | encKV(enc, "content-length", w.contentLength) | ||
229 | } | ||
230 | if w.date != "" { | ||
231 | encKV(enc, "date", w.date) | ||
232 | } | ||
233 | |||
234 | headerBlock := buf.Bytes() | ||
235 | if len(headerBlock) == 0 && w.trailers == nil { | ||
236 | panic("unexpected empty hpack") | ||
237 | } | ||
238 | |||
239 | return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) | ||
240 | } | ||
241 | |||
242 | func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error { | ||
243 | if firstFrag { | ||
244 | return ctx.Framer().WriteHeaders(HeadersFrameParam{ | ||
245 | StreamID: w.streamID, | ||
246 | BlockFragment: frag, | ||
247 | EndStream: w.endStream, | ||
248 | EndHeaders: lastFrag, | ||
249 | }) | ||
250 | } else { | ||
251 | return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) | ||
252 | } | ||
253 | } | ||
254 | |||
255 | // writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames. | ||
256 | type writePushPromise struct { | ||
257 | streamID uint32 // pusher stream | ||
258 | method string // for :method | ||
259 | url *url.URL // for :scheme, :authority, :path | ||
260 | h http.Header | ||
261 | |||
262 | // Creates an ID for a pushed stream. This runs on serveG just before | ||
263 | // the frame is written. The returned ID is copied to promisedID. | ||
264 | allocatePromisedID func() (uint32, error) | ||
265 | promisedID uint32 | ||
266 | } | ||
267 | |||
268 | func (w *writePushPromise) staysWithinBuffer(max int) bool { | ||
269 | // TODO: see writeResHeaders.staysWithinBuffer | ||
270 | return false | ||
271 | } | ||
272 | |||
273 | func (w *writePushPromise) writeFrame(ctx writeContext) error { | ||
274 | enc, buf := ctx.HeaderEncoder() | ||
275 | buf.Reset() | ||
276 | |||
277 | encKV(enc, ":method", w.method) | ||
278 | encKV(enc, ":scheme", w.url.Scheme) | ||
279 | encKV(enc, ":authority", w.url.Host) | ||
280 | encKV(enc, ":path", w.url.RequestURI()) | ||
281 | encodeHeaders(enc, w.h, nil) | ||
282 | |||
283 | headerBlock := buf.Bytes() | ||
284 | if len(headerBlock) == 0 { | ||
285 | panic("unexpected empty hpack") | ||
286 | } | ||
287 | |||
288 | return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) | ||
289 | } | ||
290 | |||
291 | func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error { | ||
292 | if firstFrag { | ||
293 | return ctx.Framer().WritePushPromise(PushPromiseParam{ | ||
294 | StreamID: w.streamID, | ||
295 | PromiseID: w.promisedID, | ||
296 | BlockFragment: frag, | ||
297 | EndHeaders: lastFrag, | ||
298 | }) | ||
299 | } else { | ||
300 | return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) | ||
301 | } | ||
302 | } | ||
303 | |||
304 | type write100ContinueHeadersFrame struct { | ||
305 | streamID uint32 | ||
306 | } | ||
307 | |||
308 | func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error { | ||
309 | enc, buf := ctx.HeaderEncoder() | ||
310 | buf.Reset() | ||
311 | encKV(enc, ":status", "100") | ||
312 | return ctx.Framer().WriteHeaders(HeadersFrameParam{ | ||
313 | StreamID: w.streamID, | ||
314 | BlockFragment: buf.Bytes(), | ||
315 | EndStream: false, | ||
316 | EndHeaders: true, | ||
317 | }) | ||
318 | } | ||
319 | |||
320 | func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool { | ||
321 | // Sloppy but conservative: | ||
322 | return 9+2*(len(":status")+len("100")) <= max | ||
323 | } | ||
324 | |||
325 | type writeWindowUpdate struct { | ||
326 | streamID uint32 // or 0 for conn-level | ||
327 | n uint32 | ||
328 | } | ||
329 | |||
330 | func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } | ||
331 | |||
332 | func (wu writeWindowUpdate) writeFrame(ctx writeContext) error { | ||
333 | return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) | ||
334 | } | ||
335 | |||
336 | // encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k]) | ||
337 | // is encoded only only if k is in keys. | ||
338 | func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) { | ||
339 | if keys == nil { | ||
340 | sorter := sorterPool.Get().(*sorter) | ||
341 | // Using defer here, since the returned keys from the | ||
342 | // sorter.Keys method is only valid until the sorter | ||
343 | // is returned: | ||
344 | defer sorterPool.Put(sorter) | ||
345 | keys = sorter.Keys(h) | ||
346 | } | ||
347 | for _, k := range keys { | ||
348 | vv := h[k] | ||
349 | k = lowerHeader(k) | ||
350 | if !validWireHeaderFieldName(k) { | ||
351 | // Skip it as backup paranoia. Per | ||
352 | // golang.org/issue/14048, these should | ||
353 | // already be rejected at a higher level. | ||
354 | continue | ||
355 | } | ||
356 | isTE := k == "transfer-encoding" | ||
357 | for _, v := range vv { | ||
358 | if !httplex.ValidHeaderFieldValue(v) { | ||
359 | // TODO: return an error? golang.org/issue/14048 | ||
360 | // For now just omit it. | ||
361 | continue | ||
362 | } | ||
363 | // TODO: more of "8.1.2.2 Connection-Specific Header Fields" | ||
364 | if isTE && v != "trailers" { | ||
365 | continue | ||
366 | } | ||
367 | encKV(enc, k, v) | ||
368 | } | ||
369 | } | ||
370 | } | ||