mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
fix(ws): give the LiveKit health check its own HTTP transport (#1356)
NewLiveKitProcess built its health-check http.Client without a Transport,
so it fell back to the process-wide http.DefaultTransport.
httptest.Server.Close calls CloseIdleConnections on http.DefaultTransport
by design ("assume most users of httptest.Server will be using the standard
transport, so help them out"), and ws is full of t.Parallel tests that each
defer srv.Close(). Any one of them finishing while a health check held a
pooled connection severed that request:
livekit_test.go:978: HealthCheck: livekit health check failed:
Get "http://127.0.0.1:41343": net/http: HTTP/1.x transport connection
broken: http: CloseIdleConnections called
That surfaced as an unrelated-looking CI failure on a TypeScript lint bump
(#1341). It is not purely a test artifact: in production the health check
also shared one connection pool with every other DefaultTransport user in
the server process.
Cloning DefaultTransport keeps its tuned defaults (proxy, dial and TLS
timeouts, HTTP/2) while giving the client a private pool.
Locked by TestHealthCheckClientOwnsItsTransport, which fails on the
unfixed constructor.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
@@ -193,6 +194,12 @@ func (p *LiveKitProcess) GenerateConfigForTest() (string, error) {
|
||||
return p.generateConfig()
|
||||
}
|
||||
|
||||
// HTTPTransportForTest exposes the health-check client's transport so tests can
|
||||
// assert it does not share http.DefaultTransport's connection pool.
|
||||
func (p *LiveKitProcess) HTTPTransportForTest() http.RoundTripper {
|
||||
return p.httpClient.Transport
|
||||
}
|
||||
|
||||
// SetProcessCmdForTest sets cmd to a non-nil value to simulate "already running".
|
||||
func (p *LiveKitProcess) SetProcessCmdForTest() {
|
||||
p.mu.Lock()
|
||||
|
||||
@@ -45,6 +45,10 @@ func NewLiveKitProcess(cfg *config.VoiceConfig, tlsCfg *config.TLSConfig, dataDi
|
||||
tlsCfg: tlsCfg,
|
||||
dataDir: dataDir,
|
||||
httpClient: &http.Client{
|
||||
// Own the transport rather than inheriting http.DefaultTransport:
|
||||
// its pool is shared process-wide, and httptest.Server.Close closes
|
||||
// its idle connections, which severs in-flight health checks.
|
||||
Transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
|
||||
@@ -982,6 +982,26 @@ func TestHealthCheck_Success(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// httptest.Server.Close calls CloseIdleConnections on the process-wide
|
||||
// http.DefaultTransport, so a health-check client that falls back to it can have
|
||||
// a pooled connection severed mid-request by any unrelated parallel test closing
|
||||
// its own server ("http: CloseIdleConnections called"). The client must own its
|
||||
// transport — in production that also keeps the health check off the connection
|
||||
// pool every other DefaultTransport user in the process shares.
|
||||
func TestHealthCheckClientOwnsItsTransport(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
proc := ws.NewLiveKitProcess(&config.VoiceConfig{}, &config.TLSConfig{}, t.TempDir())
|
||||
|
||||
tr := proc.HTTPTransportForTest()
|
||||
if tr == nil {
|
||||
t.Fatal("health-check client has no Transport, so it falls back to http.DefaultTransport")
|
||||
}
|
||||
if tr == http.DefaultTransport {
|
||||
t.Error("health-check client shares http.DefaultTransport's connection pool")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthCheck_ServerDown(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user