2021-08-17 10:29:06 +02:00
|
|
|
package websteps
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"errors"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/lucas-clemente/quic-go"
|
|
|
|
"github.com/lucas-clemente/quic-go/http3"
|
2021-08-18 16:10:27 +02:00
|
|
|
oohttp "github.com/ooni/oohttp"
|
2022-01-03 13:53:23 +01:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
2021-08-17 10:29:06 +02:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/runtimex"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrNoConnReuse = errors.New("cannot reuse connection")
|
|
|
|
|
|
|
|
func NewRequest(ctx context.Context, URL *url.URL, headers http.Header) *http.Request {
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", URL.String(), nil)
|
|
|
|
runtimex.PanicOnError(err, "NewRequestWithContect failed")
|
|
|
|
for k, vs := range headers {
|
|
|
|
for _, v := range vs {
|
|
|
|
req.Header.Add(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return req
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDialerResolver contructs a new dialer for TCP connections,
|
|
|
|
// with default, errorwrapping and resolve functionalities
|
2022-01-07 18:33:37 +01:00
|
|
|
func NewDialerResolver(resolver model.Resolver) model.Dialer {
|
2022-01-07 17:31:21 +01:00
|
|
|
var d model.Dialer = netxlite.DefaultDialer
|
|
|
|
d = &netxlite.ErrorWrapperDialer{Dialer: d}
|
2021-09-05 18:03:50 +02:00
|
|
|
d = &netxlite.DialerResolver{
|
2022-01-07 18:33:37 +01:00
|
|
|
Resolver: resolver,
|
2022-01-07 17:31:21 +01:00
|
|
|
Dialer: d,
|
2021-09-05 18:03:50 +02:00
|
|
|
}
|
2021-08-17 10:29:06 +02:00
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewQUICDialerResolver creates a new QUICDialerResolver
|
|
|
|
// with default, errorwrapping and resolve functionalities
|
2022-01-07 18:33:37 +01:00
|
|
|
func NewQUICDialerResolver(resolver model.Resolver) model.QUICDialer {
|
2022-01-07 17:31:21 +01:00
|
|
|
var ql model.QUICListener = &netxlite.QUICListenerStdlib{}
|
|
|
|
ql = &netxlite.ErrorWrapperQUICListener{QUICListener: ql}
|
|
|
|
var dialer model.QUICDialer = &netxlite.QUICDialerQUICGo{
|
2021-08-17 10:29:06 +02:00
|
|
|
QUICListener: ql,
|
|
|
|
}
|
2022-01-07 17:31:21 +01:00
|
|
|
dialer = &netxlite.ErrorWrapperQUICDialer{QUICDialer: dialer}
|
2021-09-05 18:03:50 +02:00
|
|
|
dialer = &netxlite.QUICDialerResolver{
|
2022-01-07 18:33:37 +01:00
|
|
|
Resolver: resolver,
|
2022-01-07 17:31:21 +01:00
|
|
|
Dialer: dialer,
|
2021-09-05 18:03:50 +02:00
|
|
|
}
|
2021-08-17 10:29:06 +02:00
|
|
|
return dialer
|
|
|
|
}
|
|
|
|
|
2021-08-18 16:10:27 +02:00
|
|
|
// NewSingleH3Transport creates an http3.RoundTripper.
|
2022-05-06 12:24:03 +02:00
|
|
|
func NewSingleH3Transport(qconn quic.EarlyConnection, tlscfg *tls.Config, qcfg *quic.Config) http.RoundTripper {
|
2021-08-17 10:29:06 +02:00
|
|
|
transport := &http3.RoundTripper{
|
|
|
|
DisableCompression: true,
|
|
|
|
TLSClientConfig: tlscfg,
|
|
|
|
QuicConfig: qcfg,
|
2022-05-06 12:24:03 +02:00
|
|
|
Dial: (&SingleDialerH3{qconn: &qconn}).Dial,
|
2021-08-17 10:29:06 +02:00
|
|
|
}
|
|
|
|
return transport
|
|
|
|
}
|
|
|
|
|
2021-08-18 16:10:27 +02:00
|
|
|
// NewSingleTransport creates a new HTTP transport with a single-use dialer.
|
|
|
|
func NewSingleTransport(conn net.Conn) http.RoundTripper {
|
2021-08-17 10:29:06 +02:00
|
|
|
singledialer := &SingleDialer{conn: &conn}
|
2021-08-18 16:10:27 +02:00
|
|
|
transport := newBaseTransport()
|
|
|
|
transport.DialContext = singledialer.DialContext
|
|
|
|
transport.DialTLSContext = singledialer.DialContext
|
|
|
|
return transport
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewSingleTransport creates a new HTTP transport with a custom dialer and handshaker.
|
2022-01-07 18:33:37 +01:00
|
|
|
func NewTransportWithDialer(dialer model.Dialer, tlsConfig *tls.Config, handshaker model.TLSHandshaker) http.RoundTripper {
|
2021-08-18 16:10:27 +02:00
|
|
|
transport := newBaseTransport()
|
|
|
|
transport.DialContext = dialer.DialContext
|
2021-09-06 14:12:30 +02:00
|
|
|
transport.DialTLSContext = (&netxlite.TLSDialerLegacy{
|
2021-08-18 16:10:27 +02:00
|
|
|
Config: tlsConfig,
|
2022-01-07 18:33:37 +01:00
|
|
|
Dialer: dialer,
|
2021-08-18 16:10:27 +02:00
|
|
|
TLSHandshaker: handshaker,
|
|
|
|
}).DialTLSContext
|
|
|
|
return transport
|
|
|
|
}
|
2021-08-17 10:29:06 +02:00
|
|
|
|
2021-08-18 16:10:27 +02:00
|
|
|
// newBaseTransport creates a new HTTP transport with the default dialer.
|
|
|
|
func newBaseTransport() (transport *oohttp.StdlibTransport) {
|
|
|
|
base := oohttp.DefaultTransport.(*oohttp.Transport).Clone()
|
|
|
|
base.DisableCompression = true
|
|
|
|
base.MaxConnsPerHost = 1
|
|
|
|
transport = &oohttp.StdlibTransport{Transport: base}
|
2021-08-17 10:29:06 +02:00
|
|
|
return transport
|
|
|
|
}
|
|
|
|
|
|
|
|
type SingleDialer struct {
|
|
|
|
sync.Mutex
|
|
|
|
conn *net.Conn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SingleDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
if s.conn == nil {
|
|
|
|
return nil, ErrNoConnReuse
|
|
|
|
}
|
|
|
|
c := s.conn
|
|
|
|
s.conn = nil
|
|
|
|
return *c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type SingleDialerH3 struct {
|
|
|
|
sync.Mutex
|
2022-05-06 12:24:03 +02:00
|
|
|
qconn *quic.EarlyConnection
|
2021-08-17 10:29:06 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 12:24:03 +02:00
|
|
|
func (s *SingleDialerH3) Dial(ctx context.Context, network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
2021-08-17 10:29:06 +02:00
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
2022-05-06 12:24:03 +02:00
|
|
|
if s.qconn == nil {
|
2021-08-17 10:29:06 +02:00
|
|
|
return nil, ErrNoConnReuse
|
|
|
|
}
|
2022-05-06 12:24:03 +02:00
|
|
|
qs := s.qconn
|
|
|
|
s.qconn = nil
|
2021-08-17 10:29:06 +02:00
|
|
|
return *qs, nil
|
|
|
|
}
|