Suppress benign broken pipe and context closed warnings (#2927)

This commit is contained in:
DingDongSoLong4 2022-09-19 07:01:40 +02:00 committed by GitHub
parent 5e97ecd260
commit 8efbcc1c4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 172 additions and 105 deletions

View file

@ -6,6 +6,7 @@ import (
"net/http"
"os/exec"
"strconv"
"syscall"
"github.com/go-chi/chi"
"github.com/stashapp/stash/internal/manager"
@ -84,11 +85,11 @@ func (rs imageRoutes) Thumbnail(w http.ResponseWriter, r *http.Request) {
if manager.GetInstance().Config.IsWriteImageThumbnails() {
logger.Debugf("writing thumbnail to disk: %s", img.Path)
if err := fsutil.WriteFile(filepath, data); err != nil {
logger.Errorf("error writing thumbnail for image %s: %s", img.Path, err)
logger.Errorf("error writing thumbnail for image %s: %v", img.Path, err)
}
}
if n, err := w.Write(data); err != nil {
logger.Errorf("error writing thumbnail response. Wrote %v bytes: %v", n, err)
if n, err := w.Write(data); err != nil && !errors.Is(err, syscall.EPIPE) {
logger.Errorf("error serving thumbnail (wrote %v bytes out of %v): %v", n, len(data), err)
}
}
}
@ -114,7 +115,7 @@ func (rs imageRoutes) ImageCtx(next http.Handler) http.Handler {
imageID, _ := strconv.Atoi(imageIdentifierQueryParam)
var image *models.Image
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
qb := rs.imageFinder
if imageID == 0 {
images, _ := qb.FindByChecksum(ctx, imageIdentifierQueryParam)
@ -131,10 +132,6 @@ func (rs imageRoutes) ImageCtx(next http.Handler) http.Handler {
return nil
})
if readTxnErr != nil {
logger.Warnf("read transaction failure while trying to read image by id: %v", readTxnErr)
}
if image == nil {
http.Error(w, http.StatusText(404), 404)
return

View file

@ -2,6 +2,7 @@ package api
import (
"context"
"errors"
"net/http"
"strconv"
@ -40,12 +41,15 @@ func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
defaultParam := r.URL.Query().Get("default")
var image []byte
if defaultParam != "true" {
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
image, _ = rs.movieFinder.GetFrontImage(ctx, movie.ID)
return nil
})
if err != nil {
logger.Warnf("read transaction error while getting front image: %v", err)
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch movie front image: %v", readTxnErr)
}
}
@ -54,7 +58,7 @@ func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
}
if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving front image: %v", err)
logger.Warnf("error serving movie front image: %v", err)
}
}
@ -63,12 +67,15 @@ func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
defaultParam := r.URL.Query().Get("default")
var image []byte
if defaultParam != "true" {
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
image, _ = rs.movieFinder.GetBackImage(ctx, movie.ID)
return nil
})
if err != nil {
logger.Warnf("read transaction error on fetch back image: %v", err)
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch movie back image: %v", readTxnErr)
}
}
@ -77,7 +84,7 @@ func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
}
if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error while serving image: %v", err)
logger.Warnf("error serving movie back image: %v", err)
}
}
@ -90,11 +97,11 @@ func (rs movieRoutes) MovieCtx(next http.Handler) http.Handler {
}
var movie *models.Movie
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
movie, err = rs.movieFinder.Find(ctx, movieID)
return err
}); err != nil {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
movie, _ = rs.movieFinder.Find(ctx, movieID)
return nil
})
if movie == nil {
http.Error(w, http.StatusText(404), 404)
return
}

View file

@ -2,6 +2,7 @@ package api
import (
"context"
"errors"
"net/http"
"strconv"
@ -44,8 +45,11 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
image, _ = rs.performerFinder.GetImage(ctx, performer.ID)
return nil
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("couldn't execute getting a performer image from read transaction: %v", readTxnErr)
logger.Warnf("read transaction error on fetch performer image: %v", readTxnErr)
}
}
@ -54,7 +58,7 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
}
if err := utils.ServeImage(image, w, r); err != nil {
logger.Warnf("error serving image: %v", err)
logger.Warnf("error serving performer image: %v", err)
}
}
@ -67,11 +71,12 @@ func (rs performerRoutes) PerformerCtx(next http.Handler) http.Handler {
}
var performer *models.Performer
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
performer, err = rs.performerFinder.Find(ctx, performerID)
return err
}); err != nil {
})
if performer == nil {
http.Error(w, http.StatusText(404), 404)
return
}

View file

@ -3,6 +3,7 @@ package api
import (
"bytes"
"context"
"errors"
"net/http"
"strconv"
"strings"
@ -253,12 +254,12 @@ func (rs sceneRoutes) Webp(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filepath)
}
func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.SceneMarker) string {
func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.SceneMarker) (*string, error) {
if marker.Title != "" {
return marker.Title
return &marker.Title, nil
}
var ret string
var title string
if err := txn.WithTxn(ctx, rs.txnManager, func(ctx context.Context) error {
qb := rs.tagFinder
primaryTag, err := qb.Find(ctx, marker.PrimaryTagID)
@ -266,7 +267,7 @@ func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.Sce
return err
}
ret = primaryTag.Name
title = primaryTag.Name
tags, err := qb.FindBySceneMarkerID(ctx, marker.ID)
if err != nil {
@ -274,26 +275,31 @@ func (rs sceneRoutes) getChapterVttTitle(ctx context.Context, marker *models.Sce
}
for _, t := range tags {
ret += ", " + t.Name
title += ", " + t.Name
}
return nil
}); err != nil {
panic(err)
return nil, err
}
return ret
return &title, nil
}
func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
scene := r.Context().Value(sceneKey).(*models.Scene)
var sceneMarkers []*models.SceneMarker
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
sceneMarkers, err = rs.sceneMarkerFinder.FindBySceneID(ctx, scene.ID)
return err
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch scene markers: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
@ -302,7 +308,18 @@ func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) {
vttLines = append(vttLines, strconv.Itoa(i+1))
time := utils.GetVTTTime(marker.Seconds)
vttLines = append(vttLines, time+" --> "+time)
vttLines = append(vttLines, rs.getChapterVttTitle(r.Context(), marker))
vttTitle, err := rs.getChapterVttTitle(r.Context(), marker)
if errors.Is(err, context.Canceled) {
return
}
if err != nil {
logger.Warnf("read transaction error on fetch scene marker title: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
vttLines = append(vttLines, *vttTitle)
vttLines = append(vttLines, "")
}
vtt := strings.Join(vttLines, "\n")
@ -327,35 +344,50 @@ func (rs sceneRoutes) InteractiveHeatmap(w http.ResponseWriter, r *http.Request)
func (rs sceneRoutes) Caption(w http.ResponseWriter, r *http.Request, lang string, ext string) {
s := r.Context().Value(sceneKey).(*models.Scene)
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var captions []*models.VideoCaption
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
primaryFile := s.Files.Primary()
if primaryFile == nil {
return nil
}
captions, err := rs.captionFinder.GetCaptions(ctx, primaryFile.Base().ID)
for _, caption := range captions {
if lang == caption.LanguageCode && ext == caption.CaptionType {
sub, err := video.ReadSubs(caption.Path(s.Path))
if err == nil {
var b bytes.Buffer
err = sub.WriteToWebVTT(&b)
if err == nil {
w.Header().Set("Content-Type", "text/vtt")
w.Header().Add("Cache-Control", "no-cache")
_, _ = b.WriteTo(w)
}
return err
}
logger.Debugf("Error while reading subs: %v", err)
}
}
captions, err = rs.captionFinder.GetCaptions(ctx, primaryFile.Base().ID)
return err
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch scene captions: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
for _, caption := range captions {
if lang != caption.LanguageCode || ext != caption.CaptionType {
return
}
sub, err := video.ReadSubs(caption.Path(s.Path))
if err != nil {
logger.Warnf("error while reading subs: %v", err)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
var b bytes.Buffer
err = sub.WriteToWebVTT(&b)
if err != nil {
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/vtt")
w.Header().Add("Cache-Control", "no-cache")
_, _ = b.WriteTo(w)
}
}
func (rs sceneRoutes) CaptionLang(w http.ResponseWriter, r *http.Request) {
@ -387,13 +419,17 @@ func (rs sceneRoutes) SceneMarkerStream(w http.ResponseWriter, r *http.Request)
scene := r.Context().Value(sceneKey).(*models.Scene)
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
var sceneMarker *models.SceneMarker
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
return err
}); err != nil {
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
http.Error(w, http.StatusText(500), 500)
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch scene marker: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
@ -410,13 +446,17 @@ func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request)
scene := r.Context().Value(sceneKey).(*models.Scene)
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
var sceneMarker *models.SceneMarker
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
return err
}); err != nil {
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
http.Error(w, http.StatusText(500), 500)
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch scene marker preview: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
@ -443,13 +483,17 @@ func (rs sceneRoutes) SceneMarkerScreenshot(w http.ResponseWriter, r *http.Reque
scene := r.Context().Value(sceneKey).(*models.Scene)
sceneMarkerID, _ := strconv.Atoi(chi.URLParam(r, "sceneMarkerId"))
var sceneMarker *models.SceneMarker
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
sceneMarker, err = rs.sceneMarkerFinder.Find(ctx, sceneMarkerID)
return err
}); err != nil {
logger.Warnf("Error when getting scene marker for stream: %s", err.Error())
http.Error(w, http.StatusText(500), 500)
})
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch scene marker screenshot: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
@ -480,7 +524,7 @@ func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam)
var scene *models.Scene
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
qb := rs.sceneFinder
if sceneID == 0 {
var scenes []*models.Scene
@ -505,10 +549,6 @@ func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
return nil
})
if readTxnErr != nil {
logger.Warnf("error executing SceneCtx transaction: %v", readTxnErr)
}
if scene == nil {
http.Error(w, http.StatusText(404), 404)
return

View file

@ -5,7 +5,6 @@ import (
"errors"
"net/http"
"strconv"
"syscall"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
@ -42,12 +41,15 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
var image []byte
if defaultParam != "true" {
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
image, _ = rs.studioFinder.GetImage(ctx, studio.ID)
return nil
})
if err != nil {
logger.Warnf("read transaction error while fetching studio image: %v", err)
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch studio image: %v", readTxnErr)
}
}
@ -56,12 +58,7 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
}
if err := utils.ServeImage(image, w, r); err != nil {
// Broken pipe errors are common when serving images and the remote
// connection closes the connection. Filter them out of the error
// messages, as they are benign.
if !errors.Is(err, syscall.EPIPE) {
logger.Warnf("cannot serve studio image: %v", err)
}
logger.Warnf("error serving studio image: %v", err)
}
}
@ -74,11 +71,12 @@ func (rs studioRoutes) StudioCtx(next http.Handler) http.Handler {
}
var studio *models.Studio
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
studio, err = rs.studioFinder.Find(ctx, studioID)
return err
}); err != nil {
})
if studio == nil {
http.Error(w, http.StatusText(404), 404)
return
}

View file

@ -2,6 +2,7 @@ package api
import (
"context"
"errors"
"net/http"
"strconv"
@ -40,12 +41,15 @@ func (rs tagRoutes) Image(w http.ResponseWriter, r *http.Request) {
var image []byte
if defaultParam != "true" {
err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
image, _ = rs.tagFinder.GetImage(ctx, tag.ID)
return nil
})
if err != nil {
logger.Warnf("read transaction error while getting tag image: %v", err)
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch tag image: %v", readTxnErr)
}
}
@ -67,11 +71,12 @@ func (rs tagRoutes) TagCtx(next http.Handler) http.Handler {
}
var tag *models.Tag
if err := txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
_ = txn.WithTxn(r.Context(), rs.txnManager, func(ctx context.Context) error {
var err error
tag, err = rs.tagFinder.Find(ctx, tagID)
return err
}); err != nil {
})
if tag == nil {
http.Error(w, http.StatusText(404), 404)
return
}

View file

@ -2,6 +2,7 @@ package manager
import (
"context"
"errors"
"net/http"
"github.com/stashapp/stash/internal/manager/config"
@ -81,16 +82,21 @@ func (s *SceneServer) ServeScreenshot(scene *models.Scene, w http.ResponseWriter
http.ServeFile(w, r, filepath)
} else {
var cover []byte
err := txn.WithTxn(r.Context(), s.TxnManager, func(ctx context.Context) error {
readTxnErr := txn.WithTxn(r.Context(), s.TxnManager, func(ctx context.Context) error {
cover, _ = s.SceneCoverGetter.GetCover(ctx, scene.ID)
return nil
})
if err != nil {
logger.Warnf("read transaction failed while serving screenshot: %v", err)
if errors.Is(readTxnErr, context.Canceled) {
return
}
if readTxnErr != nil {
logger.Warnf("read transaction error on fetch screenshot: %v", readTxnErr)
http.Error(w, readTxnErr.Error(), http.StatusInternalServerError)
return
}
if err = utils.ServeImage(cover, w, r); err != nil {
logger.Warnf("unable to serve screenshot image: %v", err)
if err := utils.ServeImage(cover, w, r); err != nil {
logger.Warnf("error serving screenshot image: %v", err)
}
}
}

View file

@ -2,10 +2,12 @@ package ffmpeg
import (
"context"
"errors"
"io"
"net/http"
"os/exec"
"strings"
"syscall"
"github.com/stashapp/stash/pkg/logger"
)
@ -35,8 +37,8 @@ func (s *Stream) Serve(w http.ResponseWriter, r *http.Request) {
// process killing should be handled by command context
_, err := io.Copy(w, s.Stdout)
if err != nil {
logger.Errorf("[stream] error serving transcoded video file: %s", err.Error())
if err != nil && !errors.Is(err, syscall.EPIPE) {
logger.Errorf("[stream] error serving transcoded video file: %v", err)
}
}

View file

@ -2,10 +2,12 @@ package file
import (
"context"
"errors"
"io"
"io/fs"
"net/http"
"strconv"
"syscall"
"time"
"github.com/stashapp/stash/pkg/logger"
@ -137,8 +139,9 @@ func (f *BaseFile) Serve(fs FS, w http.ResponseWriter, r *http.Request) {
return
}
if k, err := w.Write(data); err != nil {
logger.Warnf("failure while serving image (wrote %v bytes out of %v): %v", k, len(data), err)
k, err := w.Write(data)
if err != nil && !errors.Is(err, syscall.EPIPE) {
logger.Warnf("error serving file (wrote %v bytes out of %v): %v", k, len(data), err)
}
return

View file

@ -84,14 +84,10 @@ func FileExists(path string) (bool, error) {
func WriteFile(path string, file []byte) error {
pathErr := EnsureDirAll(filepath.Dir(path))
if pathErr != nil {
return fmt.Errorf("cannot ensure path %s", pathErr)
return fmt.Errorf("cannot ensure path exists: %w", pathErr)
}
err := os.WriteFile(path, file, 0755)
if err != nil {
return fmt.Errorf("write error for thumbnail %s: %s ", path, err)
}
return nil
return os.WriteFile(path, file, 0755)
}
// GetNameFromPath returns the name of a file from its path

View file

@ -5,11 +5,13 @@ import (
"crypto/md5"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
"net/http"
"regexp"
"strings"
"syscall"
"time"
)
@ -127,5 +129,11 @@ func ServeImage(image []byte, w http.ResponseWriter, r *http.Request) error {
w.Header().Add("Etag", etag)
w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
_, err := w.Write(image)
// Broken pipe errors are common when serving images and the remote
// connection closes the connection. Filter them out of the error
// messages, as they are benign.
if errors.Is(err, syscall.EPIPE) {
return nil
}
return err
}