// 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.//go:build !(go1.27 && !http2legacy)package http2import// FrameWriteRequest is a request to write a frame.//// Deprecated: User-provided write schedulers are deprecated.typeFrameWriteRequeststruct {// 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 chanerror}// 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 }return0 }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); {returnlen(.p) }return0}// 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) {varFrameWriteRequest// 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 }iflen(.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 {varstringif , := .write.(fmt.Stringer); { = .String() } else { = fmt.Sprintf("%T", .write) }returnfmt.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.//// Each writeQueue contains a queue of FrameWriteRequests, meant to store all// FrameWriteRequests associated with a given stream. This is implemented as a// two-stage queue: currQueue[currPos:] and nextQueue. Removing an item is done// by incrementing currPos of currQueue. Adding an item is done by appending it// to the nextQueue. If currQueue is empty when trying to remove an item, we// can swap currQueue and nextQueue to remedy the situation.// This two-stage queue is analogous to the use of two lists in Okasaki's// purely functional queue but without the overhead of reversing the list when// swapping stages.//// writeQueue also contains prev and next, this can be used by implementations// of WriteScheduler to construct data structures that represent the order of// writing between different streams (e.g. circular linked list).type writeQueue struct { currQueue []FrameWriteRequest nextQueue []FrameWriteRequest currPos int prev, next *writeQueue}func ( *writeQueue) () bool {return (len(.currQueue) - .currPos + len(.nextQueue)) == 0}func ( *writeQueue) ( FrameWriteRequest) { .nextQueue = append(.nextQueue, )}func ( *writeQueue) () FrameWriteRequest {if .empty() {panic("invalid use of queue") }if .currPos >= len(.currQueue) { .currQueue, .currPos, .nextQueue = .nextQueue, 0, .currQueue[:0] } := .currQueue[.currPos] .currQueue[.currPos] = FrameWriteRequest{} .currPos++return}func ( *writeQueue) () *FrameWriteRequest {if .currPos < len(.currQueue) {return &.currQueue[.currPos] }iflen(.nextQueue) > 0 {return &.nextQueue[0] }returnnil}// 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 .empty() {returnFrameWriteRequest{}, false } , , := .peek().Consume()switch {case0:returnFrameWriteRequest{}, falsecase1: .shift()case2: *.peek() = }return , true}type writeQueuePool []*writeQueue// put inserts an unused writeQueue into the pool.func ( *writeQueuePool) ( *writeQueue) {for := range .currQueue { .currQueue[] = FrameWriteRequest{} }for := range .nextQueue { .nextQueue[] = FrameWriteRequest{} } .currQueue = .currQueue[:0] .nextQueue = .nextQueue[:0] .currPos = 0 * = append(*, )}// get returns an empty writeQueue.func ( *writeQueuePool) () *writeQueue { := len(*)if == 0 {returnnew(writeQueue) } := - 1 := (*)[] (*)[] = nil * = (*)[:]return}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.