Skip to content

Commit 28830e1

Browse files
authored
fix: Input get request timeout issue (#212)
* refactor: Allow to pass request timeout to input fetcher * fix: Increase fetch timeout to 30 seconds
1 parent 26edee0 commit 28830e1

File tree

4 files changed

+53
-36
lines changed

4 files changed

+53
-36
lines changed

internal/command/command.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import (
55
"bytes"
66
"context"
77
"fmt"
8+
"net/http"
9+
"time"
810

911
"github.com/obalunenko/advent-of-code/internal/puzzles"
1012
"github.com/obalunenko/advent-of-code/internal/puzzles/input"
1113
)
1214

1315
// Run runs puzzle solving for passed year/day date.
1416
func Run(ctx context.Context, year, day string) (puzzles.Result, error) {
17+
const timeout = time.Second * 30
18+
19+
cli := input.NewFetcher(http.DefaultClient, timeout)
20+
21+
return run(ctx, cli, year, day)
22+
}
23+
24+
func run(ctx context.Context, cli input.Fetcher, year, day string) (puzzles.Result, error) {
1525
s, err := puzzles.GetSolver(year, day)
1626
if err != nil {
1727
return puzzles.Result{}, fmt.Errorf("failed to get solver: %w", err)
@@ -22,7 +32,7 @@ func Run(ctx context.Context, year, day string) (puzzles.Result, error) {
2232
return puzzles.Result{}, fmt.Errorf("failed to make full name: %w", err)
2333
}
2434

25-
asset, err := input.Get(ctx, input.Date{
35+
asset, err := cli.Fetch(ctx, input.Date{
2636
Year: year,
2737
Day: day,
2838
}, SessionFromContext(ctx))

internal/command/command_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package command_test
1+
package command
22

33
import (
44
"context"
@@ -7,10 +7,10 @@ import (
77
"net/http"
88
"strings"
99
"testing"
10+
"time"
1011

1112
"github.com/stretchr/testify/assert"
1213

13-
"github.com/obalunenko/advent-of-code/internal/command"
1414
"github.com/obalunenko/advent-of-code/internal/puzzles"
1515
"github.com/obalunenko/advent-of-code/internal/puzzles/input"
1616
)
@@ -182,9 +182,9 @@ func TestRun(t *testing.T) {
182182
tt := tests[i]
183183

184184
t.Run(tt.name, func(t *testing.T) {
185-
input.Client = newMockHTTPClient(tt.returnParams)
185+
cli := input.NewFetcher(newMockHTTPClient(tt.returnParams), time.Second*5)
186186

187-
got, err := command.Run(ctx, year, day)
187+
got, err := run(ctx, cli, year, day)
188188
if !tt.expected.wantErr(t, err) {
189189
return
190190
}

internal/puzzles/input/content.go

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,31 +32,46 @@ func (d Date) String() string {
3232
return path.Join(d.Year, d.Day)
3333
}
3434

35-
// ClientDo provides the interface for custom HTTP client implementations.
36-
type ClientDo interface {
35+
// IHTTPClient provides the interface for custom HTTP client implementations.
36+
type IHTTPClient interface {
3737
Do(*http.Request) (*http.Response, error)
3838
}
3939

40-
// Client is the default Client and is used by Get, Head, and Post.
41-
var Client ClientDo = http.DefaultClient
40+
// Fetcher is an input get client.
41+
type Fetcher interface {
42+
Fetch(ctx context.Context, d Date, session string) ([]byte, error)
43+
}
44+
45+
type client struct {
46+
cli IHTTPClient
47+
timeout time.Duration
48+
}
49+
50+
// NewFetcher constructor for Fetcher.
51+
func NewFetcher(c IHTTPClient, timeout time.Duration) Fetcher {
52+
return &client{
53+
cli: c,
54+
timeout: timeout,
55+
}
56+
}
4257

43-
// Get returns puzzle input.
44-
func Get(ctx context.Context, d Date, session string) ([]byte, error) {
58+
// Fetch returns puzzle input.
59+
func (c *client) Fetch(ctx context.Context, d Date, session string) ([]byte, error) {
4560
req, err := createInputReq(ctx, d, session)
4661
if err != nil {
4762
return nil, fmt.Errorf("create input request: %w", err)
4863
}
4964

50-
const (
51-
timeoutSecs = 5
52-
)
65+
var cancel context.CancelFunc
5366

54-
ctx, cancel := context.WithTimeout(ctx, time.Second*timeoutSecs)
55-
defer cancel()
67+
if c.timeout > 0 {
68+
ctx, cancel = context.WithTimeout(ctx, c.timeout)
69+
defer cancel()
70+
}
5671

5772
req = req.Clone(ctx)
5873

59-
resp, err := Client.Do(req)
74+
resp, err := c.cli.Do(req)
6075
if err != nil {
6176
return nil, fmt.Errorf("send request: %w", err)
6277
}
@@ -124,8 +139,5 @@ func createInputReq(ctx context.Context, d Date, sessionID string) (*http.Reques
124139
Unparsed: nil,
125140
})
126141

127-
req.Header.Set("User-Agent",
128-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36")
129-
130142
return req, nil
131143
}

internal/puzzles/input/content_test.go

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"testing"
1010
"testing/iotest"
11+
"time"
1112

1213
"github.com/stretchr/testify/assert"
1314

@@ -56,14 +57,8 @@ func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
5657
}
5758

5859
func TestGet(t *testing.T) {
59-
prevCli := input.Client
60-
61-
t.Cleanup(func() {
62-
input.Client = prevCli
63-
})
64-
6560
type client struct {
66-
input.ClientDo
61+
input.IHTTPClient
6762
}
6863

6964
type args struct {
@@ -82,7 +77,7 @@ func TestGet(t *testing.T) {
8277
{
8378
name: "",
8479
client: client{
85-
ClientDo: newMockHTTPClient(returnParams{
80+
IHTTPClient: newMockHTTPClient(returnParams{
8681
status: http.StatusOK,
8782
body: io.NopCloser(strings.NewReader("test")),
8883
}),
@@ -101,7 +96,7 @@ func TestGet(t *testing.T) {
10196
{
10297
name: "",
10398
client: client{
104-
ClientDo: newMockHTTPClient(returnParams{
99+
IHTTPClient: newMockHTTPClient(returnParams{
105100
status: http.StatusOK,
106101
body: io.NopCloser(strings.NewReader("")),
107102
}),
@@ -120,7 +115,7 @@ func TestGet(t *testing.T) {
120115
{
121116
name: "",
122117
client: client{
123-
ClientDo: newMockHTTPClient(returnParams{
118+
IHTTPClient: newMockHTTPClient(returnParams{
124119
status: http.StatusNotFound,
125120
body: http.NoBody,
126121
}),
@@ -139,7 +134,7 @@ func TestGet(t *testing.T) {
139134
{
140135
name: "",
141136
client: client{
142-
ClientDo: newMockHTTPClient(returnParams{
137+
IHTTPClient: newMockHTTPClient(returnParams{
143138
status: http.StatusBadRequest,
144139
body: io.NopCloser(strings.NewReader("no session")),
145140
}),
@@ -158,7 +153,7 @@ func TestGet(t *testing.T) {
158153
{
159154
name: "",
160155
client: client{
161-
ClientDo: newMockHTTPClient(returnParams{
156+
IHTTPClient: newMockHTTPClient(returnParams{
162157
status: http.StatusInternalServerError,
163158
body: io.NopCloser(strings.NewReader("no session")),
164159
}),
@@ -177,7 +172,7 @@ func TestGet(t *testing.T) {
177172
{
178173
name: "",
179174
client: client{
180-
ClientDo: &mockHTTPClient{
175+
IHTTPClient: &mockHTTPClient{
181176
MockDo: func(req *http.Request) (*http.Response, error) {
182177
return &http.Response{}, errors.New("error in test")
183178
},
@@ -197,7 +192,7 @@ func TestGet(t *testing.T) {
197192
{
198193
name: "",
199194
client: client{
200-
ClientDo: newMockHTTPClient(returnParams{
195+
IHTTPClient: newMockHTTPClient(returnParams{
201196
status: http.StatusOK,
202197
body: io.NopCloser(iotest.ErrReader(errors.New("custom error"))),
203198
}),
@@ -217,9 +212,9 @@ func TestGet(t *testing.T) {
217212

218213
for _, tt := range tests {
219214
t.Run(tt.name, func(t *testing.T) {
220-
input.Client = tt.client
215+
cli := input.NewFetcher(tt.client, time.Second*5)
221216

222-
got, err := input.Get(tt.args.ctx, tt.args.d, tt.args.session)
217+
got, err := cli.Fetch(tt.args.ctx, tt.args.d, tt.args.session)
223218
if !tt.wantErr(t, err) {
224219
return
225220
}

0 commit comments

Comments
 (0)