proxygen
CodecUtil.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
11 
12 #include <folly/ThreadLocal.h>
15 
16 namespace proxygen {
17 
26 const char CodecUtil::http_tokens[256] = {
27 /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
28  0, 0, 0, 0, 0, 0, 0, 0,
29 /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
30  0, 0, 0, 0, 0, 0, 0, 0,
31 /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
32  0, 0, 0, 0, 0, 0, 0, 0,
33 /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
34  0, 0, 0, 0, 0, 0, 0, 0,
35 /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
36  ' ', '!', '"', '#', '$', '%', '&', '\'',
37 /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
38  0, 0, '*', '+', 0, '-', '.', '/',
39 /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
40  '0', '1', '2', '3', '4', '5', '6', '7',
41 /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
42  '8', '9', 0, 0, 0, 0, 0, 0,
43 /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
44  0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
45 /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
46  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
47 /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
48  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
49 /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
50  'x', 'y', 'z', 0, 0, 0, '^', '_',
51 /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
52  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
53 /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
54  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
55 /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
56  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
57 /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
58  'x', 'y', 'z', 0, '|', '}', '~', 0
59 };
60 
61 bool CodecUtil::hasGzipAndDeflate(const std::string& value, bool& hasGzip,
62  bool& hasDeflate) {
64  output->clear();
65  hasGzip = false;
66  hasDeflate = false;
67  RFC2616::parseQvalues(value, *output);
68  for (const auto& encodingQ: *output) {
69  std::string lower(encodingQ.first.str());
70  std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
71  // RFC says 3 sig figs
72  if (lower == "gzip" && encodingQ.second >= 0.001) {
73  hasGzip = true;
74  } else if (lower == "deflate" && encodingQ.second >= 0.001) {
75  hasDeflate = true;
76  }
77  }
78  return hasGzip && hasDeflate;
79 }
80 
81 
82 std::vector<compress::Header> CodecUtil::prepareMessageForCompression(
83  const HTTPMessage& msg,
84  std::vector<std::string>& temps) {
85  std::vector<compress::Header> allHeaders;
86  if (msg.isRequest()) {
87  if (msg.isEgressWebsocketUpgrade()) {
88  allHeaders.emplace_back(HTTP_HEADER_COLON_METHOD,
90  allHeaders.emplace_back(HTTP_HEADER_COLON_PROTOCOL,
92  } else {
93  const std::string& method = msg.getMethodString();
94  allHeaders.emplace_back(HTTP_HEADER_COLON_METHOD, method);
95  }
96 
97  if (msg.getMethod() != HTTPMethod::CONNECT ||
99  const std::string& scheme =
101  const std::string& path = msg.getURL();
102  allHeaders.emplace_back(HTTP_HEADER_COLON_SCHEME, scheme);
103  allHeaders.emplace_back(HTTP_HEADER_COLON_PATH, path);
104  }
105  const HTTPHeaders& headers = msg.getHeaders();
106  const std::string& host = headers.getSingleOrEmpty(HTTP_HEADER_HOST);
107  if (!host.empty()) {
108  allHeaders.emplace_back(HTTP_HEADER_COLON_AUTHORITY, host);
109  }
110  } else {
111  temps.reserve(3); // must be large enough so that emplace does not resize
112  if (msg.isEgressWebsocketUpgrade()) {
113  temps.emplace_back(headers::kStatus200);
114  } else {
115  temps.emplace_back(folly::to<std::string>(msg.getStatusCode()));
116  }
117  allHeaders.emplace_back(HTTP_HEADER_COLON_STATUS, temps.back());
118  // HEADERS frames do not include a version or reason string.
119  }
120 
121  bool hasDateHeader =
122  appendHeaders(msg.getHeaders(), allHeaders, HTTP_HEADER_DATE);
123 
124  if (msg.isResponse() && !hasDateHeader) {
125  temps.emplace_back(HTTPMessage::formatDateHeader());
126  allHeaders.emplace_back(HTTP_HEADER_DATE, temps.back());
127  }
128  return allHeaders;
129 }
130 
131 bool CodecUtil::appendHeaders(const HTTPHeaders& inputHeaders,
132  std::vector<compress::Header>& headers,
133  HTTPHeaderCode headerToCheck) {
134  bool headerToCheckExists = false;
135  // Add the HTTP headers supplied by the caller, but skip
136  // any per-hop headers that aren't supported in HTTP/2.
137  inputHeaders.forEachWithCode([&](HTTPHeaderCode code,
138  const std::string& name,
139  const std::string& value) {
140  static const std::bitset<256> s_perHopHeaderCodes{[] {
141  std::bitset<256> bs;
142  // HTTP/1.x per-hop headers that have no meaning in HTTP/2
143  bs[HTTP_HEADER_CONNECTION] = true;
144  bs[HTTP_HEADER_HOST] = true;
145  bs[HTTP_HEADER_KEEP_ALIVE] = true;
146  bs[HTTP_HEADER_PROXY_CONNECTION] = true;
148  bs[HTTP_HEADER_UPGRADE] = true;
151  return bs;
152  }()};
153 
154  if (s_perHopHeaderCodes[code] || name.size() == 0 || name[0] == ':') {
155  DCHECK_GT(name.size(), 0) << "Empty header";
156  DCHECK_NE(name[0], ':') << "Invalid header=" << name;
157  return;
158  }
159  // Note this code will not drop headers named by Connection. That's the
160  // caller's job
161 
162  // see HTTP/2 spec, 8.1.2
163  DCHECK(name != "TE" || value == "trailers");
164  if ((name.size() > 0 && name[0] != ':') && code != HTTP_HEADER_HOST) {
165  headers.emplace_back(code, name, value);
166  }
167  if (code == headerToCheck) {
168  headerToCheckExists = true;
169  }
170  });
171 
172  return headerToCheckExists;
173 }
174 }
static std::vector< compress::Header > prepareMessageForCompression(const HTTPMessage &msg, std::vector< std::string > &temps)
Definition: CodecUtil.cpp:82
uint16_t getStatusCode() const
static std::string formatDateHeader()
PUSHMI_INLINE_VAR constexpr detail::transform_fn transform
Definition: transform.h:158
const char * name
Definition: http_parser.c:437
const std::string & methodToString(HTTPMethod method)
Definition: HTTPMethod.cpp:46
const std::string kStatus200
static const char http_tokens[256]
Definition: CodecUtil.h:27
static const char *const value
Definition: Conv.cpp:50
HTTPHeaders & getHeaders()
Definition: HTTPMessage.h:273
const std::string & getSingleOrEmpty(const T &nameOrCode) const
Definition: HTTPHeaders.h:420
bool isResponse() const
Definition: HTTPMessage.h:668
const std::string kHttp
const char * string
Definition: Conv.cpp:212
const std::string & getMethodString() const
static bool appendHeaders(const HTTPHeaders &inputHeaders, std::vector< compress::Header > &headers, HTTPHeaderCode headerToCheck)
Definition: CodecUtil.cpp:131
bool isSecure() const
Definition: HTTPMessage.h:535
bool isRequest() const
Definition: HTTPMessage.h:661
folly::Optional< HTTPMethod > getMethod() const
const std::string & getURL() const
Definition: HTTPMessage.h:205
const std::string kWebsocketString
void forEachWithCode(LAMBDA func) const
Definition: HTTPHeaders.h:346
bool isEgressWebsocketUpgrade() const
Definition: HTTPMessage.h:72
bool parseQvalues(folly::StringPiece value, std::vector< TokenQPair > &output)
Definition: RFC2616.cpp:64
const std::string kHttps
static bool hasGzipAndDeflate(const std::string &value, bool &hasGzip, bool &hasDeflate)
Definition: CodecUtil.cpp:61