github.com/useflyent/fhttp@v0.0.0-20211004035111-333f430cfbbf/http2/write.go (about)

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