golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/internal/socket/socket_dontwait_test.go (about)

     1  // Copyright 2021 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 darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
     6  
     7  package socket_test
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"net"
    13  	"runtime"
    14  	"syscall"
    15  	"testing"
    16  
    17  	"golang.org/x/net/internal/socket"
    18  	"golang.org/x/net/nettest"
    19  )
    20  
    21  func TestUDPDontwait(t *testing.T) {
    22  	c, err := nettest.NewLocalPacketListener("udp")
    23  	if err != nil {
    24  		t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
    25  	}
    26  	defer c.Close()
    27  	cc, err := socket.NewConn(c.(*net.UDPConn))
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	isErrWouldblock := func(err error) bool {
    32  		var errno syscall.Errno
    33  		return errors.As(err, &errno) && (errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK)
    34  	}
    35  
    36  	t.Run("Message-dontwait", func(t *testing.T) {
    37  		// Read before something was sent; expect EWOULDBLOCK
    38  		b := make([]byte, 32)
    39  		rm := socket.Message{
    40  			Buffers: [][]byte{b},
    41  		}
    42  		if err := cc.RecvMsg(&rm, syscall.MSG_DONTWAIT); !isErrWouldblock(err) {
    43  			t.Fatal(err)
    44  		}
    45  		// To trigger EWOULDBLOCK by SendMsg, we have to send faster than what the
    46  		// system/network is able to process. Whether or not we can trigger this
    47  		// depends on the system, specifically on write buffer sizes and the speed
    48  		// of the network interface.
    49  		// We cannot expect to quickly and reliably trigger this, especially not
    50  		// because this test sends data over a (fast) loopback. Consequently, we
    51  		// only check that sending with MSG_DONTWAIT works at all and don't attempt
    52  		// testing that we would eventually get EWOULDBLOCK here.
    53  		data := []byte("HELLO-R-U-THERE")
    54  		wm := socket.Message{
    55  			Buffers: [][]byte{data},
    56  			Addr:    c.LocalAddr(),
    57  		}
    58  		// Send one message, repeat until we don't get EWOULDBLOCK. This will likely succeed at the first attempt.
    59  		for {
    60  			err := cc.SendMsg(&wm, syscall.MSG_DONTWAIT)
    61  			if err == nil {
    62  				break
    63  			} else if !isErrWouldblock(err) {
    64  				t.Fatal(err)
    65  			}
    66  		}
    67  		// Read the message now available; again, this will likely succeed at the first attempt.
    68  		for {
    69  			err := cc.RecvMsg(&rm, syscall.MSG_DONTWAIT)
    70  			if err == nil {
    71  				break
    72  			} else if !isErrWouldblock(err) {
    73  				t.Fatal(err)
    74  			}
    75  		}
    76  		if !bytes.Equal(b[:rm.N], data) {
    77  			t.Fatalf("got %#v; want %#v", b[:rm.N], data)
    78  		}
    79  	})
    80  	switch runtime.GOOS {
    81  	case "android", "linux":
    82  		t.Run("Messages", func(t *testing.T) {
    83  			data := []byte("HELLO-R-U-THERE")
    84  			wmbs := bytes.SplitAfter(data, []byte("-"))
    85  			wms := []socket.Message{
    86  				{Buffers: wmbs[:1], Addr: c.LocalAddr()},
    87  				{Buffers: wmbs[1:], Addr: c.LocalAddr()},
    88  			}
    89  			b := make([]byte, 32)
    90  			rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
    91  			rms := []socket.Message{
    92  				{Buffers: rmbs[0]},
    93  				{Buffers: rmbs[1]},
    94  			}
    95  			_, err := cc.RecvMsgs(rms, syscall.MSG_DONTWAIT)
    96  			if !isErrWouldblock(err) {
    97  				t.Fatal(err)
    98  			}
    99  			for ntot := 0; ntot < len(wms); {
   100  				n, err := cc.SendMsgs(wms[ntot:], syscall.MSG_DONTWAIT)
   101  				if err == nil {
   102  					ntot += n
   103  				} else if !isErrWouldblock(err) {
   104  					t.Fatal(err)
   105  				}
   106  			}
   107  			for ntot := 0; ntot < len(rms); {
   108  				n, err := cc.RecvMsgs(rms[ntot:], syscall.MSG_DONTWAIT)
   109  				if err == nil {
   110  					ntot += n
   111  				} else if !isErrWouldblock(err) {
   112  					t.Fatal(err)
   113  				}
   114  			}
   115  			nn := 0
   116  			for i := 0; i < len(rms); i++ {
   117  				nn += rms[i].N
   118  			}
   119  			if !bytes.Equal(b[:nn], data) {
   120  				t.Fatalf("got %#v; want %#v", b[:nn], data)
   121  			}
   122  		})
   123  	}
   124  }