diff options
author | feistel <6742251-feistel@users.noreply.gitlab.com> | 2022-02-01 16:43:48 +0300 |
---|---|---|
committer | feistel <6742251-feistel@users.noreply.gitlab.com> | 2022-06-17 14:58:31 +0300 |
commit | d5f9712f65decb2a0fe2b09d126017243b8c03cc (patch) | |
tree | 27e528fdbd06047af9c48cf9c980370102001a36 | |
parent | badf997d1a195f75362dc75a268406b2aee10e68 (diff) |
Properly handle io.copy errors
Changelog: added
-rw-r--r-- | internal/feature/feature.go | 5 | ||||
-rw-r--r-- | internal/httprange/http_reader.go | 2 | ||||
-rw-r--r-- | internal/httprange/http_reader_test.go | 6 | ||||
-rw-r--r-- | internal/vfs/errors.go | 33 | ||||
-rw-r--r-- | internal/vfs/serving/serving.go | 21 | ||||
-rw-r--r-- | internal/vfs/zip/archive_test.go | 5 |
6 files changed, 64 insertions, 8 deletions
diff --git a/internal/feature/feature.go b/internal/feature/feature.go index c98fe85d..a012fc49 100644 --- a/internal/feature/feature.go +++ b/internal/feature/feature.go @@ -37,6 +37,11 @@ var RedirectsPlaceholders = Feature{ EnvVariable: "FF_ENABLE_PLACEHOLDERS", } +// HandleReadErrors reports vfs.ReadErrors to sentry and enable error handling +var HandleReadErrors = Feature{ + EnvVariable: "FF_HANDLE_READ_ERRORS", +} + // Enabled reads the environment variable responsible for the feature flag // if FF is disabled by default, the environment variable needs to be "true" to explicitly enable it // if FF is enabled by default, variable needs to be "false" to explicitly disable it diff --git a/internal/httprange/http_reader.go b/internal/httprange/http_reader.go index 8e85a6d3..fbad326d 100644 --- a/internal/httprange/http_reader.go +++ b/internal/httprange/http_reader.go @@ -190,7 +190,7 @@ func (r *Reader) Read(buf []byte) (int, error) { } if err := r.ensureResponse(); err != nil { - return 0, err + return 0, vfs.NewReadError(err) } n, err := r.res.Body.Read(buf) diff --git a/internal/httprange/http_reader_test.go b/internal/httprange/http_reader_test.go index 01edb2e5..55258b4e 100644 --- a/internal/httprange/http_reader_test.go +++ b/internal/httprange/http_reader_test.go @@ -7,6 +7,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "gitlab.com/gitlab-org/gitlab-pages/internal/vfs" ) func TestSeekAndRead(t *testing.T) { @@ -73,7 +75,7 @@ func TestSeekAndRead(t *testing.T) { readSize: testDataLen, seekOffset: int64(testDataLen), seekWhence: io.SeekStart, - expectedReadErr: ErrInvalidRange, + expectedReadErr: vfs.NewReadError(ErrInvalidRange), }, // io.SeekCurrent ... "read_all_from_seek_current": { @@ -170,7 +172,7 @@ func TestSeekAndRead(t *testing.T) { "invalid_range_reading_from_end": { readSize: testDataLen / 3, seekWhence: io.SeekEnd, - expectedReadErr: ErrInvalidRange, + expectedReadErr: vfs.NewReadError(ErrInvalidRange), }, } for name, tt := range tests { diff --git a/internal/vfs/errors.go b/internal/vfs/errors.go new file mode 100644 index 00000000..6fa2f29b --- /dev/null +++ b/internal/vfs/errors.go @@ -0,0 +1,33 @@ +package vfs + +import "fmt" + +const message = "An error occurred while trying to read vfs.File content" + +type ReadError struct { + wrapped error +} + +func NewReadError(wrapped error) *ReadError { + return &ReadError{ + wrapped: wrapped, + } +} + +func (r *ReadError) Error() string { + if r.wrapped == nil { + return message + } + + return fmt.Sprintf("%s: %s", message, r.wrapped.Error()) +} + +func (r *ReadError) Unwrap() error { + return r.wrapped +} + +func (r *ReadError) Is(target error) bool { + // nolint: errorlint // implementing type equality for errors.Is + _, ok := target.(*ReadError) + return ok +} diff --git a/internal/vfs/serving/serving.go b/internal/vfs/serving/serving.go index 4d8b7d90..c5a7d011 100644 --- a/internal/vfs/serving/serving.go +++ b/internal/vfs/serving/serving.go @@ -5,6 +5,7 @@ package serving import ( + "context" "errors" "io" "net/http" @@ -13,6 +14,7 @@ import ( "time" "gitlab.com/gitlab-org/gitlab-pages/internal/errortracking" + "gitlab.com/gitlab-org/gitlab-pages/internal/feature" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" "gitlab.com/gitlab-org/gitlab-pages/internal/logging" "gitlab.com/gitlab-org/gitlab-pages/internal/vfs" @@ -47,10 +49,23 @@ func serveContent(w http.ResponseWriter, r *http.Request, modtime time.Time, con return } - w.WriteHeader(code) + if r.Method == http.MethodHead { + w.WriteHeader(code) + return + } - if r.Method != http.MethodHead { - io.Copy(w, content) + if _, err := io.Copy(w, content); err != nil { + if errors.Is(err, &vfs.ReadError{}) { + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { + return + } + logging.LogRequest(r).WithField("handle_read_errors", feature.HandleReadErrors.Enabled()).WithError(err).Error("error reading content") + if feature.HandleReadErrors.Enabled() { + errortracking.CaptureErrWithReqAndStackTrace(err, r) + logging.LogRequest(r).WithError(err).Error("error reading content") + httperrors.Serve500(w) + } + } } } diff --git a/internal/vfs/zip/archive_test.go b/internal/vfs/zip/archive_test.go index 782c43b1..731f1608 100644 --- a/internal/vfs/zip/archive_test.go +++ b/internal/vfs/zip/archive_test.go @@ -23,6 +23,7 @@ import ( "gitlab.com/gitlab-org/gitlab-pages/internal/config" "gitlab.com/gitlab-org/gitlab-pages/internal/httprange" "gitlab.com/gitlab-org/gitlab-pages/internal/testhelpers" + "gitlab.com/gitlab-org/gitlab-pages/internal/vfs" ) var ( @@ -143,7 +144,7 @@ func TestOpenCached(t *testing.T) { filePath: "index.html", expectedRequests: 1, // we receive an error on `read` as `open` offset is already cached - expectedReadErr: httprange.ErrRangeRequestsNotSupported, + expectedReadErr: vfs.NewReadError(httprange.ErrRangeRequestsNotSupported), expectedArchiveStatus: archiveCorrupted, }, { @@ -161,7 +162,7 @@ func TestOpenCached(t *testing.T) { filePath: "subdir/hello.html", expectedRequests: 1, // we receive an error on `read` as `open` offset is already cached - expectedOpenErr: httprange.ErrRangeRequestsNotSupported, + expectedOpenErr: vfs.NewReadError(httprange.ErrRangeRequestsNotSupported), expectedArchiveStatus: archiveCorrupted, }, } |