...

Source file src/golang.org/x/net/http2/write.go

Documentation: golang.org/x/net/http2

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

View as plain text