1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

feat: take Retry-After header into consideration for rate limited feeds

This commit is contained in:
Frédéric Guillot 2024-10-05 22:26:05 -07:00 committed by GitHub
parent e555e442fb
commit e1050e21b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 143 additions and 28 deletions

View file

@ -13,7 +13,9 @@ import (
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
"miniflux.app/v2/internal/locale"
)
@ -51,6 +53,26 @@ func (r *ResponseHandler) ETag() string {
return r.httpResponse.Header.Get("ETag")
}
func (r *ResponseHandler) ParseRetryDelay() int {
retryAfterHeaderValue := r.httpResponse.Header.Get("Retry-After")
if retryAfterHeaderValue != "" {
// First, try to parse as an integer (number of seconds)
if seconds, err := strconv.Atoi(retryAfterHeaderValue); err == nil {
return seconds
}
// If not an integer, try to parse as an HTTP-date
if t, err := time.Parse(time.RFC1123, retryAfterHeaderValue); err == nil {
return int(time.Until(t).Seconds())
}
}
return 0
}
func (r *ResponseHandler) IsRateLimited() bool {
return r.httpResponse.StatusCode == http.StatusTooManyRequests
}
func (r *ResponseHandler) IsModified(lastEtagValue, lastModifiedValue string) bool {
if r.httpResponse.StatusCode == http.StatusNotModified {
return false

View file

@ -6,6 +6,7 @@ package fetcher // import "miniflux.app/v2/internal/reader/fetcher"
import (
"net/http"
"testing"
"time"
)
func TestIsModified(t *testing.T) {
@ -67,3 +68,37 @@ func TestIsModified(t *testing.T) {
})
}
}
func TestRetryDelay(t *testing.T) {
var testCases = map[string]struct {
RetryAfterHeader string
ExpectedDelay int
}{
"Empty header": {
RetryAfterHeader: "",
ExpectedDelay: 0,
},
"Integer value": {
RetryAfterHeader: "42",
ExpectedDelay: 42,
},
"HTTP-date": {
RetryAfterHeader: time.Now().Add(42 * time.Second).Format(time.RFC1123),
ExpectedDelay: 41,
},
}
for name, tc := range testCases {
t.Run(name, func(tt *testing.T) {
header := http.Header{}
header.Add("Retry-After", tc.RetryAfterHeader)
rh := ResponseHandler{
httpResponse: &http.Response{
Header: header,
},
}
if tc.ExpectedDelay != rh.ParseRetryDelay() {
tt.Errorf("Expected %d, got %d for scenario %q", tc.ExpectedDelay, rh.ParseRetryDelay(), name)
}
})
}
}