all: Use Go 1.21, new QUIC API (#9040)

This commit is contained in:
Jakob Borg
2023-08-21 15:25:52 +02:00
committed by GitHub
parent c40dae315b
commit cbf0e31f69
8 changed files with 139 additions and 122 deletions
+6 -10
View File
@@ -52,27 +52,23 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
return internalConn{}, err
}
var conn net.PacketConn
// We need to track who created the conn.
// Given we always pass the connection to quic, it assumes it's a remote connection it never closes it,
// So our wrapper around it needs to close it, but it only needs to close it if it's not the listening connection.
// If we created the conn we need to close it at the end. If we got a
// Transport from the registry we have no conn to close.
var createdConn net.PacketConn
listenConn := d.registry.Get(uri.Scheme, packetConnUnspecified)
if listenConn != nil {
conn = listenConn.(net.PacketConn)
} else {
transport, _ := d.registry.Get(uri.Scheme, transportConnUnspecified).(*quic.Transport)
if transport == nil {
if packetConn, err := net.ListenPacket("udp", ":0"); err != nil {
return internalConn{}, err
} else {
conn = packetConn
createdConn = packetConn
transport = &quic.Transport{Conn: packetConn}
}
}
ctx, cancel := context.WithTimeout(ctx, quicOperationTimeout)
defer cancel()
session, err := quic.DialContext(ctx, conn, addr, uri.Host, d.tlsCfg, quicConfig)
session, err := transport.Dial(ctx, addr, d.tlsCfg, quicConfig)
if err != nil {
if createdConn != nil {
_ = createdConn.Close()
+11 -6
View File
@@ -95,17 +95,22 @@ func (t *quicListener) serve(ctx context.Context) error {
l.Infoln("Listen (BEP/quic):", err)
return err
}
defer func() { _ = udpConn.Close() }()
defer udpConn.Close()
svc, conn := stun.New(t.cfg, t, udpConn)
defer conn.Close()
tracer := &writeTrackingTracer{}
quicTransport := &quic.Transport{
Conn: udpConn,
Tracer: tracer,
}
defer quicTransport.Close()
svc := stun.New(t.cfg, t, &transportPacketConn{tran: quicTransport}, tracer)
go svc.Serve(ctx)
t.registry.Register(t.uri.Scheme, conn)
defer t.registry.Unregister(t.uri.Scheme, conn)
t.registry.Register(t.uri.Scheme, quicTransport)
defer t.registry.Unregister(t.uri.Scheme, quicTransport)
listener, err := quic.Listen(conn, t.tlsCfg, quicConfig)
listener, err := quicTransport.Listen(t.tlsCfg, quicConfig)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
return err
+72 -6
View File
@@ -10,20 +10,22 @@
package connections
import (
"context"
"crypto/tls"
"net"
"net/url"
"sync/atomic"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/logging"
"github.com/syncthing/syncthing/lib/osutil"
)
var quicConfig = &quic.Config{
ConnectionIDLength: 4,
MaxIdleTimeout: 30 * time.Second,
KeepAlivePeriod: 15 * time.Second,
MaxIdleTimeout: 30 * time.Second,
KeepAlivePeriod: 15 * time.Second,
}
func quicNetwork(uri *url.URL) string {
@@ -61,11 +63,75 @@ func (q *quicTlsConn) Close() error {
}
func (q *quicTlsConn) ConnectionState() tls.ConnectionState {
return q.Connection.ConnectionState().TLS.ConnectionState
return q.Connection.ConnectionState().TLS
}
func packetConnUnspecified(conn interface{}) bool {
addr := conn.(net.PacketConn).LocalAddr()
func transportConnUnspecified(conn any) bool {
tran, ok := conn.(*quic.Transport)
if !ok {
return false
}
addr := tran.Conn.LocalAddr()
ip, err := osutil.IPFromAddr(addr)
return err == nil && ip.IsUnspecified()
}
type writeTrackingTracer struct {
lastWrite atomic.Int64 // unix nanos
}
func (t *writeTrackingTracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {
t.lastWrite.Store(time.Now().UnixNano())
}
func (t *writeTrackingTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src logging.ArbitraryLenConnectionID, _ []quic.VersionNumber) {
t.lastWrite.Store(time.Now().UnixNano())
}
func (t *writeTrackingTracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) {
}
func (t *writeTrackingTracer) LastWrite() time.Time {
return time.Unix(0, t.lastWrite.Load())
}
// A transportPacketConn is a net.PacketConn that uses a quic.Transport.
type transportPacketConn struct {
tran *quic.Transport
readDeadline atomic.Value // time.Time
}
func (t *transportPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
ctx := context.Background()
if deadline, ok := t.readDeadline.Load().(time.Time); ok && !deadline.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, deadline)
defer cancel()
}
return t.tran.ReadNonQUICPacket(ctx, p)
}
func (t *transportPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return t.tran.WriteTo(p, addr)
}
func (t *transportPacketConn) Close() error {
return errUnsupported
}
func (t *transportPacketConn) LocalAddr() net.Addr {
return t.tran.Conn.LocalAddr()
}
func (t *transportPacketConn) SetDeadline(deadline time.Time) error {
return t.SetReadDeadline(deadline)
}
func (t *transportPacketConn) SetReadDeadline(deadline time.Time) error {
t.readDeadline.Store(deadline)
return nil
}
func (t *transportPacketConn) SetWriteDeadline(_ time.Time) error {
return nil // yolo
}