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 { func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error {
hc := c.writeConn.(*httpConn) hc := c.writeConn.(*httpConn)
respBody, err := hc.doRequest(ctx, msg) respBody, err := hc.doRequest(ctx, msg)
if err != nil { if err != nil {
return err return err
} }
defer respBody.Close() defer cleanlyCloseBody(respBody)
var resp jsonrpcMessage var resp jsonrpcMessage
batch := [1]*jsonrpcMessage{&resp} batch := [1]*jsonrpcMessage{&resp}
@ -191,7 +199,7 @@ func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonr
if err != nil { if err != nil {
return err return err
} }
defer respBody.Close() defer cleanlyCloseBody(respBody)
var respmsgs []*jsonrpcMessage var respmsgs []*jsonrpcMessage
if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 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 { if _, err := buf.ReadFrom(resp.Body); err == nil {
body = buf.Bytes() body = buf.Bytes()
} }
resp.Body.Close() cleanlyCloseBody(resp.Body)
return nil, HTTPError{ return nil, HTTPError{
Status: resp.Status, Status: resp.Status,
StatusCode: resp.StatusCode, StatusCode: resp.StatusCode,

View file

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