Skip to content

Commit

Permalink
net/http: improve FileServer error logging
Browse files Browse the repository at this point in the history
The 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 Sep 12, 2019
1 parent 6c6ad30 commit 7976f48
Show file tree
Hide file tree
Showing 3 changed files with 50 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
28 changes: 27 additions & 1 deletion src/net/http/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,34 @@ 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 {
logf(r, "http: error copy content of %s: %v", name, err)
}
}
}

// errorSaverReader wraps an io.Reader and saves the error returned
// by the last invocation of Read in err
type errorSaverReader struct {
r io.Reader
err error
}

func (r *errorSaverReader) Read(b []byte) (int, error) {
var n int
n, r.err = r.r.Read(b)
return n, r.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 {
er := &errorSaverReader{r: src}
_, err := io.CopyN(dst, er, n)
if err == io.EOF || err == er.err {
return err
}
return nil
}

// 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
w io.Writer
r 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.w, test.r, 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 7976f48

Please sign in to comment.