in lib/limit.go [277:364]
func DraftRateLimit(h http.Header, window time.Duration) map[string]interface{} {
limit := h.Get("Rate-Limit-Limit")
remaining := h.Get("Rate-Limit-Remaining")
reset := h.Get("Rate-Limit-Reset")
if limit == "" || remaining == "" || reset == "" {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
}
}
rem, err := strconv.ParseFloat(remaining, 64)
if err != nil {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"error": err.Error(),
}
}
var (
per float64
resetTime time.Time
)
if d, err := strconv.ParseFloat(reset, 64); err == nil {
per = d
resetTime = time.Now().Add(time.Duration(d) * time.Second)
} else if t, err := time.Parse(http.TimeFormat, reset); err == nil {
per = time.Until(t).Seconds()
resetTime = t
} else if t, err := time.Parse(time.RFC1123, reset); err == nil {
per = time.Until(t).Seconds()
resetTime = t
} else {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"error": fmt.Sprintf("could not parse %q as number or timestamp", reset),
}
}
burst := 1
// Examine quota policies.
limFields := strings.Split(limit, ",")
quota, err := strconv.Atoi(limFields[0])
if err != nil {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"error": err.Error(),
}
}
win := window.Seconds()
for _, f := range limFields[1:] {
p := policy(strings.TrimSpace(f))
q, err := p.quota()
if err != nil {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"error": err.Error(),
}
}
if q > quota {
break
}
w, b, err := p.details(q)
if err != nil {
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"error": err.Error(),
}
}
if w >= 0 {
win = float64(w)
}
if b > 0 {
burst = b
}
}
return map[string]interface{}{
"headers": fmt.Sprintf("Rate-Limit-Limit=%q Rate-Limit-Remaining=%q Rate-Limit-Reset=%q",
limit, remaining, reset),
"rate": rate.Limit(rem / per),
"next": rate.Limit(float64(quota) / win),
"burst": burst,
"reset": resetTime.UTC(),
}
}