fix: retry proxy requests on transient failures
This commit is contained in:
@@ -4,29 +4,54 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) ProxyStream(ctx context.Context, targetURL string, referer string, rangeHeader string) (int, http.Header, []byte, io.ReadCloser, error) {
|
func (s *Service) ProxyStream(ctx context.Context, targetURL string, referer string, rangeHeader string) (int, http.Header, []byte, io.ReadCloser, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
const maxRetries = 2
|
||||||
if err != nil {
|
const retryDelay = 500 * time.Millisecond
|
||||||
return 0, nil, nil, nil, fmt.Errorf("invalid upstream url: %w", err)
|
|
||||||
|
var lastErr error
|
||||||
|
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return 0, nil, nil, nil, ctx.Err()
|
||||||
|
case <-time.After(retryDelay):
|
||||||
|
}
|
||||||
|
log.Printf("retrying proxy request for %s (attempt %d/%d)", targetURL, attempt, maxRetries)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, nil, nil, fmt.Errorf("invalid upstream url: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if referer != "" {
|
||||||
|
req.Header.Set("Referer", referer)
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", defaultUserAgent)
|
||||||
|
if rangeHeader != "" {
|
||||||
|
req.Header.Set("Range", rangeHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.handleProxyResponse(ctx, resp, targetURL, referer, rangeHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
if referer != "" {
|
return 0, nil, nil, nil, fmt.Errorf("upstream request failed after %d retries: %w", maxRetries+1, lastErr)
|
||||||
req.Header.Set("Referer", referer)
|
}
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", defaultUserAgent)
|
|
||||||
if rangeHeader != "" {
|
|
||||||
req.Header.Set("Range", rangeHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.httpClient.Do(req)
|
func (s *Service) handleProxyResponse(ctx context.Context, resp *http.Response, targetURL string, referer string, rangeHeader string) (int, http.Header, []byte, io.ReadCloser, error) {
|
||||||
if err != nil {
|
|
||||||
return 0, nil, nil, nil, fmt.Errorf("upstream request failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isM3U8(targetURL, resp.Header.Get("Content-Type")) {
|
if isM3U8(targetURL, resp.Header.Get("Content-Type")) {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user