cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2025.2.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

connection/header.go

147lines · modecode

1package connection
2
3import (
4 "encoding/base64"
5 "fmt"
6 "net/http"
7 "strings"
8
9 "github.com/pkg/errors"
10)
11
12var (
13 // internal special headers
14 RequestUserHeaders = "cf-cloudflared-request-headers"
15 ResponseUserHeaders = "cf-cloudflared-response-headers"
16 ResponseMetaHeader = "cf-cloudflared-response-meta"
17
18 // internal special headers
19 CanonicalResponseUserHeaders = http.CanonicalHeaderKey(ResponseUserHeaders)
20 CanonicalResponseMetaHeader = http.CanonicalHeaderKey(ResponseMetaHeader)
21)
22
23var (
24 // pre-generate possible values for res
25 responseMetaHeaderCfd = mustInitRespMetaHeader("cloudflared", false)
26 responseMetaHeaderCfdFlowRateLimited = mustInitRespMetaHeader("cloudflared", true)
27 responseMetaHeaderOrigin = mustInitRespMetaHeader("origin", false)
28)
29
30// HTTPHeader is a custom header struct that expects only ever one value for the header.
31// This structure is used to serialize the headers and attach them to the HTTP2 request when proxying.
32type HTTPHeader struct {
33 Name string
34 Value string
35}
36
37type responseMetaHeader struct {
38 Source string `json:"src"`
39 FlowRateLimited bool `json:"flow_rate_limited,omitempty"`
40}
41
42func mustInitRespMetaHeader(src string, flowRateLimited bool) string {
43 header, err := json.Marshal(responseMetaHeader{Source: src, FlowRateLimited: flowRateLimited})
44 if err != nil {
45 panic(fmt.Sprintf("Failed to serialize response meta header = %s, err: %v", src, err))
46 }
47 return string(header)
48}
49
50var headerEncoding = base64.RawStdEncoding
51
52// IsControlResponseHeader is called in the direction of eyeball <- origin.
53func IsControlResponseHeader(headerName string) bool {
54 return strings.HasPrefix(headerName, ":") ||
55 strings.HasPrefix(headerName, "cf-int-") ||
56 strings.HasPrefix(headerName, "cf-cloudflared-")
57}
58
59// isWebsocketClientHeader returns true if the header name is required by the client to upgrade properly
60func IsWebsocketClientHeader(headerName string) bool {
61 return headerName == "sec-websocket-accept" ||
62 headerName == "connection" ||
63 headerName == "upgrade"
64}
65
66// Serialize HTTP1.x headers by base64-encoding each header name and value,
67// and then joining them in the format of [key:value;]
68func SerializeHeaders(h1Headers http.Header) string {
69 // compute size of the fully serialized value and largest temp buffer we will need
70 serializedLen := 0
71 maxTempLen := 0
72 for headerName, headerValues := range h1Headers {
73 for _, headerValue := range headerValues {
74 nameLen := headerEncoding.EncodedLen(len(headerName))
75 valueLen := headerEncoding.EncodedLen(len(headerValue))
76 const delims = 2
77 serializedLen += delims + nameLen + valueLen
78 if nameLen > maxTempLen {
79 maxTempLen = nameLen
80 }
81 if valueLen > maxTempLen {
82 maxTempLen = valueLen
83 }
84 }
85 }
86 var buf strings.Builder
87 buf.Grow(serializedLen)
88
89 temp := make([]byte, maxTempLen)
90 writeB64 := func(s string) {
91 n := headerEncoding.EncodedLen(len(s))
92 if n > len(temp) {
93 temp = make([]byte, n)
94 }
95 headerEncoding.Encode(temp[:n], []byte(s))
96 buf.Write(temp[:n])
97 }
98
99 for headerName, headerValues := range h1Headers {
100 for _, headerValue := range headerValues {
101 if buf.Len() > 0 {
102 buf.WriteByte(';')
103 }
104 writeB64(headerName)
105 buf.WriteByte(':')
106 writeB64(headerValue)
107 }
108 }
109
110 return buf.String()
111}
112
113// Deserialize headers serialized by `SerializeHeader`
114func DeserializeHeaders(serializedHeaders string) ([]HTTPHeader, error) {
115 const unableToDeserializeErr = "Unable to deserialize headers"
116
117 deserialized := make([]HTTPHeader, 0)
118 for _, serializedPair := range strings.Split(serializedHeaders, ";") {
119 if len(serializedPair) == 0 {
120 continue
121 }
122
123 serializedHeaderParts := strings.Split(serializedPair, ":")
124 if len(serializedHeaderParts) != 2 {
125 return nil, errors.New(unableToDeserializeErr)
126 }
127
128 serializedName := serializedHeaderParts[0]
129 serializedValue := serializedHeaderParts[1]
130 deserializedName := make([]byte, headerEncoding.DecodedLen(len(serializedName)))
131 deserializedValue := make([]byte, headerEncoding.DecodedLen(len(serializedValue)))
132
133 if _, err := headerEncoding.Decode(deserializedName, []byte(serializedName)); err != nil {
134 return nil, errors.Wrap(err, unableToDeserializeErr)
135 }
136 if _, err := headerEncoding.Decode(deserializedValue, []byte(serializedValue)); err != nil {
137 return nil, errors.Wrap(err, unableToDeserializeErr)
138 }
139
140 deserialized = append(deserialized, HTTPHeader{
141 Name: string(deserializedName),
142 Value: string(deserializedValue),
143 })
144 }
145
146 return deserialized, nil
147}
148