11package connection
22
33import (
4- "errors"
54 "fmt"
65 "hash/fnv"
76 "sync"
@@ -13,63 +12,49 @@ import (
1312)
1413
1514const (
16- AvailableProtocolFlagMessage = "Available protocols: 'auto' - automatically chooses the best protocol over time (the default; and also the recommended one); 'quic' - based on QUIC, relying on UDP egress to Cloudflare edge; 'http2' - using Go's HTTP2 library, relying on TCP egress to Cloudflare edge; 'h2mux' - Cloudflare's implementation of HTTP/2, deprecated "
15+ AvailableProtocolFlagMessage = "Available protocols: 'auto' - automatically chooses the best protocol over time (the default; and also the recommended one); 'quic' - based on QUIC, relying on UDP egress to Cloudflare edge; 'http2' - using Go's HTTP2 library, relying on TCP egress to Cloudflare edge"
1716 // edgeH2muxTLSServerName is the server name to establish h2mux connection with edge
1817 edgeH2muxTLSServerName = "cftunnel.com"
1918 // edgeH2TLSServerName is the server name to establish http2 connection with edge
2019 edgeH2TLSServerName = "h2.cftunnel.com"
2120 // edgeQUICServerName is the server name to establish quic connection with edge.
2221 edgeQUICServerName = "quic.cftunnel.com"
2322 AutoSelectFlag = "auto"
23+ // SRV and TXT record resolution TTL
24+ ResolveTTL = time .Hour
2425)
2526
2627var (
2728 // ProtocolList represents a list of supported protocols for communication with the edge.
28- ProtocolList = []Protocol {H2mux , HTTP2 , HTTP2Warp , QUIC , QUICWarp }
29+ ProtocolList = []Protocol {HTTP2 , QUIC }
2930)
3031
3132type Protocol int64
3233
3334const (
34- // H2mux protocol can be used both with Classic and Named Tunnels. .
35- H2mux Protocol = iota
36- // HTTP2 is used only with named tunnels. It's more efficient than H2mux for L4 proxying.
37- HTTP2
38- // QUIC is used only with named tunnels.
35+ // HTTP2 using golang HTTP2 library for edge connections.
36+ HTTP2 Protocol = iota
37+ // QUIC using quic-go for edge connections.
3938 QUIC
40- // HTTP2Warp is used only with named tunnels. It's useful for warp-routing where we don't want to fallback to
41- // H2mux on HTTP2 failure to connect.
42- HTTP2Warp
43- //QUICWarp is used only with named tunnels. It's useful for warp-routing where we want to fallback to HTTP2 but
44- // don't want HTTP2 to fallback to H2mux
45- QUICWarp
4639)
4740
4841// Fallback returns the fallback protocol and whether the protocol has a fallback
4942func (p Protocol ) fallback () (Protocol , bool ) {
5043 switch p {
51- case H2mux :
52- return 0 , false
5344 case HTTP2 :
54- return H2mux , true
55- case HTTP2Warp :
5645 return 0 , false
5746 case QUIC :
5847 return HTTP2 , true
59- case QUICWarp :
60- return HTTP2Warp , true
6148 default :
6249 return 0 , false
6350 }
6451}
6552
6653func (p Protocol ) String () string {
6754 switch p {
68- case H2mux :
69- return "h2mux"
70- case HTTP2 , HTTP2Warp :
55+ case HTTP2 :
7156 return "http2"
72- case QUIC , QUICWarp :
57+ case QUIC :
7358 return "quic"
7459 default :
7560 return fmt .Sprintf ("unknown protocol" )
@@ -78,15 +63,11 @@ func (p Protocol) String() string {
7863
7964func (p Protocol ) TLSSettings () * TLSSettings {
8065 switch p {
81- case H2mux :
82- return & TLSSettings {
83- ServerName : edgeH2muxTLSServerName ,
84- }
85- case HTTP2 , HTTP2Warp :
66+ case HTTP2 :
8667 return & TLSSettings {
8768 ServerName : edgeH2TLSServerName ,
8869 }
89- case QUIC , QUICWarp :
70+ case QUIC :
9071 return & TLSSettings {
9172 ServerName : edgeQUICServerName ,
9273 NextProtos : []string {"argotunnel" },
@@ -106,6 +87,7 @@ type ProtocolSelector interface {
10687 Fallback () (Protocol , bool )
10788}
10889
90+ // staticProtocolSelector will not provide a different protocol for Fallback
10991type staticProtocolSelector struct {
11092 current Protocol
11193}
@@ -115,10 +97,11 @@ func (s *staticProtocolSelector) Current() Protocol {
11597}
11698
11799func (s * staticProtocolSelector ) Fallback () (Protocol , bool ) {
118- return 0 , false
100+ return s . current , false
119101}
120102
121- type autoProtocolSelector struct {
103+ // remoteProtocolSelector will fetch a list of remote protocols to provide for edge discovery
104+ type remoteProtocolSelector struct {
122105 lock sync.RWMutex
123106
124107 current Protocol
@@ -127,35 +110,32 @@ type autoProtocolSelector struct {
127110 protocolPool []Protocol
128111
129112 switchThreshold int32
130- fetchFunc PercentageFetcher
113+ fetchFunc edgediscovery. PercentageFetcher
131114 refreshAfter time.Time
132115 ttl time.Duration
133116 log * zerolog.Logger
134- needPQ bool
135117}
136118
137- func newAutoProtocolSelector (
119+ func newRemoteProtocolSelector (
138120 current Protocol ,
139121 protocolPool []Protocol ,
140122 switchThreshold int32 ,
141- fetchFunc PercentageFetcher ,
123+ fetchFunc edgediscovery. PercentageFetcher ,
142124 ttl time.Duration ,
143125 log * zerolog.Logger ,
144- needPQ bool ,
145- ) * autoProtocolSelector {
146- return & autoProtocolSelector {
126+ ) * remoteProtocolSelector {
127+ return & remoteProtocolSelector {
147128 current : current ,
148129 protocolPool : protocolPool ,
149130 switchThreshold : switchThreshold ,
150131 fetchFunc : fetchFunc ,
151132 refreshAfter : time .Now ().Add (ttl ),
152133 ttl : ttl ,
153134 log : log ,
154- needPQ : needPQ ,
155135 }
156136}
157137
158- func (s * autoProtocolSelector ) Current () Protocol {
138+ func (s * remoteProtocolSelector ) Current () Protocol {
159139 s .lock .Lock ()
160140 defer s .lock .Unlock ()
161141 if time .Now ().Before (s .refreshAfter ) {
@@ -173,7 +153,13 @@ func (s *autoProtocolSelector) Current() Protocol {
173153 return s .current
174154}
175155
176- func getProtocol (protocolPool []Protocol , fetchFunc PercentageFetcher , switchThreshold int32 ) (Protocol , error ) {
156+ func (s * remoteProtocolSelector ) Fallback () (Protocol , bool ) {
157+ s .lock .RLock ()
158+ defer s .lock .RUnlock ()
159+ return s .current .fallback ()
160+ }
161+
162+ func getProtocol (protocolPool []Protocol , fetchFunc edgediscovery.PercentageFetcher , switchThreshold int32 ) (Protocol , error ) {
177163 protocolPercentages , err := fetchFunc ()
178164 if err != nil {
179165 return 0 , err
@@ -188,109 +174,74 @@ func getProtocol(protocolPool []Protocol, fetchFunc PercentageFetcher, switchThr
188174 return protocolPool [len (protocolPool )- 1 ], nil
189175}
190176
191- func (s * autoProtocolSelector ) Fallback () (Protocol , bool ) {
177+ // defaultProtocolSelector will allow for a protocol to have a fallback
178+ type defaultProtocolSelector struct {
179+ lock sync.RWMutex
180+ current Protocol
181+ }
182+
183+ func newDefaultProtocolSelector (
184+ current Protocol ,
185+ ) * defaultProtocolSelector {
186+ return & defaultProtocolSelector {
187+ current : current ,
188+ }
189+ }
190+
191+ func (s * defaultProtocolSelector ) Current () Protocol {
192+ s .lock .Lock ()
193+ defer s .lock .Unlock ()
194+ return s .current
195+ }
196+
197+ func (s * defaultProtocolSelector ) Fallback () (Protocol , bool ) {
192198 s .lock .RLock ()
193199 defer s .lock .RUnlock ()
194- if s .needPQ {
195- return 0 , false
196- }
197200 return s .current .fallback ()
198201}
199202
200- type PercentageFetcher func () (edgediscovery.ProtocolPercents , error )
201-
202203func NewProtocolSelector (
203204 protocolFlag string ,
204- warpRoutingEnabled bool ,
205- namedTunnel * NamedTunnelProperties ,
206- fetchFunc PercentageFetcher ,
207- ttl time.Duration ,
208- log * zerolog.Logger ,
205+ accountTag string ,
206+ tunnelTokenProvided bool ,
209207 needPQ bool ,
208+ protocolFetcher edgediscovery.PercentageFetcher ,
209+ resolveTTL time.Duration ,
210+ log * zerolog.Logger ,
210211) (ProtocolSelector , error ) {
211- // Classic tunnel is only supported with h2mux
212- if namedTunnel == nil {
213- if needPQ {
214- return nil , errors .New ("Classic tunnel does not support post-quantum" )
215- }
216-
212+ // With --post-quantum, we force quic
213+ if needPQ {
217214 return & staticProtocolSelector {
218- current : H2mux ,
215+ current : QUIC ,
219216 }, nil
220217 }
221218
222- threshold := switchThreshold (namedTunnel .Credentials .AccountTag )
223- fetchedProtocol , err := getProtocol ([]Protocol {QUIC , HTTP2 }, fetchFunc , threshold )
224- if err != nil && protocolFlag == "auto" {
225- log .Err (err ).Msg ("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can attempt `--protocol quic` instead." )
226- if needPQ {
227- return nil , errors .New ("http2 does not support post-quantum" )
228- }
229- return & staticProtocolSelector {
230- current : HTTP2 ,
231- }, nil
232- }
233- if warpRoutingEnabled {
234- if protocolFlag == H2mux .String () || fetchedProtocol == H2mux {
235- log .Warn ().Msg ("Warp routing is not supported in h2mux protocol. Upgrading to http2 to allow it." )
236- protocolFlag = HTTP2 .String ()
237- fetchedProtocol = HTTP2Warp
238- }
239- return selectWarpRoutingProtocols (protocolFlag , fetchFunc , ttl , log , threshold , fetchedProtocol , needPQ )
219+ // When a --token is provided, we want to start with QUIC but have fallback to HTTP2
220+ if tunnelTokenProvided {
221+ return newDefaultProtocolSelector (QUIC ), nil
240222 }
241223
242- return selectNamedTunnelProtocols (protocolFlag , fetchFunc , ttl , log , threshold , fetchedProtocol , needPQ )
243- }
224+ threshold := switchThreshold (accountTag )
225+ fetchedProtocol , err := getProtocol (ProtocolList , protocolFetcher , threshold )
226+ if err != nil {
227+ log .Warn ().Msg ("Unable to lookup protocol percentage." )
228+ // Falling through here since 'auto' is handled in the switch and failing
229+ // to do the protocol lookup isn't a failure since it can be triggered again
230+ // after the TTL.
231+ }
244232
245- func selectNamedTunnelProtocols (
246- protocolFlag string ,
247- fetchFunc PercentageFetcher ,
248- ttl time.Duration ,
249- log * zerolog.Logger ,
250- threshold int32 ,
251- protocol Protocol ,
252- needPQ bool ,
253- ) (ProtocolSelector , error ) {
254233 // If the user picks a protocol, then we stick to it no matter what.
255234 switch protocolFlag {
256- case H2mux .String ():
257- return & staticProtocolSelector {current : H2mux }, nil
235+ case "h2mux" :
236+ // Any users still requesting h2mux will be upgraded to http2 instead
237+ log .Warn ().Msg ("h2mux is no longer a supported protocol: upgrading edge connection to http2. Please remove '--protocol h2mux' from runtime arguments to remove this warning." )
238+ return & staticProtocolSelector {current : HTTP2 }, nil
258239 case QUIC .String ():
259240 return & staticProtocolSelector {current : QUIC }, nil
260241 case HTTP2 .String ():
261242 return & staticProtocolSelector {current : HTTP2 }, nil
262- }
263-
264- // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and
265- // fallback on failures.
266- if protocolFlag == AutoSelectFlag {
267- return newAutoProtocolSelector (protocol , []Protocol {QUIC , HTTP2 , H2mux }, threshold , fetchFunc , ttl , log , needPQ ), nil
268- }
269-
270- return nil , fmt .Errorf ("Unknown protocol %s, %s" , protocolFlag , AvailableProtocolFlagMessage )
271- }
272-
273- func selectWarpRoutingProtocols (
274- protocolFlag string ,
275- fetchFunc PercentageFetcher ,
276- ttl time.Duration ,
277- log * zerolog.Logger ,
278- threshold int32 ,
279- protocol Protocol ,
280- needPQ bool ,
281- ) (ProtocolSelector , error ) {
282- // If the user picks a protocol, then we stick to it no matter what.
283- switch protocolFlag {
284- case QUIC .String ():
285- return & staticProtocolSelector {current : QUICWarp }, nil
286- case HTTP2 .String ():
287- return & staticProtocolSelector {current : HTTP2Warp }, nil
288- }
289-
290- // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and
291- // fallback on failures.
292- if protocolFlag == AutoSelectFlag {
293- return newAutoProtocolSelector (protocol , []Protocol {QUICWarp , HTTP2Warp }, threshold , fetchFunc , ttl , log , needPQ ), nil
243+ case AutoSelectFlag :
244+ return newRemoteProtocolSelector (fetchedProtocol , ProtocolList , threshold , protocolFetcher , resolveTTL , log ), nil
294245 }
295246
296247 return nil , fmt .Errorf ("Unknown protocol %s, %s" , protocolFlag , AvailableProtocolFlagMessage )
0 commit comments