golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/stream_limits.go (about)

     1  // Copyright 2023 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  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"context"
    11  )
    12  
    13  // Limits on the number of open streams.
    14  // Every connection has separate limits for bidirectional and unidirectional streams.
    15  //
    16  // Note that the MAX_STREAMS limit includes closed as well as open streams.
    17  // Closing a stream doesn't enable an endpoint to open a new one;
    18  // only an increase in the MAX_STREAMS limit does.
    19  
    20  // localStreamLimits are limits on the number of open streams created by us.
    21  type localStreamLimits struct {
    22  	gate   gate
    23  	max    int64 // peer-provided MAX_STREAMS
    24  	opened int64 // number of streams opened by us, -1 when conn is closed
    25  }
    26  
    27  func (lim *localStreamLimits) init() {
    28  	lim.gate = newGate()
    29  }
    30  
    31  // open creates a new local stream, blocking until MAX_STREAMS quota is available.
    32  func (lim *localStreamLimits) open(ctx context.Context, c *Conn) (num int64, err error) {
    33  	// TODO: Send a STREAMS_BLOCKED when blocked.
    34  	if err := lim.gate.waitAndLock(ctx, c.testHooks); err != nil {
    35  		return 0, err
    36  	}
    37  	if lim.opened < 0 {
    38  		lim.gate.unlock(true)
    39  		return 0, errConnClosed
    40  	}
    41  	num = lim.opened
    42  	lim.opened++
    43  	lim.gate.unlock(lim.opened < lim.max)
    44  	return num, nil
    45  }
    46  
    47  // connHasClosed indicates the connection has been closed, locally or by the peer.
    48  func (lim *localStreamLimits) connHasClosed() {
    49  	lim.gate.lock()
    50  	lim.opened = -1
    51  	lim.gate.unlock(true)
    52  }
    53  
    54  // setMax sets the MAX_STREAMS provided by the peer.
    55  func (lim *localStreamLimits) setMax(maxStreams int64) {
    56  	lim.gate.lock()
    57  	lim.max = max(lim.max, maxStreams)
    58  	lim.gate.unlock(lim.opened < lim.max)
    59  }
    60  
    61  // remoteStreamLimits are limits on the number of open streams created by the peer.
    62  type remoteStreamLimits struct {
    63  	max     int64   // last MAX_STREAMS sent to the peer
    64  	opened  int64   // number of streams opened by the peer (including subsequently closed ones)
    65  	closed  int64   // number of peer streams in the "closed" state
    66  	maxOpen int64   // how many streams we want to let the peer simultaneously open
    67  	sendMax sentVal // set when we should send MAX_STREAMS
    68  }
    69  
    70  func (lim *remoteStreamLimits) init(maxOpen int64) {
    71  	lim.maxOpen = maxOpen
    72  	lim.max = min(maxOpen, implicitStreamLimit) // initial limit sent in transport parameters
    73  	lim.opened = 0
    74  }
    75  
    76  // open handles the peer opening a new stream.
    77  func (lim *remoteStreamLimits) open(id streamID) error {
    78  	num := id.num()
    79  	if num >= lim.max {
    80  		return localTransportError{
    81  			code:   errStreamLimit,
    82  			reason: "stream limit exceeded",
    83  		}
    84  	}
    85  	if num >= lim.opened {
    86  		lim.opened = num + 1
    87  		lim.maybeUpdateMax()
    88  	}
    89  	return nil
    90  }
    91  
    92  // close handles the peer closing an open stream.
    93  func (lim *remoteStreamLimits) close() {
    94  	lim.closed++
    95  	lim.maybeUpdateMax()
    96  }
    97  
    98  // maybeUpdateMax updates the MAX_STREAMS value we will send to the peer.
    99  func (lim *remoteStreamLimits) maybeUpdateMax() {
   100  	newMax := min(
   101  		// Max streams the peer can have open at once.
   102  		lim.closed+lim.maxOpen,
   103  		// Max streams the peer can open with a single frame.
   104  		lim.opened+implicitStreamLimit,
   105  	)
   106  	avail := lim.max - lim.opened
   107  	if newMax > lim.max && (avail < 8 || newMax-lim.max >= 2*avail) {
   108  		// If the peer has less than 8 streams, or if increasing the peer's
   109  		// stream limit would double it, then send a MAX_STREAMS.
   110  		lim.max = newMax
   111  		lim.sendMax.setUnsent()
   112  	}
   113  }
   114  
   115  // appendFrame appends a MAX_STREAMS frame to the current packet, if necessary.
   116  //
   117  // It returns true if no more frames need appending,
   118  // false if not everything fit in the current packet.
   119  func (lim *remoteStreamLimits) appendFrame(w *packetWriter, typ streamType, pnum packetNumber, pto bool) bool {
   120  	if lim.sendMax.shouldSendPTO(pto) {
   121  		if !w.appendMaxStreamsFrame(typ, lim.max) {
   122  			return false
   123  		}
   124  		lim.sendMax.setSent(pnum)
   125  	}
   126  	return true
   127  }