github.com/Kolosok86/http@v0.1.2/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  	"github.com/Kolosok86/http"
    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]
   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  		}
   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  }