writesched.go (7839B)
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 "fmt" 8 9 // WriteScheduler is the interface implemented by HTTP/2 write schedulers. 10 // Methods are never called concurrently. 11 type WriteScheduler interface { 12 // OpenStream opens a new stream in the write scheduler. 13 // It is illegal to call this with streamID=0 or with a streamID that is 14 // already open -- the call may panic. 15 OpenStream(streamID uint32, options OpenStreamOptions) 16 17 // CloseStream closes a stream in the write scheduler. Any frames queued on 18 // this stream should be discarded. It is illegal to call this on a stream 19 // that is not open -- the call may panic. 20 CloseStream(streamID uint32) 21 22 // AdjustStream adjusts the priority of the given stream. This may be called 23 // on a stream that has not yet been opened or has been closed. Note that 24 // RFC 7540 allows PRIORITY frames to be sent on streams in any state. See: 25 // https://tools.ietf.org/html/rfc7540#section-5.1 26 AdjustStream(streamID uint32, priority PriorityParam) 27 28 // Push queues a frame in the scheduler. In most cases, this will not be 29 // called with wr.StreamID()!=0 unless that stream is currently open. The one 30 // exception is RST_STREAM frames, which may be sent on idle or closed streams. 31 Push(wr FrameWriteRequest) 32 33 // Pop dequeues the next frame to write. Returns false if no frames can 34 // be written. Frames with a given wr.StreamID() are Pop'd in the same 35 // order they are Push'd, except RST_STREAM frames. No frames should be 36 // discarded except by CloseStream. 37 Pop() (wr FrameWriteRequest, ok bool) 38 } 39 40 // OpenStreamOptions specifies extra options for WriteScheduler.OpenStream. 41 type OpenStreamOptions struct { 42 // PusherID is zero if the stream was initiated by the client. Otherwise, 43 // PusherID names the stream that pushed the newly opened stream. 44 PusherID uint32 45 } 46 47 // FrameWriteRequest is a request to write a frame. 48 type FrameWriteRequest struct { 49 // write is the interface value that does the writing, once the 50 // WriteScheduler has selected this frame to write. The write 51 // functions are all defined in write.go. 52 write writeFramer 53 54 // stream is the stream on which this frame will be written. 55 // nil for non-stream frames like PING and SETTINGS. 56 // nil for RST_STREAM streams, which use the StreamError.StreamID field instead. 57 stream *stream 58 59 // done, if non-nil, must be a buffered channel with space for 60 // 1 message and is sent the return value from write (or an 61 // earlier error) when the frame has been written. 62 done chan error 63 } 64 65 // StreamID returns the id of the stream this frame will be written to. 66 // 0 is used for non-stream frames such as PING and SETTINGS. 67 func (wr FrameWriteRequest) StreamID() uint32 { 68 if wr.stream == nil { 69 if se, ok := wr.write.(StreamError); ok { 70 // (*serverConn).resetStream doesn't set 71 // stream because it doesn't necessarily have 72 // one. So special case this type of write 73 // message. 74 return se.StreamID 75 } 76 return 0 77 } 78 return wr.stream.id 79 } 80 81 // isControl reports whether wr is a control frame for MaxQueuedControlFrames 82 // purposes. That includes non-stream frames and RST_STREAM frames. 83 func (wr FrameWriteRequest) isControl() bool { 84 return wr.stream == nil 85 } 86 87 // DataSize returns the number of flow control bytes that must be consumed 88 // to write this entire frame. This is 0 for non-DATA frames. 89 func (wr FrameWriteRequest) DataSize() int { 90 if wd, ok := wr.write.(*writeData); ok { 91 return len(wd.p) 92 } 93 return 0 94 } 95 96 // Consume consumes min(n, available) bytes from this frame, where available 97 // is the number of flow control bytes available on the stream. Consume returns 98 // 0, 1, or 2 frames, where the integer return value gives the number of frames 99 // returned. 100 // 101 // If flow control prevents consuming any bytes, this returns (_, _, 0). If 102 // the entire frame was consumed, this returns (wr, _, 1). Otherwise, this 103 // returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and 104 // 'rest' contains the remaining bytes. The consumed bytes are deducted from the 105 // underlying stream's flow control budget. 106 func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) { 107 var empty FrameWriteRequest 108 109 // Non-DATA frames are always consumed whole. 110 wd, ok := wr.write.(*writeData) 111 if !ok || len(wd.p) == 0 { 112 return wr, empty, 1 113 } 114 115 // Might need to split after applying limits. 116 allowed := wr.stream.flow.available() 117 if n < allowed { 118 allowed = n 119 } 120 if wr.stream.sc.maxFrameSize < allowed { 121 allowed = wr.stream.sc.maxFrameSize 122 } 123 if allowed <= 0 { 124 return empty, empty, 0 125 } 126 if len(wd.p) > int(allowed) { 127 wr.stream.flow.take(allowed) 128 consumed := FrameWriteRequest{ 129 stream: wr.stream, 130 write: &writeData{ 131 streamID: wd.streamID, 132 p: wd.p[:allowed], 133 // Even if the original had endStream set, there 134 // are bytes remaining because len(wd.p) > allowed, 135 // so we know endStream is false. 136 endStream: false, 137 }, 138 // Our caller is blocking on the final DATA frame, not 139 // this intermediate frame, so no need to wait. 140 done: nil, 141 } 142 rest := FrameWriteRequest{ 143 stream: wr.stream, 144 write: &writeData{ 145 streamID: wd.streamID, 146 p: wd.p[allowed:], 147 endStream: wd.endStream, 148 }, 149 done: wr.done, 150 } 151 return consumed, rest, 2 152 } 153 154 // The frame is consumed whole. 155 // NB: This cast cannot overflow because allowed is <= math.MaxInt32. 156 wr.stream.flow.take(int32(len(wd.p))) 157 return wr, empty, 1 158 } 159 160 // String is for debugging only. 161 func (wr FrameWriteRequest) String() string { 162 var des string 163 if s, ok := wr.write.(fmt.Stringer); ok { 164 des = s.String() 165 } else { 166 des = fmt.Sprintf("%T", wr.write) 167 } 168 return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des) 169 } 170 171 // replyToWriter sends err to wr.done and panics if the send must block 172 // This does nothing if wr.done is nil. 173 func (wr *FrameWriteRequest) replyToWriter(err error) { 174 if wr.done == nil { 175 return 176 } 177 select { 178 case wr.done <- err: 179 default: 180 panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write)) 181 } 182 wr.write = nil // prevent use (assume it's tainted after wr.done send) 183 } 184 185 // writeQueue is used by implementations of WriteScheduler. 186 type writeQueue struct { 187 s []FrameWriteRequest 188 prev, next *writeQueue 189 } 190 191 func (q *writeQueue) empty() bool { return len(q.s) == 0 } 192 193 func (q *writeQueue) push(wr FrameWriteRequest) { 194 q.s = append(q.s, wr) 195 } 196 197 func (q *writeQueue) shift() FrameWriteRequest { 198 if len(q.s) == 0 { 199 panic("invalid use of queue") 200 } 201 wr := q.s[0] 202 // TODO: less copy-happy queue. 203 copy(q.s, q.s[1:]) 204 q.s[len(q.s)-1] = FrameWriteRequest{} 205 q.s = q.s[:len(q.s)-1] 206 return wr 207 } 208 209 // consume consumes up to n bytes from q.s[0]. If the frame is 210 // entirely consumed, it is removed from the queue. If the frame 211 // is partially consumed, the frame is kept with the consumed 212 // bytes removed. Returns true iff any bytes were consumed. 213 func (q *writeQueue) consume(n int32) (FrameWriteRequest, bool) { 214 if len(q.s) == 0 { 215 return FrameWriteRequest{}, false 216 } 217 consumed, rest, numresult := q.s[0].Consume(n) 218 switch numresult { 219 case 0: 220 return FrameWriteRequest{}, false 221 case 1: 222 q.shift() 223 case 2: 224 q.s[0] = rest 225 } 226 return consumed, true 227 } 228 229 type writeQueuePool []*writeQueue 230 231 // put inserts an unused writeQueue into the pool. 232 func (p *writeQueuePool) put(q *writeQueue) { 233 for i := range q.s { 234 q.s[i] = FrameWriteRequest{} 235 } 236 q.s = q.s[:0] 237 *p = append(*p, q) 238 } 239 240 // get returns an empty writeQueue. 241 func (p *writeQueuePool) get() *writeQueue { 242 ln := len(*p) 243 if ln == 0 { 244 return new(writeQueue) 245 } 246 x := ln - 1 247 q := (*p)[x] 248 (*p)[x] = nil 249 *p = (*p)[:x] 250 return q 251 }