github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/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  	"net/http"
    11  	"sort"
    12  	"time"
    13  
    14  	"golang.org/x/net/http2/hpack"
    15  )
    16  
    17  // writeFramer is implemented by any type that is used to write frames.
    18  type writeFramer interface {
    19  	writeFrame(writeContext) error
    20  }
    21  
    22  // writeContext is the interface needed by the various frame writer
    23  // types below. All the writeFrame methods below are scheduled via the
    24  // frame writing scheduler (see writeScheduler in writesched.go).
    25  //
    26  // This interface is implemented by *serverConn.
    27  //
    28  // TODO: decide whether to a) use this in the client code (which didn't
    29  // end up using this yet, because it has a simpler design, not
    30  // currently implementing priorities), or b) delete this and
    31  // make the server code a bit more concrete.
    32  type writeContext interface {
    33  	Framer() *Framer
    34  	Flush() error
    35  	CloseConn() error
    36  	// HeaderEncoder returns an HPACK encoder that writes to the
    37  	// returned buffer.
    38  	HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
    39  }
    40  
    41  // endsStream reports whether the given frame writer w will locally
    42  // close the stream.
    43  func endsStream(w writeFramer) bool {
    44  	switch v := w.(type) {
    45  	case *writeData:
    46  		return v.endStream
    47  	case *writeResHeaders:
    48  		return v.endStream
    49  	case nil:
    50  		// This can only happen if the caller reuses w after it's
    51  		// been intentionally nil'ed out to prevent use. Keep this
    52  		// here to catch future refactoring breaking it.
    53  		panic("endsStream called on nil writeFramer")
    54  	}
    55  	return false
    56  }
    57  
    58  type flushFrameWriter struct{}
    59  
    60  func (flushFrameWriter) writeFrame(ctx writeContext) error {
    61  	return ctx.Flush()
    62  }
    63  
    64  type writeSettings []Setting
    65  
    66  func (s writeSettings) writeFrame(ctx writeContext) error {
    67  	return ctx.Framer().WriteSettings([]Setting(s)...)
    68  }
    69  
    70  type writeGoAway struct {
    71  	maxStreamID uint32
    72  	code        ErrCode
    73  }
    74  
    75  func (p *writeGoAway) writeFrame(ctx writeContext) error {
    76  	err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
    77  	if p.code != 0 {
    78  		ctx.Flush() // ignore error: we're hanging up on them anyway
    79  		time.Sleep(50 * time.Millisecond)
    80  		ctx.CloseConn()
    81  	}
    82  	return err
    83  }
    84  
    85  type writeData struct {
    86  	streamID  uint32
    87  	p         []byte
    88  	endStream bool
    89  }
    90  
    91  func (w *writeData) String() string {
    92  	return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
    93  }
    94  
    95  func (w *writeData) writeFrame(ctx writeContext) error {
    96  	return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
    97  }
    98  
    99  // handlerPanicRST is the message sent from handler goroutines when
   100  // the handler panics.
   101  type handlerPanicRST struct {
   102  	StreamID uint32
   103  }
   104  
   105  func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
   106  	return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
   107  }
   108  
   109  func (se StreamError) writeFrame(ctx writeContext) error {
   110  	return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
   111  }
   112  
   113  type writePingAck struct{ pf *PingFrame }
   114  
   115  func (w writePingAck) writeFrame(ctx writeContext) error {
   116  	return ctx.Framer().WritePing(true, w.pf.Data)
   117  }
   118  
   119  type writeSettingsAck struct{}
   120  
   121  func (writeSettingsAck) writeFrame(ctx writeContext) error {
   122  	return ctx.Framer().WriteSettingsAck()
   123  }
   124  
   125  // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
   126  // for HTTP response headers or trailers from a server handler.
   127  type writeResHeaders struct {
   128  	streamID    uint32
   129  	httpResCode int         // 0 means no ":status" line
   130  	h           http.Header // may be nil
   131  	trailers    []string    // if non-nil, which keys of h to write. nil means all.
   132  	endStream   bool
   133  
   134  	date          string
   135  	contentType   string
   136  	contentLength string
   137  }
   138  
   139  func (w *writeResHeaders) writeFrame(ctx writeContext) error {
   140  	enc, buf := ctx.HeaderEncoder()
   141  	buf.Reset()
   142  
   143  	if w.httpResCode != 0 {
   144  		enc.WriteField(hpack.HeaderField{
   145  			Name:  ":status",
   146  			Value: httpCodeString(w.httpResCode),
   147  		})
   148  	}
   149  
   150  	encodeHeaders(enc, w.h, w.trailers)
   151  
   152  	if w.contentType != "" {
   153  		enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType})
   154  	}
   155  	if w.contentLength != "" {
   156  		enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength})
   157  	}
   158  	if w.date != "" {
   159  		enc.WriteField(hpack.HeaderField{Name: "date", Value: w.date})
   160  	}
   161  
   162  	headerBlock := buf.Bytes()
   163  	if len(headerBlock) == 0 && w.trailers == nil {
   164  		panic("unexpected empty hpack")
   165  	}
   166  
   167  	// For now we're lazy and just pick the minimum MAX_FRAME_SIZE
   168  	// that all peers must support (16KB). Later we could care
   169  	// more and send larger frames if the peer advertised it, but
   170  	// there's little point. Most headers are small anyway (so we
   171  	// generally won't have CONTINUATION frames), and extra frames
   172  	// only waste 9 bytes anyway.
   173  	const maxFrameSize = 16384
   174  
   175  	first := true
   176  	for len(headerBlock) > 0 {
   177  		frag := headerBlock
   178  		if len(frag) > maxFrameSize {
   179  			frag = frag[:maxFrameSize]
   180  		}
   181  		headerBlock = headerBlock[len(frag):]
   182  		endHeaders := len(headerBlock) == 0
   183  		var err error
   184  		if first {
   185  			first = false
   186  			err = ctx.Framer().WriteHeaders(HeadersFrameParam{
   187  				StreamID:      w.streamID,
   188  				BlockFragment: frag,
   189  				EndStream:     w.endStream,
   190  				EndHeaders:    endHeaders,
   191  			})
   192  		} else {
   193  			err = ctx.Framer().WriteContinuation(w.streamID, endHeaders, frag)
   194  		}
   195  		if err != nil {
   196  			return err
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  type write100ContinueHeadersFrame struct {
   203  	streamID uint32
   204  }
   205  
   206  func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
   207  	enc, buf := ctx.HeaderEncoder()
   208  	buf.Reset()
   209  	enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
   210  	return ctx.Framer().WriteHeaders(HeadersFrameParam{
   211  		StreamID:      w.streamID,
   212  		BlockFragment: buf.Bytes(),
   213  		EndStream:     false,
   214  		EndHeaders:    true,
   215  	})
   216  }
   217  
   218  type writeWindowUpdate struct {
   219  	streamID uint32 // or 0 for conn-level
   220  	n        uint32
   221  }
   222  
   223  func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
   224  	return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
   225  }
   226  
   227  func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
   228  	// TODO: garbage. pool sorters like http1? hot path for 1 key?
   229  	if keys == nil {
   230  		keys = make([]string, 0, len(h))
   231  		for k := range h {
   232  			keys = append(keys, k)
   233  		}
   234  		sort.Strings(keys)
   235  	}
   236  	for _, k := range keys {
   237  		vv := h[k]
   238  		k = lowerHeader(k)
   239  		isTE := k == "transfer-encoding"
   240  		for _, v := range vv {
   241  			// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
   242  			if isTE && v != "trailers" {
   243  				continue
   244  			}
   245  			enc.WriteField(hpack.HeaderField{Name: k, Value: v})
   246  		}
   247  	}
   248  }