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

View as plain text