1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 func TestSplice(t
*testing
.T
) {
19 t
.Run("simple", testSpliceSimple
)
20 t
.Run("multipleWrite", testSpliceMultipleWrite
)
21 t
.Run("big", testSpliceBig
)
22 t
.Run("honorsLimitedReader", testSpliceHonorsLimitedReader
)
23 t
.Run("readerAtEOF", testSpliceReaderAtEOF
)
24 t
.Run("issue25985", testSpliceIssue25985
)
27 func testSpliceSimple(t
*testing
.T
) {
28 srv
, err
:= newSpliceTestServer()
33 copyDone
:= srv
.Copy()
34 msg
:= []byte("splice test")
35 if _
, err
:= srv
.Write(msg
); err
!= nil {
38 got
:= make([]byte, len(msg
))
39 if _
, err
:= io
.ReadFull(srv
, got
); err
!= nil {
42 if !bytes
.Equal(got
, msg
) {
43 t
.Errorf("got %q, wrote %q", got
, msg
)
47 if err
:= <-copyDone
; err
!= nil {
48 t
.Errorf("splice: %v", err
)
52 func testSpliceMultipleWrite(t
*testing
.T
) {
53 srv
, err
:= newSpliceTestServer()
58 copyDone
:= srv
.Copy()
59 msg1
:= []byte("splice test part 1 ")
60 msg2
:= []byte(" splice test part 2")
61 if _
, err
:= srv
.Write(msg1
); err
!= nil {
62 t
.Fatalf("Write: %v", err
)
64 if _
, err
:= srv
.Write(msg2
); err
!= nil {
67 got
:= make([]byte, len(msg1
)+len(msg2
))
68 if _
, err
:= io
.ReadFull(srv
, got
); err
!= nil {
71 want
:= append(msg1
, msg2
...)
72 if !bytes
.Equal(got
, want
) {
73 t
.Errorf("got %q, wrote %q", got
, want
)
77 if err
:= <-copyDone
; err
!= nil {
78 t
.Errorf("splice: %v", err
)
82 func testSpliceBig(t
*testing
.T
) {
83 // The maximum amount of data that internal/poll.Splice will use in a
84 // splice(2) call is 4 << 20. Use a bigger size here so that we test an
85 // amount that doesn't fit in a single call.
87 srv
, err
:= newSpliceTestServer()
92 big
:= make([]byte, size
)
93 copyDone
:= srv
.Copy()
94 type readResult
struct {
98 readDone
:= make(chan readResult
)
100 got
:= make([]byte, len(big
))
101 _
, err
:= io
.ReadFull(srv
, got
)
102 readDone
<- readResult
{got
, err
}
104 if _
, err
:= srv
.Write(big
); err
!= nil {
112 if !bytes
.Equal(got
, big
) {
113 t
.Errorf("input and output differ")
117 if err
:= <-copyDone
; err
!= nil {
118 t
.Errorf("splice: %v", err
)
122 func testSpliceHonorsLimitedReader(t
*testing
.T
) {
123 t
.Run("stopsAfterN", testSpliceStopsAfterN
)
124 t
.Run("updatesN", testSpliceUpdatesN
)
127 func testSpliceStopsAfterN(t
*testing
.T
) {
128 clientUp
, serverUp
, err
:= spliceTestSocketPair("tcp")
132 defer clientUp
.Close()
133 defer serverUp
.Close()
134 clientDown
, serverDown
, err
:= spliceTestSocketPair("tcp")
138 defer clientDown
.Close()
139 defer serverDown
.Close()
141 copyDone
:= make(chan error
)
142 lr
:= &io
.LimitedReader
{
147 _
, err
:= io
.Copy(serverDown
, lr
)
151 msg
:= make([]byte, 2*count
)
152 if _
, err
:= clientUp
.Write(msg
); err
!= nil {
157 if _
, err
:= io
.Copy(&buf
, clientDown
); err
!= nil {
160 if buf
.Len() != count
{
161 t
.Errorf("splice transferred %d bytes, want to stop after %d", buf
.Len(), count
)
164 if err
:= <-copyDone
; err
!= nil {
165 t
.Errorf("splice: %v", err
)
169 func testSpliceUpdatesN(t
*testing
.T
) {
170 clientUp
, serverUp
, err
:= spliceTestSocketPair("tcp")
174 defer clientUp
.Close()
175 defer serverUp
.Close()
176 clientDown
, serverDown
, err
:= spliceTestSocketPair("tcp")
180 defer clientDown
.Close()
181 defer serverDown
.Close()
183 copyDone
:= make(chan error
)
184 lr
:= &io
.LimitedReader
{
185 N
: int64(100 + count
),
189 _
, err
:= io
.Copy(serverDown
, lr
)
192 msg
:= make([]byte, count
)
193 if _
, err
:= clientUp
.Write(msg
); err
!= nil {
197 got
:= make([]byte, count
)
198 if _
, err
:= io
.ReadFull(clientDown
, got
); err
!= nil {
202 if err
:= <-copyDone
; err
!= nil {
203 t
.Errorf("splice: %v", err
)
207 t
.Errorf("lr.N = %d, want %d", lr
.N
, wantN
)
211 func testSpliceReaderAtEOF(t
*testing
.T
) {
212 clientUp
, serverUp
, err
:= spliceTestSocketPair("tcp")
216 defer clientUp
.Close()
217 defer serverUp
.Close()
218 clientDown
, serverDown
, err
:= spliceTestSocketPair("tcp")
222 defer clientDown
.Close()
223 defer serverDown
.Close()
226 _
, err
, handled
:= splice(serverDown
.(*TCPConn
).fd
, serverUp
)
228 t
.Errorf("closed connection: got err = %v, handled = %t, want handled = true", err
, handled
)
230 lr
:= &io
.LimitedReader
{
234 _
, err
, handled
= splice(serverDown
.(*TCPConn
).fd
, lr
)
236 t
.Errorf("exhausted LimitedReader: got err = %v, handled = %t, want handled = true", err
, handled
)
240 func testSpliceIssue25985(t
*testing
.T
) {
241 front
, err
:= newLocalListener("tcp")
246 back
, err
:= newLocalListener("tcp")
252 var wg sync
.WaitGroup
256 src
, err
:= front
.Accept()
260 dst
, err
:= Dial("tcp", back
.Addr().String())
278 toFront
, err
:= Dial("tcp", front
.Addr().String())
283 io
.WriteString(toFront
, "foo")
286 fromProxy
, err
:= back
.Accept()
290 defer fromProxy
.Close()
292 _
, err
= ioutil
.ReadAll(fromProxy
)
300 func BenchmarkTCPReadFrom(b
*testing
.B
) {
301 testHookUninstaller
.Do(uninstallTestHooks
)
304 for i
:= uint(10); i
<= 20; i
++ {
305 chunkSizes
= append(chunkSizes
, 1<<i
)
307 // To benchmark the genericReadFrom code path, set this to false.
309 for _
, chunkSize
:= range chunkSizes
{
310 b
.Run(fmt
.Sprint(chunkSize
), func(b
*testing
.B
) {
311 benchmarkSplice(b
, chunkSize
, useSplice
)
316 func benchmarkSplice(b
*testing
.B
, chunkSize
int, useSplice
bool) {
317 srv
, err
:= newSpliceTestServer()
322 var copyDone
<-chan error
324 copyDone
= srv
.Copy()
326 copyDone
= srv
.CopyNoSplice()
328 chunk
:= make([]byte, chunkSize
)
329 discardDone
:= make(chan struct{})
332 buf
:= make([]byte, chunkSize
)
333 _
, err
:= srv
.Read(buf
)
338 discardDone
<- struct{}{}
340 b
.SetBytes(int64(chunkSize
))
342 for i
:= 0; i
< b
.N
; i
++ {
351 type spliceTestServer
struct {
352 clientUp io
.WriteCloser
353 clientDown io
.ReadCloser
354 serverUp io
.ReadCloser
355 serverDown io
.WriteCloser
358 func newSpliceTestServer() (*spliceTestServer
, error
) {
359 // For now, both networks are hard-coded to TCP.
360 // If splice is enabled for non-tcp upstream connections,
361 // newSpliceTestServer will need to take a network parameter.
362 clientUp
, serverUp
, err
:= spliceTestSocketPair("tcp")
366 clientDown
, serverDown
, err
:= spliceTestSocketPair("tcp")
372 return &spliceTestServer
{clientUp
, clientDown
, serverUp
, serverDown
}, nil
375 // Read reads from the downstream connection.
376 func (srv
*spliceTestServer
) Read(b
[]byte) (int, error
) {
377 return srv
.clientDown
.Read(b
)
380 // Write writes to the upstream connection.
381 func (srv
*spliceTestServer
) Write(b
[]byte) (int, error
) {
382 return srv
.clientUp
.Write(b
)
385 // Close closes the server.
386 func (srv
*spliceTestServer
) Close() error
{
388 err1
:= srv
.closeDown()
395 // CloseWrite closes the client side of the upstream connection.
396 func (srv
*spliceTestServer
) CloseWrite() error
{
397 return srv
.clientUp
.Close()
400 // CloseRead closes the client side of the downstream connection.
401 func (srv
*spliceTestServer
) CloseRead() error
{
402 return srv
.clientDown
.Close()
405 // Copy copies from the server side of the upstream connection
406 // to the server side of the downstream connection, in a separate
407 // goroutine. Copy is done when the first send on the returned
409 func (srv
*spliceTestServer
) Copy() <-chan error
{
410 ch
:= make(chan error
)
412 _
, err
:= io
.Copy(srv
.serverDown
, srv
.serverUp
)
419 // CopyNoSplice is like Copy, but ensures that the splice code path
421 func (srv
*spliceTestServer
) CopyNoSplice() <-chan error
{
422 type onlyReader
struct {
425 ch
:= make(chan error
)
427 _
, err
:= io
.Copy(srv
.serverDown
, onlyReader
{srv
.serverUp
})
434 func (srv
*spliceTestServer
) closeUp() error
{
436 if srv
.serverUp
!= nil {
437 err
= srv
.serverUp
.Close()
439 if srv
.clientUp
!= nil {
440 err1
= srv
.clientUp
.Close()
448 func (srv
*spliceTestServer
) closeDown() error
{
450 if srv
.serverDown
!= nil {
451 err
= srv
.serverDown
.Close()
453 if srv
.clientDown
!= nil {
454 err1
= srv
.clientDown
.Close()
462 func spliceTestSocketPair(net
string) (client
, server Conn
, err error
) {
463 ln
, err
:= newLocalListener(net
)
469 acceptDone
:= make(chan struct{})
471 server
, serr
= ln
.Accept()
472 acceptDone
<- struct{}{}
474 client
, cerr
= Dial(ln
.Addr().Network(), ln
.Addr().String())
480 return nil, nil, cerr
486 return nil, nil, serr
488 return client
, server
, nil