Source File
writesched.go
Belonging Package
golang.org/x/net/http2
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http2
import
// WriteScheduler is the interface implemented by HTTP/2 write schedulers.
// Methods are never called concurrently.
type WriteScheduler interface {
// OpenStream opens a new stream in the write scheduler.
// It is illegal to call this with streamID=0 or with a streamID that is
// already open -- the call may panic.
OpenStream(streamID uint32, options OpenStreamOptions)
// CloseStream closes a stream in the write scheduler. Any frames queued on
// this stream should be discarded. It is illegal to call this on a stream
// that is not open -- the call may panic.
CloseStream(streamID uint32)
// AdjustStream adjusts the priority of the given stream. This may be called
// on a stream that has not yet been opened or has been closed. Note that
// RFC 7540 allows PRIORITY frames to be sent on streams in any state. See:
// https://tools.ietf.org/html/rfc7540#section-5.1
AdjustStream(streamID uint32, priority PriorityParam)
// Push queues a frame in the scheduler. In most cases, this will not be
// called with wr.StreamID()!=0 unless that stream is currently open. The one
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
Push(wr FrameWriteRequest)
// Pop dequeues the next frame to write. Returns false if no frames can
// be written. Frames with a given wr.StreamID() are Pop'd in the same
// order they are Push'd, except RST_STREAM frames. No frames should be
// discarded except by CloseStream.
Pop() (wr FrameWriteRequest, ok bool)
}
// OpenStreamOptions specifies extra options for WriteScheduler.OpenStream.
type OpenStreamOptions struct {
// PusherID is zero if the stream was initiated by the client. Otherwise,
// PusherID names the stream that pushed the newly opened stream.
PusherID uint32
}
// FrameWriteRequest is a request to write a frame.
type FrameWriteRequest struct {
// write is the interface value that does the writing, once the
// WriteScheduler has selected this frame to write. The write
// functions are all defined in write.go.
write writeFramer
// stream is the stream on which this frame will be written.
// nil for non-stream frames like PING and SETTINGS.
// nil for RST_STREAM streams, which use the StreamError.StreamID field instead.
stream *stream
// done, if non-nil, must be a buffered channel with space for
// 1 message and is sent the return value from write (or an
// earlier error) when the frame has been written.
done chan error
}
// StreamID returns the id of the stream this frame will be written to.
// 0 is used for non-stream frames such as PING and SETTINGS.
func ( FrameWriteRequest) () uint32 {
if .stream == nil {
if , := .write.(StreamError); {
// (*serverConn).resetStream doesn't set
// stream because it doesn't necessarily have
// one. So special case this type of write
// message.
return .StreamID
}
return 0
}
return .stream.id
}
// isControl reports whether wr is a control frame for MaxQueuedControlFrames
// purposes. That includes non-stream frames and RST_STREAM frames.
func ( FrameWriteRequest) () bool {
return .stream == nil
}
// DataSize returns the number of flow control bytes that must be consumed
// to write this entire frame. This is 0 for non-DATA frames.
func ( FrameWriteRequest) () int {
if , := .write.(*writeData); {
return len(.p)
}
return 0
}
// Consume consumes min(n, available) bytes from this frame, where available
// is the number of flow control bytes available on the stream. Consume returns
// 0, 1, or 2 frames, where the integer return value gives the number of frames
// returned.
//
// If flow control prevents consuming any bytes, this returns (_, _, 0). If
// the entire frame was consumed, this returns (wr, _, 1). Otherwise, this
// returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and
// 'rest' contains the remaining bytes. The consumed bytes are deducted from the
// underlying stream's flow control budget.
func ( FrameWriteRequest) ( int32) (FrameWriteRequest, FrameWriteRequest, int) {
var FrameWriteRequest
// Non-DATA frames are always consumed whole.
, := .write.(*writeData)
if ! || len(.p) == 0 {
return , , 1
}
// Might need to split after applying limits.
:= .stream.flow.available()
if < {
=
}
if .stream.sc.maxFrameSize < {
= .stream.sc.maxFrameSize
}
if <= 0 {
return , , 0
}
if len(.p) > int() {
.stream.flow.take()
:= FrameWriteRequest{
stream: .stream,
write: &writeData{
streamID: .streamID,
p: .p[:],
// Even if the original had endStream set, there
// are bytes remaining because len(wd.p) > allowed,
// so we know endStream is false.
endStream: false,
},
// Our caller is blocking on the final DATA frame, not
// this intermediate frame, so no need to wait.
done: nil,
}
:= FrameWriteRequest{
stream: .stream,
write: &writeData{
streamID: .streamID,
p: .p[:],
endStream: .endStream,
},
done: .done,
}
return , , 2
}
// The frame is consumed whole.
// NB: This cast cannot overflow because allowed is <= math.MaxInt32.
.stream.flow.take(int32(len(.p)))
return , , 1
}
// String is for debugging only.
func ( FrameWriteRequest) () string {
var string
if , := .write.(fmt.Stringer); {
= .String()
} else {
= fmt.Sprintf("%T", .write)
}
return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", .StreamID(), .done != nil, )
}
// replyToWriter sends err to wr.done and panics if the send must block
// This does nothing if wr.done is nil.
func ( *FrameWriteRequest) ( error) {
if .done == nil {
return
}
select {
case .done <- :
default:
panic(fmt.Sprintf("unbuffered done channel passed in for type %T", .write))
}
.write = nil // prevent use (assume it's tainted after wr.done send)
}
// writeQueue is used by implementations of WriteScheduler.
type writeQueue struct {
s []FrameWriteRequest
}
func ( *writeQueue) () bool { return len(.s) == 0 }
func ( *writeQueue) ( FrameWriteRequest) {
.s = append(.s, )
}
func ( *writeQueue) () FrameWriteRequest {
if len(.s) == 0 {
panic("invalid use of queue")
}
:= .s[0]
// TODO: less copy-happy queue.
copy(.s, .s[1:])
.s[len(.s)-1] = FrameWriteRequest{}
.s = .s[:len(.s)-1]
return
}
// consume consumes up to n bytes from q.s[0]. If the frame is
// entirely consumed, it is removed from the queue. If the frame
// is partially consumed, the frame is kept with the consumed
// bytes removed. Returns true iff any bytes were consumed.
func ( *writeQueue) ( int32) (FrameWriteRequest, bool) {
if len(.s) == 0 {
return FrameWriteRequest{}, false
}
, , := .s[0].Consume()
switch {
case 0:
return FrameWriteRequest{}, false
case 1:
.shift()
case 2:
.s[0] =
}
return , true
}
type writeQueuePool []*writeQueue
// put inserts an unused writeQueue into the pool.
func ( *writeQueuePool) ( *writeQueue) {
for := range .s {
.s[] = FrameWriteRequest{}
}
.s = .s[:0]
* = append(*, )
}
// get returns an empty writeQueue.
func ( *writeQueuePool) () *writeQueue {
:= len(*)
if == 0 {
return new(writeQueue)
}
:= - 1
:= (*)[]
(*)[] = nil
* = (*)[:]
return
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)