-
Notifications
You must be signed in to change notification settings - Fork 18k
/
Copy pathwriteto_linux_test.go
133 lines (119 loc) · 3.32 KB
/
writeto_linux_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2023 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 os_test
import (
"bytes"
"internal/poll"
"io"
"net"
. "os"
"strconv"
"syscall"
"testing"
)
func TestSendFile(t *testing.T) {
sizes := []int{
1,
42,
1025,
syscall.Getpagesize() + 1,
32769,
}
t.Run("sendfile-to-unix", func(t *testing.T) {
for _, size := range sizes {
t.Run(strconv.Itoa(size), func(t *testing.T) {
testSendFile(t, "unix", int64(size))
})
}
})
t.Run("sendfile-to-tcp", func(t *testing.T) {
for _, size := range sizes {
t.Run(strconv.Itoa(size), func(t *testing.T) {
testSendFile(t, "tcp", int64(size))
})
}
})
}
func testSendFile(t *testing.T, proto string, size int64) {
dst, src, recv, data, hook := newSendFileTest(t, proto, size)
// Now call WriteTo (through io.Copy), which will hopefully call poll.SendFile
n, err := io.Copy(dst, src)
if err != nil {
t.Fatalf("io.Copy error: %v", err)
}
// We should have called poll.Splice with the right file descriptor arguments.
if n > 0 && !hook.called {
t.Fatal("expected to called poll.SendFile")
}
if hook.called && hook.srcfd != int(src.Fd()) {
t.Fatalf("wrong source file descriptor: got %d, want %d", hook.srcfd, src.Fd())
}
sc, ok := dst.(syscall.Conn)
if !ok {
t.Fatalf("destination is not a syscall.Conn")
}
rc, err := sc.SyscallConn()
if err != nil {
t.Fatalf("destination SyscallConn error: %v", err)
}
if err = rc.Control(func(fd uintptr) {
if hook.called && hook.dstfd != int(fd) {
t.Fatalf("wrong destination file descriptor: got %d, want %d", hook.dstfd, int(fd))
}
}); err != nil {
t.Fatalf("destination Conn Control error: %v", err)
}
// Verify the data size and content.
dataSize := len(data)
dstData := make([]byte, dataSize)
m, err := io.ReadFull(recv, dstData)
if err != nil {
t.Fatalf("server Conn Read error: %v", err)
}
if n != int64(dataSize) {
t.Fatalf("data length mismatch for io.Copy, got %d, want %d", n, dataSize)
}
if m != dataSize {
t.Fatalf("data length mismatch for net.Conn.Read, got %d, want %d", m, dataSize)
}
if !bytes.Equal(dstData, data) {
t.Errorf("data mismatch, got %s, want %s", dstData, data)
}
}
// newSendFileTest initializes a new test for sendfile.
//
// It creates source file and destination sockets, and populates the source file
// with random data of the specified size. It also hooks package os' call
// to poll.Sendfile and returns the hook so it can be inspected.
func newSendFileTest(t *testing.T, proto string, size int64) (net.Conn, *File, net.Conn, []byte, *sendFileHook) {
t.Helper()
hook := hookSendFile(t)
client, server := createSocketPair(t, proto)
tempFile, data := createTempFile(t, "writeto-sendfile-to-socket", size)
return client, tempFile, server, data, hook
}
func hookSendFile(t *testing.T) *sendFileHook {
h := new(sendFileHook)
orig := poll.TestHookDidSendFile
t.Cleanup(func() {
poll.TestHookDidSendFile = orig
})
poll.TestHookDidSendFile = func(dstFD *poll.FD, src uintptr, written int64, err error, handled bool) {
h.called = true
h.dstfd = dstFD.Sysfd
h.srcfd = int(src)
h.written = written
h.err = err
h.handled = handled
}
return h
}
type sendFileHook struct {
called bool
dstfd int
srcfd int
written int64
handled bool
err error
}