1
1
package utils
2
2
3
3
import (
4
- "bytes"
5
- "fmt"
6
- "io"
7
- "io/ioutil"
8
4
"log"
9
5
"net/http"
6
+ "strconv"
10
7
"sync"
11
8
"time"
12
9
@@ -55,45 +52,37 @@ func (rlt *rateLimitTransport) RoundTrip(req *http.Request) (*http.Response, err
55
52
return resp , err
56
53
}
57
54
58
- // Make response body accessible for retries & debugging
59
- // (work around bug in GitHub SDK)
60
- // See https://github.com/google/go-github/pull/986
61
- r1 , r2 , err := drainBody (resp .Body )
62
- if err != nil {
63
- return nil , err
64
- }
65
- resp .Body = r1
66
- ghErr := github .CheckResponse (resp )
67
- resp .Body = r2
68
-
69
- // When you have been limited, use the Retry-After response header to slow down.
70
- if arlErr , ok := ghErr .(* github.AbuseRateLimitError ); ok {
55
+ if resp .Header .Get ("X-RateLimit-Remaining" ) == "0" {
71
56
rlt .delayNextRequest = false
72
- retryAfter := arlErr .GetRetryAfter ()
73
- log .Printf ("[DEBUG] Abuse detection mechanism triggered, sleeping for %s before retrying" ,
74
- retryAfter )
75
- time .Sleep (retryAfter )
76
- rlt .unlock (req )
77
- return rlt .RoundTrip (req )
78
- }
79
57
80
- if rlErr , ok := ghErr .(* github.RateLimitError ); ok {
81
- rlt .delayNextRequest = false
82
- retryAfter := rlErr .Rate .Reset .Sub (time .Now ())
58
+ var limit int
59
+ if limitHeader := resp .Header .Get ("X-RateLimit-Limit" ); limitHeader != "" {
60
+ limit , _ = strconv .Atoi (limitHeader )
61
+ }
83
62
84
- if retryAfter < 0 {
85
- fmt .Println ("what!" , rlErr .Rate .Reset , time .Now ())
63
+ var reset github.Timestamp
64
+ if resetHeader := resp .Header .Get ("X-RateLimit-Reset" ); resetHeader != "" {
65
+ if v , _ := strconv .ParseInt (resetHeader , 10 , 64 ); v != 0 {
66
+ reset = github.Timestamp {time .Unix (v , 0 )}
67
+ }
86
68
}
87
69
70
+ retryAfter := reset .Sub (time .Now ())
71
+
88
72
log .Printf ("[DEBUG] Rate limit %d reached, sleeping for %s before retrying" ,
89
- rlErr .Rate .Limit , retryAfter )
90
- time .Sleep (retryAfter )
73
+ limit , retryAfter )
74
+ if retryAfter < 0 {
75
+ log .Printf ("[WARN] retryAfter < 0. reset: %v | now: %v" ,
76
+ reset , time .Now ())
77
+ } else {
78
+ time .Sleep (retryAfter )
79
+ }
80
+
91
81
rlt .unlock (req )
92
82
return rlt .RoundTrip (req )
93
83
}
94
84
95
85
rlt .unlock (req )
96
-
97
86
return resp , nil
98
87
}
99
88
@@ -105,23 +94,6 @@ func (rlt *rateLimitTransport) unlock(req *http.Request) {
105
94
rlt .m .Unlock ()
106
95
}
107
96
108
- // drainBody reads all of b to memory and then returns two equivalent
109
- // ReadClosers yielding the same bytes.
110
- func drainBody (b io.ReadCloser ) (r1 , r2 io.ReadCloser , err error ) {
111
- if b == http .NoBody {
112
- // No copying needed. Preserve the magic sentinel meaning of NoBody.
113
- return http .NoBody , http .NoBody , nil
114
- }
115
- var buf bytes.Buffer
116
- if _ , err = buf .ReadFrom (b ); err != nil {
117
- return nil , b , err
118
- }
119
- if err = b .Close (); err != nil {
120
- return nil , b , err
121
- }
122
- return ioutil .NopCloser (& buf ), ioutil .NopCloser (bytes .NewReader (buf .Bytes ())), nil
123
- }
124
-
125
97
func isWriteMethod (method string ) bool {
126
98
switch method {
127
99
case "POST" , "PATCH" , "PUT" , "DELETE" :
0 commit comments