From b03e90fc47c008867bc965f0314441182beb558f Mon Sep 17 00:00:00 2001 From: mkelvers Date: Tue, 12 May 2026 16:05:36 +0200 Subject: [PATCH] fix: handle close errors --- api/playback/allanime_client.go | 4 ++-- api/playback/handler.go | 16 ++++++++++++---- api/playback/progress.go | 4 ++-- api/playback/provider_extractor.go | 4 ++-- api/playback/service_http.go | 2 +- api/playback/service_proxy.go | 2 +- api/playback/service_sources.go | 4 ++-- api/watchlist/service.go | 2 +- cmd/server/main.go | 2 +- cmd/user/main.go | 4 ++-- integrations/jikan/client.go | 11 +++-------- integrations/jikan/relations.go | 2 +- integrations/watchorder/watch_order.go | 4 ++-- internal/db/migrate.go | 2 +- pkg/net/utls/utls.go | 6 +++--- 15 files changed, 36 insertions(+), 33 deletions(-) diff --git a/api/playback/allanime_client.go b/api/playback/allanime_client.go index 87a2192..9cb173b 100644 --- a/api/playback/allanime_client.go +++ b/api/playback/allanime_client.go @@ -88,7 +88,7 @@ func (c *allAnimeClient) graphqlRequest(ctx context.Context, query string, varia if err != nil { 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)) if err != nil { @@ -143,7 +143,7 @@ func (c *allAnimeClient) graphqlRequestWithHash(ctx context.Context, showID, epi if err != nil { 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)) if err != nil { diff --git a/api/playback/handler.go b/api/playback/handler.go index 62aab1b..b07e2a1 100644 --- a/api/playback/handler.go +++ b/api/playback/handler.go @@ -243,7 +243,7 @@ func (h *Handler) HandleProxy(w http.ResponseWriter, r *http.Request) { w.WriteHeader(statusCode) if bodyReader != nil { - defer bodyReader.Close() + defer func() { _ = bodyReader.Close() }() _, _ = io.Copy(w, bodyReader) } else { _, _ = w.Write(content) @@ -389,7 +389,7 @@ func (h *Handler) HandleEpisodeData(w http.ResponseWriter, r *http.Request) { } 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, "title": watchData.Title, "current_episode": watchData.CurrentEpisode, @@ -400,7 +400,9 @@ func (h *Handler) HandleEpisodeData(w http.ResponseWriter, r *http.Request) { "mode_sources": watchData.ModeSources, "segments": watchData.Segments, "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. @@ -459,5 +461,11 @@ func (h *Handler) HandleEpisodeThumbnails(w http.ResponseWriter, r *http.Request } 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) } diff --git a/api/playback/progress.go b/api/playback/progress.go index 24ab631..99896ed 100644 --- a/api/playback/progress.go +++ b/api/playback/progress.go @@ -23,7 +23,7 @@ func (s *Service) SaveProgress(ctx context.Context, userID string, animeID int64 return err } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() if animeSeed != 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 } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() watchListEntry, watchListErr := txQueries.GetWatchListEntry(ctx, db.GetWatchListEntryParams{ UserID: userID, diff --git a/api/playback/provider_extractor.go b/api/playback/provider_extractor.go index 4b11a3b..0757c2c 100644 --- a/api/playback/provider_extractor.go +++ b/api/playback/provider_extractor.go @@ -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 if err != nil { @@ -155,7 +155,7 @@ func (e *providerExtractor) parseM3U8(ctx context.Context, masterURL string, ref if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024)) // 512KB limit if err != nil { diff --git a/api/playback/service_http.go b/api/playback/service_http.go index 5fd4f84..9eefec3 100644 --- a/api/playback/service_http.go +++ b/api/playback/service_http.go @@ -23,7 +23,7 @@ func (s *Service) fetchSkipSegments(ctx context.Context, malID int, episode stri if err != nil { return nil } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil diff --git a/api/playback/service_proxy.go b/api/playback/service_proxy.go index 9cbc14e..ea61f1c 100644 --- a/api/playback/service_proxy.go +++ b/api/playback/service_proxy.go @@ -59,7 +59,7 @@ func (s *Service) handleProxyResponse(ctx context.Context, resp *http.Response, // check if response is an m3u8 playlist that needs rewriting 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)) if readErr != nil { return 0, nil, nil, nil, fmt.Errorf("read playlist failed: %w", readErr) diff --git a/api/playback/service_sources.go b/api/playback/service_sources.go index c731aad..cefef71 100644 --- a/api/playback/service_sources.go +++ b/api/playback/service_sources.go @@ -140,7 +140,7 @@ func (s *Service) probeDirectMedia(ctx context.Context, source StreamSource) (bo if err != nil { return false, "" } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // check content-type header first 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 { return false } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode >= http.StatusBadRequest { return false diff --git a/api/watchlist/service.go b/api/watchlist/service.go index 3242739..b489c1c 100644 --- a/api/watchlist/service.go +++ b/api/watchlist/service.go @@ -171,7 +171,7 @@ func (s *Service) DeleteContinueWatching(ctx context.Context, userID string, ani if err != nil { return fmt.Errorf("failed to begin transaction: %w", err) } - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() if err := txQueries.DeleteContinueWatchingEntry(ctx, params); err != nil { return fmt.Errorf("failed to delete continue watching entry: %w", err) diff --git a/cmd/server/main.go b/cmd/server/main.go index 8040d73..9712041 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -26,7 +26,7 @@ func main() { if err != nil { log.Fatalf("failed to open db: %v", err) } - defer dbConn.Close() + defer func() { _ = dbConn.Close() }() queries, err := db.Init(dbConn) if err != nil { diff --git a/cmd/user/main.go b/cmd/user/main.go index cb3fb2d..8545262 100644 --- a/cmd/user/main.go +++ b/cmd/user/main.go @@ -18,7 +18,7 @@ func main() { if err != nil { 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" { updateAvatars(dbConn) @@ -83,7 +83,7 @@ func updateAvatars(dbConn *sql.DB) { if err != nil { log.Fatalf("failed to fetch users: %v", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() count := 0 for rows.Next() { diff --git a/integrations/jikan/client.go b/integrations/jikan/client.go index 4df586b..8a29210 100644 --- a/integrations/jikan/client.go +++ b/integrations/jikan/client.go @@ -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. func isEmptyResult(out any) bool { 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 { - resp.Body.Close() + _ = resp.Body.Close() delay := max(retryAfter, retryDelay(attempt)) 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) - resp.Body.Close() + _ = resp.Body.Close() if err == 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) - resp.Body.Close() + _ = resp.Body.Close() if err == nil { return nil } diff --git a/integrations/jikan/relations.go b/integrations/jikan/relations.go index 0bd2536..06a6a17 100644 --- a/integrations/jikan/relations.go +++ b/integrations/jikan/relations.go @@ -155,7 +155,7 @@ func (c *Client) GetFullRelations(ctx context.Context, id int) ([]RelationEntry, } go func() { - g.Wait() + _ = g.Wait() close(results) }() diff --git a/integrations/watchorder/watch_order.go b/integrations/watchorder/watch_order.go index 9a000e5..78679f3 100644 --- a/integrations/watchorder/watch_order.go +++ b/integrations/watchorder/watch_order.go @@ -106,7 +106,7 @@ func fetchDocument(ctx context.Context, httpClient *http.Client, url string) (*g if err != nil { return nil, fmt.Errorf("request failed: %w", err) } - defer response.Body.Close() + defer func() { _ = response.Body.Close() }() if response.StatusCode != http.StatusOK { // 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 { return "", fmt.Errorf("proxy request failed: %w", err) } - defer response.Body.Close() + defer func() { _ = response.Body.Close() }() if response.StatusCode != http.StatusOK { return "", fmt.Errorf("proxy status %d", response.StatusCode) diff --git a/internal/db/migrate.go b/internal/db/migrate.go index 32cec18..d954eaa 100644 --- a/internal/db/migrate.go +++ b/internal/db/migrate.go @@ -77,7 +77,7 @@ func loadAppliedMigrationNames(db *sql.DB) (map[string]struct{}, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() applied := make(map[string]struct{}) for rows.Next() { diff --git a/pkg/net/utls/utls.go b/pkg/net/utls/utls.go index c3903e4..3be4bc0 100644 --- a/pkg/net/utls/utls.go +++ b/pkg/net/utls/utls.go @@ -38,7 +38,7 @@ func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) }, utls.HelloFirefox_120) if err := uconn.HandshakeContext(req.Context()); err != nil { - uconn.Close() + _ = uconn.Close() 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{} cc, err := t.NewClientConn(uconn) if err != nil { - uconn.Close() + _ = uconn.Close() return nil, fmt.Errorf("http2 client conn: %w", err) } return cc.RoundTrip(req) @@ -55,7 +55,7 @@ func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) // Fallback to HTTP/1.1 if err := req.Write(uconn); err != nil { - uconn.Close() + _ = uconn.Close() return nil, fmt.Errorf("http1 write: %w", err) } return http.ReadResponse(bufio.NewReader(uconn), req)