aboutsummaryrefslogtreecommitdiff
path: root/src/vendor/golang.org/x/net/quic/conn_recv.go
diff options
context:
space:
mode:
authorNicholas S. Husin <nsh@golang.org>2026-03-30 19:17:03 -0400
committerNicholas Husin <nsh@golang.org>2026-04-10 08:24:28 -0700
commit2f3c778b232dd53c41e1b623d25cd9f4ab28aaa5 (patch)
tree3959d70ffde2c7c385b0cc5c62cb3ac56dbe1516 /src/vendor/golang.org/x/net/quic/conn_recv.go
parentce4459cf0ee339b3bcf0ed10427079a234aade36 (diff)
downloadgo-2f3c778b232dd53c41e1b623d25cd9f4ab28aaa5.tar.xz
net/http: add support for running HTTP tests against HTTP/3
Add support within clientserver_test.go to bring up a test HTTP/3 server and client when http3Mode testMode option is passed. To be able to reuse net/http/httptest, net/http/httptest.Server.StartTLS (and Start) have been modified so they can be called with a nil Listener. In such cases, both methods will behave identically as usual, but will not actually make its server serve or set its transport dialer, both of which requires having a listener. This should be a no-op for regular users of the package, whose entrypoint via functions such as NewServer will automatically set a local listener. Actually enabling HTTP/3 for our tests will be done in a separate CL. For #70914 Change-Id: Ibc5fc83287b6a04b46e668a54924761a92b620a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/740122 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <husin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/vendor/golang.org/x/net/quic/conn_recv.go')
-rw-r--r--src/vendor/golang.org/x/net/quic/conn_recv.go631
1 files changed, 631 insertions, 0 deletions
diff --git a/src/vendor/golang.org/x/net/quic/conn_recv.go b/src/vendor/golang.org/x/net/quic/conn_recv.go
new file mode 100644
index 0000000000..2bf127a479
--- /dev/null
+++ b/src/vendor/golang.org/x/net/quic/conn_recv.go
@@ -0,0 +1,631 @@
+// 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.
+
+package quic
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "time"
+)
+
+func (c *Conn) handleDatagram(now time.Time, dgram *datagram) (handled bool) {
+ if !c.localAddr.IsValid() {
+ // We don't have any way to tell in the general case what address we're
+ // sending packets from. Set our address from the destination address of
+ // the first packet received from the peer.
+ c.localAddr = dgram.localAddr
+ }
+ if dgram.peerAddr.IsValid() && dgram.peerAddr != c.peerAddr {
+ if c.side == clientSide {
+ // "If a client receives packets from an unknown server address,
+ // the client MUST discard these packets."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-9-6
+ return false
+ }
+ // We currently don't support connection migration,
+ // so for now the server also drops packets from an unknown address.
+ return false
+ }
+ buf := dgram.b
+ c.loss.datagramReceived(now, len(buf))
+ if c.isDraining() {
+ return false
+ }
+ for len(buf) > 0 {
+ var n int
+ ptype := getPacketType(buf)
+ switch ptype {
+ case packetTypeInitial:
+ if c.side == serverSide && len(dgram.b) < paddedInitialDatagramSize {
+ // Discard client-sent Initial packets in too-short datagrams.
+ // https://www.rfc-editor.org/rfc/rfc9000#section-14.1-4
+ return false
+ }
+ n = c.handleLongHeader(now, dgram, ptype, initialSpace, c.keysInitial.r, buf)
+ case packetTypeHandshake:
+ n = c.handleLongHeader(now, dgram, ptype, handshakeSpace, c.keysHandshake.r, buf)
+ case packetType1RTT:
+ n = c.handle1RTT(now, dgram, buf)
+ case packetTypeRetry:
+ c.handleRetry(now, buf)
+ return true
+ case packetTypeVersionNegotiation:
+ c.handleVersionNegotiation(now, buf)
+ return true
+ default:
+ n = -1
+ }
+ if n <= 0 {
+ // We don't expect to get a stateless reset with a valid
+ // destination connection ID, since the sender of a stateless
+ // reset doesn't know what the connection ID is.
+ //
+ // We're required to perform this check anyway.
+ //
+ // "[...] the comparison MUST be performed when the first packet
+ // in an incoming datagram [...] cannot be decrypted."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-2
+ if len(buf) == len(dgram.b) && len(buf) > statelessResetTokenLen {
+ var token statelessResetToken
+ copy(token[:], buf[len(buf)-len(token):])
+ if c.handleStatelessReset(now, token) {
+ return true
+ }
+ }
+ // Invalid data at the end of a datagram is ignored.
+ return false
+ }
+ c.idleHandlePacketReceived(now)
+ buf = buf[n:]
+ }
+ return true
+}
+
+func (c *Conn) handleLongHeader(now time.Time, dgram *datagram, ptype packetType, space numberSpace, k fixedKeys, buf []byte) int {
+ if !k.isSet() {
+ return skipLongHeaderPacket(buf)
+ }
+
+ pnumMax := c.acks[space].largestSeen()
+ p, n := parseLongHeaderPacket(buf, k, pnumMax)
+ if n < 0 {
+ return -1
+ }
+ if buf[0]&reservedLongBits != 0 {
+ // Reserved header bits must be 0.
+ // https://www.rfc-editor.org/rfc/rfc9000#section-17.2-8.2.1
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "reserved header bits are not zero",
+ })
+ return -1
+ }
+ if p.version != quicVersion1 {
+ // The peer has changed versions on us mid-handshake?
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "protocol version changed during handshake",
+ })
+ return -1
+ }
+
+ if !c.acks[space].shouldProcess(p.num) {
+ return n
+ }
+
+ if logPackets {
+ logInboundLongPacket(c, p)
+ }
+ if c.logEnabled(QLogLevelPacket) {
+ c.logLongPacketReceived(p, buf[:n])
+ }
+ c.connIDState.handlePacket(c, p.ptype, p.srcConnID)
+ ackEliciting := c.handleFrames(now, dgram, ptype, space, p.payload)
+ c.acks[space].receive(now, space, p.num, ackEliciting, dgram.ecn)
+ if p.ptype == packetTypeHandshake && c.side == serverSide {
+ c.loss.validateClientAddress()
+
+ // "[...] a server MUST discard Initial keys when it first successfully
+ // processes a Handshake packet [...]"
+ // https://www.rfc-editor.org/rfc/rfc9001#section-4.9.1-2
+ c.discardKeys(now, initialSpace)
+ }
+ return n
+}
+
+func (c *Conn) handle1RTT(now time.Time, dgram *datagram, buf []byte) int {
+ if !c.keysAppData.canRead() {
+ // 1-RTT packets extend to the end of the datagram,
+ // so skip the remainder of the datagram if we can't parse this.
+ return len(buf)
+ }
+
+ pnumMax := c.acks[appDataSpace].largestSeen()
+ p, err := parse1RTTPacket(buf, &c.keysAppData, connIDLen, pnumMax)
+ if err != nil {
+ // A localTransportError terminates the connection.
+ // Other errors indicate an unparsable packet, but otherwise may be ignored.
+ if _, ok := err.(localTransportError); ok {
+ c.abort(now, err)
+ }
+ return -1
+ }
+ if buf[0]&reserved1RTTBits != 0 {
+ // Reserved header bits must be 0.
+ // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.8.1
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "reserved header bits are not zero",
+ })
+ return -1
+ }
+
+ if !c.acks[appDataSpace].shouldProcess(p.num) {
+ return len(buf)
+ }
+
+ if logPackets {
+ logInboundShortPacket(c, p)
+ }
+ if c.logEnabled(QLogLevelPacket) {
+ c.log1RTTPacketReceived(p, buf)
+ }
+ ackEliciting := c.handleFrames(now, dgram, packetType1RTT, appDataSpace, p.payload)
+ c.acks[appDataSpace].receive(now, appDataSpace, p.num, ackEliciting, dgram.ecn)
+ return len(buf)
+}
+
+func (c *Conn) handleRetry(now time.Time, pkt []byte) {
+ if c.side != clientSide {
+ return // clients don't send Retry packets
+ }
+ // "After the client has received and processed an Initial or Retry packet
+ // from the server, it MUST discard any subsequent Retry packets that it receives."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-1
+ if !c.keysInitial.canRead() {
+ return // discarded Initial keys, connection is already established
+ }
+ if c.acks[initialSpace].seen.numRanges() != 0 {
+ return // processed at least one packet
+ }
+ if c.retryToken != nil {
+ return // received a Retry already
+ }
+ // "Clients MUST discard Retry packets that have a Retry Integrity Tag
+ // that cannot be validated."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-2
+ p, ok := parseRetryPacket(pkt, c.connIDState.originalDstConnID)
+ if !ok {
+ return
+ }
+ // "A client MUST discard a Retry packet with a zero-length Retry Token field."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-2
+ if len(p.token) == 0 {
+ return
+ }
+ c.retryToken = cloneBytes(p.token)
+ c.connIDState.handleRetryPacket(p.srcConnID)
+ c.keysInitial = initialKeys(p.srcConnID, c.side)
+ // We need to resend any data we've already sent in Initial packets.
+ // We must not reuse already sent packet numbers.
+ c.loss.discardPackets(initialSpace, c.log, c.handleAckOrLoss)
+ // TODO: Discard 0-RTT packets as well, once we support 0-RTT.
+ if c.testHooks != nil {
+ c.testHooks.init(false)
+ }
+}
+
+var errVersionNegotiation = errors.New("server does not support QUIC version 1")
+
+func (c *Conn) handleVersionNegotiation(now time.Time, pkt []byte) {
+ if c.side != clientSide {
+ return // servers don't handle Version Negotiation packets
+ }
+ // "A client MUST discard any Version Negotiation packet if it has
+ // received and successfully processed any other packet [...]"
+ // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2
+ if !c.keysInitial.canRead() {
+ return // discarded Initial keys, connection is already established
+ }
+ if c.acks[initialSpace].seen.numRanges() != 0 {
+ return // processed at least one packet
+ }
+ _, srcConnID, versions := parseVersionNegotiation(pkt)
+ if len(c.connIDState.remote) < 1 || !bytes.Equal(c.connIDState.remote[0].cid, srcConnID) {
+ return // Source Connection ID doesn't match what we sent
+ }
+ for len(versions) >= 4 {
+ ver := binary.BigEndian.Uint32(versions)
+ if ver == 1 {
+ // "A client MUST discard a Version Negotiation packet that lists
+ // the QUIC version selected by the client."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2
+ return
+ }
+ versions = versions[4:]
+ }
+ // "A client that supports only this version of QUIC MUST
+ // abandon the current connection attempt if it receives
+ // a Version Negotiation packet, [with the two exceptions handled above]."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2
+ c.abortImmediately(now, errVersionNegotiation)
+}
+
+func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, space numberSpace, payload []byte) (ackEliciting bool) {
+ if len(payload) == 0 {
+ // "An endpoint MUST treat receipt of a packet containing no frames
+ // as a connection error of type PROTOCOL_VIOLATION."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-12.4-3
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "packet contains no frames",
+ })
+ return false
+ }
+ // frameOK verifies that ptype is one of the packets in mask.
+ frameOK := func(c *Conn, ptype, mask packetType) (ok bool) {
+ if ptype&mask == 0 {
+ // "An endpoint MUST treat receipt of a frame in a packet type
+ // that is not permitted as a connection error of type
+ // PROTOCOL_VIOLATION."
+ // https://www.rfc-editor.org/rfc/rfc9000#section-12.4-3
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "frame not allowed in packet",
+ })
+ return false
+ }
+ return true
+ }
+ // Packet masks from RFC 9000 Table 3.
+ // https://www.rfc-editor.org/rfc/rfc9000#table-3
+ const (
+ IH_1 = packetTypeInitial | packetTypeHandshake | packetType1RTT
+ __01 = packetType0RTT | packetType1RTT
+ ___1 = packetType1RTT
+ )
+ hasCrypto := false
+ for len(payload) > 0 {
+ switch payload[0] {
+ case frameTypePadding, frameTypeAck, frameTypeAckECN,
+ frameTypeConnectionCloseTransport, frameTypeConnectionCloseApplication:
+ default:
+ ackEliciting = true
+ }
+ n := -1
+ switch payload[0] {
+ case frameTypePadding:
+ // PADDING is OK in all spaces.
+ n = 1
+ case frameTypePing:
+ // PING is OK in all spaces.
+ //
+ // A PING frame causes us to respond with an ACK by virtue of being
+ // an ack-eliciting frame, but requires no other action.
+ n = 1
+ case frameTypeAck, frameTypeAckECN:
+ if !frameOK(c, ptype, IH_1) {
+ return
+ }
+ n = c.handleAckFrame(now, space, payload)
+ case frameTypeResetStream:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleResetStreamFrame(now, space, payload)
+ case frameTypeStopSending:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleStopSendingFrame(now, space, payload)
+ case frameTypeCrypto:
+ if !frameOK(c, ptype, IH_1) {
+ return
+ }
+ hasCrypto = true
+ n = c.handleCryptoFrame(now, space, payload)
+ case frameTypeNewToken:
+ if !frameOK(c, ptype, ___1) {
+ return
+ }
+ _, n = consumeNewTokenFrame(payload)
+ case 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f: // STREAM
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleStreamFrame(now, space, payload)
+ case frameTypeMaxData:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleMaxDataFrame(now, payload)
+ case frameTypeMaxStreamData:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleMaxStreamDataFrame(now, payload)
+ case frameTypeMaxStreamsBidi, frameTypeMaxStreamsUni:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleMaxStreamsFrame(now, payload)
+ case frameTypeDataBlocked:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ _, n = consumeDataBlockedFrame(payload)
+ case frameTypeStreamsBlockedBidi, frameTypeStreamsBlockedUni:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ _, _, n = consumeStreamsBlockedFrame(payload)
+ case frameTypeStreamDataBlocked:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ _, _, n = consumeStreamDataBlockedFrame(payload)
+ case frameTypeNewConnectionID:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleNewConnectionIDFrame(now, space, payload)
+ case frameTypeRetireConnectionID:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleRetireConnectionIDFrame(now, space, payload)
+ case frameTypePathChallenge:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handlePathChallengeFrame(now, dgram, space, payload)
+ case frameTypePathResponse:
+ if !frameOK(c, ptype, ___1) {
+ return
+ }
+ n = c.handlePathResponseFrame(now, space, payload)
+ case frameTypeConnectionCloseTransport:
+ // Transport CONNECTION_CLOSE is OK in all spaces.
+ n = c.handleConnectionCloseTransportFrame(now, payload)
+ case frameTypeConnectionCloseApplication:
+ if !frameOK(c, ptype, __01) {
+ return
+ }
+ n = c.handleConnectionCloseApplicationFrame(now, payload)
+ case frameTypeHandshakeDone:
+ if !frameOK(c, ptype, ___1) {
+ return
+ }
+ n = c.handleHandshakeDoneFrame(now, space, payload)
+ }
+ if n < 0 {
+ c.abort(now, localTransportError{
+ code: errFrameEncoding,
+ reason: "frame encoding error",
+ })
+ return false
+ }
+ payload = payload[n:]
+ }
+ if hasCrypto {
+ // Process TLS events after handling all frames in a packet.
+ // TLS events can cause us to drop state for a number space,
+ // so do that last, to avoid handling frames differently
+ // depending on whether they come before or after a CRYPTO frame.
+ if err := c.handleTLSEvents(now); err != nil {
+ c.abort(now, err)
+ }
+ }
+ return ackEliciting
+}
+
+func (c *Conn) handleAckFrame(now time.Time, space numberSpace, payload []byte) int {
+ c.loss.receiveAckStart()
+ largest, ackDelay, ecn, n := consumeAckFrame(payload, func(rangeIndex int, start, end packetNumber) {
+ if err := c.loss.receiveAckRange(now, space, rangeIndex, start, end, c.handleAckOrLoss); err != nil {
+ c.abort(now, err)
+ return
+ }
+ })
+ // TODO: Make use of ECN feedback.
+ // https://www.rfc-editor.org/rfc/rfc9000.html#section-19.3.2
+ _ = ecn
+ // Prior to receiving the peer's transport parameters, we cannot
+ // interpret the ACK Delay field because we don't know the ack_delay_exponent
+ // to apply.
+ //
+ // For servers, we should always know the ack_delay_exponent because the
+ // client's transport parameters are carried in its Initial packets and we
+ // won't send an ack-eliciting Initial packet until after receiving the last
+ // client Initial packet.
+ //
+ // For clients, we won't receive the server's transport parameters until handling
+ // its Handshake flight, which will probably happen after reading its ACK for our
+ // Initial packet(s). However, the peer's acknowledgement delay cannot reduce our
+ // adjusted RTT sample below min_rtt, and min_rtt is generally going to be set
+ // by the packet containing the ACK for our Initial flight. Therefore, the
+ // ACK Delay for an ACK in the Initial space is likely to be ignored anyway.
+ //
+ // Long story short, setting the delay to 0 prior to reading transport parameters
+ // is usually going to have no effect, will have only a minor effect in the rare
+ // cases when it happens, and there aren't any good alternatives anyway since we
+ // can't interpret the ACK Delay field without knowing the exponent.
+ var delay time.Duration
+ if c.peerAckDelayExponent >= 0 {
+ delay = ackDelay.Duration(uint8(c.peerAckDelayExponent))
+ }
+ c.loss.receiveAckEnd(now, c.log, space, delay, c.handleAckOrLoss)
+ if space == appDataSpace {
+ c.keysAppData.handleAckFor(largest)
+ }
+ return n
+}
+
+func (c *Conn) handleMaxDataFrame(now time.Time, payload []byte) int {
+ maxData, n := consumeMaxDataFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.streams.outflow.setMaxData(maxData)
+ return n
+}
+
+func (c *Conn) handleMaxStreamDataFrame(now time.Time, payload []byte) int {
+ id, maxStreamData, n := consumeMaxStreamDataFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if s := c.streamForFrame(now, id, sendStream); s != nil {
+ if err := s.handleMaxStreamData(maxStreamData); err != nil {
+ c.abort(now, err)
+ return -1
+ }
+ }
+ return n
+}
+
+func (c *Conn) handleMaxStreamsFrame(now time.Time, payload []byte) int {
+ styp, max, n := consumeMaxStreamsFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.streams.localLimit[styp].setMax(max)
+ return n
+}
+
+func (c *Conn) handleResetStreamFrame(now time.Time, space numberSpace, payload []byte) int {
+ id, code, finalSize, n := consumeResetStreamFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if s := c.streamForFrame(now, id, recvStream); s != nil {
+ if err := s.handleReset(code, finalSize); err != nil {
+ c.abort(now, err)
+ }
+ }
+ return n
+}
+
+func (c *Conn) handleStopSendingFrame(now time.Time, space numberSpace, payload []byte) int {
+ id, code, n := consumeStopSendingFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if s := c.streamForFrame(now, id, sendStream); s != nil {
+ if err := s.handleStopSending(code); err != nil {
+ c.abort(now, err)
+ }
+ }
+ return n
+}
+
+func (c *Conn) handleCryptoFrame(now time.Time, space numberSpace, payload []byte) int {
+ off, data, n := consumeCryptoFrame(payload)
+ err := c.handleCrypto(now, space, off, data)
+ if err != nil {
+ c.abort(now, err)
+ return -1
+ }
+ return n
+}
+
+func (c *Conn) handleStreamFrame(now time.Time, space numberSpace, payload []byte) int {
+ id, off, fin, b, n := consumeStreamFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if s := c.streamForFrame(now, id, recvStream); s != nil {
+ if err := s.handleData(off, b, fin); err != nil {
+ c.abort(now, err)
+ }
+ }
+ return n
+}
+
+func (c *Conn) handleNewConnectionIDFrame(now time.Time, space numberSpace, payload []byte) int {
+ seq, retire, connID, resetToken, n := consumeNewConnectionIDFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if err := c.connIDState.handleNewConnID(c, seq, retire, connID, resetToken); err != nil {
+ c.abort(now, err)
+ }
+ return n
+}
+
+func (c *Conn) handleRetireConnectionIDFrame(now time.Time, space numberSpace, payload []byte) int {
+ seq, n := consumeRetireConnectionIDFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ if err := c.connIDState.handleRetireConnID(c, seq); err != nil {
+ c.abort(now, err)
+ }
+ return n
+}
+
+func (c *Conn) handlePathChallengeFrame(now time.Time, dgram *datagram, space numberSpace, payload []byte) int {
+ data, n := consumePathChallengeFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.handlePathChallenge(now, dgram, data)
+ return n
+}
+
+func (c *Conn) handlePathResponseFrame(now time.Time, space numberSpace, payload []byte) int {
+ data, n := consumePathResponseFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.handlePathResponse(now, data)
+ return n
+}
+
+func (c *Conn) handleConnectionCloseTransportFrame(now time.Time, payload []byte) int {
+ code, _, reason, n := consumeConnectionCloseTransportFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.handlePeerConnectionClose(now, peerTransportError{code: code, reason: reason})
+ return n
+}
+
+func (c *Conn) handleConnectionCloseApplicationFrame(now time.Time, payload []byte) int {
+ code, reason, n := consumeConnectionCloseApplicationFrame(payload)
+ if n < 0 {
+ return -1
+ }
+ c.handlePeerConnectionClose(now, &ApplicationError{Code: code, Reason: reason})
+ return n
+}
+
+func (c *Conn) handleHandshakeDoneFrame(now time.Time, space numberSpace, payload []byte) int {
+ if c.side == serverSide {
+ // Clients should never send HANDSHAKE_DONE.
+ // https://www.rfc-editor.org/rfc/rfc9000#section-19.20-4
+ c.abort(now, localTransportError{
+ code: errProtocolViolation,
+ reason: "client sent HANDSHAKE_DONE",
+ })
+ return -1
+ }
+ if c.isAlive() {
+ c.confirmHandshake(now)
+ }
+ return 1
+}
+
+var errStatelessReset = errors.New("received stateless reset")
+
+func (c *Conn) handleStatelessReset(now time.Time, resetToken statelessResetToken) (valid bool) {
+ if !c.connIDState.isValidStatelessResetToken(resetToken) {
+ return false
+ }
+ c.setFinalError(errStatelessReset)
+ c.enterDraining(now)
+ return true
+}