/
httpquirks.go
136 lines (120 loc) · 5.51 KB
/
httpquirks.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package netxlite
//
// QUIRKy Legacy HTTP code and behavior assumed by ./legacy/netx 😅
//
// Ideally, we should not modify this code or apply minimal and obvious changes.
//
// TODO(https://github.com/ooni/probe/issues/2534)
//
import (
"net/http"
oohttp "github.com/ooni/oohttp"
"github.com/ooni/probe-engine/pkg/model"
)
// NewHTTPTransportWithResolver creates a new HTTP transport using
// the stdlib for everything but the given resolver.
//
// This function behavior is QUIRKY as documented in [NewHTTPTransport].
func NewHTTPTransportWithResolver(netx *Netx, logger model.DebugLogger, reso model.Resolver) model.HTTPTransport {
dialer := netx.NewDialerWithResolver(logger, reso)
thx := netx.NewTLSHandshakerStdlib(logger)
tlsDialer := NewTLSDialer(dialer, thx)
return NewHTTPTransport(logger, dialer, tlsDialer)
}
// NewHTTPTransport returns a wrapped HTTP transport for HTTP2 and HTTP/1.1
// using the given dialer and logger.
//
// The returned transport will gracefully handle TLS connections
// created using gitlab.com/yawning/utls.git, if the TLS dialer
// is a dialer using such library for TLS operations.
//
// The returned transport will not have a configured proxy, not
// even the proxy configurable from the environment.
//
// QUIRK: the returned transport will disable transparent decompression
// of compressed response bodies (and will not automatically
// ask for such compression, though you can always do that manually).
//
// The returned transport will configure TCP and TLS connections
// created using its dialer and TLS dialer to always have a
// read watchdog timeout to address https://github.com/ooni/probe/issues/1609.
//
// QUIRK: the returned transport will always enforce 1 connection per host
// and we cannot get rid of this QUIRK requirement because it is
// necessary to perform sane measurements with tracing. We will be
// able to possibly relax this requirement after we change the
// way in which we perform measurements.
func NewHTTPTransport(logger model.DebugLogger, dialer model.Dialer, tlsDialer model.TLSDialer) model.HTTPTransport {
return WrapHTTPTransport(logger, newOOHTTPBaseTransport(dialer, tlsDialer))
}
// newOOHTTPBaseTransport is the low-level factory used by NewHTTPTransport
// to create a new, suitable HTTPTransport for HTTP2 and HTTP/1.1.
//
// This factory uses github.com/ooni/oohttp, hence its name.
//
// This function behavior is QUIRKY as documented in [NewHTTPTransport].
func newOOHTTPBaseTransport(dialer model.Dialer, tlsDialer model.TLSDialer) model.HTTPTransport {
// Using oohttp to support any TLS library.
txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone()
// This wrapping ensures that we always have a timeout when we
// are using HTTP; see https://github.com/ooni/probe/issues/1609.
dialer = &httpDialerWithReadTimeout{dialer}
txp.DialContext = dialer.DialContext
tlsDialer = &httpTLSDialerWithReadTimeout{tlsDialer}
txp.DialTLSContext = tlsDialer.DialTLSContext
// We are using a different strategy to implement proxy: we
// use a specific dialer that knows about proxying.
txp.Proxy = nil
// Better for Cloudflare DNS and also better because we have less
// noisy events and we can better understand what happened.
txp.MaxConnsPerHost = 1
// The following (1) reduces the number of headers that Go will
// automatically send for us and (2) ensures that we always receive
// back the true headers, such as Content-Length. This change is
// functional to OONI's goal of observing the network.
txp.DisableCompression = true
// Required to enable using HTTP/2 (which will be anyway forced
// upon us when we are using TLS parroting).
txp.ForceAttemptHTTP2 = true
// Ensure we correctly forward CloseIdleConnections.
return &httpTransportConnectionsCloser{
HTTPTransport: &httpTransportStdlib{&oohttp.StdlibTransport{Transport: txp}},
Dialer: dialer,
TLSDialer: tlsDialer,
}
}
// NewHTTPTransportStdlib creates a new HTTPTransport using
// the stdlib for DNS resolutions and TLS.
//
// This factory calls NewHTTPTransport with suitable dialers.
//
// This function behavior is QUIRKY as documented in [NewHTTPTransport].
func (netx *Netx) NewHTTPTransportStdlib(logger model.DebugLogger) model.HTTPTransport {
dialer := netx.NewDialerWithResolver(logger, netx.NewStdlibResolver(logger))
tlsDialer := NewTLSDialer(dialer, netx.NewTLSHandshakerStdlib(logger))
return NewHTTPTransport(logger, dialer, tlsDialer)
}
// NewHTTPClientStdlib creates a new HTTPClient that uses the
// standard library for TLS and DNS resolutions.
//
// This function behavior is QUIRKY as documented in [NewHTTPTransport].
func NewHTTPClientStdlib(logger model.DebugLogger) model.HTTPClient {
netx := &Netx{}
txp := netx.NewHTTPTransportStdlib(logger)
return NewHTTPClient(txp)
}
// NewHTTPClientWithResolver creates a new HTTPTransport using the
// given resolver and then from that builds an HTTPClient.
//
// This function behavior is QUIRKY as documented in [NewHTTPTransport].
func NewHTTPClientWithResolver(netx *Netx, logger model.Logger, reso model.Resolver) model.HTTPClient {
return NewHTTPClient(NewHTTPTransportWithResolver(netx, logger, reso))
}
// NewHTTPClient creates a new, wrapped HTTPClient using the given transport.
//
// This function behavior is QUIRKY because it does not configure a cookie jar, which
// is probably not the right thing to do in many cases, but legacy code MAY depend
// on this behavior. TODO(https://github.com/ooni/probe/issues/2534).
func NewHTTPClient(txp model.HTTPTransport) model.HTTPClient {
return WrapHTTPClient(&http.Client{Transport: txp})
}