mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
For golang/go#58547 Change-Id: I119d820824f82bfdd236c6826f960d0c934745ca Reviewed-on: https://go-review.googlesource.com/c/net/+/566295 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
// Copyright 2023 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.21
|
|
|
|
package quic
|
|
|
|
import (
|
|
"context"
|
|
)
|
|
|
|
// Limits on the number of open streams.
|
|
// Every connection has separate limits for bidirectional and unidirectional streams.
|
|
//
|
|
// Note that the MAX_STREAMS limit includes closed as well as open streams.
|
|
// Closing a stream doesn't enable an endpoint to open a new one;
|
|
// only an increase in the MAX_STREAMS limit does.
|
|
|
|
// localStreamLimits are limits on the number of open streams created by us.
|
|
type localStreamLimits struct {
|
|
gate gate
|
|
max int64 // peer-provided MAX_STREAMS
|
|
opened int64 // number of streams opened by us, -1 when conn is closed
|
|
}
|
|
|
|
func (lim *localStreamLimits) init() {
|
|
lim.gate = newGate()
|
|
}
|
|
|
|
// open creates a new local stream, blocking until MAX_STREAMS quota is available.
|
|
func (lim *localStreamLimits) open(ctx context.Context, c *Conn) (num int64, err error) {
|
|
// TODO: Send a STREAMS_BLOCKED when blocked.
|
|
if err := lim.gate.waitAndLock(ctx, c.testHooks); err != nil {
|
|
return 0, err
|
|
}
|
|
if lim.opened < 0 {
|
|
lim.gate.unlock(true)
|
|
return 0, errConnClosed
|
|
}
|
|
num = lim.opened
|
|
lim.opened++
|
|
lim.gate.unlock(lim.opened < lim.max)
|
|
return num, nil
|
|
}
|
|
|
|
// connHasClosed indicates the connection has been closed, locally or by the peer.
|
|
func (lim *localStreamLimits) connHasClosed() {
|
|
lim.gate.lock()
|
|
lim.opened = -1
|
|
lim.gate.unlock(true)
|
|
}
|
|
|
|
// setMax sets the MAX_STREAMS provided by the peer.
|
|
func (lim *localStreamLimits) setMax(maxStreams int64) {
|
|
lim.gate.lock()
|
|
lim.max = max(lim.max, maxStreams)
|
|
lim.gate.unlock(lim.opened < lim.max)
|
|
}
|
|
|
|
// remoteStreamLimits are limits on the number of open streams created by the peer.
|
|
type remoteStreamLimits struct {
|
|
max int64 // last MAX_STREAMS sent to the peer
|
|
opened int64 // number of streams opened by the peer (including subsequently closed ones)
|
|
closed int64 // number of peer streams in the "closed" state
|
|
maxOpen int64 // how many streams we want to let the peer simultaneously open
|
|
sendMax sentVal // set when we should send MAX_STREAMS
|
|
}
|
|
|
|
func (lim *remoteStreamLimits) init(maxOpen int64) {
|
|
lim.maxOpen = maxOpen
|
|
lim.max = min(maxOpen, implicitStreamLimit) // initial limit sent in transport parameters
|
|
lim.opened = 0
|
|
}
|
|
|
|
// open handles the peer opening a new stream.
|
|
func (lim *remoteStreamLimits) open(id streamID) error {
|
|
num := id.num()
|
|
if num >= lim.max {
|
|
return localTransportError{
|
|
code: errStreamLimit,
|
|
reason: "stream limit exceeded",
|
|
}
|
|
}
|
|
if num >= lim.opened {
|
|
lim.opened = num + 1
|
|
lim.maybeUpdateMax()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// close handles the peer closing an open stream.
|
|
func (lim *remoteStreamLimits) close() {
|
|
lim.closed++
|
|
lim.maybeUpdateMax()
|
|
}
|
|
|
|
// maybeUpdateMax updates the MAX_STREAMS value we will send to the peer.
|
|
func (lim *remoteStreamLimits) maybeUpdateMax() {
|
|
newMax := min(
|
|
// Max streams the peer can have open at once.
|
|
lim.closed+lim.maxOpen,
|
|
// Max streams the peer can open with a single frame.
|
|
lim.opened+implicitStreamLimit,
|
|
)
|
|
avail := lim.max - lim.opened
|
|
if newMax > lim.max && (avail < 8 || newMax-lim.max >= 2*avail) {
|
|
// If the peer has less than 8 streams, or if increasing the peer's
|
|
// stream limit would double it, then send a MAX_STREAMS.
|
|
lim.max = newMax
|
|
lim.sendMax.setUnsent()
|
|
}
|
|
}
|
|
|
|
// appendFrame appends a MAX_STREAMS frame to the current packet, if necessary.
|
|
//
|
|
// It returns true if no more frames need appending,
|
|
// false if not everything fit in the current packet.
|
|
func (lim *remoteStreamLimits) appendFrame(w *packetWriter, typ streamType, pnum packetNumber, pto bool) bool {
|
|
if lim.sendMax.shouldSendPTO(pto) {
|
|
if !w.appendMaxStreamsFrame(typ, lim.max) {
|
|
return false
|
|
}
|
|
lim.sendMax.setSent(pnum)
|
|
}
|
|
return true
|
|
}
|