Home | History | Annotate | Download | only in 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 package net
      6 
      7 import (
      8 	"bytes"
      9 	"fmt"
     10 	"internal/poll"
     11 	"io"
     12 	"io/ioutil"
     13 	"reflect"
     14 	"runtime"
     15 	"sync"
     16 	"testing"
     17 )
     18 
     19 func TestBuffers_read(t *testing.T) {
     20 	const story = "once upon a time in Gopherland ... "
     21 	buffers := Buffers{
     22 		[]byte("once "),
     23 		[]byte("upon "),
     24 		[]byte("a "),
     25 		[]byte("time "),
     26 		[]byte("in "),
     27 		[]byte("Gopherland ... "),
     28 	}
     29 	got, err := ioutil.ReadAll(&buffers)
     30 	if err != nil {
     31 		t.Fatal(err)
     32 	}
     33 	if string(got) != story {
     34 		t.Errorf("read %q; want %q", got, story)
     35 	}
     36 	if len(buffers) != 0 {
     37 		t.Errorf("len(buffers) = %d; want 0", len(buffers))
     38 	}
     39 }
     40 
     41 func TestBuffers_consume(t *testing.T) {
     42 	tests := []struct {
     43 		in      Buffers
     44 		consume int64
     45 		want    Buffers
     46 	}{
     47 		{
     48 			in:      Buffers{[]byte("foo"), []byte("bar")},
     49 			consume: 0,
     50 			want:    Buffers{[]byte("foo"), []byte("bar")},
     51 		},
     52 		{
     53 			in:      Buffers{[]byte("foo"), []byte("bar")},
     54 			consume: 2,
     55 			want:    Buffers{[]byte("o"), []byte("bar")},
     56 		},
     57 		{
     58 			in:      Buffers{[]byte("foo"), []byte("bar")},
     59 			consume: 3,
     60 			want:    Buffers{[]byte("bar")},
     61 		},
     62 		{
     63 			in:      Buffers{[]byte("foo"), []byte("bar")},
     64 			consume: 4,
     65 			want:    Buffers{[]byte("ar")},
     66 		},
     67 		{
     68 			in:      Buffers{nil, nil, nil, []byte("bar")},
     69 			consume: 1,
     70 			want:    Buffers{[]byte("ar")},
     71 		},
     72 		{
     73 			in:      Buffers{nil, nil, nil, []byte("foo")},
     74 			consume: 0,
     75 			want:    Buffers{[]byte("foo")},
     76 		},
     77 		{
     78 			in:      Buffers{nil, nil, nil},
     79 			consume: 0,
     80 			want:    Buffers{},
     81 		},
     82 	}
     83 	for i, tt := range tests {
     84 		in := tt.in
     85 		in.consume(tt.consume)
     86 		if !reflect.DeepEqual(in, tt.want) {
     87 			t.Errorf("%d. after consume(%d) = %+v, want %+v", i, tt.consume, in, tt.want)
     88 		}
     89 	}
     90 }
     91 
     92 func TestBuffers_WriteTo(t *testing.T) {
     93 	for _, name := range []string{"WriteTo", "Copy"} {
     94 		for _, size := range []int{0, 10, 1023, 1024, 1025} {
     95 			t.Run(fmt.Sprintf("%s/%d", name, size), func(t *testing.T) {
     96 				testBuffer_writeTo(t, size, name == "Copy")
     97 			})
     98 		}
     99 	}
    100 }
    101 
    102 func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) {
    103 	oldHook := poll.TestHookDidWritev
    104 	defer func() { poll.TestHookDidWritev = oldHook }()
    105 	var writeLog struct {
    106 		sync.Mutex
    107 		log []int
    108 	}
    109 	poll.TestHookDidWritev = func(size int) {
    110 		writeLog.Lock()
    111 		writeLog.log = append(writeLog.log, size)
    112 		writeLog.Unlock()
    113 	}
    114 	var want bytes.Buffer
    115 	for i := 0; i < chunks; i++ {
    116 		want.WriteByte(byte(i))
    117 	}
    118 
    119 	withTCPConnPair(t, func(c *TCPConn) error {
    120 		buffers := make(Buffers, chunks)
    121 		for i := range buffers {
    122 			buffers[i] = want.Bytes()[i : i+1]
    123 		}
    124 		var n int64
    125 		var err error
    126 		if useCopy {
    127 			n, err = io.Copy(c, &buffers)
    128 		} else {
    129 			n, err = buffers.WriteTo(c)
    130 		}
    131 		if err != nil {
    132 			return err
    133 		}
    134 		if len(buffers) != 0 {
    135 			return fmt.Errorf("len(buffers) = %d; want 0", len(buffers))
    136 		}
    137 		if n != int64(want.Len()) {
    138 			return fmt.Errorf("Buffers.WriteTo returned %d; want %d", n, want.Len())
    139 		}
    140 		return nil
    141 	}, func(c *TCPConn) error {
    142 		all, err := ioutil.ReadAll(c)
    143 		if !bytes.Equal(all, want.Bytes()) || err != nil {
    144 			return fmt.Errorf("client read %q, %v; want %q, nil", all, err, want.Bytes())
    145 		}
    146 
    147 		writeLog.Lock() // no need to unlock
    148 		var gotSum int
    149 		for _, v := range writeLog.log {
    150 			gotSum += v
    151 		}
    152 
    153 		var wantSum int
    154 		switch runtime.GOOS {
    155 		case "android", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
    156 			var wantMinCalls int
    157 			wantSum = want.Len()
    158 			v := chunks
    159 			for v > 0 {
    160 				wantMinCalls++
    161 				v -= 1024
    162 			}
    163 			if len(writeLog.log) < wantMinCalls {
    164 				t.Errorf("write calls = %v < wanted min %v", len(writeLog.log), wantMinCalls)
    165 			}
    166 		case "windows":
    167 			var wantCalls int
    168 			wantSum = want.Len()
    169 			if wantSum > 0 {
    170 				wantCalls = 1 // windows will always do 1 syscall, unless sending empty buffer
    171 			}
    172 			if len(writeLog.log) != wantCalls {
    173 				t.Errorf("write calls = %v; want %v", len(writeLog.log), wantCalls)
    174 			}
    175 		}
    176 		if gotSum != wantSum {
    177 			t.Errorf("writev call sum  = %v; want %v", gotSum, wantSum)
    178 		}
    179 		return nil
    180 	})
    181 }
    182 
    183 func TestWritevError(t *testing.T) {
    184 	if runtime.GOOS == "windows" {
    185 		t.Skipf("skipping the test: windows does not have problem sending large chunks of data")
    186 	}
    187 
    188 	ln, err := newLocalListener("tcp")
    189 	if err != nil {
    190 		t.Fatal(err)
    191 	}
    192 	defer ln.Close()
    193 
    194 	ch := make(chan Conn, 1)
    195 	go func() {
    196 		defer close(ch)
    197 		c, err := ln.Accept()
    198 		if err != nil {
    199 			t.Error(err)
    200 			return
    201 		}
    202 		ch <- c
    203 	}()
    204 	c1, err := Dial("tcp", ln.Addr().String())
    205 	if err != nil {
    206 		t.Fatal(err)
    207 	}
    208 	defer c1.Close()
    209 	c2 := <-ch
    210 	if c2 == nil {
    211 		t.Fatal("no server side connection")
    212 	}
    213 	c2.Close()
    214 
    215 	// 1 GB of data should be enough to notice the connection is gone.
    216 	// Just a few bytes is not enough.
    217 	// Arrange to reuse the same 1 MB buffer so that we don't allocate much.
    218 	buf := make([]byte, 1<<20)
    219 	buffers := make(Buffers, 1<<10)
    220 	for i := range buffers {
    221 		buffers[i] = buf
    222 	}
    223 	if _, err := buffers.WriteTo(c1); err == nil {
    224 		t.Fatal("Buffers.WriteTo(closed conn) succeeded, want error")
    225 	}
    226 }
    227