diff options
author | Wei Fu <fuweid89@gmail.com> | 2021-01-24 18:21:06 +0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-02-26 15:40:37 +0000 |
commit | 30357d6ef6513b18fade686a629ab8d49987260f (patch) | |
tree | 67b1214a76cf051d6ad2ee68c32b191bedfda267 | |
parent | 023c46676db26e75d244b1d38ccc4a4b8bfe3eef (diff) | |
download | go-30357d6ef6513b18fade686a629ab8d49987260f.tar.gz go-30357d6ef6513b18fade686a629ab8d49987260f.zip |
[release-branch.go1.15] internal/poll: netpollcheckerr before sendfile
In net/http package, the ServeContent/ServeFile doesn't check the I/O
timeout error from chunkWriter or *net.TCPConn, which means that both
HTTP status and headers might be missing when WriteTimeout happens. If
the poll.SendFile() doesn't check the *poll.FD state before sending
data, the client will only receive the response body with status and
report "malformed http response/status code".
This patch is to enable netpollcheckerr before sendfile, which should
align with normal *poll.FD.Write() and Splice().
For #43822
Fixes #44294
Change-Id: I32517e3f261bab883a58b577b813ef189214b954
Reviewed-on: https://go-review.googlesource.com/c/go/+/285914
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
(cherry picked from commit f0d23c9dbb2142b975fa8fb13a57213d0c15bdd1)
Reviewed-on: https://go-review.googlesource.com/c/go/+/296530
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
-rw-r--r-- | src/internal/poll/sendfile_bsd.go | 4 | ||||
-rw-r--r-- | src/internal/poll/sendfile_linux.go | 3 | ||||
-rw-r--r-- | src/internal/poll/sendfile_solaris.go | 3 | ||||
-rw-r--r-- | src/net/sendfile_test.go | 64 |
4 files changed, 74 insertions, 0 deletions
diff --git a/src/internal/poll/sendfile_bsd.go b/src/internal/poll/sendfile_bsd.go index a24e41dcaa8..66005a9f5c9 100644 --- a/src/internal/poll/sendfile_bsd.go +++ b/src/internal/poll/sendfile_bsd.go @@ -18,6 +18,10 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } + dst := int(dstFD.Sysfd) var written int64 var err error diff --git a/src/internal/poll/sendfile_linux.go b/src/internal/poll/sendfile_linux.go index d64283007db..d6442e86664 100644 --- a/src/internal/poll/sendfile_linux.go +++ b/src/internal/poll/sendfile_linux.go @@ -16,6 +16,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } dst := int(dstFD.Sysfd) var written int64 diff --git a/src/internal/poll/sendfile_solaris.go b/src/internal/poll/sendfile_solaris.go index 762992e9eb3..748c85131e6 100644 --- a/src/internal/poll/sendfile_solaris.go +++ b/src/internal/poll/sendfile_solaris.go @@ -20,6 +20,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } dst := int(dstFD.Sysfd) var written int64 diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go index 13842a1261f..0e344b36493 100644 --- a/src/net/sendfile_test.go +++ b/src/net/sendfile_test.go @@ -10,6 +10,7 @@ import ( "bytes" "crypto/sha256" "encoding/hex" + "errors" "fmt" "io" "io/ioutil" @@ -314,3 +315,66 @@ func TestSendfilePipe(t *testing.T) { wg.Wait() } + +// Issue 43822: tests that returns EOF when conn write timeout. +func TestSendfileOnWriteTimeoutExceeded(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func(ln Listener) (retErr error) { + defer func() { + errc <- retErr + close(errc) + }() + + conn, err := ln.Accept() + if err != nil { + return err + } + defer conn.Close() + + // Set the write deadline in the past(1h ago). It makes + // sure that it is always write timeout. + if err := conn.SetWriteDeadline(time.Now().Add(-1 * time.Hour)); err != nil { + return err + } + + f, err := os.Open(newton) + if err != nil { + return err + } + defer f.Close() + + _, err = io.Copy(conn, f) + if errors.Is(err, os.ErrDeadlineExceeded) { + return nil + } + + if err == nil { + err = fmt.Errorf("expected ErrDeadlineExceeded, but got nil") + } + return err + }(ln) + + conn, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + n, err := io.Copy(ioutil.Discard, conn) + if err != nil { + t.Fatalf("expected nil error, but got %v", err) + } + if n != 0 { + t.Fatalf("expected receive zero, but got %d byte(s)", n) + } + + if err := <-errc; err != nil { + t.Fatal(err) + } +} |