mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
b1851a3c11
commit
98080a6c64
2 changed files with 97 additions and 2 deletions
|
|
@ -26,8 +26,6 @@ const maxSendfileSize int = 4 << 20
|
||||||
//
|
//
|
||||||
// if handled == false, sendFile performed no work.
|
// if handled == false, sendFile performed no work.
|
||||||
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
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
|
// 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
|
// 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
|
// 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
|
pos1 := pos
|
||||||
n, err1 := syscall.Sendfile(dst, src, &pos1, n)
|
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 {
|
if n > 0 {
|
||||||
pos += int64(n)
|
pos += int64(n)
|
||||||
written += int64(n)
|
written += int64(n)
|
||||||
|
|
|
||||||
90
src/net/sendfile_test.go
Normal file
90
src/net/sendfile_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue