Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions go/internal/cloudlink/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ type fakeService struct {
// that is stopping.
wrongShard string
restartBeforeOK bool
// beforeWebSocket, when set, runs before each WebSocket upgrade.
beforeWebSocket func()

wmu sync.Mutex
mu sync.Mutex
Expand Down Expand Up @@ -266,6 +268,12 @@ func (s *fakeService) write(conn uplinkConn, key *ecdsa.PrivateKey, home, t stri
}

func (s *fakeService) handle(w http.ResponseWriter, r *http.Request) {
s.mu.Lock()
hold := s.beforeWebSocket
s.mu.Unlock()
if hold != nil {
hold()
}
conn, err := (&websocket.Upgrader{}).Upgrade(w, r, nil)
if err != nil {
return
Expand Down
31 changes: 29 additions & 2 deletions go/internal/cloudlink/quic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,39 @@ func TestAQUICSessionThatEndsBeforeHelloOKDemotesQUIC(t *testing.T) {
return
}
refused.Add(1)
_ = qc.CloseWithError(0x110, "")
// Close once the box has opened its control stream: its dial
// has then succeeded, and the session ends before hello_ok. A
// close straight after the handshake can beat the end of the
// dial, which then fails as on a path that blocks UDP and
// demotes nothing.
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, _ = qc.AcceptStream(ctx)
_ = qc.CloseWithError(0x110, "")
}()
}
}()

// Under load the QUIC handshake can outlast cloudwire.RaceDelay, and the
// WebSocket would then win before QUIC was tried. Hold the WebSocket
// until QUIC has failed here, which puts it first on this network.
network := localNetwork(addr)
var link atomic.Pointer[Link]
svc.mu.Lock()
svc.beforeWebSocket = func() {
deadline := time.Now().Add(20 * time.Second)
for time.Now().Before(deadline) {
if l := link.Load(); l != nil && l.preferred(network) == cloudwire.TransportWebSocket {
return
}
time.Sleep(5 * time.Millisecond)
}
}
svc.mu.Unlock()

h := newQUICHarness(t, svc)
link.Store(h.link)
h.grant(t, protocol.ScopeLiveRead)
waitWithin(t, 20*time.Second, "the uplink up", func() bool { return h.link.Status().Connected })
if got := h.link.Status().Transport; got != cloudwire.TransportWebSocket {
Expand All @@ -472,7 +500,6 @@ func TestAQUICSessionThatEndsBeforeHelloOKDemotesQUIC(t *testing.T) {
if refused.Load() == 0 {
t.Fatal("QUIC was never tried")
}
network := localNetwork(addr)
if got := h.link.preferred(network); got != cloudwire.TransportWebSocket {
t.Fatalf("after QUIC failed, %q goes first", got)
}
Expand Down
Loading