A Go library for simulating flaky HTTP server behavior, useful for testing HTTP client retry logic and error handling.
go get github.com/orian/flakyhttp- Simulate various HTTP connection failure scenarios
- Test client retry behavior with configurable failure counts
- Raw TCP-level connection drops for low-level testing
- Optional structured logging with
slog.Logger
import "github.com/posthog/eofail/flakyhttp"
// Create a server that closes connections without sending a response
server := flakyhttp.NewServer(flakyhttp.Config{
Scenario: flakyhttp.ScenarioNoResponse,
FailCount: 3, // Fail 3 times, then succeed
})
url, _ := server.Start()
defer server.Close()
// Make requests to url...| Scenario | Description | Client Error |
|---|---|---|
ScenarioNormal |
Normal successful response | None |
ScenarioIdleTimeout |
Closes idle keep-alive connections | EOF or connection reset on reused connection |
ScenarioMidRequestClose |
Closes while client sends request body | connection reset, broken pipe |
ScenarioHeadersOnlyClose |
Sends headers, closes before body | unexpected EOF when reading body |
ScenarioNoResponse |
Reads request, closes without response | EOF |
ScenarioStatusOnly |
Sends status line only, then closes | unexpected EOF |
flakyhttp.Config{
Scenario: flakyhttp.ScenarioNoResponse,
FailCount: 3, // Fail N times, then succeed (0 = always fail)
IdleCloseDelay: 100 * time.Millisecond, // For ScenarioIdleTimeout
PartialReadSize: 50, // For ScenarioMidRequestClose: bytes to read before closing
PartialBodySize: 10, // For ScenarioHeadersOnlyClose: bytes of body to send
Logger: slog.Default(), // Optional: enable logging
}For testing lower-level connection failures during request transmission:
server := flakyhttp.NewTCPServer(flakyhttp.TCPServerConfig{
Scenario: flakyhttp.TCPScenarioDropAfterHeaders,
FailCount: 3,
SendRST: true, // Send TCP RST instead of graceful FIN
})
url, _ := server.Start()
defer server.Close()| Scenario | Description |
|---|---|
TCPScenarioDropAfterConnect |
Drop immediately after TCP accept |
TCPScenarioDropAfterHeaders |
Drop after reading request headers |
TCPScenarioDropDuringBody |
Drop while reading request body |
TCPScenarioDropAfterPartialBody |
Drop after reading N bytes of body |
flakyhttp.TCPServerConfig{
Scenario: flakyhttp.TCPScenarioDropAfterPartialBody,
BytesToRead: 100, // Bytes to read before dropping
FailCount: 3, // Fail N times, then succeed (0 = always fail)
SendRST: true, // true = TCP RST, false = graceful FIN
Logger: slog.Default(), // Optional: enable logging
}func TestClientRetry(t *testing.T) {
// Server fails 3 times, then succeeds
server := flakyhttp.NewServer(flakyhttp.Config{
Scenario: flakyhttp.ScenarioNoResponse,
FailCount: 3,
})
url, err := server.Start()
require.NoError(t, err)
defer server.Close()
// Your client with retry logic
client := mypackage.NewClientWithRetry(url)
// Should succeed after retries
err = client.SendData(data)
assert.NoError(t, err)
// Verify server received multiple attempts
assert.Equal(t, 4, server.RequestCount()) // 3 failures + 1 success
assert.Equal(t, 1, server.SuccessCount())
}Both Server and TCPServer provide:
Start() (string, error)- Start server, returns URLClose() error- Stop serverURL() string- Get server URLRequestCount() int/ConnCount() int- Number of requests/connections receivedSuccessCount() int- Number of successful responses sent
MIT