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 }