avoid triggering macOS dual-stack flakiness in HTTP/3 integration tests (#5187)

Unfortunately, there’s still no fix in sight for https://github.com/golang/go/issues/67226.
This commit is contained in:
Marten Seemann
2025-05-31 16:40:20 +08:00
committed by GitHub
parent eb427b599e
commit 7ca7a973ef
4 changed files with 55 additions and 8 deletions

View File

@@ -368,6 +368,7 @@ func testHTTP3ListenerClosing(t *testing.T, graceful, useApplicationListener boo
tlsConf.NextProtos = []string{http3.NextProtoH3}
tr := &http3.Transport{TLSClientConfig: tlsConf}
defer tr.Close()
addDialCallback(t, tr)
cl := &http.Client{Transport: tr}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
require.NoError(t, err)
@@ -396,7 +397,7 @@ func testHTTP3ListenerClosing(t *testing.T, graceful, useApplicationListener boo
// the following values will be ignored when using ServeListener
TLSConfig: tlsConf,
QUICConfig: getQuicConfig(nil),
Addr: "127.0.0.1:47283",
Addr: "127.0.0.1:0",
}
serveChan := make(chan error, 1)
@@ -411,7 +412,17 @@ func testHTTP3ListenerClosing(t *testing.T, graceful, useApplicationListener boo
go func() { serveChan <- server.ServeListener(ln) }()
} else {
go func() { serveChan <- server.ListenAndServe() }()
host = server.Addr
// The server is listening on a random port, and the only way to get the port
// is to parse the Alt-Svc header.
var port int
require.Eventually(t, func() bool {
hdr := make(http.Header)
server.SetQUICHeaders(hdr)
altSvc := hdr.Get("Alt-Svc")
n, err := fmt.Sscanf(altSvc, `h3=":%d"`, &port)
return err == nil && n == 1
}, time.Second, 10*time.Millisecond)
host = fmt.Sprintf("127.0.0.1:%d", port)
}
u := &url.URL{Scheme: "https", Host: host, Path: "/ok"}
@@ -471,10 +482,7 @@ func testHTTP3ListenerClosing(t *testing.T, graceful, useApplicationListener boo
for range 2 {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
err := dial(t, ctx, u)
var h3Err *http3.Error
require.ErrorAs(t, err, &h3Err)
require.Equal(t, http3.ErrCode(1337), h3Err.ErrorCode)
require.ErrorIs(t, dial(t, ctx, u), &http3.Error{ErrorCode: 1337, Remote: true})
select {
case err := <-errChan:
require.NoError(t, err)

View File

@@ -85,6 +85,7 @@ func newHTTP3Client(t *testing.T) *http.Client {
QUICConfig: getQuicConfig(&quic.Config{MaxIdleTimeout: 10 * time.Second}),
DisableCompression: true,
}
addDialCallback(t, tr)
t.Cleanup(func() { tr.Close() })
return &http.Client{Transport: tr}
}
@@ -356,7 +357,13 @@ func TestHTTPDifferentOrigins(t *testing.T) {
})
port := startHTTPServer(t, mux)
cl := newHTTP3Client(t)
tr := &http3.Transport{
TLSClientConfig: getTLSClientConfigWithoutServerName(),
QUICConfig: getQuicConfig(nil),
}
t.Cleanup(func() { tr.Close() })
cl := &http.Client{Transport: tr}
resp, err := cl.Get(fmt.Sprintf("https://localhost:%d/remote-addr", port))
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
@@ -888,6 +895,7 @@ func TestHTTP0RTT(t *testing.T) {
DisableCompression: true,
}
defer tr.Close()
addDialCallback(t, tr)
proxyPort := proxy.LocalAddr().(*net.UDPAddr).Port
req, err := http.NewRequest(http3.MethodGet0RTT, fmt.Sprintf("https://localhost:%d/0rtt", proxyPort), nil)
@@ -912,6 +920,7 @@ func TestHTTP0RTT(t *testing.T) {
DisableCompression: true,
}
defer tr2.Close()
addDialCallback(t, tr2)
rsp, err = tr2.RoundTrip(req)
require.NoError(t, err)
require.Equal(t, 200, rsp.StatusCode)
@@ -949,6 +958,7 @@ func TestHTTPStreamer(t *testing.T) {
require.NoError(t, err)
defer conn.CloseWithError(0, "")
tr := http3.Transport{}
addDialCallback(t, &tr)
cc := tr.NewClientConn(conn)
str, err := cc.OpenRequestStream(ctx)
require.NoError(t, err)

View File

@@ -11,6 +11,7 @@ import (
"testing"
"time"
"github.com/quic-go/quic-go/http3"
"github.com/stretchr/testify/require"
)
@@ -62,7 +63,13 @@ func TestHTTPClientTrace(t *testing.T) {
}
ctx := httptrace.WithClientTrace(context.Background(), &trace)
cl := newHTTP3Client(t)
tr := &http3.Transport{
TLSClientConfig: getTLSClientConfigWithoutServerName(),
QUICConfig: getQuicConfig(nil),
}
t.Cleanup(func() { tr.Close() })
cl := &http.Client{Transport: tr}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/client-trace", port), nil)
require.NoError(t, err)
resp, err := cl.Do(req)

View File

@@ -10,11 +10,13 @@ import (
"math/rand/v2"
"net"
"os"
"runtime"
"strconv"
"testing"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/quic-go/quic-go/integrationtests/tools"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/wire"
@@ -298,3 +300,23 @@ func contains0RTTPacket(data []byte) bool {
}
return false
}
// addDialCallback explicitly adds the http3.Transport's Dial callback.
// This is needed since dialing on dual-stack sockets is flaky on macOS,
// see https://github.com/golang/go/issues/67226.
func addDialCallback(t *testing.T, tr *http3.Transport) {
t.Helper()
if runtime.GOOS != "darwin" {
return
}
require.Nil(t, tr.Dial)
tr.Dial = func(ctx context.Context, addr string, tlsConf *tls.Config, conf *quic.Config) (quic.EarlyConnection, error) {
a, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
return quic.DialEarly(ctx, newUDPConnLocalhost(t), a, tlsConf, conf)
}
}