-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathhttp.go
165 lines (145 loc) · 3.99 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package smugmug
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"time"
log "github.com/sirupsen/logrus"
)
type header struct {
name string
value string
}
type handler struct {
baseUrl string
maxRetries int
oauth *oauthConf
}
func newHTTPHandler(baseUrl string, maxRetries int, apiKey, apiSecret, userToken, userSecret string) *handler {
return &handler{
baseUrl: baseUrl,
maxRetries: maxRetries,
oauth: newOauthConf(apiKey, apiSecret, userToken, userSecret),
}
}
// get calls getJSON with the given url
func (s *handler) get(url string, obj interface{}) error {
if url == "" {
return errors.New("can't get empty url")
}
return s.getJSON(fmt.Sprintf("%s%s", s.baseUrl, url), obj)
}
// download the resource (image or video) from the given url to the given destination, checking
// if a file with the same size exists (and skipping the download in that case, returning false)
func (s *handler) download(dest, downloadURL string, fileSize int64) (bool, error) {
if _, err := os.Stat(dest); err == nil {
if sameFileSizes(dest, fileSize) {
log.Debug("File exists with same size:", downloadURL)
return false, nil
}
}
log.Info("Getting ", downloadURL)
response, err := s.makeAPICall(downloadURL)
if err != nil {
return false, fmt.Errorf("%s: download failed with: %s", downloadURL, err)
}
defer response.Body.Close()
// Create empty destination file
file, err := os.Create(dest)
if err != nil {
return false, fmt.Errorf("%s: file creation failed with: %s", dest, err)
}
defer file.Close()
// Copy the content to the file
_, err = io.Copy(file, response.Body)
if err != nil {
return false, fmt.Errorf("%s: file content copy failed with: %s", dest, err)
}
log.Info("Saved ", dest)
return true, nil
}
// getJSON makes a http calls to the given url, trying to decode the JSON response on the given obj
func (s *handler) getJSON(url string, obj interface{}) error {
var result interface{}
for i := 1; i <= s.maxRetries; i++ {
log.Debug("Calling ", url)
resp, err := s.makeAPICall(url)
if err != nil {
return err
}
err = json.NewDecoder(resp.Body).Decode(&obj)
defer resp.Body.Close()
if err != nil {
log.Errorf("%s: reading response. %s", url, err)
if i >= s.maxRetries {
return err
}
} else {
obj = result
break
}
}
return nil
}
// makeAPICall performs an HTTP call to the given url, returning the response
func (s *handler) makeAPICall(url string) (*http.Response, error) {
client := &http.Client{}
var resp *http.Response
var errorsList []error
for i := 1; i <= s.maxRetries; i++ {
req, _ := http.NewRequest("GET", url, nil)
// Auth header must be generate every time (nonce must change)
h, err := s.oauth.authorizationHeader(url)
if err != nil {
panic(err)
}
headers := []header{
{name: "Accept", value: "application/json"},
{name: "Authorization", value: h},
}
log.Debug(headers)
addHeaders(req, headers)
r, err := client.Do(req)
if err != nil {
log.Debugf("#%d %s: %s\n", i, url, err)
errorsList = append(errorsList, err)
if i >= s.maxRetries {
for _, e := range errorsList {
log.Error(e)
}
return nil, errors.New("too many errors")
}
// Go on and try again after a little pause
time.Sleep(2 * time.Second)
continue
}
if r.StatusCode >= 400 {
errorsList = append(errorsList, errors.New(r.Status))
if i >= s.maxRetries {
for _, e := range errorsList {
log.Error(e)
}
return nil, errors.New("too many errors")
}
if r.StatusCode == 429 {
// Header Retry-After tells the number of seconds until the end of the current window
log.Error("Got 429 too many requests, let's try to wait 10 seconds...")
log.Errorf("Retry-After header: %s\n", r.Header.Get("Retry-After"))
time.Sleep(10 * time.Second)
}
continue
}
resp = r
break
}
return resp, nil
}
// addHeaders to the provided http request
func addHeaders(req *http.Request, headers []header) {
for _, h := range headers {
req.Header.Add(h.name, h.value)
}
}