cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2021.12.4

Branches

Tags

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

Clone

HTTPS

Download ZIP

connection/connection.go

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