diff options
| author | Damien Neil <dneil@google.com> | 2024-10-25 11:47:53 -0700 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2024-10-28 23:45:31 +0000 |
| commit | 98b3be702b05e8fc071fececf7bf44f078bf032f (patch) | |
| tree | 6dd003824218c5120d94be622030f7cdddf07248 /src/net | |
| parent | cd54b9bae94b36f67869ef174cbb432bc4012183 (diff) | |
| download | go-98b3be702b05e8fc071fececf7bf44f078bf032f.tar.xz | |
os, net, internal/poll: combine unix sendfile implementations
The internal/poll/sendfile_{bsd,linux,solaris}.go implementations
have more in common than not. Combine into a single sendfile_unix.go.
The net and os packages have redundant code dealing with sendfile
quirks on non-Linux Unix systems, such as the need to determine the
size of the source file before sending. Move the common code into
internal/poll.
Remove some obsolete or incorrect behaviors:
Drop the maximum sendfile chunk size. If we ask the kernel
to copy more data than it is willing to send, it'll copy up to
its limit.
There was a comment in net/sendfile_unix_alt.go indicating that
copying more bytes than a file contains results in the kernel
looping back to the start of the file. I am unable to replicate
this behavior anywhere. Dropped the comment, the workarounds,
and added a test covering this case.
Darwin, Dragonfly, and FreeBSD all support copying the entire
contents of a file by passing 0 for the copy limit.
Take advantage of this.
Change-Id: I9f707ac7a27c165020ae02a6b5bb8f6f16f3c530
Reviewed-on: https://go-review.googlesource.com/c/go/+/621416
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Diffstat (limited to 'src/net')
| -rw-r--r-- | src/net/sendfile_test.go | 14 | ||||
| -rw-r--r-- | src/net/sendfile_unix_alt.go | 41 |
2 files changed, 15 insertions, 40 deletions
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go index 4f3411565b..64b33a54e2 100644 --- a/src/net/sendfile_test.go +++ b/src/net/sendfile_test.go @@ -69,7 +69,10 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) { } } -func TestSendfile(t *testing.T) { +func TestSendfile(t *testing.T) { testSendfile(t, 0) } +func TestSendfileWithExactLimit(t *testing.T) { testSendfile(t, newtonLen) } +func TestSendfileWithLimitLargerThanFile(t *testing.T) { testSendfile(t, newtonLen*2) } +func testSendfile(t *testing.T, limit int64) { ln := newLocalListener(t, "tcp") defer ln.Close() @@ -104,7 +107,14 @@ func TestSendfile(t *testing.T) { sbytes, err = io.Copy(conn, f) default: expectSendfile(t, conn, func() { - sbytes, err = io.Copy(conn, f) + if limit > 0 { + sbytes, err = io.CopyN(conn, f, limit) + if err == io.EOF && limit > newtonLen { + err = nil + } + } else { + sbytes, err = io.Copy(conn, f) + } }) } if err != nil { diff --git a/src/net/sendfile_unix_alt.go b/src/net/sendfile_unix_alt.go index 9e46c4e607..db788753f1 100644 --- a/src/net/sendfile_unix_alt.go +++ b/src/net/sendfile_unix_alt.go @@ -9,7 +9,6 @@ package net import ( "internal/poll" "io" - "io/fs" "syscall" ) @@ -23,13 +22,7 @@ const supportsSendfile = true // // if handled == false, sendFile performed no work. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - // Darwin, FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. - // If you pass in more bytes than the file contains, it will - // loop back to the beginning ad nauseam until it's sent - // exactly the number of bytes told to. As such, we need to - // know exactly how many bytes to send. - var remain int64 = 0 - + var remain int64 = 0 // 0 writes the entire file lr, ok := r.(*io.LimitedReader) if ok { remain, r = lr.N, lr.R @@ -39,34 +32,11 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { } // r might be an *os.File or an os.fileWithoutWriteTo. // Type assert to an interface rather than *os.File directly to handle the latter case. - f, ok := r.(interface { - fs.File - io.Seeker - syscall.Conn - }) + f, ok := r.(syscall.Conn) if !ok { return 0, nil, false } - if remain == 0 { - fi, err := f.Stat() - if err != nil { - return 0, err, false - } - - remain = fi.Size() - } - - // The other quirk with Darwin/FreeBSD/DragonFly/Solaris's sendfile - // implementation is that it doesn't use the current position - // of the file -- if you pass it offset 0, it starts from - // offset 0. There's no way to tell it "start from current - // position", so we have to manage that explicitly. - pos, err := f.Seek(0, io.SeekCurrent) - if err != nil { - return 0, err, false - } - sc, err := f.SyscallConn() if err != nil { return 0, nil, false @@ -74,7 +44,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { var werr error err = sc.Read(func(fd uintptr) bool { - written, werr, handled = poll.SendFile(&c.pfd, int(fd), pos, remain) + written, werr, handled = poll.SendFile(&c.pfd, int(fd), remain) return true }) if err == nil { @@ -85,10 +55,5 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { lr.N = remain - written } - _, err1 := f.Seek(written, io.SeekCurrent) - if err1 != nil && err == nil { - return written, err1, handled - } - return written, wrapSyscallError("sendfile", err), handled } |
