cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
98deb95eaec1a2e2e43320e526e6a192a4eba249

Branches

Tags

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

Clone

HTTPS

Download ZIP

connection/connection.go

209lines · modecode

1package connection
2
3import (
4 "context"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "math"
9 "net/http"
10 "strconv"
11 "strings"
12 "time"
13
14 "github.com/google/uuid"
15 "github.com/pkg/errors"
16
17 "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
18 "github.com/cloudflare/cloudflared/websocket"
19)
20
21const (
22 lbProbeUserAgentPrefix = "Mozilla/5.0 (compatible; Cloudflare-Traffic-Manager/1.0; +https://www.cloudflare.com/traffic-manager/;"
23 LogFieldConnIndex = "connIndex"
24 MaxGracePeriod = time.Minute * 3
25 MaxConcurrentStreams = math.MaxUint32
26)
27
28var switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))
29
30type Orchestrator interface {
31 UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse
32 GetOriginProxy() (OriginProxy, error)
33}
34
35type NamedTunnelProperties struct {
36 Credentials Credentials
37 Client pogs.ClientInfo
38 QuickTunnelUrl string
39}
40
41// Credentials are stored in the credentials file and contain all info needed to run a tunnel.
42type Credentials struct {
43 AccountTag string
44 TunnelSecret []byte
45 TunnelID uuid.UUID
46}
47
48func (c *Credentials) Auth() pogs.TunnelAuth {
49 return pogs.TunnelAuth{
50 AccountTag: c.AccountTag,
51 TunnelSecret: c.TunnelSecret,
52 }
53}
54
55// TunnelToken are Credentials but encoded with custom fields namings.
56type TunnelToken struct {
57 AccountTag string `json:"a"`
58 TunnelSecret []byte `json:"s"`
59 TunnelID uuid.UUID `json:"t"`
60}
61
62func (t TunnelToken) Credentials() Credentials {
63 return Credentials{
64 AccountTag: t.AccountTag,
65 TunnelSecret: t.TunnelSecret,
66 TunnelID: t.TunnelID,
67 }
68}
69
70func (t TunnelToken) Encode() (string, error) {
71 val, err := json.Marshal(t)
72 if err != nil {
73 return "", errors.Wrap(err, "could not JSON encode token")
74 }
75
76 return base64.StdEncoding.EncodeToString(val), nil
77}
78
79type ClassicTunnelProperties struct {
80 Hostname string
81 OriginCert []byte
82 // feature-flag to use new edge reconnect tokens
83 UseReconnectToken bool
84}
85
86// Type indicates the connection type of the connection.
87type Type int
88
89const (
90 TypeWebsocket Type = iota
91 TypeTCP
92 TypeControlStream
93 TypeHTTP
94 TypeConfiguration
95)
96
97// ShouldFlush returns whether this kind of connection should actively flush data
98func (t Type) shouldFlush() bool {
99 switch t {
100 case TypeWebsocket, TypeTCP, TypeControlStream:
101 return true
102 default:
103 return false
104 }
105}
106
107func (t Type) String() string {
108 switch t {
109 case TypeWebsocket:
110 return "websocket"
111 case TypeTCP:
112 return "tcp"
113 case TypeControlStream:
114 return "control stream"
115 case TypeHTTP:
116 return "http"
117 default:
118 return fmt.Sprintf("Unknown Type %d", t)
119 }
120}
121
122// OriginProxy is how data flows from cloudflared to the origin services running behind it.
123type OriginProxy interface {
124 ProxyHTTP(w ResponseWriter, req *http.Request, isWebsocket bool) error
125 ProxyTCP(ctx context.Context, rwa ReadWriteAcker, req *TCPRequest) error
126}
127
128// TCPRequest defines the input format needed to perform a TCP proxy.
129type TCPRequest struct {
130 Dest string
131 CFRay string
132 LBProbe bool
133}
134
135// ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has
136// accepted the connection.
137type ReadWriteAcker interface {
138 io.ReadWriter
139 AckConnection() error
140}
141
142// HTTPResponseReadWriteAcker is an HTTP implementation of ReadWriteAcker.
143type HTTPResponseReadWriteAcker struct {
144 r io.Reader
145 w ResponseWriter
146 req *http.Request
147}
148
149// NewHTTPResponseReadWriterAcker returns a new instance of HTTPResponseReadWriteAcker.
150func NewHTTPResponseReadWriterAcker(w ResponseWriter, req *http.Request) *HTTPResponseReadWriteAcker {
151 return &HTTPResponseReadWriteAcker{
152 r: req.Body,
153 w: w,
154 req: req,
155 }
156}
157
158func (h *HTTPResponseReadWriteAcker) Read(p []byte) (int, error) {
159 return h.r.Read(p)
160}
161
162func (h *HTTPResponseReadWriteAcker) Write(p []byte) (int, error) {
163 return h.w.Write(p)
164}
165
166// AckConnection acks an HTTP connection by sending a switch protocols status code that enables the caller to
167// upgrade to streams.
168func (h *HTTPResponseReadWriteAcker) AckConnection() error {
169 resp := &http.Response{
170 Status: switchingProtocolText,
171 StatusCode: http.StatusSwitchingProtocols,
172 ContentLength: -1,
173 }
174
175 if secWebsocketKey := h.req.Header.Get("Sec-WebSocket-Key"); secWebsocketKey != "" {
176 resp.Header = websocket.NewResponseHeader(h.req)
177 }
178
179 return h.w.WriteRespHeaders(resp.StatusCode, resp.Header)
180}
181
182type ResponseWriter interface {
183 WriteRespHeaders(status int, header http.Header) error
184 io.Writer
185}
186
187type ConnectedFuse interface {
188 Connected()
189 IsConnected() bool
190}
191
192func IsServerSentEvent(headers http.Header) bool {
193 if contentType := headers.Get("content-type"); contentType != "" {
194 return strings.HasPrefix(strings.ToLower(contentType), "text/event-stream")
195 }
196 return false
197}
198
199func uint8ToString(input uint8) string {
200 return strconv.FormatUint(uint64(input), 10)
201}
202
203func FindCfRayHeader(req *http.Request) string {
204 return req.Header.Get("Cf-Ray")
205}
206
207func IsLBProbeRequest(req *http.Request) bool {
208 return strings.HasPrefix(req.UserAgent(), lbProbeUserAgentPrefix)
209}
210