Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/sendfile_test.go

Documentation: net

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

View as plain text