github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/send_conn.go (about)

     1  package quic
     2  
     3  import (
     4  	"net"
     5  
     6  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol"
     7  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/utils"
     8  )
     9  
    10  // A sendConn allows sending using a simple Write() on a non-connected packet conn.
    11  type sendConn interface {
    12  	Write(b []byte, gsoSize uint16, ecn protocol.ECN) error
    13  	Close() error
    14  	LocalAddr() net.Addr
    15  	RemoteAddr() net.Addr
    16  
    17  	capabilities() connCapabilities
    18  }
    19  
    20  type sconn struct {
    21  	rawConn
    22  
    23  	localAddr  net.Addr
    24  	remoteAddr net.Addr
    25  
    26  	logger utils.Logger
    27  
    28  	packetInfoOOB []byte
    29  	// If GSO enabled, and we receive a GSO error for this remote address, GSO is disabled.
    30  	gotGSOError bool
    31  	// Used to catch the error sometimes returned by the first sendmsg call on Linux,
    32  	// see https://github.com/golang/go/issues/63322.
    33  	wroteFirstPacket bool
    34  }
    35  
    36  var _ sendConn = &sconn{}
    37  
    38  func newSendConn(c rawConn, remote net.Addr, info packetInfo, logger utils.Logger) *sconn {
    39  	localAddr := c.LocalAddr()
    40  	if info.addr.IsValid() {
    41  		if udpAddr, ok := localAddr.(*net.UDPAddr); ok {
    42  			addrCopy := *udpAddr
    43  			addrCopy.IP = info.addr.AsSlice()
    44  			localAddr = &addrCopy
    45  		}
    46  	}
    47  
    48  	oob := info.OOB()
    49  	// increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating
    50  	l := len(oob)
    51  	oob = append(oob, make([]byte, 64)...)[:l]
    52  	return &sconn{
    53  		rawConn:       c,
    54  		localAddr:     localAddr,
    55  		remoteAddr:    remote,
    56  		packetInfoOOB: oob,
    57  		logger:        logger,
    58  	}
    59  }
    60  
    61  func (c *sconn) Write(p []byte, gsoSize uint16, ecn protocol.ECN) error {
    62  	err := c.writePacket(p, c.remoteAddr, c.packetInfoOOB, gsoSize, ecn)
    63  	if err != nil && isGSOError(err) {
    64  		// disable GSO for future calls
    65  		c.gotGSOError = true
    66  		if c.logger.Debug() {
    67  			c.logger.Debugf("GSO failed when sending to %s", c.remoteAddr)
    68  		}
    69  		// send out the packets one by one
    70  		for len(p) > 0 {
    71  			l := len(p)
    72  			if l > int(gsoSize) {
    73  				l = int(gsoSize)
    74  			}
    75  			if err := c.writePacket(p[:l], c.remoteAddr, c.packetInfoOOB, 0, ecn); err != nil {
    76  				return err
    77  			}
    78  			p = p[l:]
    79  		}
    80  		return nil
    81  	}
    82  	return err
    83  }
    84  
    85  func (c *sconn) writePacket(p []byte, addr net.Addr, oob []byte, gsoSize uint16, ecn protocol.ECN) error {
    86  	_, err := c.WritePacket(p, addr, oob, gsoSize, ecn)
    87  	if err != nil && !c.wroteFirstPacket && isPermissionError(err) {
    88  		_, err = c.WritePacket(p, addr, oob, gsoSize, ecn)
    89  	}
    90  	c.wroteFirstPacket = true
    91  	return err
    92  }
    93  
    94  func (c *sconn) capabilities() connCapabilities {
    95  	capabilities := c.rawConn.capabilities()
    96  	if capabilities.GSO {
    97  		capabilities.GSO = !c.gotGSOError
    98  	}
    99  	return capabilities
   100  }
   101  
   102  func (c *sconn) RemoteAddr() net.Addr { return c.remoteAddr }
   103  func (c *sconn) LocalAddr() net.Addr  { return c.localAddr }