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/header.go

140lines · modecode

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