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.
15 "golang.org/x/net/http2/hpack"
16 "golang.org/x/net/lex/httplex"
19 // writeFramer is implemented by any type that is used to write frames.
20 type writeFramer interface {
21 writeFrame(writeContext) error
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
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).
33 // This interface is implemented by *serverConn.
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 {
43 // HeaderEncoder returns an HPACK encoder that writes to the
45 HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
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) {
55 case *writeResHeaders:
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")
66 type flushFrameWriter struct{}
68 func (flushFrameWriter) writeFrame(ctx writeContext) error {
72 func (flushFrameWriter) staysWithinBuffer(max int) bool { return false }
74 type writeSettings []Setting
76 func (s writeSettings) staysWithinBuffer(max int) bool {
77 const settingSize = 6 // uint16 + uint32
78 return frameHeaderLen+settingSize*len(s) <= max
82 func (s writeSettings) writeFrame(ctx writeContext) error {
83 return ctx.Framer().WriteSettings([]Setting(s)...)
86 type writeGoAway struct {
91 func (p *writeGoAway) writeFrame(ctx writeContext) error {
92 err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
94 ctx.Flush() // ignore error: we're hanging up on them anyway
95 time.Sleep(50 * time.Millisecond)
101 func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes
103 type writeData struct {
109 func (w *writeData) String() string {
110 return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
113 func (w *writeData) writeFrame(ctx writeContext) error {
114 return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
117 func (w *writeData) staysWithinBuffer(max int) bool {
118 return frameHeaderLen+len(w.p) <= max
121 // handlerPanicRST is the message sent from handler goroutines when
122 // the handler panics.
123 type handlerPanicRST struct {
127 func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
128 return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
131 func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
133 func (se StreamError) writeFrame(ctx writeContext) error {
134 return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
137 func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
139 type writePingAck struct{ pf *PingFrame }
141 func (w writePingAck) writeFrame(ctx writeContext) error {
142 return ctx.Framer().WritePing(true, w.pf.Data)
145 func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max }
147 type writeSettingsAck struct{}
149 func (writeSettingsAck) writeFrame(ctx writeContext) error {
150 return ctx.Framer().WriteSettingsAck()
153 func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max }
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
168 for len(headerBlock) > 0 {
170 if len(frag) > maxFrameSize {
171 frag = frag[:maxFrameSize]
173 headerBlock = headerBlock[len(frag):]
174 if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil {
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 {
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.
196 func encKV(enc *hpack.Encoder, k, v string) {
198 log.Printf("http2: server encoding header %q = %q", k, v)
200 enc.WriteField(hpack.HeaderField{Name: k, Value: v})
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.
214 func (w *writeResHeaders) writeFrame(ctx writeContext) error {
215 enc, buf := ctx.HeaderEncoder()
218 if w.httpResCode != 0 {
219 encKV(enc, ":status", httpCodeString(w.httpResCode))
222 encodeHeaders(enc, w.h, w.trailers)
224 if w.contentType != "" {
225 encKV(enc, "content-type", w.contentType)
227 if w.contentLength != "" {
228 encKV(enc, "content-length", w.contentLength)
231 encKV(enc, "date", w.date)
234 headerBlock := buf.Bytes()
235 if len(headerBlock) == 0 && w.trailers == nil {
236 panic("unexpected empty hpack")
239 return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
242 func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
244 return ctx.Framer().WriteHeaders(HeadersFrameParam{
245 StreamID: w.streamID,
247 EndStream: w.endStream,
248 EndHeaders: lastFrag,
251 return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
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
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)
268 func (w *writePushPromise) staysWithinBuffer(max int) bool {
269 // TODO: see writeResHeaders.staysWithinBuffer
273 func (w *writePushPromise) writeFrame(ctx writeContext) error {
274 enc, buf := ctx.HeaderEncoder()
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)
283 headerBlock := buf.Bytes()
284 if len(headerBlock) == 0 {
285 panic("unexpected empty hpack")
288 return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
291 func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
293 return ctx.Framer().WritePushPromise(PushPromiseParam{
294 StreamID: w.streamID,
295 PromiseID: w.promisedID,
297 EndHeaders: lastFrag,
300 return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
304 type write100ContinueHeadersFrame struct {
308 func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
309 enc, buf := ctx.HeaderEncoder()
311 encKV(enc, ":status", "100")
312 return ctx.Framer().WriteHeaders(HeadersFrameParam{
313 StreamID: w.streamID,
314 BlockFragment: buf.Bytes(),
320 func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
321 // Sloppy but conservative:
322 return 9+2*(len(":status")+len("100")) <= max
325 type writeWindowUpdate struct {
326 streamID uint32 // or 0 for conn-level
330 func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
332 func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
333 return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
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) {
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
344 defer sorterPool.Put(sorter)
345 keys = sorter.Keys(h)
347 for _, k := range keys {
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.
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.
363 // TODO: more of "8.1.2.2 Connection-Specific Header Fields"
364 if isTE && v != "trailers" {