cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
a490443630e424ef71a9726a91eb8029e75bd79f

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

connection/protocol.go

179lines · modecode

1package connection
2
3import (
4 "fmt"
5 "hash/fnv"
6 "sync"
7 "time"
8
9 "github.com/cloudflare/cloudflared/logger"
10)
11
12const (
13 AvailableProtocolFlagMessage = "Available protocols: http2 - Go's implementation, h2mux - Cloudflare's implementation of HTTP/2, and auto - automatically select between http2 and h2mux"
14 // edgeH2muxTLSServerName is the server name to establish h2mux connection with edge
15 edgeH2muxTLSServerName = "cftunnel.com"
16 // edgeH2TLSServerName is the server name to establish http2 connection with edge
17 edgeH2TLSServerName = "h2.cftunnel.com"
18 // threshold to switch back to h2mux when the user intentionally pick --protocol http2
19 explicitHTTP2FallbackThreshold = -1
20 autoSelectFlag = "auto"
21)
22
23var (
24 ProtocolList = []Protocol{H2mux, HTTP2}
25)
26
27type Protocol int64
28
29const (
30 H2mux Protocol = iota
31 HTTP2
32)
33
34func (p Protocol) ServerName() string {
35 switch p {
36 case H2mux:
37 return edgeH2muxTLSServerName
38 case HTTP2:
39 return edgeH2TLSServerName
40 default:
41 return ""
42 }
43}
44
45// Fallback returns the fallback protocol and whether the protocol has a fallback
46func (p Protocol) fallback() (Protocol, bool) {
47 switch p {
48 case H2mux:
49 return 0, false
50 case HTTP2:
51 return H2mux, true
52 default:
53 return 0, false
54 }
55}
56
57func (p Protocol) String() string {
58 switch p {
59 case H2mux:
60 return "h2mux"
61 case HTTP2:
62 return "http2"
63 default:
64 return fmt.Sprintf("unknown protocol")
65 }
66}
67
68type ProtocolSelector interface {
69 Current() Protocol
70 Fallback() (Protocol, bool)
71}
72
73type staticProtocolSelector struct {
74 current Protocol
75}
76
77func (s *staticProtocolSelector) Current() Protocol {
78 return s.current
79}
80
81func (s *staticProtocolSelector) Fallback() (Protocol, bool) {
82 return 0, false
83}
84
85type autoProtocolSelector struct {
86 lock sync.RWMutex
87 current Protocol
88 switchThrehold int32
89 fetchFunc PercentageFetcher
90 refreshAfter time.Time
91 ttl time.Duration
92 logger logger.Service
93}
94
95func newAutoProtocolSelector(
96 current Protocol,
97 switchThrehold int32,
98 fetchFunc PercentageFetcher,
99 ttl time.Duration,
100 logger logger.Service,
101) *autoProtocolSelector {
102 return &autoProtocolSelector{
103 current: current,
104 switchThrehold: switchThrehold,
105 fetchFunc: fetchFunc,
106 refreshAfter: time.Now().Add(ttl),
107 ttl: ttl,
108 logger: logger,
109 }
110}
111
112func (s *autoProtocolSelector) Current() Protocol {
113 s.lock.Lock()
114 defer s.lock.Unlock()
115 if time.Now().Before(s.refreshAfter) {
116 return s.current
117 }
118
119 percentage, err := s.fetchFunc()
120 if err != nil {
121 s.logger.Errorf("Failed to refresh protocol, err: %v", err)
122 return s.current
123 }
124
125 if s.switchThrehold < percentage {
126 s.current = HTTP2
127 } else {
128 s.current = H2mux
129 }
130 s.refreshAfter = time.Now().Add(s.ttl)
131 return s.current
132}
133
134func (s *autoProtocolSelector) Fallback() (Protocol, bool) {
135 s.lock.RLock()
136 defer s.lock.RUnlock()
137 return s.current.fallback()
138}
139
140type PercentageFetcher func() (int32, error)
141
142func NewProtocolSelector(protocolFlag string, namedTunnel *NamedTunnelConfig, fetchFunc PercentageFetcher, ttl time.Duration, logger logger.Service) (ProtocolSelector, error) {
143 if namedTunnel == nil {
144 return &staticProtocolSelector{
145 current: H2mux,
146 }, nil
147 }
148 if protocolFlag == H2mux.String() {
149 return &staticProtocolSelector{
150 current: H2mux,
151 }, nil
152 }
153
154 http2Percentage, err := fetchFunc()
155 if err != nil {
156 return nil, err
157 }
158 if protocolFlag == HTTP2.String() {
159 if http2Percentage < 0 {
160 return newAutoProtocolSelector(H2mux, explicitHTTP2FallbackThreshold, fetchFunc, ttl, logger), nil
161 }
162 return newAutoProtocolSelector(HTTP2, explicitHTTP2FallbackThreshold, fetchFunc, ttl, logger), nil
163 }
164
165 if protocolFlag != autoSelectFlag {
166 return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage)
167 }
168 threshold := switchThreshold(namedTunnel.Auth.AccountTag)
169 if threshold < http2Percentage {
170 return newAutoProtocolSelector(HTTP2, threshold, fetchFunc, ttl, logger), nil
171 }
172 return newAutoProtocolSelector(H2mux, threshold, fetchFunc, ttl, logger), nil
173}
174
175func switchThreshold(accountTag string) int32 {
176 h := fnv.New32a()
177 h.Write([]byte(accountTag))
178 return int32(h.Sum32() % 100)
179}
180