aboutsummaryrefslogtreecommitdiff
path: root/src/net/sendfile_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/sendfile_test.go')
-rw-r--r--src/net/sendfile_test.go71
1 files changed, 68 insertions, 3 deletions
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
index 8fadb47c15..4f3411565b 100644
--- a/src/net/sendfile_test.go
+++ b/src/net/sendfile_test.go
@@ -11,6 +11,7 @@ import (
"encoding/hex"
"errors"
"fmt"
+ "internal/poll"
"io"
"os"
"runtime"
@@ -26,6 +27,48 @@ const (
newtonSHA256 = "d4a9ac22462b35e7821a4f2706c211093da678620a8f9997989ee7cf8d507bbd"
)
+// expectSendfile runs f, and verifies that internal/poll.SendFile successfully handles
+// a write to wantConn during f's execution.
+//
+// On platforms where supportsSendfile is false, expectSendfile runs f but does not
+// expect a call to SendFile.
+func expectSendfile(t *testing.T, wantConn Conn, f func()) {
+ t.Helper()
+ if !supportsSendfile {
+ f()
+ return
+ }
+ orig := poll.TestHookDidSendFile
+ defer func() {
+ poll.TestHookDidSendFile = orig
+ }()
+ var (
+ called bool
+ gotHandled bool
+ gotFD *poll.FD
+ )
+ poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
+ if called {
+ t.Error("internal/poll.SendFile called multiple times, want one call")
+ }
+ called = true
+ gotHandled = handled
+ gotFD = dstFD
+ }
+ f()
+ if !called {
+ t.Error("internal/poll.SendFile was not called, want it to be")
+ return
+ }
+ if !gotHandled {
+ t.Error("internal/poll.SendFile did not handle the write, want it to")
+ return
+ }
+ if &wantConn.(*TCPConn).fd.pfd != gotFD {
+ t.Error("internal.poll.SendFile called with unexpected FD")
+ }
+}
+
func TestSendfile(t *testing.T) {
ln := newLocalListener(t, "tcp")
defer ln.Close()
@@ -53,7 +96,17 @@ func TestSendfile(t *testing.T) {
// Return file data using io.Copy, which should use
// sendFile if available.
- sbytes, err := io.Copy(conn, f)
+ var sbytes int64
+ switch runtime.GOOS {
+ case "windows":
+ // Windows is not using sendfile for some reason:
+ // https://go.dev/issue/67042
+ sbytes, err = io.Copy(conn, f)
+ default:
+ expectSendfile(t, conn, func() {
+ sbytes, err = io.Copy(conn, f)
+ })
+ }
if err != nil {
errc <- err
return
@@ -121,7 +174,9 @@ func TestSendfileParts(t *testing.T) {
for i := 0; i < 3; i++ {
// Return file data using io.CopyN, which should use
// sendFile if available.
- _, err = io.CopyN(conn, f, 3)
+ expectSendfile(t, conn, func() {
+ _, err = io.CopyN(conn, f, 3)
+ })
if err != nil {
errc <- err
return
@@ -180,7 +235,9 @@ func TestSendfileSeeked(t *testing.T) {
return
}
- _, err = io.CopyN(conn, f, sendSize)
+ expectSendfile(t, conn, func() {
+ _, err = io.CopyN(conn, f, sendSize)
+ })
if err != nil {
errc <- err
return
@@ -240,6 +297,10 @@ func TestSendfilePipe(t *testing.T) {
return
}
defer conn.Close()
+ // The comment above states that this should call into sendfile,
+ // but empirically it doesn't seem to do so at this time.
+ // If it does, or does on some platforms, this CopyN should be wrapped
+ // in expectSendfile.
_, err = io.CopyN(conn, r, 1)
if err != nil {
t.Error(err)
@@ -333,6 +394,10 @@ func TestSendfileOnWriteTimeoutExceeded(t *testing.T) {
}
defer f.Close()
+ // We expect this to use sendfile, but as of the time this comment was written
+ // poll.SendFile on an FD past its timeout can return an error indicating that
+ // it didn't handle the operation, resulting in a non-sendfile retry.
+ // So don't use expectSendfile here.
_, err = io.Copy(conn, f)
if errors.Is(err, os.ErrDeadlineExceeded) {
return nil