]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.15] internal/poll: netpollcheckerr before sendfile
authorWei Fu <fuweid89@gmail.com>
Sun, 24 Jan 2021 10:21:06 +0000 (18:21 +0800)
committerIan Lance Taylor <iant@golang.org>
Fri, 26 Feb 2021 15:40:37 +0000 (15:40 +0000)
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>

src/internal/poll/sendfile_bsd.go
src/internal/poll/sendfile_linux.go
src/internal/poll/sendfile_solaris.go
src/net/sendfile_test.go

index a24e41dcaa8742c8104e5071455e9e83cb92ee84..66005a9f5c952a68574a50d64cecab36b13728f1 100644 (file)
@@ -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
index d64283007db44c03db78d768c5b9a4258f2e9c01..d6442e8666481b67c636f9bbbe00a34a1d1fe84e 100644 (file)
@@ -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
index 762992e9eb398eac2b0ec7ec9eda78466a831ab4..748c85131e688da0c42eb4d8f8fd65195a587a77 100644 (file)
@@ -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
index 13842a1261f74684fb06dfe23d783a71e2f2cc0d..0e344b36493516696937b9708f2c433fb060c5dd 100644 (file)
@@ -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)
+       }
+}