github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/send_conn_test.go (about)

     1  package quic
     2  
     3  import (
     4  	"net"
     5  	"net/netip"
     6  	"runtime"
     7  
     8  	"github.com/apernet/quic-go/internal/protocol"
     9  	"github.com/apernet/quic-go/internal/utils"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"go.uber.org/mock/gomock"
    14  )
    15  
    16  // Only if appendUDPSegmentSizeMsg actually appends a message (and isn't only a stub implementation),
    17  // GSO is actually supported on this platform.
    18  var platformSupportsGSO = len(appendUDPSegmentSizeMsg([]byte{}, 1337)) > 0
    19  
    20  var _ = Describe("Connection (for sending packets)", func() {
    21  	remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 100, 200), Port: 1337}
    22  
    23  	It("gets the local and remote addresses", func() {
    24  		localAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1234}
    25  		rawConn := NewMockRawConn(mockCtrl)
    26  		rawConn.EXPECT().LocalAddr().Return(localAddr)
    27  		c := newSendConn(rawConn, remoteAddr, packetInfo{}, utils.DefaultLogger)
    28  		Expect(c.LocalAddr().String()).To(Equal("192.168.0.1:1234"))
    29  		Expect(c.RemoteAddr().String()).To(Equal("192.168.100.200:1337"))
    30  	})
    31  
    32  	It("uses the local address from the packet info", func() {
    33  		localAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1234}
    34  		rawConn := NewMockRawConn(mockCtrl)
    35  		rawConn.EXPECT().LocalAddr().Return(localAddr)
    36  		c := newSendConn(rawConn, remoteAddr, packetInfo{addr: netip.AddrFrom4([4]byte{127, 0, 0, 42})}, utils.DefaultLogger)
    37  		Expect(c.LocalAddr().String()).To(Equal("127.0.0.42:1234"))
    38  	})
    39  
    40  	// We're not using an OOB conn on windows, and packetInfo.OOB() always returns an empty slice.
    41  	if runtime.GOOS != "windows" {
    42  		It("sets the OOB", func() {
    43  			rawConn := NewMockRawConn(mockCtrl)
    44  			rawConn.EXPECT().LocalAddr()
    45  			rawConn.EXPECT().capabilities().AnyTimes()
    46  			pi := packetInfo{addr: netip.IPv6Loopback()}
    47  			Expect(pi.OOB()).ToNot(BeEmpty())
    48  			c := newSendConn(rawConn, remoteAddr, pi, utils.DefaultLogger)
    49  			rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, pi.OOB(), uint16(0), protocol.ECT1)
    50  			Expect(c.Write([]byte("foobar"), 0, protocol.ECT1)).To(Succeed())
    51  		})
    52  	}
    53  
    54  	It("writes", func() {
    55  		rawConn := NewMockRawConn(mockCtrl)
    56  		rawConn.EXPECT().LocalAddr()
    57  		rawConn.EXPECT().capabilities().AnyTimes()
    58  		c := newSendConn(rawConn, remoteAddr, packetInfo{}, utils.DefaultLogger)
    59  		rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, gomock.Any(), uint16(3), protocol.ECNCE)
    60  		Expect(c.Write([]byte("foobar"), 3, protocol.ECNCE)).To(Succeed())
    61  	})
    62  
    63  	if platformSupportsGSO {
    64  		It("disables GSO if sending fails", func() {
    65  			rawConn := NewMockRawConn(mockCtrl)
    66  			rawConn.EXPECT().LocalAddr()
    67  			rawConn.EXPECT().capabilities().Return(connCapabilities{GSO: true}).AnyTimes()
    68  			c := newSendConn(rawConn, remoteAddr, packetInfo{}, utils.DefaultLogger)
    69  			Expect(c.capabilities().GSO).To(BeTrue())
    70  			gomock.InOrder(
    71  				rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, gomock.Any(), uint16(4), protocol.ECNCE).Return(0, errGSO),
    72  				rawConn.EXPECT().WritePacket([]byte("foob"), remoteAddr, gomock.Any(), uint16(0), protocol.ECNCE).Return(4, nil),
    73  				rawConn.EXPECT().WritePacket([]byte("ar"), remoteAddr, gomock.Any(), uint16(0), protocol.ECNCE).Return(2, nil),
    74  			)
    75  			Expect(c.Write([]byte("foobar"), 4, protocol.ECNCE)).To(Succeed())
    76  			Expect(c.capabilities().GSO).To(BeFalse())
    77  		})
    78  	}
    79  
    80  	if runtime.GOOS == "linux" {
    81  		It("doesn't fail if the very first sendmsg call fails", func() {
    82  			rawConn := NewMockRawConn(mockCtrl)
    83  			rawConn.EXPECT().LocalAddr()
    84  			rawConn.EXPECT().capabilities().AnyTimes()
    85  			c := newSendConn(rawConn, remoteAddr, packetInfo{}, utils.DefaultLogger)
    86  			gomock.InOrder(
    87  				rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, gomock.Any(), gomock.Any(), protocol.ECNCE).Return(0, errNotPermitted),
    88  				rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, gomock.Any(), uint16(0), protocol.ECNCE).Return(6, nil),
    89  			)
    90  			Expect(c.Write([]byte("foobar"), 0, protocol.ECNCE)).To(Succeed())
    91  		})
    92  
    93  		It("fails if the sendmsg calls fail multiple times", func() {
    94  			rawConn := NewMockRawConn(mockCtrl)
    95  			rawConn.EXPECT().LocalAddr()
    96  			rawConn.EXPECT().capabilities().AnyTimes()
    97  			c := newSendConn(rawConn, remoteAddr, packetInfo{}, utils.DefaultLogger)
    98  			rawConn.EXPECT().WritePacket([]byte("foobar"), remoteAddr, gomock.Any(), gomock.Any(), protocol.ECNCE).Return(0, errNotPermitted).Times(2)
    99  			Expect(c.Write([]byte("foobar"), 0, protocol.ECNCE)).To(MatchError(errNotPermitted))
   100  		})
   101  	}
   102  })