cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2024.11.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

connection/header.go

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