mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-28 12:19:57 +00:00
TUN-6724: Migrate to sentry-go from raven-go
This commit is contained in:
46
vendor/github.com/getsentry/sentry-go/internal/ratelimit/category.go
generated
vendored
Normal file
46
vendor/github.com/getsentry/sentry-go/internal/ratelimit/category.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Reference:
|
||||
// https://github.com/getsentry/relay/blob/0424a2e017d193a93918053c90cdae9472d164bf/relay-common/src/constants.rs#L116-L127
|
||||
|
||||
// Category classifies supported payload types that can be ingested by Sentry
|
||||
// and, therefore, rate limited.
|
||||
type Category string
|
||||
|
||||
// Known rate limit categories. As a special case, the CategoryAll applies to
|
||||
// all known payload types.
|
||||
const (
|
||||
CategoryAll Category = ""
|
||||
CategoryError Category = "error"
|
||||
CategoryTransaction Category = "transaction"
|
||||
)
|
||||
|
||||
// knownCategories is the set of currently known categories. Other categories
|
||||
// are ignored for the purpose of rate-limiting.
|
||||
var knownCategories = map[Category]struct{}{
|
||||
CategoryAll: {},
|
||||
CategoryError: {},
|
||||
CategoryTransaction: {},
|
||||
}
|
||||
|
||||
// String returns the category formatted for debugging.
|
||||
func (c Category) String() string {
|
||||
switch c {
|
||||
case "":
|
||||
return "CategoryAll"
|
||||
default:
|
||||
caser := cases.Title(language.English)
|
||||
rv := "Category"
|
||||
for _, w := range strings.Fields(string(c)) {
|
||||
rv += caser.String(w)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
}
|
22
vendor/github.com/getsentry/sentry-go/internal/ratelimit/deadline.go
generated
vendored
Normal file
22
vendor/github.com/getsentry/sentry-go/internal/ratelimit/deadline.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package ratelimit
|
||||
|
||||
import "time"
|
||||
|
||||
// A Deadline is a time instant when a rate limit expires.
|
||||
type Deadline time.Time
|
||||
|
||||
// After reports whether the deadline d is after other.
|
||||
func (d Deadline) After(other Deadline) bool {
|
||||
return time.Time(d).After(time.Time(other))
|
||||
}
|
||||
|
||||
// Equal reports whether d and e represent the same deadline.
|
||||
func (d Deadline) Equal(e Deadline) bool {
|
||||
return time.Time(d).Equal(time.Time(e))
|
||||
}
|
||||
|
||||
// String returns the deadline formatted for debugging.
|
||||
func (d Deadline) String() string {
|
||||
// Like time.Time.String, but without the monotonic clock reading.
|
||||
return time.Time(d).Round(0).String()
|
||||
}
|
3
vendor/github.com/getsentry/sentry-go/internal/ratelimit/doc.go
generated
vendored
Normal file
3
vendor/github.com/getsentry/sentry-go/internal/ratelimit/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package ratelimit provides tools to work with rate limits imposed by Sentry's
|
||||
// data ingestion pipeline.
|
||||
package ratelimit
|
64
vendor/github.com/getsentry/sentry-go/internal/ratelimit/map.go
generated
vendored
Normal file
64
vendor/github.com/getsentry/sentry-go/internal/ratelimit/map.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Map maps categories to rate limit deadlines.
|
||||
//
|
||||
// A rate limit is in effect for a given category if either the category's
|
||||
// deadline or the deadline for the special CategoryAll has not yet expired.
|
||||
//
|
||||
// Use IsRateLimited to check whether a category is rate-limited.
|
||||
type Map map[Category]Deadline
|
||||
|
||||
// IsRateLimited returns true if the category is currently rate limited.
|
||||
func (m Map) IsRateLimited(c Category) bool {
|
||||
return m.isRateLimited(c, time.Now())
|
||||
}
|
||||
|
||||
func (m Map) isRateLimited(c Category, now time.Time) bool {
|
||||
return m.Deadline(c).After(Deadline(now))
|
||||
}
|
||||
|
||||
// Deadline returns the deadline when the rate limit for the given category or
|
||||
// the special CategoryAll expire, whichever is furthest into the future.
|
||||
func (m Map) Deadline(c Category) Deadline {
|
||||
categoryDeadline := m[c]
|
||||
allDeadline := m[CategoryAll]
|
||||
if categoryDeadline.After(allDeadline) {
|
||||
return categoryDeadline
|
||||
}
|
||||
return allDeadline
|
||||
}
|
||||
|
||||
// Merge merges the other map into m.
|
||||
//
|
||||
// If a category appears in both maps, the deadline that is furthest into the
|
||||
// future is preserved.
|
||||
func (m Map) Merge(other Map) {
|
||||
for c, d := range other {
|
||||
if d.After(m[c]) {
|
||||
m[c] = d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FromResponse returns a rate limit map from an HTTP response.
|
||||
func FromResponse(r *http.Response) Map {
|
||||
return fromResponse(r, time.Now())
|
||||
}
|
||||
|
||||
func fromResponse(r *http.Response, now time.Time) Map {
|
||||
s := r.Header.Get("X-Sentry-Rate-Limits")
|
||||
if s != "" {
|
||||
return parseXSentryRateLimits(s, now)
|
||||
}
|
||||
if r.StatusCode == http.StatusTooManyRequests {
|
||||
s := r.Header.Get("Retry-After")
|
||||
deadline, _ := parseRetryAfter(s, now)
|
||||
return Map{CategoryAll: deadline}
|
||||
}
|
||||
return Map{}
|
||||
}
|
76
vendor/github.com/getsentry/sentry-go/internal/ratelimit/rate_limits.go
generated
vendored
Normal file
76
vendor/github.com/getsentry/sentry-go/internal/ratelimit/rate_limits.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errInvalidXSRLRetryAfter = errors.New("invalid retry-after value")
|
||||
|
||||
// parseXSentryRateLimits returns a RateLimits map by parsing an input string in
|
||||
// the format of the X-Sentry-Rate-Limits header.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// X-Sentry-Rate-Limits: 60:transaction, 2700:default;error;security
|
||||
//
|
||||
// This will rate limit transactions for the next 60 seconds and errors for the
|
||||
// next 2700 seconds.
|
||||
//
|
||||
// Limits for unknown categories are ignored.
|
||||
func parseXSentryRateLimits(s string, now time.Time) Map {
|
||||
// https://github.com/getsentry/relay/blob/0424a2e017d193a93918053c90cdae9472d164bf/relay-server/src/utils/rate_limits.rs#L44-L82
|
||||
m := make(Map, len(knownCategories))
|
||||
for _, limit := range strings.Split(s, ",") {
|
||||
limit = strings.TrimSpace(limit)
|
||||
if limit == "" {
|
||||
continue
|
||||
}
|
||||
components := strings.Split(limit, ":")
|
||||
if len(components) == 0 {
|
||||
continue
|
||||
}
|
||||
retryAfter, err := parseXSRLRetryAfter(strings.TrimSpace(components[0]), now)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
categories := ""
|
||||
if len(components) > 1 {
|
||||
categories = components[1]
|
||||
}
|
||||
for _, category := range strings.Split(categories, ";") {
|
||||
c := Category(strings.ToLower(strings.TrimSpace(category)))
|
||||
if _, ok := knownCategories[c]; !ok {
|
||||
// skip unknown categories, keep m small
|
||||
continue
|
||||
}
|
||||
// always keep the deadline furthest into the future
|
||||
if retryAfter.After(m[c]) {
|
||||
m[c] = retryAfter
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// parseXSRLRetryAfter parses a string into a retry-after rate limit deadline.
|
||||
//
|
||||
// Valid input is a number, possibly signed and possibly floating-point,
|
||||
// indicating the number of seconds to wait before sending another request.
|
||||
// Negative values are treated as zero. Fractional values are rounded to the
|
||||
// next integer.
|
||||
func parseXSRLRetryAfter(s string, now time.Time) (Deadline, error) {
|
||||
// https://github.com/getsentry/relay/blob/0424a2e017d193a93918053c90cdae9472d164bf/relay-quotas/src/rate_limit.rs#L88-L96
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return Deadline{}, errInvalidXSRLRetryAfter
|
||||
}
|
||||
d := time.Duration(math.Ceil(math.Max(f, 0.0))) * time.Second
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
return Deadline(now.Add(d)), nil
|
||||
}
|
40
vendor/github.com/getsentry/sentry-go/internal/ratelimit/retry_after.go
generated
vendored
Normal file
40
vendor/github.com/getsentry/sentry-go/internal/ratelimit/retry_after.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultRetryAfter = 1 * time.Minute
|
||||
|
||||
var errInvalidRetryAfter = errors.New("invalid input")
|
||||
|
||||
// parseRetryAfter parses a string s as in the standard Retry-After HTTP header
|
||||
// and returns a deadline until when requests are rate limited and therefore new
|
||||
// requests should not be sent. The input may be either a date or a non-negative
|
||||
// integer number of seconds.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
||||
//
|
||||
// parseRetryAfter always returns a usable deadline, even in case of an error.
|
||||
//
|
||||
// This is the original rate limiting mechanism used by Sentry, superseeded by
|
||||
// the X-Sentry-Rate-Limits response header.
|
||||
func parseRetryAfter(s string, now time.Time) (Deadline, error) {
|
||||
if s == "" {
|
||||
goto invalid
|
||||
}
|
||||
if n, err := strconv.Atoi(s); err == nil {
|
||||
if n < 0 {
|
||||
goto invalid
|
||||
}
|
||||
d := time.Duration(n) * time.Second
|
||||
return Deadline(now.Add(d)), nil
|
||||
}
|
||||
if date, err := time.Parse(time.RFC1123, s); err == nil {
|
||||
return Deadline(date), nil
|
||||
}
|
||||
invalid:
|
||||
return Deadline(now.Add(defaultRetryAfter)), errInvalidRetryAfter
|
||||
}
|
Reference in New Issue
Block a user