Skip to content

Commit 30357d6

Browse files
fuweidianlancetaylor
authored andcommitted
[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 <[email protected]> Trust: Emmanuel Odeke <[email protected]> Trust: Bryan C. Mills <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> (cherry picked from commit f0d23c9) Reviewed-on: https://go-review.googlesource.com/c/go/+/296530 Trust: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent 023c466 commit 30357d6

File tree

4 files changed

+74
-0
lines changed

4 files changed

+74
-0
lines changed

src/internal/poll/sendfile_bsd.go

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
1818
return 0, err
1919
}
2020
defer dstFD.writeUnlock()
21+
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
22+
return 0, err
23+
}
24+
2125
dst := int(dstFD.Sysfd)
2226
var written int64
2327
var err error

src/internal/poll/sendfile_linux.go

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) {
1616
return 0, err
1717
}
1818
defer dstFD.writeUnlock()
19+
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
20+
return 0, err
21+
}
1922

2023
dst := int(dstFD.Sysfd)
2124
var written int64

src/internal/poll/sendfile_solaris.go

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
2020
return 0, err
2121
}
2222
defer dstFD.writeUnlock()
23+
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
24+
return 0, err
25+
}
2326

2427
dst := int(dstFD.Sysfd)
2528
var written int64

src/net/sendfile_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bytes"
1111
"crypto/sha256"
1212
"encoding/hex"
13+
"errors"
1314
"fmt"
1415
"io"
1516
"io/ioutil"
@@ -314,3 +315,66 @@ func TestSendfilePipe(t *testing.T) {
314315

315316
wg.Wait()
316317
}
318+
319+
// Issue 43822: tests that returns EOF when conn write timeout.
320+
func TestSendfileOnWriteTimeoutExceeded(t *testing.T) {
321+
ln, err := newLocalListener("tcp")
322+
if err != nil {
323+
t.Fatal(err)
324+
}
325+
defer ln.Close()
326+
327+
errc := make(chan error, 1)
328+
go func(ln Listener) (retErr error) {
329+
defer func() {
330+
errc <- retErr
331+
close(errc)
332+
}()
333+
334+
conn, err := ln.Accept()
335+
if err != nil {
336+
return err
337+
}
338+
defer conn.Close()
339+
340+
// Set the write deadline in the past(1h ago). It makes
341+
// sure that it is always write timeout.
342+
if err := conn.SetWriteDeadline(time.Now().Add(-1 * time.Hour)); err != nil {
343+
return err
344+
}
345+
346+
f, err := os.Open(newton)
347+
if err != nil {
348+
return err
349+
}
350+
defer f.Close()
351+
352+
_, err = io.Copy(conn, f)
353+
if errors.Is(err, os.ErrDeadlineExceeded) {
354+
return nil
355+
}
356+
357+
if err == nil {
358+
err = fmt.Errorf("expected ErrDeadlineExceeded, but got nil")
359+
}
360+
return err
361+
}(ln)
362+
363+
conn, err := Dial("tcp", ln.Addr().String())
364+
if err != nil {
365+
t.Fatal(err)
366+
}
367+
defer conn.Close()
368+
369+
n, err := io.Copy(ioutil.Discard, conn)
370+
if err != nil {
371+
t.Fatalf("expected nil error, but got %v", err)
372+
}
373+
if n != 0 {
374+
t.Fatalf("expected receive zero, but got %d byte(s)", n)
375+
}
376+
377+
if err := <-errc; err != nil {
378+
t.Fatal(err)
379+
}
380+
}

0 commit comments

Comments
 (0)