aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Fu <fuweid89@gmail.com>2021-01-24 18:21:06 +0800
committerIan Lance Taylor <iant@golang.org>2021-02-26 15:40:37 +0000
commit30357d6ef6513b18fade686a629ab8d49987260f (patch)
tree67b1214a76cf051d6ad2ee68c32b191bedfda267
parent023c46676db26e75d244b1d38ccc4a4b8bfe3eef (diff)
downloadgo-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.go4
-rw-r--r--src/internal/poll/sendfile_linux.go3
-rw-r--r--src/internal/poll/sendfile_solaris.go3
-rw-r--r--src/net/sendfile_test.go64
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)
+ }
+}