mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
Now that the x/net module requires Go 1.25.0, the go1.25 build constraint is always satisfied. Simplify the code accordingly. Change-Id: I3d6fe4a132a26918455489b998730b494f5273c4 Reviewed-on: https://go-review.googlesource.com/c/net/+/744800 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Nicholas Husin <nsh@golang.org> Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
237 lines
7.8 KiB
Go
237 lines
7.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.
|
|
|
|
package quic
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"testing"
|
|
"testing/synctest"
|
|
"time"
|
|
)
|
|
|
|
func TestHandshakeTimeoutExpiresServer(t *testing.T) {
|
|
synctest.Test(t, testHandshakeTimeoutExpiresServer)
|
|
}
|
|
func testHandshakeTimeoutExpiresServer(t *testing.T) {
|
|
const timeout = 5 * time.Second
|
|
tc := newTestConn(t, serverSide, func(c *Config) {
|
|
c.HandshakeTimeout = timeout
|
|
})
|
|
tc.ignoreFrame(frameTypeAck)
|
|
tc.ignoreFrame(frameTypeNewConnectionID)
|
|
tc.writeFrames(packetTypeInitial,
|
|
debugFrameCrypto{
|
|
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
|
|
})
|
|
// Server starts its end of the handshake.
|
|
// Client acks these packets to avoid starting the PTO timer.
|
|
tc.wantFrameType("server sends Initial CRYPTO flight",
|
|
packetTypeInitial, debugFrameCrypto{})
|
|
tc.writeAckForAll()
|
|
tc.wantFrameType("server sends Handshake CRYPTO flight",
|
|
packetTypeHandshake, debugFrameCrypto{})
|
|
tc.writeAckForAll()
|
|
|
|
if got, want := tc.timeUntilEvent(), timeout; got != want {
|
|
t.Errorf("connection timer = %v, want %v (handshake timeout)", got, want)
|
|
}
|
|
|
|
// Client sends a packet, but this does not extend the handshake timer.
|
|
time.Sleep(1 * time.Second)
|
|
tc.writeFrames(packetTypeHandshake, debugFrameCrypto{
|
|
data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake][:1], // partial data
|
|
})
|
|
tc.wantIdle("handshake is not complete")
|
|
|
|
time.Sleep(timeout - 1*time.Second)
|
|
tc.wantFrame("server closes connection after handshake timeout",
|
|
packetTypeHandshake, debugFrameConnectionCloseTransport{
|
|
code: errConnectionRefused,
|
|
})
|
|
}
|
|
|
|
func TestHandshakeTimeoutExpiresClient(t *testing.T) {
|
|
synctest.Test(t, testHandshakeTimeoutExpiresClient)
|
|
}
|
|
func testHandshakeTimeoutExpiresClient(t *testing.T) {
|
|
const timeout = 5 * time.Second
|
|
tc := newTestConn(t, clientSide, func(c *Config) {
|
|
c.HandshakeTimeout = timeout
|
|
})
|
|
tc.ignoreFrame(frameTypeAck)
|
|
tc.ignoreFrame(frameTypeNewConnectionID)
|
|
// Start the handshake.
|
|
// The client always sets a PTO timer until it gets an ack for a handshake packet
|
|
// or confirms the handshake, so proceed far enough through the handshake to
|
|
// let us not worry about PTO.
|
|
tc.wantFrameType("client sends Initial CRYPTO flight",
|
|
packetTypeInitial, debugFrameCrypto{})
|
|
tc.writeAckForAll()
|
|
tc.writeFrames(packetTypeInitial,
|
|
debugFrameCrypto{
|
|
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
|
|
})
|
|
tc.writeFrames(packetTypeHandshake,
|
|
debugFrameCrypto{
|
|
data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
|
|
})
|
|
tc.wantFrameType("client sends Handshake CRYPTO flight",
|
|
packetTypeHandshake, debugFrameCrypto{})
|
|
tc.writeAckForAll()
|
|
tc.wantIdle("client is waiting for end of handshake")
|
|
|
|
if got, want := tc.timeUntilEvent(), timeout; got != want {
|
|
t.Errorf("connection timer = %v, want %v (handshake timeout)", got, want)
|
|
}
|
|
time.Sleep(timeout)
|
|
tc.wantFrame("client closes connection after handshake timeout",
|
|
packetTypeHandshake, debugFrameConnectionCloseTransport{
|
|
code: errConnectionRefused,
|
|
})
|
|
}
|
|
|
|
func TestIdleTimeoutExpires(t *testing.T) {
|
|
for _, test := range []struct {
|
|
localMaxIdleTimeout time.Duration
|
|
peerMaxIdleTimeout time.Duration
|
|
wantTimeout time.Duration
|
|
}{{
|
|
localMaxIdleTimeout: 10 * time.Second,
|
|
peerMaxIdleTimeout: 20 * time.Second,
|
|
wantTimeout: 10 * time.Second,
|
|
}, {
|
|
localMaxIdleTimeout: 20 * time.Second,
|
|
peerMaxIdleTimeout: 10 * time.Second,
|
|
wantTimeout: 10 * time.Second,
|
|
}, {
|
|
localMaxIdleTimeout: 0,
|
|
peerMaxIdleTimeout: 10 * time.Second,
|
|
wantTimeout: 10 * time.Second,
|
|
}, {
|
|
localMaxIdleTimeout: 10 * time.Second,
|
|
peerMaxIdleTimeout: 0,
|
|
wantTimeout: 10 * time.Second,
|
|
}} {
|
|
name := fmt.Sprintf("local=%v/peer=%v", test.localMaxIdleTimeout, test.peerMaxIdleTimeout)
|
|
synctestSubtest(t, name, func(t *testing.T) {
|
|
tc := newTestConn(t, serverSide, func(p *transportParameters) {
|
|
p.maxIdleTimeout = test.peerMaxIdleTimeout
|
|
}, func(c *Config) {
|
|
c.MaxIdleTimeout = test.localMaxIdleTimeout
|
|
})
|
|
tc.handshake()
|
|
if got, want := tc.timeUntilEvent(), test.wantTimeout; got != want {
|
|
t.Errorf("new conn timeout=%v, want %v (idle timeout)", got, want)
|
|
}
|
|
time.Sleep(test.wantTimeout - 1)
|
|
tc.wantIdle("connection is idle and alive prior to timeout")
|
|
ctx := canceledContext()
|
|
if err := tc.conn.Wait(ctx); err != context.Canceled {
|
|
t.Fatalf("conn.Wait() = %v, want Canceled", err)
|
|
}
|
|
time.Sleep(1)
|
|
tc.wantIdle("connection exits after timeout")
|
|
if err := tc.conn.Wait(ctx); err != errIdleTimeout {
|
|
t.Fatalf("conn.Wait() = %v, want errIdleTimeout", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIdleTimeoutKeepAlive(t *testing.T) {
|
|
for _, test := range []struct {
|
|
idleTimeout time.Duration
|
|
keepAlive time.Duration
|
|
wantTimeout time.Duration
|
|
}{{
|
|
idleTimeout: 30 * time.Second,
|
|
keepAlive: 10 * time.Second,
|
|
wantTimeout: 10 * time.Second,
|
|
}, {
|
|
idleTimeout: 10 * time.Second,
|
|
keepAlive: 30 * time.Second,
|
|
wantTimeout: 5 * time.Second,
|
|
}, {
|
|
idleTimeout: -1, // disabled
|
|
keepAlive: 30 * time.Second,
|
|
wantTimeout: 30 * time.Second,
|
|
}} {
|
|
name := fmt.Sprintf("idle_timeout=%v/keepalive=%v", test.idleTimeout, test.keepAlive)
|
|
synctestSubtest(t, name, func(t *testing.T) {
|
|
tc := newTestConn(t, serverSide, func(c *Config) {
|
|
c.MaxIdleTimeout = test.idleTimeout
|
|
c.KeepAlivePeriod = test.keepAlive
|
|
})
|
|
tc.handshake()
|
|
if got, want := tc.timeUntilEvent(), test.wantTimeout; got != want {
|
|
t.Errorf("new conn timeout=%v, want %v (keepalive timeout)", got, want)
|
|
}
|
|
time.Sleep(test.wantTimeout - 1)
|
|
tc.wantIdle("connection is idle prior to timeout")
|
|
time.Sleep(1)
|
|
tc.wantFrameType("keep-alive ping is sent", packetType1RTT,
|
|
debugFramePing{})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIdleLongTermKeepAliveSent(t *testing.T) {
|
|
synctest.Test(t, testIdleLongTermKeepAliveSent)
|
|
}
|
|
func testIdleLongTermKeepAliveSent(t *testing.T) {
|
|
// This test examines a connection sitting idle and sending periodic keep-alive pings.
|
|
const keepAlivePeriod = 30 * time.Second
|
|
tc := newTestConn(t, clientSide, func(c *Config) {
|
|
c.KeepAlivePeriod = keepAlivePeriod
|
|
c.MaxIdleTimeout = -1
|
|
})
|
|
tc.handshake()
|
|
// The handshake will have completed a little bit after the point at which the
|
|
// keepalive timer was set. Send two PING frames to the conn, triggering an immediate ack
|
|
// and resetting the timer.
|
|
tc.writeFrames(packetType1RTT, debugFramePing{})
|
|
tc.writeFrames(packetType1RTT, debugFramePing{})
|
|
tc.wantFrameType("conn acks received pings", packetType1RTT, debugFrameAck{})
|
|
for i := 0; i < 10; i++ {
|
|
tc.wantIdle("conn has nothing more to send")
|
|
if got, want := tc.timeUntilEvent(), keepAlivePeriod; got != want {
|
|
t.Errorf("i=%v conn timeout=%v, want %v (keepalive timeout)", i, got, want)
|
|
}
|
|
time.Sleep(keepAlivePeriod)
|
|
tc.wantFrameType("keep-alive ping is sent", packetType1RTT,
|
|
debugFramePing{})
|
|
tc.writeAckForAll()
|
|
}
|
|
}
|
|
|
|
func TestIdleLongTermKeepAliveReceived(t *testing.T) {
|
|
synctest.Test(t, testIdleLongTermKeepAliveReceived)
|
|
}
|
|
func testIdleLongTermKeepAliveReceived(t *testing.T) {
|
|
// This test examines a connection sitting idle, but receiving periodic peer
|
|
// traffic to keep the connection alive.
|
|
const idleTimeout = 30 * time.Second
|
|
tc := newTestConn(t, serverSide, func(c *Config) {
|
|
c.MaxIdleTimeout = idleTimeout
|
|
})
|
|
tc.handshake()
|
|
for i := 0; i < 10; i++ {
|
|
time.Sleep(idleTimeout - 1*time.Second)
|
|
tc.writeFrames(packetType1RTT, debugFramePing{})
|
|
if got, want := tc.timeUntilEvent(), maxAckDelay-timerGranularity; got != want {
|
|
t.Errorf("i=%v conn timeout=%v, want %v (max_ack_delay)", i, got, want)
|
|
}
|
|
tc.advanceToTimer()
|
|
tc.wantFrameType("conn acks received ping", packetType1RTT, debugFrameAck{})
|
|
}
|
|
// Connection is still alive.
|
|
ctx := canceledContext()
|
|
if err := tc.conn.Wait(ctx); err != context.Canceled {
|
|
t.Fatalf("conn.Wait() = %v, want Canceled", err)
|
|
}
|
|
}
|