forked from coder/coder
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub.go
More file actions
101 lines (87 loc) · 2.43 KB
/
github.go
File metadata and controls
101 lines (87 loc) · 2.43 KB
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
package promoauth
import (
"net/http"
"strconv"
"time"
"golang.org/x/xerrors"
)
type rateLimits struct {
Limit int
Remaining int
Used int
Reset time.Time
Resource string
}
// githubRateLimits returns rate limit information from a GitHub response.
// GitHub rate limits are on a per-user basis, and tracking each user as
// a prometheus label might be too much. So only track rate limits for
// unauthorized responses.
//
// Unauthorized responses have a much stricter rate limit of 60 per hour.
// Tracking this is vital to ensure we do not hit the limit.
func githubRateLimits(resp *http.Response, err error) (rateLimits, bool) {
if err != nil || resp == nil {
return rateLimits{}, false
}
// Only track 401 responses which indicates we are using the 60 per hour
// rate limit.
if resp.StatusCode != http.StatusUnauthorized {
return rateLimits{}, false
}
p := headerParser{header: resp.Header}
// See
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit
limits := rateLimits{
Limit: p.int("x-ratelimit-limit"),
Remaining: p.int("x-ratelimit-remaining"),
Used: p.int("x-ratelimit-used"),
Resource: p.string("x-ratelimit-resource") + "-unauthorized",
}
if limits.Limit == 0 &&
limits.Remaining == 0 &&
limits.Used == 0 {
// For some requests, github has no rate limit. In which case,
// it returns all 0s. We can just omit these.
return limits, false
}
// Reset is when the rate limit "used" will be reset to 0.
// If it's unix 0, then we do not know when it will reset.
// Change it to a zero time as that is easier to handle in golang.
unix := p.int("x-ratelimit-reset")
resetAt := time.Unix(int64(unix), 0)
if unix == 0 {
resetAt = time.Time{}
}
limits.Reset = resetAt
if len(p.errors) > 0 {
// If we are missing any headers, then do not try and guess
// what the rate limits are.
return limits, false
}
return limits, true
}
type headerParser struct {
errors map[string]error
header http.Header
}
func (p *headerParser) string(key string) string {
if p.errors == nil {
p.errors = make(map[string]error)
}
v := p.header.Get(key)
if v == "" {
p.errors[key] = xerrors.Errorf("missing header %q", key)
}
return v
}
func (p *headerParser) int(key string) int {
v := p.string(key)
if v == "" {
return -1
}
i, err := strconv.Atoi(v)
if err != nil {
p.errors[key] = err
}
return i
}