cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
d78a5ba5da61c3df6cebf2c8254bdfc2aa6a0106

Branches

Tags

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

Clone

HTTPS

Download ZIP

connection/connection.go

183lines · modecode

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