write.go

  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
  5package http2
  6
  7import (
  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	"golang.org/x/net/internal/httpcommon"
 17)
 18
 19// writeFramer is implemented by any type that is used to write frames.
 20type 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.
 39type 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).
 51func 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
 66type flushFrameWriter struct{}
 67
 68func (flushFrameWriter) writeFrame(ctx writeContext) error {
 69	return ctx.Flush()
 70}
 71
 72func (flushFrameWriter) staysWithinBuffer(max int) bool { return false }
 73
 74type writeSettings []Setting
 75
 76func (s writeSettings) staysWithinBuffer(max int) bool {
 77	const settingSize = 6 // uint16 + uint32
 78	return frameHeaderLen+settingSize*len(s) <= max
 79
 80}
 81
 82func (s writeSettings) writeFrame(ctx writeContext) error {
 83	return ctx.Framer().WriteSettings([]Setting(s)...)
 84}
 85
 86type writeGoAway struct {
 87	maxStreamID uint32
 88	code        ErrCode
 89}
 90
 91func (p *writeGoAway) writeFrame(ctx writeContext) error {
 92	err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
 93	ctx.Flush() // ignore error: we're hanging up on them anyway
 94	return err
 95}
 96
 97func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes
 98
 99type writeData struct {
100	streamID  uint32
101	p         []byte
102	endStream bool
103}
104
105func (w *writeData) String() string {
106	return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
107}
108
109func (w *writeData) writeFrame(ctx writeContext) error {
110	return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
111}
112
113func (w *writeData) staysWithinBuffer(max int) bool {
114	return frameHeaderLen+len(w.p) <= max
115}
116
117// handlerPanicRST is the message sent from handler goroutines when
118// the handler panics.
119type handlerPanicRST struct {
120	StreamID uint32
121}
122
123func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
124	return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
125}
126
127func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
128
129func (se StreamError) writeFrame(ctx writeContext) error {
130	return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
131}
132
133func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
134
135type writePing struct {
136	data [8]byte
137}
138
139func (w writePing) writeFrame(ctx writeContext) error {
140	return ctx.Framer().WritePing(false, w.data)
141}
142
143func (w writePing) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.data) <= max }
144
145type writePingAck struct{ pf *PingFrame }
146
147func (w writePingAck) writeFrame(ctx writeContext) error {
148	return ctx.Framer().WritePing(true, w.pf.Data)
149}
150
151func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max }
152
153type writeSettingsAck struct{}
154
155func (writeSettingsAck) writeFrame(ctx writeContext) error {
156	return ctx.Framer().WriteSettingsAck()
157}
158
159func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max }
160
161// splitHeaderBlock splits headerBlock into fragments so that each fragment fits
162// in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true
163// for the first/last fragment, respectively.
164func splitHeaderBlock(ctx writeContext, headerBlock []byte, fn func(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error) error {
165	// For now we're lazy and just pick the minimum MAX_FRAME_SIZE
166	// that all peers must support (16KB). Later we could care
167	// more and send larger frames if the peer advertised it, but
168	// there's little point. Most headers are small anyway (so we
169	// generally won't have CONTINUATION frames), and extra frames
170	// only waste 9 bytes anyway.
171	const maxFrameSize = 16384
172
173	first := true
174	for len(headerBlock) > 0 {
175		frag := headerBlock
176		if len(frag) > maxFrameSize {
177			frag = frag[:maxFrameSize]
178		}
179		headerBlock = headerBlock[len(frag):]
180		if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil {
181			return err
182		}
183		first = false
184	}
185	return nil
186}
187
188// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
189// for HTTP response headers or trailers from a server handler.
190type writeResHeaders struct {
191	streamID    uint32
192	httpResCode int         // 0 means no ":status" line
193	h           http.Header // may be nil
194	trailers    []string    // if non-nil, which keys of h to write. nil means all.
195	endStream   bool
196
197	date          string
198	contentType   string
199	contentLength string
200}
201
202func encKV(enc *hpack.Encoder, k, v string) {
203	if VerboseLogs {
204		log.Printf("http2: server encoding header %q = %q", k, v)
205	}
206	enc.WriteField(hpack.HeaderField{Name: k, Value: v})
207}
208
209func (w *writeResHeaders) staysWithinBuffer(max int) bool {
210	// TODO: this is a common one. It'd be nice to return true
211	// here and get into the fast path if we could be clever and
212	// calculate the size fast enough, or at least a conservative
213	// upper bound that usually fires. (Maybe if w.h and
214	// w.trailers are nil, so we don't need to enumerate it.)
215	// Otherwise I'm afraid that just calculating the length to
216	// answer this question would be slower than the ~2ยตs benefit.
217	return false
218}
219
220func (w *writeResHeaders) writeFrame(ctx writeContext) error {
221	enc, buf := ctx.HeaderEncoder()
222	buf.Reset()
223
224	if w.httpResCode != 0 {
225		encKV(enc, ":status", httpCodeString(w.httpResCode))
226	}
227
228	encodeHeaders(enc, w.h, w.trailers)
229
230	if w.contentType != "" {
231		encKV(enc, "content-type", w.contentType)
232	}
233	if w.contentLength != "" {
234		encKV(enc, "content-length", w.contentLength)
235	}
236	if w.date != "" {
237		encKV(enc, "date", w.date)
238	}
239
240	headerBlock := buf.Bytes()
241	if len(headerBlock) == 0 && w.trailers == nil {
242		panic("unexpected empty hpack")
243	}
244
245	return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
246}
247
248func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
249	if firstFrag {
250		return ctx.Framer().WriteHeaders(HeadersFrameParam{
251			StreamID:      w.streamID,
252			BlockFragment: frag,
253			EndStream:     w.endStream,
254			EndHeaders:    lastFrag,
255		})
256	} else {
257		return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
258	}
259}
260
261// writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames.
262type writePushPromise struct {
263	streamID uint32   // pusher stream
264	method   string   // for :method
265	url      *url.URL // for :scheme, :authority, :path
266	h        http.Header
267
268	// Creates an ID for a pushed stream. This runs on serveG just before
269	// the frame is written. The returned ID is copied to promisedID.
270	allocatePromisedID func() (uint32, error)
271	promisedID         uint32
272}
273
274func (w *writePushPromise) staysWithinBuffer(max int) bool {
275	// TODO: see writeResHeaders.staysWithinBuffer
276	return false
277}
278
279func (w *writePushPromise) writeFrame(ctx writeContext) error {
280	enc, buf := ctx.HeaderEncoder()
281	buf.Reset()
282
283	encKV(enc, ":method", w.method)
284	encKV(enc, ":scheme", w.url.Scheme)
285	encKV(enc, ":authority", w.url.Host)
286	encKV(enc, ":path", w.url.RequestURI())
287	encodeHeaders(enc, w.h, nil)
288
289	headerBlock := buf.Bytes()
290	if len(headerBlock) == 0 {
291		panic("unexpected empty hpack")
292	}
293
294	return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
295}
296
297func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
298	if firstFrag {
299		return ctx.Framer().WritePushPromise(PushPromiseParam{
300			StreamID:      w.streamID,
301			PromiseID:     w.promisedID,
302			BlockFragment: frag,
303			EndHeaders:    lastFrag,
304		})
305	} else {
306		return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
307	}
308}
309
310type write100ContinueHeadersFrame struct {
311	streamID uint32
312}
313
314func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
315	enc, buf := ctx.HeaderEncoder()
316	buf.Reset()
317	encKV(enc, ":status", "100")
318	return ctx.Framer().WriteHeaders(HeadersFrameParam{
319		StreamID:      w.streamID,
320		BlockFragment: buf.Bytes(),
321		EndStream:     false,
322		EndHeaders:    true,
323	})
324}
325
326func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
327	// Sloppy but conservative:
328	return 9+2*(len(":status")+len("100")) <= max
329}
330
331type writeWindowUpdate struct {
332	streamID uint32 // or 0 for conn-level
333	n        uint32
334}
335
336func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
337
338func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
339	return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
340}
341
342// encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k])
343// is encoded only if k is in keys.
344func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
345	if keys == nil {
346		sorter := sorterPool.Get().(*sorter)
347		// Using defer here, since the returned keys from the
348		// sorter.Keys method is only valid until the sorter
349		// is returned:
350		defer sorterPool.Put(sorter)
351		keys = sorter.Keys(h)
352	}
353	for _, k := range keys {
354		vv := h[k]
355		k, ascii := httpcommon.LowerHeader(k)
356		if !ascii {
357			// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
358			// field names have to be ASCII characters (just as in HTTP/1.x).
359			continue
360		}
361		if !validWireHeaderFieldName(k) {
362			// Skip it as backup paranoia. Per
363			// golang.org/issue/14048, these should
364			// already be rejected at a higher level.
365			continue
366		}
367		isTE := k == "transfer-encoding"
368		for _, v := range vv {
369			if !httpguts.ValidHeaderFieldValue(v) {
370				// TODO: return an error? golang.org/issue/14048
371				// For now just omit it.
372				continue
373			}
374			// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
375			if isTE && v != "trailers" {
376				continue
377			}
378			encKV(enc, k, v)
379		}
380	}
381}