Matteo Scandolo | a428586 | 2020-12-01 18:10:10 -0800 | [diff] [blame] | 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 | |
| 14 | "golang.org/x/net/http/httpguts" |
| 15 | "golang.org/x/net/http2/hpack" |
| 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) |
| 92 | ctx.Flush() // ignore error: we're hanging up on them anyway |
| 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 |
| 202 | // upper bound that usually fires. (Maybe if w.h and |
| 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]) |
| 332 | // is encoded only if k is in keys. |
| 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] |
David K. Bainbridge | 0663189 | 2021-08-19 13:07:00 +0000 | [diff] [blame^] | 344 | k, ascii := lowerHeader(k) |
| 345 | if !ascii { |
| 346 | // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header |
| 347 | // field names have to be ASCII characters (just as in HTTP/1.x). |
| 348 | continue |
| 349 | } |
Matteo Scandolo | a428586 | 2020-12-01 18:10:10 -0800 | [diff] [blame] | 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 !httpguts.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 | } |