Skip to content
This repository was archived by the owner on Nov 5, 2022. It is now read-only.

Commit c11c3e7

Browse files
authored
Merge pull request #20 from Fallenstedt/0.3.2
0.3.2
2 parents 7d90444 + 39bfcf0 commit c11c3e7

17 files changed

+366
-233
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.1
1+
0.3.2

example/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ module github.com/fallenstedt/twitter-stream/example
22

33
replace github.com/fallenstedt/twitter-stream => /Users/alex/Projects/twitter-stream/
44

5-
go 1.15
5+
go 1.16
66

77
require github.com/fallenstedt/twitter-stream v0.2.1

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/fallenstedt/twitter-stream
22

3-
go 1.15
3+
go 1.16

http_client.go

Lines changed: 0 additions & 159 deletions
This file was deleted.

http_client_mock.go

Lines changed: 0 additions & 36 deletions
This file was deleted.

httpclient/http_client_mock.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package httpclient
2+
3+
import "net/http"
4+
5+
type mockHttpClient struct {
6+
token string
7+
MockNewHttpRequest func(opts *RequestOpts) (*http.Response, error)
8+
MockGetSearchStream func(queryParams string) (*http.Response, error)
9+
MockGetRules func() (*http.Response, error)
10+
MockAddRules func(queryParams string, body string) (*http.Response, error)
11+
MockGenerateUrl func(name string, queryParams string) (string, error)
12+
}
13+
14+
func NewHttpClientMock(token string) *mockHttpClient {
15+
return &mockHttpClient{token: token}
16+
}
17+
18+
func (t *mockHttpClient) GenerateUrl(name string, queryParams string) (string, error) {
19+
return t.MockGenerateUrl(name, queryParams)
20+
}
21+
22+
func (t *mockHttpClient) GetRules() (*http.Response, error) {
23+
return t.MockGetRules()
24+
}
25+
26+
func (t *mockHttpClient) AddRules(queryParams string, body string) (*http.Response, error) {
27+
return t.MockAddRules(queryParams, body)
28+
}
29+
30+
func (t *mockHttpClient) GetSearchStream(queryParams string) (*http.Response, error) {
31+
return t.MockGetSearchStream(queryParams)
32+
}
33+
34+
func (t *mockHttpClient) NewHttpRequest(opts *RequestOpts) (*http.Response, error) {
35+
return t.MockNewHttpRequest(opts)
36+
}

httpclient/http_response_parser.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package httpclient
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"math"
9+
"net/http"
10+
"time"
11+
)
12+
13+
// httpResponseParser is a struct that will retry network requests if the response has a status code of 429.
14+
type httpResponseParser struct{}
15+
16+
func (h httpResponseParser) handleResponse(resp *http.Response, opts *RequestOpts, fn func(opts *RequestOpts) (*http.Response, error)) (*http.Response, error) {
17+
// Retry with backoff if 429
18+
if resp.StatusCode == 429 {
19+
log.Printf("Retrying network request %s with backoff", opts.Url)
20+
21+
delay := h.getBackOffTime(opts.Retries)
22+
log.Printf("Sleeping for %v seconds", delay)
23+
time.Sleep(delay)
24+
25+
opts.Retries += 1
26+
27+
return fn(opts)
28+
}
29+
30+
// Reject if 400 or greater
31+
if resp.StatusCode >= 400 {
32+
log.Printf("Network Request at %s failed: %v", opts.Url, resp.StatusCode)
33+
34+
var msg string
35+
if resp.Body != nil {
36+
body, _ := ioutil.ReadAll(resp.Body)
37+
msg = "Network request failed: " + string(body)
38+
} else {
39+
msg = "Network request failed with status" + fmt.Sprint(resp.StatusCode)
40+
}
41+
42+
return nil, errors.New(msg)
43+
}
44+
45+
return resp, nil
46+
}
47+
48+
func (h httpResponseParser) getBackOffTime(retries uint8) time.Duration {
49+
exponentialBackoffCeilingSecs := 30
50+
delaySecs := int(math.Floor((math.Pow(2, float64(retries)) - 1) * 0.5))
51+
if delaySecs > exponentialBackoffCeilingSecs {
52+
delaySecs = 30
53+
}
54+
return time.Duration(delaySecs) * time.Second
55+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package httpclient
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
)
7+
8+
func givenHttpResponseParserInstance() *httpResponseParser {
9+
return new(httpResponseParser)
10+
}
11+
12+
func givenFakeHttpResponse(statusCode int) *http.Response {
13+
res := new(http.Response)
14+
res.StatusCode = statusCode
15+
return res
16+
}
17+
18+
func TestHandleResponseShouldReturnIf200(t *testing.T) {
19+
instance := givenHttpResponseParserInstance()
20+
opts := new(RequestOpts)
21+
resp := givenFakeHttpResponse(200)
22+
23+
result, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) {
24+
return nil, nil
25+
})
26+
27+
if err != nil {
28+
t.Errorf("Expected not error, got %v", err)
29+
}
30+
31+
if result.StatusCode != 200 {
32+
t.Errorf("Expected a status code of 200")
33+
}
34+
}
35+
36+
func TestHandleResponseShouldRetryRequestIf429(t *testing.T) {
37+
instance := givenHttpResponseParserInstance()
38+
opts := new(RequestOpts)
39+
resp := givenFakeHttpResponse(429)
40+
41+
result, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) {
42+
return givenFakeHttpResponse(200), nil
43+
})
44+
45+
if opts.Retries != 1 {
46+
t.Errorf("Expected atleast on retry attempt, got %v", opts.Retries)
47+
}
48+
49+
if err != nil {
50+
t.Errorf("Expected not error, got %v", err)
51+
}
52+
53+
if result.StatusCode != 200 {
54+
t.Errorf("Expected a status code of 200")
55+
}
56+
}
57+
58+
func TestHandleResponseShouldRejectIf400OrHigher(t *testing.T) {
59+
instance := givenHttpResponseParserInstance()
60+
opts := new(RequestOpts)
61+
resp := givenFakeHttpResponse(401)
62+
63+
_, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) {
64+
return nil, nil
65+
})
66+
67+
if err == nil {
68+
t.Errorf("Expected error, got nil")
69+
}
70+
}

0 commit comments

Comments
 (0)