Skip to content

Commit

Permalink
net/http: improve FileServer error logging
Browse files Browse the repository at this point in the history
Errors returned during copying the file content in ServeContent,
ServeFile and FileServer were ignored to avoid excessive, meaningless
logging. This commit introduces error filtering and logging to ensure
that the errors occurring during reading the content are logged.

Updates golang#27128
  • Loading branch information
hullarb committed Oct 30, 2019
1 parent 6c6ad30 commit df595d2
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/net/http/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var (
ExportErrRequestCanceledConn = errRequestCanceledConn
ExportErrServerClosedIdle = errServerClosedIdle
ExportServeFile = serveFile
ExportCopyNIgnoreWriteError = copyNIgnoreWriteError
ExportScanETag = scanETag
ExportHttp2ConfigureServer = http2ConfigureServer
Export_shouldCopyHeaderOnRedirect = shouldCopyHeaderOnRedirect
Expand Down
32 changes: 31 additions & 1 deletion src/net/http/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,38 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
w.WriteHeader(code)

if r.Method != "HEAD" {
io.CopyN(w, sendContent, sendSize)
if err := copyNIgnoreWriteError(w, sendContent, sendSize); err != nil {
n := name
if n == "" {
n = r.URL.Path
}
logf(r, "http: read error in ServeContent: %s: %v", n, err)
}
}
}

// errorSaverWriter wraps an io.Writer and saves the error returned
// by the last invocation of Write in err
type errorSaverWriter struct {
w io.Writer
err error
}

func (w *errorSaverWriter) Write(b []byte) (int, error) {
var n int
n, w.err = w.w.Write(b)
return n, w.err
}

// copyNIgnoreWriteError copies n bytes (or until an error) from src to dst.
// It returns only the errors encountered during reading from src.
func copyNIgnoreWriteError(dst io.Writer, src io.Reader, n int64) error {
ew := &errorSaverWriter{w: dst}
_, err := io.CopyN(ew, src, n)
if err == ew.err {
return nil
}
return err
}

// scanETag determines if a syntactically valid ETag is present at s. If so,
Expand Down
22 changes: 22 additions & 0 deletions src/net/http/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,28 @@ func (d fileServerCleanPathDir) Open(path string) (File, error) {

type panicOnSeek struct{ io.ReadSeeker }

func Test_copyNIgnoreWriteError(t *testing.T) {
e := errors.New("io error")
tests := []struct {
name string
dst io.Writer
src io.Reader
n int64
wantErr error
}{
{"short read", ioutil.Discard, new(bytes.Buffer), 1, io.EOF},
{"read err", ioutil.Discard, errorReader{e}, 1, e},
{"no err", ioutil.Discard, strings.NewReader("content"), 3, nil},
{"write err", &net.IPConn{}, strings.NewReader("content"), 3, nil},
}
for _, test := range tests {
err := ExportCopyNIgnoreWriteError(test.dst, test.src, test.n)
if err != test.wantErr {
t.Errorf("%s copyNIgnoreWriteError got %v, want %v", test.name, err, test.wantErr)
}
}
}

func Test_scanETag(t *testing.T) {
tests := []struct {
in string
Expand Down

0 comments on commit df595d2

Please sign in to comment.