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