1
0
mirror of https://github.com/golang/go synced 2024-11-26 21:31:32 -07:00

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().

Fixes #43822

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>
This commit is contained in:
Wei Fu 2021-01-24 18:21:06 +08:00 committed by Emmanuel Odeke
parent 0cb3415154
commit f0d23c9dbb
4 changed files with 75 additions and 0 deletions

View File

@ -18,6 +18,10 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
return 0, err return 0, err
} }
defer dstFD.writeUnlock() defer dstFD.writeUnlock()
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
return 0, err
}
dst := int(dstFD.Sysfd) dst := int(dstFD.Sysfd)
var written int64 var written int64
var err error var err error

View File

@ -16,6 +16,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) {
return 0, err return 0, err
} }
defer dstFD.writeUnlock() defer dstFD.writeUnlock()
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
return 0, err
}
dst := int(dstFD.Sysfd) dst := int(dstFD.Sysfd)
var written int64 var written int64

View File

@ -20,6 +20,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
return 0, err return 0, err
} }
defer dstFD.writeUnlock() defer dstFD.writeUnlock()
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
return 0, err
}
dst := int(dstFD.Sysfd) dst := int(dstFD.Sysfd)
var written int64 var written int64

View File

@ -10,8 +10,10 @@ import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"runtime" "runtime"
"sync" "sync"
@ -313,3 +315,66 @@ func TestSendfilePipe(t *testing.T) {
wg.Wait() 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)
}
}