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