Source file src/net/sendfile_test.go

     1  // Copyright 2016 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.
     4  
     5  package net
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"runtime"
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  const (
    23  	newton       = "../testdata/Isaac.Newton-Opticks.txt"
    24  	newtonLen    = 567198
    25  	newtonSHA256 = "d4a9ac22462b35e7821a4f2706c211093da678620a8f9997989ee7cf8d507bbd"
    26  )
    27  
    28  func TestSendfile(t *testing.T) {
    29  	ln := newLocalListener(t, "tcp")
    30  	defer ln.Close()
    31  
    32  	errc := make(chan error, 1)
    33  	go func(ln Listener) {
    34  		// Wait for a connection.
    35  		conn, err := ln.Accept()
    36  		if err != nil {
    37  			errc <- err
    38  			close(errc)
    39  			return
    40  		}
    41  
    42  		go func() {
    43  			defer close(errc)
    44  			defer conn.Close()
    45  
    46  			f, err := os.Open(newton)
    47  			if err != nil {
    48  				errc <- err
    49  				return
    50  			}
    51  			defer f.Close()
    52  
    53  			// Return file data using io.Copy, which should use
    54  			// sendFile if available.
    55  			sbytes, err := io.Copy(conn, f)
    56  			if err != nil {
    57  				errc <- err
    58  				return
    59  			}
    60  
    61  			if sbytes != newtonLen {
    62  				errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, newtonLen)
    63  				return
    64  			}
    65  		}()
    66  	}(ln)
    67  
    68  	// Connect to listener to retrieve file and verify digest matches
    69  	// expected.
    70  	c, err := Dial("tcp", ln.Addr().String())
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	defer c.Close()
    75  
    76  	h := sha256.New()
    77  	rbytes, err := io.Copy(h, c)
    78  	if err != nil {
    79  		t.Error(err)
    80  	}
    81  
    82  	if rbytes != newtonLen {
    83  		t.Errorf("received %d bytes; expected %d", rbytes, newtonLen)
    84  	}
    85  
    86  	if res := hex.EncodeToString(h.Sum(nil)); res != newtonSHA256 {
    87  		t.Error("retrieved data hash did not match")
    88  	}
    89  
    90  	for err := range errc {
    91  		t.Error(err)
    92  	}
    93  }
    94  
    95  func TestSendfileParts(t *testing.T) {
    96  	ln := newLocalListener(t, "tcp")
    97  	defer ln.Close()
    98  
    99  	errc := make(chan error, 1)
   100  	go func(ln Listener) {
   101  		// Wait for a connection.
   102  		conn, err := ln.Accept()
   103  		if err != nil {
   104  			errc <- err
   105  			close(errc)
   106  			return
   107  		}
   108  
   109  		go func() {
   110  			defer close(errc)
   111  			defer conn.Close()
   112  
   113  			f, err := os.Open(newton)
   114  			if err != nil {
   115  				errc <- err
   116  				return
   117  			}
   118  			defer f.Close()
   119  
   120  			for i := 0; i < 3; i++ {
   121  				// Return file data using io.CopyN, which should use
   122  				// sendFile if available.
   123  				_, err = io.CopyN(conn, f, 3)
   124  				if err != nil {
   125  					errc <- err
   126  					return
   127  				}
   128  			}
   129  		}()
   130  	}(ln)
   131  
   132  	c, err := Dial("tcp", ln.Addr().String())
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  	defer c.Close()
   137  
   138  	buf := new(bytes.Buffer)
   139  	buf.ReadFrom(c)
   140  
   141  	if want, have := "Produced ", buf.String(); have != want {
   142  		t.Errorf("unexpected server reply %q, want %q", have, want)
   143  	}
   144  
   145  	for err := range errc {
   146  		t.Error(err)
   147  	}
   148  }
   149  
   150  func TestSendfileSeeked(t *testing.T) {
   151  	ln := newLocalListener(t, "tcp")
   152  	defer ln.Close()
   153  
   154  	const seekTo = 65 << 10
   155  	const sendSize = 10 << 10
   156  
   157  	errc := make(chan error, 1)
   158  	go func(ln Listener) {
   159  		// Wait for a connection.
   160  		conn, err := ln.Accept()
   161  		if err != nil {
   162  			errc <- err
   163  			close(errc)
   164  			return
   165  		}
   166  
   167  		go func() {
   168  			defer close(errc)
   169  			defer conn.Close()
   170  
   171  			f, err := os.Open(newton)
   172  			if err != nil {
   173  				errc <- err
   174  				return
   175  			}
   176  			defer f.Close()
   177  			if _, err := f.Seek(seekTo, io.SeekStart); err != nil {
   178  				errc <- err
   179  				return
   180  			}
   181  
   182  			_, err = io.CopyN(conn, f, sendSize)
   183  			if err != nil {
   184  				errc <- err
   185  				return
   186  			}
   187  		}()
   188  	}(ln)
   189  
   190  	c, err := Dial("tcp", ln.Addr().String())
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	defer c.Close()
   195  
   196  	buf := new(bytes.Buffer)
   197  	buf.ReadFrom(c)
   198  
   199  	if buf.Len() != sendSize {
   200  		t.Errorf("Got %d bytes; want %d", buf.Len(), sendSize)
   201  	}
   202  
   203  	for err := range errc {
   204  		t.Error(err)
   205  	}
   206  }
   207  
   208  // Test that sendfile doesn't put a pipe into blocking mode.
   209  func TestSendfilePipe(t *testing.T) {
   210  	switch runtime.GOOS {
   211  	case "plan9", "windows", "js", "wasip1":
   212  		// These systems don't support deadlines on pipes.
   213  		t.Skipf("skipping on %s", runtime.GOOS)
   214  	}
   215  
   216  	t.Parallel()
   217  
   218  	ln := newLocalListener(t, "tcp")
   219  	defer ln.Close()
   220  
   221  	r, w, err := os.Pipe()
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	defer w.Close()
   226  	defer r.Close()
   227  
   228  	copied := make(chan bool)
   229  
   230  	var wg sync.WaitGroup
   231  	wg.Add(1)
   232  	go func() {
   233  		// Accept a connection and copy 1 byte from the read end of
   234  		// the pipe to the connection. This will call into sendfile.
   235  		defer wg.Done()
   236  		conn, err := ln.Accept()
   237  		if err != nil {
   238  			t.Error(err)
   239  			return
   240  		}
   241  		defer conn.Close()
   242  		_, err = io.CopyN(conn, r, 1)
   243  		if err != nil {
   244  			t.Error(err)
   245  			return
   246  		}
   247  		// Signal the main goroutine that we've copied the byte.
   248  		close(copied)
   249  	}()
   250  
   251  	wg.Add(1)
   252  	go func() {
   253  		// Write 1 byte to the write end of the pipe.
   254  		defer wg.Done()
   255  		_, err := w.Write([]byte{'a'})
   256  		if err != nil {
   257  			t.Error(err)
   258  		}
   259  	}()
   260  
   261  	wg.Add(1)
   262  	go func() {
   263  		// Connect to the server started two goroutines up and
   264  		// discard any data that it writes.
   265  		defer wg.Done()
   266  		conn, err := Dial("tcp", ln.Addr().String())
   267  		if err != nil {
   268  			t.Error(err)
   269  			return
   270  		}
   271  		defer conn.Close()
   272  		io.Copy(io.Discard, conn)
   273  	}()
   274  
   275  	// Wait for the byte to be copied, meaning that sendfile has
   276  	// been called on the pipe.
   277  	<-copied
   278  
   279  	// Set a very short deadline on the read end of the pipe.
   280  	if err := r.SetDeadline(time.Now().Add(time.Microsecond)); err != nil {
   281  		t.Fatal(err)
   282  	}
   283  
   284  	wg.Add(1)
   285  	go func() {
   286  		// Wait for much longer than the deadline and write a byte
   287  		// to the pipe.
   288  		defer wg.Done()
   289  		time.Sleep(50 * time.Millisecond)
   290  		w.Write([]byte{'b'})
   291  	}()
   292  
   293  	// If this read does not time out, the pipe was incorrectly
   294  	// put into blocking mode.
   295  	_, err = r.Read(make([]byte, 1))
   296  	if err == nil {
   297  		t.Error("Read did not time out")
   298  	} else if !os.IsTimeout(err) {
   299  		t.Errorf("got error %v, expected a time out", err)
   300  	}
   301  
   302  	wg.Wait()
   303  }
   304  
   305  // Issue 43822: tests that returns EOF when conn write timeout.
   306  func TestSendfileOnWriteTimeoutExceeded(t *testing.T) {
   307  	ln := newLocalListener(t, "tcp")
   308  	defer ln.Close()
   309  
   310  	errc := make(chan error, 1)
   311  	go func(ln Listener) (retErr error) {
   312  		defer func() {
   313  			errc <- retErr
   314  			close(errc)
   315  		}()
   316  
   317  		conn, err := ln.Accept()
   318  		if err != nil {
   319  			return err
   320  		}
   321  		defer conn.Close()
   322  
   323  		// Set the write deadline in the past(1h ago). It makes
   324  		// sure that it is always write timeout.
   325  		if err := conn.SetWriteDeadline(time.Now().Add(-1 * time.Hour)); err != nil {
   326  			return err
   327  		}
   328  
   329  		f, err := os.Open(newton)
   330  		if err != nil {
   331  			return err
   332  		}
   333  		defer f.Close()
   334  
   335  		_, err = io.Copy(conn, f)
   336  		if errors.Is(err, os.ErrDeadlineExceeded) {
   337  			return nil
   338  		}
   339  
   340  		if err == nil {
   341  			err = fmt.Errorf("expected ErrDeadlineExceeded, but got nil")
   342  		}
   343  		return err
   344  	}(ln)
   345  
   346  	conn, err := Dial("tcp", ln.Addr().String())
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	defer conn.Close()
   351  
   352  	n, err := io.Copy(io.Discard, conn)
   353  	if err != nil {
   354  		t.Fatalf("expected nil error, but got %v", err)
   355  	}
   356  	if n != 0 {
   357  		t.Fatalf("expected receive zero, but got %d byte(s)", n)
   358  	}
   359  
   360  	if err := <-errc; err != nil {
   361  		t.Fatal(err)
   362  	}
   363  }
   364  
   365  func BenchmarkSendfileZeroBytes(b *testing.B) {
   366  	var (
   367  		wg          sync.WaitGroup
   368  		ctx, cancel = context.WithCancel(context.Background())
   369  	)
   370  
   371  	defer wg.Wait()
   372  
   373  	ln := newLocalListener(b, "tcp")
   374  	defer ln.Close()
   375  
   376  	tempFile, err := os.CreateTemp(b.TempDir(), "test.txt")
   377  	if err != nil {
   378  		b.Fatalf("failed to create temp file: %v", err)
   379  	}
   380  	defer tempFile.Close()
   381  
   382  	fileName := tempFile.Name()
   383  
   384  	dataSize := b.N
   385  	wg.Add(1)
   386  	go func(f *os.File) {
   387  		defer wg.Done()
   388  
   389  		for i := 0; i < dataSize; i++ {
   390  			if _, err := f.Write([]byte{1}); err != nil {
   391  				b.Errorf("failed to write: %v", err)
   392  				return
   393  			}
   394  			if i%1000 == 0 {
   395  				f.Sync()
   396  			}
   397  		}
   398  	}(tempFile)
   399  
   400  	b.ResetTimer()
   401  	b.ReportAllocs()
   402  
   403  	wg.Add(1)
   404  	go func(ln Listener, fileName string) {
   405  		defer wg.Done()
   406  
   407  		conn, err := ln.Accept()
   408  		if err != nil {
   409  			b.Errorf("failed to accept: %v", err)
   410  			return
   411  		}
   412  		defer conn.Close()
   413  
   414  		f, err := os.OpenFile(fileName, os.O_RDONLY, 0660)
   415  		if err != nil {
   416  			b.Errorf("failed to open file: %v", err)
   417  			return
   418  		}
   419  		defer f.Close()
   420  
   421  		for {
   422  			if ctx.Err() != nil {
   423  				return
   424  			}
   425  
   426  			if _, err := io.Copy(conn, f); err != nil {
   427  				b.Errorf("failed to copy: %v", err)
   428  				return
   429  			}
   430  		}
   431  	}(ln, fileName)
   432  
   433  	conn, err := Dial("tcp", ln.Addr().String())
   434  	if err != nil {
   435  		b.Fatalf("failed to dial: %v", err)
   436  	}
   437  	defer conn.Close()
   438  
   439  	n, err := io.CopyN(io.Discard, conn, int64(dataSize))
   440  	if err != nil {
   441  		b.Fatalf("failed to copy: %v", err)
   442  	}
   443  	if n != int64(dataSize) {
   444  		b.Fatalf("expected %d copied bytes, but got %d", dataSize, n)
   445  	}
   446  
   447  	cancel()
   448  }
   449  

View as plain text