proxygen
CompressionUtils.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-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 
13 
14 using std::string;
15 using std::vector;
16 
17 namespace {
18 
19 using namespace proxygen;
20 
21 std::string combineCookieCrumbsSorted(std::vector<std::string> crumbs) {
22  std::string retval;
23  sort(crumbs.begin(), crumbs.end());
24  folly::join("; ", crumbs.begin(), crumbs.end(), retval);
25  return retval;
26 }
27 
28 bool containsAllHeaders(const HTTPHeaders& h1, const HTTPHeaders& h2) {
29  bool allValuesPresent = true;
30  bool verifyCookies = false;
31  h1.forEachWithCode(
32  [&](HTTPHeaderCode code, const string& name, const string& value1) {
33  bool h2HasValue =
34  h2.forEachValueOfHeader(code, [&value1](const std::string& value2) {
35  return (value1 == value2);
36  });
37  if (!h2HasValue && code == HTTP_HEADER_COOKIE) {
38  verifyCookies = true;
39  return;
40  }
41  DCHECK(h2HasValue) << "h2 does not contain name=" << name
42  << " value=" << value1;
43  allValuesPresent &= h2HasValue;
44  });
45 
46  if (verifyCookies) {
47  const HTTPHeaders* headers[] = {
48  &h1,
49  &h2,
50  };
51  std::string cookies[2] = {
52  "",
53  "",
54  };
55  unsigned i;
56  for (i = 0; i < 2; ++i) {
57  std::vector<std::string> crumbs;
59  [&](const std::string& crumb) {
60  crumbs.push_back(crumb);
61  return false;
62  });
63  cookies[i] = combineCookieCrumbsSorted(crumbs);
64  }
65  if (cookies[0] == cookies[1]) {
66  LOG(INFO) << "Cookie crumbs are reordered";
67  } else {
68  LOG(INFO) << "Cookies are not equal: `" << cookies[0] << "' vs. `"
69  << cookies[1] << "'";
70  return false;
71  }
72  }
73 
74  return allValuesPresent;
75 }
76 
77 }
78 
79 namespace proxygen {
80 
81 namespace compress {
82 
83 std::vector<compress::Header> prepareMessageForCompression(
84  const HTTPMessage& msg,
85  std::vector<string>& cookies) {
86  std::vector<compress::Header> allHeaders;
87  // The encode API is pretty bad. We should just let HPACK directly encode
88  // HTTP messages
89  const HTTPHeaders& headers = msg.getHeaders();
90  const string& host = headers.getSingleOrEmpty(HTTP_HEADER_HOST);
91  bool isPublic = msg.getMethodString().empty();
92  if (!isPublic) {
93  allHeaders.emplace_back(HTTP_HEADER_COLON_METHOD, headers::kMethod,
94  msg.getMethodString());
95  if (msg.getMethod() != HTTPMethod::CONNECT) {
96  allHeaders.emplace_back(
99  allHeaders.emplace_back(HTTP_HEADER_COLON_PATH, headers::kPath,
100  msg.getURL());
101  }
102 
103  if (!host.empty()) {
104  allHeaders.emplace_back(
106  }
107  }
108  // Cookies are coalesced in the HAR file but need to be added as separate
109  // headers to optimize compression ratio
110  headers.forEachWithCode(
111  [&](HTTPHeaderCode code, const string& name, const string& value) {
112  if (code == HTTP_HEADER_COOKIE) {
113  vector<folly::StringPiece> cookiePieces;
114  folly::split(';', value, cookiePieces);
115  cookies.reserve(cookies.size() + cookiePieces.size());
116  for (auto cookie : cookiePieces) {
117  cookies.push_back(ltrimWhitespace(cookie).str());
118  allHeaders.emplace_back(code, name, cookies.back());
119  }
120  } else if (code != HTTP_HEADER_HOST && (isPublic || name[0] != ':')) {
121  // HAR files contain actual serialized headers protocol headers like
122  // :authority, which we are re-adding above. Strip them so our
123  // equality test works
124  allHeaders.emplace_back(code, name, value);
125  }
126  });
127  return allHeaders;
128 }
129 
130 } // end compress
131 
132 bool operator==(const HTTPMessage& msg1, const HTTPMessage& msg2) {
133  return (msg1.getMethodString() == msg2.getMethodString() &&
134  msg1.getURL() == msg2.getURL() &&
135  msg1.isSecure() == msg2.isSecure() &&
136  containsAllHeaders(msg1.getHeaders(), msg2.getHeaders()) &&
137  containsAllHeaders(msg2.getHeaders(), msg1.getHeaders()));
138 }
139 
140 }
StringPiece ltrimWhitespace(StringPiece sp)
Definition: String.cpp:131
StringPiece cookie
bool operator==(const HTTPMessage &msg1, const HTTPMessage &msg2)
std::vector< compress::Header > prepareMessageForCompression(const HTTPMessage &msg, std::vector< string > &cookies)
void split(const Delim &delimiter, const String &input, std::vector< OutputType > &out, bool ignoreEmpty)
Definition: String-inl.h:382
const std::string kScheme
bool forEachValueOfHeader(folly::StringPiece name, LAMBDA func) const
Definition: HTTPHeaders.h:355
const char * name
Definition: http_parser.c:437
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
const std::string kHttp
const char * string
Definition: Conv.cpp:212
const std::string kPath
const std::string & getMethodString() const
bool isSecure() const
Definition: HTTPMessage.h:535
void join(const Delim &delimiter, Iterator begin, Iterator end, String &output)
Definition: String-inl.h:498
const std::string kAuthority
folly::Optional< HTTPMethod > getMethod() const
const std::string & getURL() const
Definition: HTTPMessage.h:205
void forEachWithCode(LAMBDA func) const
Definition: HTTPHeaders.h:346
const std::string kMethod
const std::string kHttps