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 }