diff --git a/internal/dslx/dns.go b/internal/dslx/dns.go index 62b86293f6..88d3bd65ec 100644 --- a/internal/dslx/dns.go +++ b/internal/dslx/dns.go @@ -72,117 +72,92 @@ type ResolvedAddresses struct { // DNSLookupGetaddrinfo returns a function that resolves a domain name to // IP addresses using libc's getaddrinfo function. func DNSLookupGetaddrinfo(rt Runtime) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] { - return &dnsLookupGetaddrinfoFunc{rt} -} - -// dnsLookupGetaddrinfoFunc is the function returned by DNSLookupGetaddrinfo. -type dnsLookupGetaddrinfoFunc struct { - rt Runtime -} - -// Apply implements Func. -func (f *dnsLookupGetaddrinfoFunc) Apply( - ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] { - - // create trace - trace := f.rt.NewTrace(f.rt.IDGenerator().Add(1), f.rt.ZeroTime(), input.Tags...) - - // start the operation logger - ol := logx.NewOperationLogger( - f.rt.Logger(), - "[#%d] DNSLookup[getaddrinfo] %s", - trace.Index(), - input.Domain, - ) - - // setup - const timeout = 4 * time.Second - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // create the resolver - resolver := trace.NewStdlibResolver(f.rt.Logger()) - - // lookup - addrs, err := resolver.LookupHost(ctx, input.Domain) - - // stop the operation logger - ol.Stop(err) - - state := &ResolvedAddresses{ - Addresses: addrs, // maybe empty - Domain: input.Domain, - Trace: trace, - } - - return &Maybe[*ResolvedAddresses]{ - Error: err, - Observations: maybeTraceToObservations(trace), - Operation: netxlite.ResolveOperation, - State: state, - } + return FuncAdapter[*DomainToResolve, *Maybe[*ResolvedAddresses]](func(ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] { + // create trace + trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...) + + // start the operation logger + ol := logx.NewOperationLogger( + rt.Logger(), + "[#%d] DNSLookup[getaddrinfo] %s", + trace.Index(), + input.Domain, + ) + + // setup + const timeout = 4 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // create the resolver + resolver := trace.NewStdlibResolver(rt.Logger()) + + // lookup + addrs, err := resolver.LookupHost(ctx, input.Domain) + + // stop the operation logger + ol.Stop(err) + + state := &ResolvedAddresses{ + Addresses: addrs, // maybe empty + Domain: input.Domain, + Trace: trace, + } + + return &Maybe[*ResolvedAddresses]{ + Error: err, + Observations: maybeTraceToObservations(trace), + Operation: netxlite.ResolveOperation, + State: state, + } + }) } // DNSLookupUDP returns a function that resolves a domain name to // IP addresses using the given DNS-over-UDP resolver. -func DNSLookupUDP(rt Runtime, resolver string) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] { - return &dnsLookupUDPFunc{ - Resolver: resolver, - rt: rt, - } -} - -// dnsLookupUDPFunc is the function returned by DNSLookupUDP. -type dnsLookupUDPFunc struct { - // Resolver is the MANDATORY endpointed of the resolver to use. - Resolver string - rt Runtime -} - -// Apply implements Func. -func (f *dnsLookupUDPFunc) Apply( - ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] { - - // create trace - trace := f.rt.NewTrace(f.rt.IDGenerator().Add(1), f.rt.ZeroTime(), input.Tags...) - - // start the operation logger - ol := logx.NewOperationLogger( - f.rt.Logger(), - "[#%d] DNSLookup[%s/udp] %s", - trace.Index(), - f.Resolver, - input.Domain, - ) - - // setup - const timeout = 4 * time.Second - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // create the resolver - resolver := trace.NewParallelUDPResolver( - f.rt.Logger(), - trace.NewDialerWithoutResolver(f.rt.Logger()), - f.Resolver, - ) - - // lookup - addrs, err := resolver.LookupHost(ctx, input.Domain) - - // stop the operation logger - ol.Stop(err) - - state := &ResolvedAddresses{ - Addresses: addrs, // maybe empty - Domain: input.Domain, - Trace: trace, - } - - return &Maybe[*ResolvedAddresses]{ - Error: err, - Observations: maybeTraceToObservations(trace), - Operation: netxlite.ResolveOperation, - State: state, - } +func DNSLookupUDP(rt Runtime, endpoint string) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] { + return FuncAdapter[*DomainToResolve, *Maybe[*ResolvedAddresses]](func(ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] { + // create trace + trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...) + + // start the operation logger + ol := logx.NewOperationLogger( + rt.Logger(), + "[#%d] DNSLookup[%s/udp] %s", + trace.Index(), + endpoint, + input.Domain, + ) + + // setup + const timeout = 4 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // create the resolver + resolver := trace.NewParallelUDPResolver( + rt.Logger(), + trace.NewDialerWithoutResolver(rt.Logger()), + endpoint, + ) + + // lookup + addrs, err := resolver.LookupHost(ctx, input.Domain) + + // stop the operation logger + ol.Stop(err) + + state := &ResolvedAddresses{ + Addresses: addrs, // maybe empty + Domain: input.Domain, + Trace: trace, + } + + return &Maybe[*ResolvedAddresses]{ + Error: err, + Observations: maybeTraceToObservations(trace), + Operation: netxlite.ResolveOperation, + State: state, + } + }) } diff --git a/internal/dslx/dns_test.go b/internal/dslx/dns_test.go index 5dd6f79bd1..2ef3b82d3f 100644 --- a/internal/dslx/dns_test.go +++ b/internal/dslx/dns_test.go @@ -54,13 +54,6 @@ Test cases: - with success */ func TestGetaddrinfo(t *testing.T) { - t.Run("Get dnsLookupGetaddrinfoFunc", func(t *testing.T) { - f := DNSLookupGetaddrinfo(NewMinimalRuntime(model.DiscardLogger, time.Now())) - if _, ok := f.(*dnsLookupGetaddrinfoFunc); !ok { - t.Fatal("unexpected type, want dnsLookupGetaddrinfoFunc") - } - }) - t.Run("Apply dnsLookupGetaddrinfoFunc", func(t *testing.T) { domain := &DomainToResolve{ Domain: "example.com", @@ -68,9 +61,9 @@ func TestGetaddrinfo(t *testing.T) { } t.Run("with nil resolver", func(t *testing.T) { - f := dnsLookupGetaddrinfoFunc{ - rt: NewMinimalRuntime(model.DiscardLogger, time.Now()), - } + f := DNSLookupGetaddrinfo( + NewMinimalRuntime(model.DiscardLogger, time.Now()), + ) ctx, cancel := context.WithCancel(context.Background()) cancel() // immediately cancel the lookup res := f.Apply(ctx, domain) @@ -84,8 +77,8 @@ func TestGetaddrinfo(t *testing.T) { t.Run("with lookup error", func(t *testing.T) { mockedErr := errors.New("mocked") - f := dnsLookupGetaddrinfoFunc{ - rt: NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{ + f := DNSLookupGetaddrinfo( + NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{ MockNewStdlibResolver: func(logger model.DebugLogger) model.Resolver { return &mocks.Resolver{ MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { @@ -94,7 +87,7 @@ func TestGetaddrinfo(t *testing.T) { } }, })), - } + ) res := f.Apply(context.Background(), domain) if res.Observations == nil || len(res.Observations) <= 0 { t.Fatal("unexpected empty observations") @@ -111,8 +104,8 @@ func TestGetaddrinfo(t *testing.T) { }) t.Run("with success", func(t *testing.T) { - f := dnsLookupGetaddrinfoFunc{ - rt: NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{ + f := DNSLookupGetaddrinfo( + NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{ MockNewStdlibResolver: func(logger model.DebugLogger) model.Resolver { return &mocks.Resolver{ MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { @@ -121,7 +114,7 @@ func TestGetaddrinfo(t *testing.T) { } }, })), - } + ) res := f.Apply(context.Background(), domain) if res.Observations == nil || len(res.Observations) <= 0 { t.Fatal("unexpected empty observations") @@ -151,14 +144,6 @@ Test cases: - with success */ func TestLookupUDP(t *testing.T) { - t.Run("Get dnsLookupUDPFunc", func(t *testing.T) { - rt := NewMinimalRuntime(model.DiscardLogger, time.Now()) - f := DNSLookupUDP(rt, "1.1.1.1:53") - if _, ok := f.(*dnsLookupUDPFunc); !ok { - t.Fatal("unexpected type, want dnsLookupUDPFunc") - } - }) - t.Run("Apply dnsLookupGetaddrinfoFunc", func(t *testing.T) { domain := &DomainToResolve{ Domain: "example.com", @@ -166,7 +151,7 @@ func TestLookupUDP(t *testing.T) { } t.Run("with nil resolver", func(t *testing.T) { - f := dnsLookupUDPFunc{Resolver: "1.1.1.1:53", rt: NewMinimalRuntime(model.DiscardLogger, time.Now())} + f := DNSLookupUDP(NewMinimalRuntime(model.DiscardLogger, time.Now()), "1.1.1.1:53") ctx, cancel := context.WithCancel(context.Background()) cancel() res := f.Apply(ctx, domain) @@ -180,9 +165,8 @@ func TestLookupUDP(t *testing.T) { t.Run("with lookup error", func(t *testing.T) { mockedErr := errors.New("mocked") - f := dnsLookupUDPFunc{ - Resolver: "1.1.1.1:53", - rt: NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{ + f := DNSLookupUDP( + NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{ MockNewParallelUDPResolver: func(logger model.DebugLogger, dialer model.Dialer, endpoint string) model.Resolver { return &mocks.Resolver{ MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { @@ -198,7 +182,8 @@ func TestLookupUDP(t *testing.T) { } }, })), - } + "1.1.1.1:53", + ) res := f.Apply(context.Background(), domain) if res.Observations == nil || len(res.Observations) <= 0 { t.Fatal("unexpected empty observations") @@ -215,9 +200,8 @@ func TestLookupUDP(t *testing.T) { }) t.Run("with success", func(t *testing.T) { - f := dnsLookupUDPFunc{ - Resolver: "1.1.1.1:53", - rt: NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{ + f := DNSLookupUDP( + NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{ MockNewParallelUDPResolver: func(logger model.DebugLogger, dialer model.Dialer, address string) model.Resolver { return &mocks.Resolver{ MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { @@ -233,7 +217,8 @@ func TestLookupUDP(t *testing.T) { } }, })), - } + "1.1.1.1:53", + ) res := f.Apply(context.Background(), domain) if res.Observations == nil || len(res.Observations) <= 0 { t.Fatal("unexpected empty observations") diff --git a/internal/dslx/fxcore.go b/internal/dslx/fxcore.go index 64f95bbc0d..329a8fc23c 100644 --- a/internal/dslx/fxcore.go +++ b/internal/dslx/fxcore.go @@ -17,6 +17,14 @@ type Func[A, B any] interface { Apply(ctx context.Context, a A) B } +// FuncAdapter adapts a func to be a Func. +type FuncAdapter[A, B any] func(ctx context.Context, a A) B + +// Apply implements Func. +func (fa FuncAdapter[A, B]) Apply(ctx context.Context, a A) B { + return fa(ctx, a) +} + // Maybe is the result of an operation implemented by this package // that may fail such as [TCPConnect] or [TLSHandshake]. type Maybe[State any] struct { diff --git a/internal/dslx/quic.go b/internal/dslx/quic.go index 327b6d4be6..3442b6822d 100644 --- a/internal/dslx/quic.go +++ b/internal/dslx/quic.go @@ -16,82 +16,64 @@ import ( ) // QUICHandshake returns a function performing QUIC handshakes. -func QUICHandshake(rt Runtime, options ...TLSHandshakeOption) Func[ - *Endpoint, *Maybe[*QUICConnection]] { - f := &quicHandshakeFunc{ - Options: options, - Rt: rt, - } - return f -} - -// quicHandshakeFunc performs QUIC handshakes. -type quicHandshakeFunc struct { - // Options contains the options. - Options []TLSHandshakeOption - - // Rt is the Runtime that owns us. - Rt Runtime -} - -// Apply implements Func. -func (f *quicHandshakeFunc) Apply( - ctx context.Context, input *Endpoint) *Maybe[*QUICConnection] { - // create trace - trace := f.Rt.NewTrace(f.Rt.IDGenerator().Add(1), f.Rt.ZeroTime(), input.Tags...) - - // create a suitable TLS configuration - config := tlsNewConfig(input.Address, []string{"h3"}, input.Domain, f.Rt.Logger(), f.Options...) - - // start the operation logger - ol := logx.NewOperationLogger( - f.Rt.Logger(), - "[#%d] QUICHandshake with %s SNI=%s ALPN=%v", - trace.Index(), - input.Address, - config.ServerName, - config.NextProtos, - ) - - // setup - udpListener := netxlite.NewUDPListener() - quicDialer := trace.NewQUICDialerWithoutResolver(udpListener, f.Rt.Logger()) - const timeout = 10 * time.Second - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // handshake - quicConn, err := quicDialer.DialContext(ctx, input.Address, config, &quic.Config{}) - - var closerConn io.Closer - var tlsState tls.ConnectionState - if quicConn != nil { - closerConn = &quicCloserConn{quicConn} - tlsState = quicConn.ConnectionState().TLS // only quicConn can be nil - } - - // possibly track established conn for late close - f.Rt.MaybeTrackConn(closerConn) - - // stop the operation logger - ol.Stop(err) - - state := &QUICConnection{ - Address: input.Address, - QUICConn: quicConn, // possibly nil - Domain: input.Domain, - Network: input.Network, - TLSConfig: config, - TLSState: tlsState, - Trace: trace, - } - - return &Maybe[*QUICConnection]{ - Error: err, - Observations: maybeTraceToObservations(trace), - Operation: netxlite.QUICHandshakeOperation, - State: state, - } +func QUICHandshake(rt Runtime, options ...TLSHandshakeOption) Func[*Endpoint, *Maybe[*QUICConnection]] { + return FuncAdapter[*Endpoint, *Maybe[*QUICConnection]](func(ctx context.Context, input *Endpoint) *Maybe[*QUICConnection] { + // create trace + trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...) + + // create a suitable TLS configuration + config := tlsNewConfig(input.Address, []string{"h3"}, input.Domain, rt.Logger(), options...) + + // start the operation logger + ol := logx.NewOperationLogger( + rt.Logger(), + "[#%d] QUICHandshake with %s SNI=%s ALPN=%v", + trace.Index(), + input.Address, + config.ServerName, + config.NextProtos, + ) + + // setup + udpListener := netxlite.NewUDPListener() + quicDialer := trace.NewQUICDialerWithoutResolver(udpListener, rt.Logger()) + const timeout = 10 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // handshake + quicConn, err := quicDialer.DialContext(ctx, input.Address, config, &quic.Config{}) + + var closerConn io.Closer + var tlsState tls.ConnectionState + if quicConn != nil { + closerConn = &quicCloserConn{quicConn} + tlsState = quicConn.ConnectionState().TLS // only quicConn can be nil + } + + // possibly track established conn for late close + rt.MaybeTrackConn(closerConn) + + // stop the operation logger + ol.Stop(err) + + state := &QUICConnection{ + Address: input.Address, + QUICConn: quicConn, // possibly nil + Domain: input.Domain, + Network: input.Network, + TLSConfig: config, + TLSState: tlsState, + Trace: trace, + } + + return &Maybe[*QUICConnection]{ + Error: err, + Observations: maybeTraceToObservations(trace), + Operation: netxlite.QUICHandshakeOperation, + State: state, + } + }) } // QUICConnection is an established QUIC connection. If you initialize diff --git a/internal/dslx/quic_test.go b/internal/dslx/quic_test.go index d8a8b066ac..9512783a71 100644 --- a/internal/dslx/quic_test.go +++ b/internal/dslx/quic_test.go @@ -3,7 +3,6 @@ package dslx import ( "context" "crypto/tls" - "crypto/x509" "io" "testing" "time" @@ -16,25 +15,12 @@ import ( /* Test cases: -- Get quicHandshakeFunc with options - Apply quicHandshakeFunc: - with EOF - success - with sni */ func TestQUICHandshake(t *testing.T) { - t.Run("Get quicHandshakeFunc with options", func(t *testing.T) { - certpool := x509.NewCertPool() - certpool.AddCert(&x509.Certificate{}) - - f := QUICHandshake( - NewMinimalRuntime(model.DiscardLogger, time.Now()), - ) - if _, ok := f.(*quicHandshakeFunc); !ok { - t.Fatal("unexpected type. Expected: quicHandshakeFunc") - } - }) - t.Run("Apply quicHandshakeFunc", func(t *testing.T) { wasClosed := false plainConn := &mocks.QUICEarlyConnection{ diff --git a/internal/dslx/tcp.go b/internal/dslx/tcp.go index eaa54c2d30..d576b84fce 100644 --- a/internal/dslx/tcp.go +++ b/internal/dslx/tcp.go @@ -15,61 +15,50 @@ import ( // TCPConnect returns a function that establishes TCP connections. func TCPConnect(rt Runtime) Func[*Endpoint, *Maybe[*TCPConnection]] { - f := &tcpConnectFunc{rt} - return f -} - -// tcpConnectFunc is a function that establishes TCP connections. -type tcpConnectFunc struct { - rt Runtime -} - -// Apply applies the function to its arguments. -func (f *tcpConnectFunc) Apply( - ctx context.Context, input *Endpoint) *Maybe[*TCPConnection] { - - // create trace - trace := f.rt.NewTrace(f.rt.IDGenerator().Add(1), f.rt.ZeroTime(), input.Tags...) - - // start the operation logger - ol := logx.NewOperationLogger( - f.rt.Logger(), - "[#%d] TCPConnect %s", - trace.Index(), - input.Address, - ) - - // setup - const timeout = 15 * time.Second - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // obtain the dialer to use - dialer := trace.NewDialerWithoutResolver(f.rt.Logger()) - - // connect - conn, err := dialer.DialContext(ctx, "tcp", input.Address) - - // possibly register established conn for late close - f.rt.MaybeTrackConn(conn) - - // stop the operation logger - ol.Stop(err) - - state := &TCPConnection{ - Address: input.Address, - Conn: conn, // possibly nil - Domain: input.Domain, - Network: input.Network, - Trace: trace, - } - - return &Maybe[*TCPConnection]{ - Error: err, - Observations: maybeTraceToObservations(trace), - Operation: netxlite.ConnectOperation, - State: state, - } + return FuncAdapter[*Endpoint, *Maybe[*TCPConnection]](func(ctx context.Context, input *Endpoint) *Maybe[*TCPConnection] { + // create trace + trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...) + + // start the operation logger + ol := logx.NewOperationLogger( + rt.Logger(), + "[#%d] TCPConnect %s", + trace.Index(), + input.Address, + ) + + // setup + const timeout = 15 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // obtain the dialer to use + dialer := trace.NewDialerWithoutResolver(rt.Logger()) + + // connect + conn, err := dialer.DialContext(ctx, "tcp", input.Address) + + // possibly register established conn for late close + rt.MaybeTrackConn(conn) + + // stop the operation logger + ol.Stop(err) + + state := &TCPConnection{ + Address: input.Address, + Conn: conn, // possibly nil + Domain: input.Domain, + Network: input.Network, + Trace: trace, + } + + return &Maybe[*TCPConnection]{ + Error: err, + Observations: maybeTraceToObservations(trace), + Operation: netxlite.ConnectOperation, + State: state, + } + }) } // TCPConnection is an established TCP connection. If you initialize diff --git a/internal/dslx/tcp_test.go b/internal/dslx/tcp_test.go index 8748b634bb..f5f1e28532 100644 --- a/internal/dslx/tcp_test.go +++ b/internal/dslx/tcp_test.go @@ -13,15 +13,6 @@ import ( ) func TestTCPConnect(t *testing.T) { - t.Run("Get tcpConnectFunc", func(t *testing.T) { - f := TCPConnect( - NewMinimalRuntime(model.DiscardLogger, time.Now()), - ) - if _, ok := f.(*tcpConnectFunc); !ok { - t.Fatal("unexpected type. Expected: tcpConnectFunc") - } - }) - t.Run("Apply tcpConnectFunc", func(t *testing.T) { wasClosed := false plainConn := &mocks.Conn{ @@ -72,7 +63,7 @@ func TestTCPConnect(t *testing.T) { return tt.dialer }, })) - tcpConnect := &tcpConnectFunc{rt} + tcpConnect := TCPConnect(rt) endpoint := &Endpoint{ Address: "1.2.3.4:567", Network: "tcp", diff --git a/internal/dslx/tls.go b/internal/dslx/tls.go index 6ed1a63cdc..dd1f4f0838 100644 --- a/internal/dslx/tls.go +++ b/internal/dslx/tls.go @@ -48,75 +48,57 @@ func TLSHandshakeOptionServerName(value string) TLSHandshakeOption { } // TLSHandshake returns a function performing TSL handshakes. -func TLSHandshake(rt Runtime, options ...TLSHandshakeOption) Func[ - *TCPConnection, *Maybe[*TLSConnection]] { - f := &tlsHandshakeFunc{ - Options: options, - Rt: rt, - } - return f -} - -// tlsHandshakeFunc performs TLS handshakes. -type tlsHandshakeFunc struct { - // Options contains the options. - Options []TLSHandshakeOption - - // Rt is the Runtime that owns us. - Rt Runtime -} - -// Apply implements Func. -func (f *tlsHandshakeFunc) Apply( - ctx context.Context, input *TCPConnection) *Maybe[*TLSConnection] { - // keep using the same trace - trace := input.Trace - - // create a suitable TLS configuration - config := tlsNewConfig(input.Address, []string{"h2", "http/1.1"}, input.Domain, f.Rt.Logger(), f.Options...) - - // start the operation logger - ol := logx.NewOperationLogger( - f.Rt.Logger(), - "[#%d] TLSHandshake with %s SNI=%s ALPN=%v", - trace.Index(), - input.Address, - config.ServerName, - config.NextProtos, - ) - - // obtain the handshaker for use - handshaker := trace.NewTLSHandshakerStdlib(f.Rt.Logger()) - - // setup - const timeout = 10 * time.Second - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // handshake - conn, err := handshaker.Handshake(ctx, input.Conn, config) - - // possibly register established conn for late close - f.Rt.MaybeTrackConn(conn) - - // stop the operation logger - ol.Stop(err) - - state := &TLSConnection{ - Address: input.Address, - Conn: conn, // possibly nil - Domain: input.Domain, - Network: input.Network, - TLSState: netxlite.MaybeTLSConnectionState(conn), - Trace: trace, - } - - return &Maybe[*TLSConnection]{ - Error: err, - Observations: maybeTraceToObservations(trace), - Operation: netxlite.TLSHandshakeOperation, - State: state, - } +func TLSHandshake(rt Runtime, options ...TLSHandshakeOption) Func[*TCPConnection, *Maybe[*TLSConnection]] { + return FuncAdapter[*TCPConnection, *Maybe[*TLSConnection]](func(ctx context.Context, input *TCPConnection) *Maybe[*TLSConnection] { + // keep using the same trace + trace := input.Trace + + // create a suitable TLS configuration + config := tlsNewConfig(input.Address, []string{"h2", "http/1.1"}, input.Domain, rt.Logger(), options...) + + // start the operation logger + ol := logx.NewOperationLogger( + rt.Logger(), + "[#%d] TLSHandshake with %s SNI=%s ALPN=%v", + trace.Index(), + input.Address, + config.ServerName, + config.NextProtos, + ) + + // obtain the handshaker for use + handshaker := trace.NewTLSHandshakerStdlib(rt.Logger()) + + // setup + const timeout = 10 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // handshake + conn, err := handshaker.Handshake(ctx, input.Conn, config) + + // possibly register established conn for late close + rt.MaybeTrackConn(conn) + + // stop the operation logger + ol.Stop(err) + + state := &TLSConnection{ + Address: input.Address, + Conn: conn, // possibly nil + Domain: input.Domain, + Network: input.Network, + TLSState: netxlite.MaybeTLSConnectionState(conn), + Trace: trace, + } + + return &Maybe[*TLSConnection]{ + Error: err, + Observations: maybeTraceToObservations(trace), + Operation: netxlite.TLSHandshakeOperation, + State: state, + } + }) } // tlsNewConfig is an utility function to create a new TLS config.