rpc: avoid unnecessary RST_STREAM, PING frames sent by client (#33122)
Some checks are pending
/ Linux Build (push) Waiting to run
/ Linux Build (arm) (push) Waiting to run
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run
/ Docker Image (push) Waiting to run

Context from Cloudflare blog:
https://blog.cloudflare.com/go-and-enhance-your-calm/#reading-bodies-in-go-can-be-unintuitive

We were able to reproduce the same issue discussed by Cloudflare in
their recent blog post above using the `ethclient`.
This commit is contained in:
Michael Kaplan 2025-11-11 09:54:36 -05:00 committed by GitHub
parent 7368b34a4b
commit d8f9801305
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 12 additions and 4 deletions

View file

@ -168,13 +168,21 @@ func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc {
}
}
// cleanlyCloseBody avoids sending unnecessary RST_STREAM and PING frames by
// ensuring the whole body is read before being closed.
// See https://blog.cloudflare.com/go-and-enhance-your-calm/#reading-bodies-in-go-can-be-unintuitive
func cleanlyCloseBody(body io.ReadCloser) error {
io.Copy(io.Discard, body)
return body.Close()
}
func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error {
hc := c.writeConn.(*httpConn)
respBody, err := hc.doRequest(ctx, msg)
if err != nil {
return err
}
defer respBody.Close()
defer cleanlyCloseBody(respBody)
var resp jsonrpcMessage
batch := [1]*jsonrpcMessage{&resp}
@ -191,7 +199,7 @@ func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonr
if err != nil {
return err
}
defer respBody.Close()
defer cleanlyCloseBody(respBody)
var respmsgs []*jsonrpcMessage
if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil {
@ -236,7 +244,7 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
if _, err := buf.ReadFrom(resp.Body); err == nil {
body = buf.Bytes()
}
resp.Body.Close()
cleanlyCloseBody(resp.Body)
return nil, HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,

View file

@ -106,7 +106,7 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body
if err != nil {
t.Fatalf("request failed: %v", err)
}
resp.Body.Close()
cleanlyCloseBody(resp.Body)
confirmStatusCode(t, resp.StatusCode, expectedStatusCode)
}