header.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package h2mux
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. "github.com/pkg/errors"
  11. )
  12. type Header struct {
  13. Name, Value string
  14. }
  15. var headerEncoding = base64.RawStdEncoding
  16. const (
  17. RequestUserHeadersField = "cf-cloudflared-request-headers"
  18. ResponseUserHeadersField = "cf-cloudflared-response-headers"
  19. ResponseMetaHeaderField = "cf-cloudflared-response-meta"
  20. ResponseSourceCloudflared = "cloudflared"
  21. ResponseSourceOrigin = "origin"
  22. CFAccessTokenHeader = "cf-access-token"
  23. CFJumpDestinationHeader = "CF-Access-Jump-Destination"
  24. CFAccessClientIDHeader = "CF-Access-Client-Id"
  25. CFAccessClientSecretHeader = "CF-Access-Client-Secret"
  26. )
  27. // H2RequestHeadersToH1Request converts the HTTP/2 headers coming from origintunneld
  28. // to an HTTP/1 Request object destined for the local origin web service.
  29. // This operation includes conversion of the pseudo-headers into their closest
  30. // HTTP/1 equivalents. See https://tools.ietf.org/html/rfc7540#section-8.1.2.3
  31. func H2RequestHeadersToH1Request(h2 []Header, h1 *http.Request) error {
  32. for _, header := range h2 {
  33. name := strings.ToLower(header.Name)
  34. if !IsControlHeader(name) {
  35. continue
  36. }
  37. switch name {
  38. case ":method":
  39. h1.Method = header.Value
  40. case ":scheme":
  41. // noop - use the preexisting scheme from h1.URL
  42. case ":authority":
  43. // Otherwise the host header will be based on the origin URL
  44. h1.Host = header.Value
  45. case ":path":
  46. // We don't want to be an "opinionated" proxy, so ideally we would use :path as-is.
  47. // However, this HTTP/1 Request object belongs to the Go standard library,
  48. // whose URL package makes some opinionated decisions about the encoding of
  49. // URL characters: see the docs of https://godoc.org/net/url#URL,
  50. // in particular the EscapedPath method https://godoc.org/net/url#URL.EscapedPath,
  51. // which is always used when computing url.URL.String(), whether we'd like it or not.
  52. //
  53. // Well, not *always*. We could circumvent this by using url.URL.Opaque. But
  54. // that would present unusual difficulties when using an HTTP proxy: url.URL.Opaque
  55. // is treated differently when HTTP_PROXY is set!
  56. // See https://github.com/golang/go/issues/5684#issuecomment-66080888
  57. //
  58. // This means we are subject to the behavior of net/url's function `shouldEscape`
  59. // (as invoked with mode=encodePath): https://github.com/golang/go/blob/go1.12.7/src/net/url/url.go#L101
  60. if header.Value == "*" {
  61. h1.URL.Path = "*"
  62. continue
  63. }
  64. // Due to the behavior of validation.ValidateUrl, h1.URL may
  65. // already have a partial value, with or without a trailing slash.
  66. base := h1.URL.String()
  67. base = strings.TrimRight(base, "/")
  68. // But we know :path begins with '/', because we handled '*' above - see RFC7540
  69. requestURL, err := url.Parse(base + header.Value)
  70. if err != nil {
  71. return errors.Wrap(err, fmt.Sprintf("invalid path '%v'", header.Value))
  72. }
  73. h1.URL = requestURL
  74. case "content-length":
  75. contentLength, err := strconv.ParseInt(header.Value, 10, 64)
  76. if err != nil {
  77. return fmt.Errorf("unparseable content length")
  78. }
  79. h1.ContentLength = contentLength
  80. case RequestUserHeadersField:
  81. // Do not forward the serialized headers to the origin -- deserialize them, and ditch the serialized version
  82. // Find and parse user headers serialized into a single one
  83. userHeaders, err := ParseUserHeaders(RequestUserHeadersField, h2)
  84. if err != nil {
  85. return errors.Wrap(err, "Unable to parse user headers")
  86. }
  87. for _, userHeader := range userHeaders {
  88. h1.Header.Add(http.CanonicalHeaderKey(userHeader.Name), userHeader.Value)
  89. }
  90. default:
  91. // All other control headers shall just be proxied transparently
  92. h1.Header.Add(http.CanonicalHeaderKey(header.Name), header.Value)
  93. }
  94. }
  95. return nil
  96. }
  97. func ParseUserHeaders(headerNameToParseFrom string, headers []Header) ([]Header, error) {
  98. for _, header := range headers {
  99. if header.Name == headerNameToParseFrom {
  100. return DeserializeHeaders(header.Value)
  101. }
  102. }
  103. return nil, fmt.Errorf("%v header not found", RequestUserHeadersField)
  104. }
  105. func IsControlHeader(headerName string) bool {
  106. return headerName == "content-length" ||
  107. headerName == "connection" || headerName == "upgrade" || // Websocket headers
  108. strings.HasPrefix(headerName, ":") ||
  109. strings.HasPrefix(headerName, "cf-")
  110. }
  111. // isWebsocketClientHeader returns true if the header name is required by the client to upgrade properly
  112. func isWebsocketClientHeader(headerName string) bool {
  113. return headerName == "sec-websocket-accept" ||
  114. headerName == "connection" ||
  115. headerName == "upgrade"
  116. }
  117. func H1ResponseToH2ResponseHeaders(h1 *http.Response) (h2 []Header) {
  118. h2 = []Header{
  119. {Name: ":status", Value: strconv.Itoa(h1.StatusCode)},
  120. }
  121. userHeaders := make(http.Header, len(h1.Header))
  122. for header, values := range h1.Header {
  123. h2name := strings.ToLower(header)
  124. if h2name == "content-length" {
  125. // This header has meaning in HTTP/2 and will be used by the edge,
  126. // so it should be sent as an HTTP/2 response header.
  127. // Since these are http2 headers, they're required to be lowercase
  128. h2 = append(h2, Header{Name: "content-length", Value: values[0]})
  129. } else if !IsControlHeader(h2name) || isWebsocketClientHeader(h2name) {
  130. // User headers, on the other hand, must all be serialized so that
  131. // HTTP/2 header validation won't be applied to HTTP/1 header values
  132. userHeaders[header] = values
  133. }
  134. }
  135. // Perform user header serialization and set them in the single header
  136. h2 = append(h2, Header{ResponseUserHeadersField, SerializeHeaders(userHeaders)})
  137. return h2
  138. }
  139. // Serialize HTTP1.x headers by base64-encoding each header name and value,
  140. // and then joining them in the format of [key:value;]
  141. func SerializeHeaders(h1Headers http.Header) string {
  142. // compute size of the fully serialized value and largest temp buffer we will need
  143. serializedLen := 0
  144. maxTempLen := 0
  145. for headerName, headerValues := range h1Headers {
  146. for _, headerValue := range headerValues {
  147. nameLen := headerEncoding.EncodedLen(len(headerName))
  148. valueLen := headerEncoding.EncodedLen(len(headerValue))
  149. const delims = 2
  150. serializedLen += delims + nameLen + valueLen
  151. if nameLen > maxTempLen {
  152. maxTempLen = nameLen
  153. }
  154. if valueLen > maxTempLen {
  155. maxTempLen = valueLen
  156. }
  157. }
  158. }
  159. var buf strings.Builder
  160. buf.Grow(serializedLen)
  161. temp := make([]byte, maxTempLen)
  162. writeB64 := func(s string) {
  163. n := headerEncoding.EncodedLen(len(s))
  164. if n > len(temp) {
  165. temp = make([]byte, n)
  166. }
  167. headerEncoding.Encode(temp[:n], []byte(s))
  168. buf.Write(temp[:n])
  169. }
  170. for headerName, headerValues := range h1Headers {
  171. for _, headerValue := range headerValues {
  172. if buf.Len() > 0 {
  173. buf.WriteByte(';')
  174. }
  175. writeB64(headerName)
  176. buf.WriteByte(':')
  177. writeB64(headerValue)
  178. }
  179. }
  180. return buf.String()
  181. }
  182. // Deserialize headers serialized by `SerializeHeader`
  183. func DeserializeHeaders(serializedHeaders string) ([]Header, error) {
  184. const unableToDeserializeErr = "Unable to deserialize headers"
  185. var deserialized []Header
  186. for _, serializedPair := range strings.Split(serializedHeaders, ";") {
  187. if len(serializedPair) == 0 {
  188. continue
  189. }
  190. serializedHeaderParts := strings.Split(serializedPair, ":")
  191. if len(serializedHeaderParts) != 2 {
  192. return nil, errors.New(unableToDeserializeErr)
  193. }
  194. serializedName := serializedHeaderParts[0]
  195. serializedValue := serializedHeaderParts[1]
  196. deserializedName := make([]byte, headerEncoding.DecodedLen(len(serializedName)))
  197. deserializedValue := make([]byte, headerEncoding.DecodedLen(len(serializedValue)))
  198. if _, err := headerEncoding.Decode(deserializedName, []byte(serializedName)); err != nil {
  199. return nil, errors.Wrap(err, unableToDeserializeErr)
  200. }
  201. if _, err := headerEncoding.Decode(deserializedValue, []byte(serializedValue)); err != nil {
  202. return nil, errors.Wrap(err, unableToDeserializeErr)
  203. }
  204. deserialized = append(deserialized, Header{
  205. Name: string(deserializedName),
  206. Value: string(deserializedValue),
  207. })
  208. }
  209. return deserialized, nil
  210. }
  211. type ResponseMetaHeader struct {
  212. Source string `json:"src"`
  213. }
  214. func CreateResponseMetaHeader(headerName, source string) Header {
  215. jsonResponseMetaHeader, err := json.Marshal(ResponseMetaHeader{Source: source})
  216. if err != nil {
  217. panic(err)
  218. }
  219. return Header{
  220. Name: headerName,
  221. Value: string(jsonResponseMetaHeader),
  222. }
  223. }