fix: handle close errors
This commit is contained in:
@@ -88,7 +88,7 @@ func (c *allAnimeClient) graphqlRequest(ctx context.Context, query string, varia
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("execute graphql request: %w", err)
|
return nil, fmt.Errorf("execute graphql request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024))
|
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -143,7 +143,7 @@ func (c *allAnimeClient) graphqlRequestWithHash(ctx context.Context, showID, epi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("execute GET request: %w", err)
|
return nil, fmt.Errorf("execute GET request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1022))
|
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1022))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ func (h *Handler) HandleProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
|
|
||||||
if bodyReader != nil {
|
if bodyReader != nil {
|
||||||
defer bodyReader.Close()
|
defer func() { _ = bodyReader.Close() }()
|
||||||
_, _ = io.Copy(w, bodyReader)
|
_, _ = io.Copy(w, bodyReader)
|
||||||
} else {
|
} else {
|
||||||
_, _ = w.Write(content)
|
_, _ = w.Write(content)
|
||||||
@@ -389,7 +389,7 @@ func (h *Handler) HandleEpisodeData(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(map[string]any{
|
if err := writeJSON(w, map[string]any{
|
||||||
"mal_id": watchData.MalID,
|
"mal_id": watchData.MalID,
|
||||||
"title": watchData.Title,
|
"title": watchData.Title,
|
||||||
"current_episode": watchData.CurrentEpisode,
|
"current_episode": watchData.CurrentEpisode,
|
||||||
@@ -400,7 +400,9 @@ func (h *Handler) HandleEpisodeData(w http.ResponseWriter, r *http.Request) {
|
|||||||
"mode_sources": watchData.ModeSources,
|
"mode_sources": watchData.ModeSources,
|
||||||
"segments": watchData.Segments,
|
"segments": watchData.Segments,
|
||||||
"episode_title": "", // Find episode title if possible
|
"episode_title": "", // Find episode title if possible
|
||||||
})
|
}); err != nil {
|
||||||
|
log.Printf("watch page encode error: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleEpisodeThumbnails returns episode list for the thumbnail strip.
|
// HandleEpisodeThumbnails returns episode list for the thumbnail strip.
|
||||||
@@ -459,5 +461,11 @@ func (h *Handler) HandleEpisodeThumbnails(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(results)
|
if err := writeJSON(w, results); err != nil {
|
||||||
|
log.Printf("thumbnails encode error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJSON(w http.ResponseWriter, v any) error {
|
||||||
|
return json.NewEncoder(w).Encode(v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (s *Service) SaveProgress(ctx context.Context, userID string, animeID int64
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer tx.Rollback()
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
if animeSeed != nil {
|
if animeSeed != nil {
|
||||||
if _, err := txQueries.UpsertAnime(ctx, *animeSeed); err != nil {
|
if _, err := txQueries.UpsertAnime(ctx, *animeSeed); err != nil {
|
||||||
@@ -89,7 +89,7 @@ func (s *Service) CompleteAnime(ctx context.Context, userID string, animeID int6
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer tx.Rollback()
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
watchListEntry, watchListErr := txQueries.GetWatchListEntry(ctx, db.GetWatchListEntryParams{
|
watchListEntry, watchListErr := txQueries.GetWatchListEntry(ctx, db.GetWatchListEntryParams{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (e *providerExtractor) ExtractVideoLinks(ctx context.Context, providerPath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024)) // 2MB limit
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024)) // 2MB limit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -155,7 +155,7 @@ func (e *providerExtractor) parseM3U8(ctx context.Context, masterURL string, ref
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024)) // 512KB limit
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024)) // 512KB limit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (s *Service) fetchSkipSegments(ctx context.Context, malID int, episode stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (s *Service) handleProxyResponse(ctx context.Context, resp *http.Response,
|
|||||||
|
|
||||||
// check if response is an m3u8 playlist that needs rewriting
|
// check if response is an m3u8 playlist that needs rewriting
|
||||||
if isM3U8(targetURL, resp.Header.Get("Content-Type")) {
|
if isM3U8(targetURL, resp.Header.Get("Content-Type")) {
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024))
|
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 2*1024*1024))
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
return 0, nil, nil, nil, fmt.Errorf("read playlist failed: %w", readErr)
|
return 0, nil, nil, nil, fmt.Errorf("read playlist failed: %w", readErr)
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func (s *Service) probeDirectMedia(ctx context.Context, source StreamSource) (bo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
// check content-type header first
|
// check content-type header first
|
||||||
contentType := strings.ToLower(resp.Header.Get("Content-Type"))
|
contentType := strings.ToLower(resp.Header.Get("Content-Type"))
|
||||||
@@ -192,7 +192,7 @@ func (s *Service) probeEmbedSource(ctx context.Context, source StreamSource) boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusBadRequest {
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func (s *Service) DeleteContinueWatching(ctx context.Context, userID string, ani
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
if err := txQueries.DeleteContinueWatchingEntry(ctx, params); err != nil {
|
if err := txQueries.DeleteContinueWatchingEntry(ctx, params); err != nil {
|
||||||
return fmt.Errorf("failed to delete continue watching entry: %w", err)
|
return fmt.Errorf("failed to delete continue watching entry: %w", err)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to open db: %v", err)
|
log.Fatalf("failed to open db: %v", err)
|
||||||
}
|
}
|
||||||
defer dbConn.Close()
|
defer func() { _ = dbConn.Close() }()
|
||||||
|
|
||||||
queries, err := db.Init(dbConn)
|
queries, err := db.Init(dbConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to open db: %v", err)
|
log.Fatalf("failed to open db: %v", err)
|
||||||
}
|
}
|
||||||
defer dbConn.Close()
|
defer func() { _ = dbConn.Close() }()
|
||||||
|
|
||||||
if len(os.Args) == 2 && os.Args[1] == "update-avatar" {
|
if len(os.Args) == 2 && os.Args[1] == "update-avatar" {
|
||||||
updateAvatars(dbConn)
|
updateAvatars(dbConn)
|
||||||
@@ -83,7 +83,7 @@ func updateAvatars(dbConn *sql.DB) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to fetch users: %v", err)
|
log.Fatalf("failed to fetch users: %v", err)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func() { _ = rows.Close() }()
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
|||||||
@@ -236,11 +236,6 @@ func (c *Client) setCache(parentCtx context.Context, key string, data any, ttl t
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type cacheResult struct {
|
|
||||||
data any
|
|
||||||
hasStale bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmptyResult detects if response contains no meaningful data.
|
// isEmptyResult detects if response contains no meaningful data.
|
||||||
func isEmptyResult(out any) bool {
|
func isEmptyResult(out any) bool {
|
||||||
switch v := out.(type) {
|
switch v := out.(type) {
|
||||||
@@ -336,7 +331,7 @@ func (c *Client) fetchWithRetry(ctx context.Context, urlStr string, out any) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if retryable && attempt < maxRetries-1 {
|
if retryable && attempt < maxRetries-1 {
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
delay := max(retryAfter, retryDelay(attempt))
|
delay := max(retryAfter, retryDelay(attempt))
|
||||||
|
|
||||||
if retryErr := waitForRetry(ctx, delay); retryErr != nil {
|
if retryErr := waitForRetry(ctx, delay); retryErr != nil {
|
||||||
@@ -347,7 +342,7 @@ func (c *Client) fetchWithRetry(ctx context.Context, urlStr string, out any) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(out)
|
err = json.NewDecoder(resp.Body).Decode(out)
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -356,7 +351,7 @@ func (c *Client) fetchWithRetry(ctx context.Context, urlStr string, out any) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(out)
|
err = json.NewDecoder(resp.Body).Decode(out)
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ func (c *Client) GetFullRelations(ctx context.Context, id int) ([]RelationEntry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
g.Wait()
|
_ = g.Wait()
|
||||||
close(results)
|
close(results)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func fetchDocument(ctx context.Context, httpClient *http.Client, url string) (*g
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("request failed: %w", err)
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer func() { _ = response.Body.Close() }()
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
if response.StatusCode != http.StatusOK {
|
||||||
// limit body read for error context; avoid reading large error pages
|
// limit body read for error context; avoid reading large error pages
|
||||||
@@ -235,7 +235,7 @@ func fetchProxyText(ctx context.Context, httpClient *http.Client, url string) (s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("proxy request failed: %w", err)
|
return "", fmt.Errorf("proxy request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer func() { _ = response.Body.Close() }()
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
if response.StatusCode != http.StatusOK {
|
||||||
return "", fmt.Errorf("proxy status %d", response.StatusCode)
|
return "", fmt.Errorf("proxy status %d", response.StatusCode)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func loadAppliedMigrationNames(db *sql.DB) (map[string]struct{}, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func() { _ = rows.Close() }()
|
||||||
|
|
||||||
applied := make(map[string]struct{})
|
applied := make(map[string]struct{})
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
|||||||
}, utls.HelloFirefox_120)
|
}, utls.HelloFirefox_120)
|
||||||
|
|
||||||
if err := uconn.HandshakeContext(req.Context()); err != nil {
|
if err := uconn.HandshakeContext(req.Context()); err != nil {
|
||||||
uconn.Close()
|
_ = uconn.Close()
|
||||||
return nil, fmt.Errorf("utls handshake: %w", err)
|
return nil, fmt.Errorf("utls handshake: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
|||||||
t := &http2.Transport{}
|
t := &http2.Transport{}
|
||||||
cc, err := t.NewClientConn(uconn)
|
cc, err := t.NewClientConn(uconn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
uconn.Close()
|
_ = uconn.Close()
|
||||||
return nil, fmt.Errorf("http2 client conn: %w", err)
|
return nil, fmt.Errorf("http2 client conn: %w", err)
|
||||||
}
|
}
|
||||||
return cc.RoundTrip(req)
|
return cc.RoundTrip(req)
|
||||||
@@ -55,7 +55,7 @@ func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
|||||||
|
|
||||||
// Fallback to HTTP/1.1
|
// Fallback to HTTP/1.1
|
||||||
if err := req.Write(uconn); err != nil {
|
if err := req.Write(uconn); err != nil {
|
||||||
uconn.Close()
|
_ = uconn.Close()
|
||||||
return nil, fmt.Errorf("http1 write: %w", err)
|
return nil, fmt.Errorf("http1 write: %w", err)
|
||||||
}
|
}
|
||||||
return http.ReadResponse(bufio.NewReader(uconn), req)
|
return http.ReadResponse(bufio.NewReader(uconn), req)
|
||||||
|
|||||||
Reference in New Issue
Block a user