github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/Godeps/_workspace/src/golang.org/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 // See https://code.google.com/p/go/source/browse/CONTRIBUTORS 5 // Licensed under the same terms as Go itself: 6 // https://code.google.com/p/go/source/browse/LICENSE 7 8 package http2 9 10 import ( 11 "bytes" 12 "fmt" 13 "net/http" 14 "time" 15 16 "golang.org/x/net/http2/hpack" 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 24 // writeContext is the interface needed by the various frame writer 25 // types below. All the writeFrame methods below are scheduled via the 26 // frame writing scheduler (see writeScheduler in writesched.go). 27 // 28 // This interface is implemented by *serverConn. 29 // TODO: use it from the client code too, once it exists. 30 type writeContext interface { 31 Framer() *Framer 32 Flush() error 33 CloseConn() error 34 // HeaderEncoder returns an HPACK encoder that writes to the 35 // returned buffer. 36 HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) 37 } 38 39 // endsStream reports whether the given frame writer w will locally 40 // close the stream. 41 func endsStream(w writeFramer) bool { 42 switch v := w.(type) { 43 case *writeData: 44 return v.endStream 45 case *writeResHeaders: 46 return v.endStream 47 } 48 return false 49 } 50 51 type flushFrameWriter struct{} 52 53 func (flushFrameWriter) writeFrame(ctx writeContext) error { 54 return ctx.Flush() 55 } 56 57 type writeSettings []Setting 58 59 func (s writeSettings) writeFrame(ctx writeContext) error { 60 return ctx.Framer().WriteSettings([]Setting(s)...) 61 } 62 63 type writeGoAway struct { 64 maxStreamID uint32 65 code ErrCode 66 } 67 68 func (p *writeGoAway) writeFrame(ctx writeContext) error { 69 err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) 70 if p.code != 0 { 71 ctx.Flush() // ignore error: we're hanging up on them anyway 72 time.Sleep(50 * time.Millisecond) 73 ctx.CloseConn() 74 } 75 return err 76 } 77 78 type writeData struct { 79 streamID uint32 80 p []byte 81 endStream bool 82 } 83 84 func (w *writeData) String() string { 85 return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream) 86 } 87 88 func (w *writeData) writeFrame(ctx writeContext) error { 89 return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) 90 } 91 92 func (se StreamError) writeFrame(ctx writeContext) error { 93 return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) 94 } 95 96 type writePingAck struct{ pf *PingFrame } 97 98 func (w writePingAck) writeFrame(ctx writeContext) error { 99 return ctx.Framer().WritePing(true, w.pf.Data) 100 } 101 102 type writeSettingsAck struct{} 103 104 func (writeSettingsAck) writeFrame(ctx writeContext) error { 105 return ctx.Framer().WriteSettingsAck() 106 } 107 108 // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames 109 // for HTTP response headers from a server handler. 110 type writeResHeaders struct { 111 streamID uint32 112 httpResCode int 113 h http.Header // may be nil 114 endStream bool 115 116 contentType string 117 contentLength string 118 } 119 120 func (w *writeResHeaders) writeFrame(ctx writeContext) error { 121 enc, buf := ctx.HeaderEncoder() 122 buf.Reset() 123 enc.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(w.httpResCode)}) 124 for k, vv := range w.h { 125 k = lowerHeader(k) 126 for _, v := range vv { 127 // TODO: more of "8.1.2.2 Connection-Specific Header Fields" 128 if k == "transfer-encoding" && v != "trailers" { 129 continue 130 } 131 enc.WriteField(hpack.HeaderField{Name: k, Value: v}) 132 } 133 } 134 if w.contentType != "" { 135 enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType}) 136 } 137 if w.contentLength != "" { 138 enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength}) 139 } 140 141 headerBlock := buf.Bytes() 142 if len(headerBlock) == 0 { 143 panic("unexpected empty hpack") 144 } 145 146 // For now we're lazy and just pick the minimum MAX_FRAME_SIZE 147 // that all peers must support (16KB). Later we could care 148 // more and send larger frames if the peer advertised it, but 149 // there's little point. Most headers are small anyway (so we 150 // generally won't have CONTINUATION frames), and extra frames 151 // only waste 9 bytes anyway. 152 const maxFrameSize = 16384 153 154 first := true 155 for len(headerBlock) > 0 { 156 frag := headerBlock 157 if len(frag) > maxFrameSize { 158 frag = frag[:maxFrameSize] 159 } 160 headerBlock = headerBlock[len(frag):] 161 endHeaders := len(headerBlock) == 0 162 var err error 163 if first { 164 first = false 165 err = ctx.Framer().WriteHeaders(HeadersFrameParam{ 166 StreamID: w.streamID, 167 BlockFragment: frag, 168 EndStream: w.endStream, 169 EndHeaders: endHeaders, 170 }) 171 } else { 172 err = ctx.Framer().WriteContinuation(w.streamID, endHeaders, frag) 173 } 174 if err != nil { 175 return err 176 } 177 } 178 return nil 179 } 180 181 type write100ContinueHeadersFrame struct { 182 streamID uint32 183 } 184 185 func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error { 186 enc, buf := ctx.HeaderEncoder() 187 buf.Reset() 188 enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"}) 189 return ctx.Framer().WriteHeaders(HeadersFrameParam{ 190 StreamID: w.streamID, 191 BlockFragment: buf.Bytes(), 192 EndStream: false, 193 EndHeaders: true, 194 }) 195 } 196 197 type writeWindowUpdate struct { 198 streamID uint32 // or 0 for conn-level 199 n uint32 200 } 201 202 func (wu writeWindowUpdate) writeFrame(ctx writeContext) error { 203 return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) 204 }