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
24 changes: 24 additions & 0 deletions h1client.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,18 @@ func (c *h1Client) DoRequest(ctx context.Context, workerID int) (int, error) {
if ctx.Err() != nil {
return 0, ctx.Err()
}
// Close-after-response / churn guard (mirrors the write-error path
// above): a Connection:close server — or one closing mid-response under
// churn-close — yields a read EOF here. Reconnect so the NEXT request
// gets a fresh conn, and back off ONLY if the server is genuinely down.
// Without this, a close-after-one server (drogon collapsed to 0 req from
// exactly this) spins read-EOFs with no pacing and never recovers.
if reconnErr := hc.reconnect(); reconnErr != nil {
recordConnectError()
hc.backoff.sleep(ctx, nil)
return 0, fmt.Errorf("h1client: conn[%d] read status (reconnect failed): %w", connIdx, reconnErr)
}
hc.backoff.reset()
return 0, fmt.Errorf("h1client: conn[%d] read status: %w", connIdx, err)
}

Expand All @@ -267,6 +279,18 @@ func (c *h1Client) DoRequest(ctx context.Context, workerID int) (int, error) {
for {
line, err := hc.reader.ReadSlice('\n')
if err != nil {
if ctx.Err() != nil {
return 0, ctx.Err()
}
// Same close-after-response / churn guard as the status read:
// reconnect for the next request, back off only if the server is
// genuinely down.
if reconnErr := hc.reconnect(); reconnErr != nil {
recordConnectError()
hc.backoff.sleep(ctx, nil)
return 0, fmt.Errorf("h1client: conn[%d] read header (reconnect failed): %w", connIdx, reconnErr)
}
hc.backoff.reset()
return 0, fmt.Errorf("h1client: conn[%d] read header: %w", connIdx, err)
}
if len(line) <= 2 {
Expand Down
Loading