Skip to content

Commit 5565231

Browse files
Avoid unnecessary RST_STREAM and PING frames causing GOAWAY messages (#4480)
Co-authored-by: Stephen Buttolph <[email protected]>
1 parent 779dd20 commit 5565231

File tree

6 files changed

+31
-18
lines changed

6 files changed

+31
-18
lines changed

api/metrics/client.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212

1313
"github.com/prometheus/common/expfmt"
1414

15+
"github.com/ava-labs/avalanchego/utils/rpc"
16+
1517
dto "github.com/prometheus/client_model/go"
1618
)
1719

@@ -45,24 +47,18 @@ func (c *Client) GetMetrics(ctx context.Context) (map[string]*dto.MetricFamily,
4547
return nil, fmt.Errorf("failed to create request: %w", err)
4648
}
4749

50+
//nolint:bodyclose // body is closed via rpc.CleanlyCloseBody
4851
resp, err := http.DefaultClient.Do(request)
4952
if err != nil {
5053
return nil, fmt.Errorf("failed to issue request: %w", err)
5154
}
55+
defer rpc.CleanlyCloseBody(resp.Body)
5256

5357
// Return an error for any non successful status code
5458
if resp.StatusCode < 200 || resp.StatusCode > 299 {
55-
// Drop any error during close to report the original error
56-
_ = resp.Body.Close()
5759
return nil, fmt.Errorf("received status code: %d", resp.StatusCode)
5860
}
5961

6062
var parser expfmt.TextParser
61-
metrics, err := parser.TextToMetricFamilies(resp.Body)
62-
if err != nil {
63-
// Drop any error during close to report the original error
64-
_ = resp.Body.Close()
65-
return nil, err
66-
}
67-
return metrics, resp.Body.Close()
63+
return parser.TextToMetricFamilies(resp.Body)
6864
}

tests/fixture/tmpnet/check_monitoring.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/ava-labs/avalanchego/tests/fixture/stacktrace"
2626
"github.com/ava-labs/avalanchego/utils/logging"
27+
"github.com/ava-labs/avalanchego/utils/rpc"
2728
)
2829

2930
type getCountFunc func() (int, error)
@@ -110,11 +111,12 @@ func queryLoki(
110111
req.Header.Set("Authorization", "Basic "+auth)
111112

112113
// Execute request
114+
//nolint:bodyclose // body is closed via rpc.CleanlyCloseBody
113115
resp, err := http.DefaultClient.Do(req)
114116
if err != nil {
115117
return 0, stacktrace.Errorf("failed to execute request: %w", err)
116118
}
117-
defer resp.Body.Close()
119+
defer rpc.CleanlyCloseBody(resp.Body)
118120

119121
// Read and parse response
120122
body, err := io.ReadAll(resp.Body)

tests/fixture/tmpnet/monitor_processes.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ava-labs/avalanchego/tests/fixture/stacktrace"
2525
"github.com/ava-labs/avalanchego/utils/logging"
2626
"github.com/ava-labs/avalanchego/utils/perms"
27+
"github.com/ava-labs/avalanchego/utils/rpc"
2728
)
2829

2930
const (
@@ -587,11 +588,12 @@ func checkReadiness(ctx context.Context, url string) (bool, string, error) {
587588
return false, "", stacktrace.Wrap(err)
588589
}
589590

591+
//nolint:bodyclose // body is closed via rpc.CleanlyCloseBody
590592
resp, err := http.DefaultClient.Do(req)
591593
if err != nil {
592594
return false, "", stacktrace.Errorf("request failed: %w", err)
593595
}
594-
defer resp.Body.Close()
596+
defer rpc.CleanlyCloseBody(resp.Body)
595597

596598
body, err := io.ReadAll(resp.Body)
597599
if err != nil {

tests/fixture/tmpnet/node.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ava-labs/avalanchego/tests/fixture/stacktrace"
2525
"github.com/ava-labs/avalanchego/utils/constants"
2626
"github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner"
27+
"github.com/ava-labs/avalanchego/utils/rpc"
2728
"github.com/ava-labs/avalanchego/vms/platformvm/signer"
2829
)
2930

@@ -220,11 +221,13 @@ func (n *Node) SaveMetricsSnapshot(ctx context.Context) error {
220221
if err != nil {
221222
return stacktrace.Wrap(err)
222223
}
224+
//nolint:bodyclose // body is closed via rpc.CleanlyCloseBody
223225
resp, err := http.DefaultClient.Do(req)
224226
if err != nil {
225227
return stacktrace.Wrap(err)
226228
}
227-
defer resp.Body.Close()
229+
defer rpc.CleanlyCloseBody(resp.Body)
230+
228231
body, err := io.ReadAll(resp.Body)
229232
if err != nil {
230233
return stacktrace.Wrap(err)

utils/dynamicip/ifconfig_resolver.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313

1414
"github.com/ava-labs/avalanchego/utils/ips"
15+
"github.com/ava-labs/avalanchego/utils/rpc"
1516
)
1617

1718
var _ Resolver = (*ifConfigResolver)(nil)
@@ -27,11 +28,12 @@ func (r *ifConfigResolver) Resolve(ctx context.Context) (netip.Addr, error) {
2728
return netip.Addr{}, err
2829
}
2930

31+
//nolint:bodyclose // body is closed via rpc.CleanlyCloseBody
3032
resp, err := http.DefaultClient.Do(req)
3133
if err != nil {
3234
return netip.Addr{}, err
3335
}
34-
defer resp.Body.Close()
36+
defer rpc.CleanlyCloseBody(resp.Body)
3537

3638
ipBytes, err := io.ReadAll(resp.Body)
3739
if err != nil {

utils/rpc/json.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ import (
77
"bytes"
88
"context"
99
"fmt"
10+
"io"
1011
"net/http"
1112
"net/url"
1213

1314
rpc "github.com/gorilla/rpc/v2/json2"
1415
)
1516

17+
// CleanlyCloseBody avoids sending unnecessary RST_STREAM and PING frames by
18+
// ensuring the whole body is read before being closed.
19+
// See https://blog.cloudflare.com/go-and-enhance-your-calm/#reading-bodies-in-go-can-be-unintuitive
20+
func CleanlyCloseBody(body io.ReadCloser) {
21+
_, _ = io.Copy(io.Discard, body)
22+
_ = body.Close()
23+
}
24+
1625
func SendJSONRequest(
1726
ctx context.Context,
1827
uri *url.URL,
@@ -42,22 +51,21 @@ func SendJSONRequest(
4251
request.Header = ops.headers
4352
request.Header.Set("Content-Type", "application/json")
4453

54+
//nolint:bodyclose // body is closed via CleanlyCloseBody
4555
resp, err := http.DefaultClient.Do(request)
4656
if err != nil {
4757
return fmt.Errorf("failed to issue request: %w", err)
4858
}
59+
defer CleanlyCloseBody(resp.Body)
4960

5061
// Return an error for any non successful status code
5162
if resp.StatusCode < 200 || resp.StatusCode > 299 {
52-
// Drop any error during close to report the original error
53-
_ = resp.Body.Close()
5463
return fmt.Errorf("received status code: %d", resp.StatusCode)
5564
}
5665

5766
if err := rpc.DecodeClientResponse(resp.Body, reply); err != nil {
58-
// Drop any error during close to report the original error
59-
_ = resp.Body.Close()
6067
return fmt.Errorf("failed to decode client response: %w", err)
6168
}
62-
return resp.Body.Close()
69+
70+
return nil
6371
}

0 commit comments

Comments
 (0)