aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShawn Walker-Salas <shawn.walker@oracle.com>2016-04-08 15:59:04 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2016-04-12 19:00:46 +0000
commit98080a6c64c2d9bc2a759b66a9b861af4ef7367b (patch)
treebf486e483620060bd181d7280ef81cc437592842 /src
parentb1851a3c11a179d4eb55f9d0dd25ef81668a9f81 (diff)
downloadgo-98080a6c64c2d9bc2a759b66a9b861af4ef7367b.tar.xz
net: broken sendfile on SmartOS/Solaris
In the event of a partial write on Solaris and some BSDs, the offset pointer passed to sendfile() will be updated even though the function returns -1 if errno is set to EAGAIN/EINTR. In that case, calculate the bytes written based on the difference between the updated offset and the original offset. If no bytes were written, and errno is set to EAGAIN/EINTR, ignore the errno. Fixes #13892 Change-Id: I6334b5ef2edcbebdaa7db36fa4f7785967313c2d Reviewed-on: https://go-review.googlesource.com/21769 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/net/sendfile_solaris.go9
-rw-r--r--src/net/sendfile_test.go90
2 files changed, 97 insertions, 2 deletions
diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go
index eb9d2d1830..20d2cddeea 100644
--- a/src/net/sendfile_solaris.go
+++ b/src/net/sendfile_solaris.go
@@ -26,8 +26,6 @@ const maxSendfileSize int = 4 << 20
//
// if handled == false, sendFile performed no work.
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
- return // Solaris sendfile is disabled until Issue 13892 is understood and fixed
-
// Solaris uses 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
@@ -78,6 +76,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
}
pos1 := pos
n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if err1 == syscall.EAGAIN || err1 == syscall.EINTR {
+ // partial write may have occurred
+ if n = int(pos1 - pos); n == 0 {
+ // nothing more to write
+ err1 = nil
+ }
+ }
if n > 0 {
pos += int64(n)
written += int64(n)
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
new file mode 100644
index 0000000000..add2bcb5c6
--- /dev/null
+++ b/src/net/sendfile_test.go
@@ -0,0 +1,90 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "testing"
+)
+
+const (
+ twain = "../compress/testdata/Mark.Twain-Tom.Sawyer.txt"
+ twainLen = 387851
+ twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e"
+)
+
+func TestSendFile(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) {
+ // Wait for a connection.
+ conn, err := ln.Accept()
+ if err != nil {
+ errc <- err
+ close(errc)
+ return
+ }
+
+ go func() {
+ defer close(errc)
+ defer conn.Close()
+
+ f, err := os.Open(twain)
+ if err != nil {
+ errc <- err
+ return
+ }
+ defer f.Close()
+
+ // Return file data using io.Copy, which should use
+ // sendFile if available.
+ sbytes, err := io.Copy(conn, f)
+ if err != nil {
+ errc <- err
+ return
+ }
+
+ if sbytes != twainLen {
+ errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, twainLen)
+ return
+ }
+ }()
+ }(ln)
+
+ // Connect to listener to retrieve file and verify digest matches
+ // expected.
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ h := sha256.New()
+ rbytes, err := io.Copy(h, c)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if rbytes != twainLen {
+ t.Errorf("received %d bytes; expected %d", rbytes, twainLen)
+ }
+
+ if res := hex.EncodeToString(h.Sum(nil)); res != twainSHA256 {
+ t.Error("retrieved data hash did not match")
+ }
+
+ for err := range errc {
+ t.Error(err)
+ }
+}