1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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.
7 */
8
9 #include <proxygen/lib/http/HTTPMessage.h>
10
11 #include <boost/algorithm/string.hpp>
12 #include <folly/Format.h>
13 #include <folly/Range.h>
14 #include <folly/SingletonThreadLocal.h>
15 #include <string>
16 #include <vector>
17
18 using folly::StringPiece;
19 using std::pair;
20 using std::string;
21
22 namespace {
23 /**
24 * Create a C locale once and pass it to all the boost string methods
25 * that would otherwise create and destruct a temporary locale object
26 * per call. (Performance profiling showed that we were spending
27 * approximately 1% of our total CPU time on temporary locale objects.)
28 */
29 std::locale defaultLocale;
30 } // namespace
31
32 namespace proxygen {
33
httpPriorityToString(uint8_t urgency,bool incremental)34 std::string httpPriorityToString(uint8_t urgency, bool incremental) {
35 return folly::to<std::string>(
36 "u=",
37 std::min(static_cast<uint8_t>(proxygen::kMaxPriority), urgency),
38 incremental ? ",i" : "");
39 }
40
41 std::mutex HTTPMessage::mutexDump_;
42
43 const pair<uint8_t, uint8_t> HTTPMessage::kHTTPVersion09(0, 9);
44 const pair<uint8_t, uint8_t> HTTPMessage::kHTTPVersion10(1, 0);
45 const pair<uint8_t, uint8_t> HTTPMessage::kHTTPVersion11(1, 1);
46
stripPerHopHeaders()47 void HTTPMessage::stripPerHopHeaders() {
48 // Some code paths end up recyling a single HTTPMessage instance for multiple
49 // requests, and adding their own per-hop headers each time. In that case, we
50 // don't want to accumulate these headers.
51 if (!strippedPerHopHeaders_) {
52 strippedPerHopHeaders_ = std::make_unique<HTTPHeaders>();
53 } else {
54 strippedPerHopHeaders_->removeAll();
55 }
56
57 if (!trailersAllowed_) {
58 // Because stripPerHopHeaders can be called multiple times, don't
59 // let subsequent instances clear this flag
60 trailersAllowed_ = checkForHeaderToken(HTTP_HEADER_TE, "trailers", false);
61 }
62
63 headers_.stripPerHopHeaders(*strippedPerHopHeaders_);
64 }
65
HTTPMessage()66 HTTPMessage::HTTPMessage()
67 : startTime_(getCurrentTime()),
68 localIP_(),
69 versionStr_("1.0"),
70 fields_(),
71 upgradeWebsocket_(HTTPMessage::WebSocketUpgrade::NONE),
72 seqNo_(-1),
73 sslVersion_(0),
74 sslCipher_(nullptr),
75 protoStr_(nullptr),
76 pri_(kDefaultHttpPriorityUrgency),
77 version_(1, 0),
78 parsedCookies_(false),
79 parsedQueryParams_(false),
80 chunked_(false),
81 upgraded_(false),
82 wantsKeepalive_(true),
83 trailersAllowed_(false) {
84 }
85
~HTTPMessage()86 HTTPMessage::~HTTPMessage() {
87 }
88
HTTPMessage(const HTTPMessage & message)89 HTTPMessage::HTTPMessage(const HTTPMessage& message)
90 : startTime_(message.startTime_),
91 dstAddress_(message.dstAddress_),
92 dstIP_(message.dstIP_),
93 dstPort_(message.dstPort_),
94 localIP_(message.localIP_),
95 versionStr_(message.versionStr_),
96 fields_(message.fields_),
97 cookies_(message.cookies_),
98 queryParams_(message.queryParams_),
99 headers_(message.headers_),
100 upgradeWebsocket_(message.upgradeWebsocket_),
101 seqNo_(message.seqNo_),
102 sslVersion_(message.sslVersion_),
103 sslCipher_(message.sslCipher_),
104 protoStr_(message.protoStr_),
105 pri_(message.pri_),
106 h2Pri_(message.h2Pri_),
107 version_(message.version_),
108 parsedCookies_(message.parsedCookies_),
109 parsedQueryParams_(message.parsedQueryParams_),
110 chunked_(message.chunked_),
111 upgraded_(message.upgraded_),
112 wantsKeepalive_(message.wantsKeepalive_),
113 trailersAllowed_(message.trailersAllowed_),
114 scheme_(message.scheme_) {
115 if (isRequest()) {
116 setURL(request().url_);
117 }
118 if (message.strippedPerHopHeaders_) {
119 strippedPerHopHeaders_ =
120 std::make_unique<HTTPHeaders>(*message.strippedPerHopHeaders_);
121 }
122 if (message.trailers_) {
123 trailers_ = std::make_unique<HTTPHeaders>(*message.trailers_);
124 }
125 }
126
HTTPMessage(HTTPMessage && message)127 HTTPMessage::HTTPMessage(HTTPMessage&& message) noexcept
128 : startTime_(message.startTime_),
129 dstAddress_(std::move(message.dstAddress_)),
130 dstIP_(std::move(message.dstIP_)),
131 dstPort_(message.dstPort_),
132 localIP_(std::move(message.localIP_)),
133 versionStr_(std::move(message.versionStr_)),
134 fields_(std::move(message.fields_)),
135 cookies_(std::move(message.cookies_)),
136 queryParams_(std::move(message.queryParams_)),
137 headers_(std::move(message.headers_)),
138 strippedPerHopHeaders_(std::move(message.strippedPerHopHeaders_)),
139 upgradeWebsocket_(message.upgradeWebsocket_),
140 trailers_(std::move(message.trailers_)),
141 seqNo_(message.seqNo_),
142 sslVersion_(message.sslVersion_),
143 sslCipher_(message.sslCipher_),
144 protoStr_(message.protoStr_),
145 pri_(message.pri_),
146 h2Pri_(message.h2Pri_),
147 version_(message.version_),
148 parsedCookies_(message.parsedCookies_),
149 parsedQueryParams_(message.parsedQueryParams_),
150 chunked_(message.chunked_),
151 upgraded_(message.upgraded_),
152 wantsKeepalive_(message.wantsKeepalive_),
153 trailersAllowed_(message.trailersAllowed_),
154 scheme_(message.scheme_) {
155 if (isRequest()) {
156 setURL(request().url_);
157 }
158 }
159
operator =(const HTTPMessage & message)160 HTTPMessage& HTTPMessage::operator=(const HTTPMessage& message) {
161 if (&message == this) {
162 return *this;
163 }
164 startTime_ = message.startTime_;
165 seqNo_ = message.seqNo_;
166 dstAddress_ = message.dstAddress_;
167 dstIP_ = message.dstIP_;
168 dstPort_ = message.dstPort_;
169 localIP_ = message.localIP_;
170 versionStr_ = message.versionStr_;
171 fields_ = message.fields_;
172 if (isRequest()) {
173 setURL(request().url_);
174 }
175 cookies_ = message.cookies_;
176 queryParams_ = message.queryParams_;
177 version_ = message.version_;
178 headers_ = message.headers_;
179 if (message.strippedPerHopHeaders_) {
180 strippedPerHopHeaders_ =
181 std::make_unique<HTTPHeaders>(*message.strippedPerHopHeaders_);
182 } else {
183 strippedPerHopHeaders_.reset();
184 }
185 sslVersion_ = message.sslVersion_;
186 sslCipher_ = message.sslCipher_;
187 protoStr_ = message.protoStr_;
188 pri_ = message.pri_;
189 h2Pri_ = message.h2Pri_;
190 parsedCookies_ = message.parsedCookies_;
191 parsedQueryParams_ = message.parsedQueryParams_;
192 chunked_ = message.chunked_;
193 upgraded_ = message.upgraded_;
194 wantsKeepalive_ = message.wantsKeepalive_;
195 trailersAllowed_ = message.trailersAllowed_;
196 scheme_ = message.scheme_;
197 upgradeWebsocket_ = message.upgradeWebsocket_;
198
199 if (message.trailers_) {
200 trailers_ = std::make_unique<HTTPHeaders>(*message.trailers_);
201 } else {
202 trailers_.reset();
203 }
204 return *this;
205 }
206
operator =(HTTPMessage && message)207 HTTPMessage& HTTPMessage::operator=(HTTPMessage&& message) {
208 if (&message == this) {
209 return *this;
210 }
211 startTime_ = message.startTime_;
212 seqNo_ = message.seqNo_;
213 dstAddress_ = std::move(message.dstAddress_);
214 dstIP_ = std::move(message.dstIP_);
215 dstPort_ = message.dstPort_;
216 localIP_ = std::move(message.localIP_);
217 versionStr_ = std::move(message.versionStr_);
218 fields_ = std::move(message.fields_);
219 if (isRequest()) {
220 setURL(request().url_);
221 }
222 cookies_ = std::move(message.cookies_);
223 queryParams_ = std::move(message.queryParams_);
224 version_ = message.version_;
225 headers_ = std::move(message.headers_);
226 strippedPerHopHeaders_ = std::move(message.strippedPerHopHeaders_);
227 sslVersion_ = message.sslVersion_;
228 sslCipher_ = message.sslCipher_;
229 protoStr_ = message.protoStr_;
230 pri_ = message.pri_;
231 h2Pri_ = message.h2Pri_;
232 parsedCookies_ = message.parsedCookies_;
233 parsedQueryParams_ = message.parsedQueryParams_;
234 chunked_ = message.chunked_;
235 upgraded_ = message.upgraded_;
236 wantsKeepalive_ = message.wantsKeepalive_;
237 trailersAllowed_ = message.trailersAllowed_;
238 scheme_ = message.scheme_;
239 upgradeWebsocket_ = message.upgradeWebsocket_;
240 trailers_ = std::move(message.trailers_);
241 return *this;
242 }
243
setMethod(HTTPMethod method)244 void HTTPMessage::setMethod(HTTPMethod method) {
245 Request& req = request();
246 req.method_ = method;
247 }
248
setMethod(folly::StringPiece method)249 void HTTPMessage::setMethod(folly::StringPiece method) {
250 VLOG(9) << "setMethod: " << method;
251 Request& req = request();
252 folly::Optional<HTTPMethod> result = stringToMethod(method);
253 if (result) {
254 req.method_ = *result;
255 } else {
256 req.method_ = std::make_unique<std::string>(method.str());
257 auto& storedMethod = *boost::get<std::unique_ptr<std::string>>(req.method_);
258 std::transform(storedMethod.begin(),
259 storedMethod.end(),
260 storedMethod.begin(),
261 ::toupper);
262 }
263 }
264
getMethod() const265 folly::Optional<HTTPMethod> HTTPMessage::getMethod() const {
266 const auto& req = request();
267 if (req.method_.which() == 2) {
268 return boost::get<HTTPMethod>(req.method_);
269 }
270 return folly::none;
271 }
272
273 /**
274 * @Returns a string representation of the request method (fpreq)
275 */
getMethodString() const276 const std::string& HTTPMessage::getMethodString() const {
277 const auto& req = request();
278 if (req.method_.which() == 1) {
279 return *boost::get<std::unique_ptr<std::string>>(req.method_);
280 } else if (req.method_.which() == 2) {
281 return methodToString(boost::get<HTTPMethod>(req.method_));
282 }
283 return empty_string;
284 }
285
setHTTPVersion(uint8_t maj,uint8_t min)286 void HTTPMessage::setHTTPVersion(uint8_t maj, uint8_t min) {
287 version_.first = maj;
288 version_.second = min;
289 if (version_.first >= 10 || version_.second >= 10) {
290 versionStr_ = folly::to<std::string>(maj, '.', min);
291 } else {
292 versionStr_.reserve(3);
293 versionStr_.clear();
294 versionStr_.append(1, maj + '0');
295 versionStr_.append(1, '.');
296 versionStr_.append(1, min + '0');
297 }
298 }
299
getHTTPVersion() const300 const pair<uint8_t, uint8_t>& HTTPMessage::getHTTPVersion() const {
301 return version_;
302 }
303
processMaxForwards()304 int HTTPMessage::processMaxForwards() {
305 if (getMethod() == HTTPMethod::TRACE || getMethod() == HTTPMethod::OPTIONS) {
306 const string& value = headers_.getSingleOrEmpty(HTTP_HEADER_MAX_FORWARDS);
307 if (value.length() > 0) {
308 int64_t max_forwards = 0;
309 try {
310 max_forwards = folly::to<int64_t>(value);
311 } catch (const std::range_error&) {
312 return 400;
313 }
314
315 if (max_forwards < 0) {
316 return 400;
317 } else if (max_forwards == 0) {
318 return 501;
319 } else {
320 headers_.set(HTTP_HEADER_MAX_FORWARDS,
321 folly::to<string>(max_forwards - 1));
322 }
323 }
324 }
325 return 0;
326 }
327
isHTTP1_0() const328 bool HTTPMessage::isHTTP1_0() const {
329 return version_ == kHTTPVersion10;
330 }
331
isHTTP1_1() const332 bool HTTPMessage::isHTTP1_1() const {
333 return version_ == kHTTPVersion11;
334 }
335
336 namespace {
337 struct FormattedDate {
338 time_t lastTime{0};
339 string date;
340
formatDateproxygen::__anon8d7a29d20211::FormattedDate341 string formatDate() {
342 const auto now =
343 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
344
345 if (now != lastTime) {
346 char buff[1024];
347 tm timeTupple;
348 gmtime_r(&now, &timeTupple);
349
350 strftime(buff, 1024, "%a, %d %b %Y %H:%M:%S %Z", &timeTupple);
351 date = std::string(buff);
352 lastTime = now;
353 }
354 return date;
355 }
356 };
357 } // namespace
358
formatDateHeader()359 string HTTPMessage::formatDateHeader() {
360 struct DateTag {};
361 auto& obj = folly::SingletonThreadLocal<FormattedDate, DateTag>::get();
362 return obj.formatDate();
363 }
364
ensureHostHeader()365 void HTTPMessage::ensureHostHeader() {
366 if (!headers_.exists(HTTP_HEADER_HOST)) {
367 headers_.add(HTTP_HEADER_HOST,
368 getDstAddress().getFamily() == AF_INET6
369 ? '[' + getDstIP() + ']'
370 : getDstIP());
371 }
372 }
373
setStatusCode(uint16_t status)374 void HTTPMessage::setStatusCode(uint16_t status) {
375 response().status_ = status;
376 response().statusStr_ = folly::to<string>(status);
377 }
378
getStatusCode() const379 uint16_t HTTPMessage::getStatusCode() const {
380 return response().status_;
381 }
382
setPushStatusCode(uint16_t status)383 void HTTPMessage::setPushStatusCode(uint16_t status) {
384 request().pushStatus_ = status;
385 }
386
getPushStatusStr() const387 std::string HTTPMessage::getPushStatusStr() const {
388 return folly::to<string>(request().pushStatus_);
389 }
390
getPushStatusCode() const391 uint16_t HTTPMessage::getPushStatusCode() const {
392 return request().pushStatus_;
393 }
394
constructDirectResponse(const pair<uint8_t,uint8_t> & version,const int statusCode,const string & statusMsg,int contentLength)395 void HTTPMessage::constructDirectResponse(const pair<uint8_t, uint8_t>& version,
396 const int statusCode,
397 const string& statusMsg,
398 int contentLength) {
399 setStatusCode(statusCode);
400 setStatusMessage(statusMsg);
401 constructDirectResponse(version, contentLength);
402 }
403
constructDirectResponse(const pair<uint8_t,uint8_t> & version,int contentLength)404 void HTTPMessage::constructDirectResponse(const pair<uint8_t, uint8_t>& version,
405 int contentLength) {
406 setHTTPVersion(version.first, version.second);
407
408 headers_.set(HTTP_HEADER_CONTENT_LENGTH, folly::to<string>(contentLength));
409
410 if (!headers_.exists(HTTP_HEADER_CONTENT_TYPE)) {
411 headers_.add(HTTP_HEADER_CONTENT_TYPE, "text/plain");
412 }
413 setIsChunked(false);
414 setIsUpgraded(false);
415 }
416
parseCookies() const417 void HTTPMessage::parseCookies() const {
418 DCHECK(!parsedCookies_);
419 parsedCookies_ = true;
420
421 headers_.forEachValueOfHeader(
422 HTTP_HEADER_COOKIE, [&](const string& headerval) {
423 splitNameValuePieces(
424 headerval,
425 ';',
426 '=',
427 [this](StringPiece cookieName, StringPiece cookieValue) {
428 cookies_.emplace(cookieName, cookieValue);
429 });
430
431 return false; // continue processing "cookie" headers
432 });
433 }
434
unparseCookies() const435 void HTTPMessage::unparseCookies() const {
436 cookies_.clear();
437 parsedCookies_ = false;
438 }
439
getCookie(const string & name) const440 const StringPiece HTTPMessage::getCookie(const string& name) const {
441 // clear previous parsed cookies. They might store raw pointers to a vector
442 // in headers_, which can resize on add()
443 // Parse the cookies if we haven't done so yet
444 unparseCookies();
445 if (!parsedCookies_) {
446 parseCookies();
447 }
448
449 auto it = cookies_.find(name);
450 if (it == cookies_.end()) {
451 return StringPiece();
452 } else {
453 return it->second;
454 }
455 }
456
parseQueryParams() const457 void HTTPMessage::parseQueryParams() const {
458 DCHECK(!parsedQueryParams_);
459 const Request& req = request();
460
461 parsedQueryParams_ = true;
462 if (req.query_.empty()) {
463 return;
464 }
465
466 splitNameValue(
467 req.query_, '&', '=', [this](string&& paramName, string&& paramValue) {
468 auto it = queryParams_.find(paramName);
469 if (it == queryParams_.end()) {
470 queryParams_.emplace(std::move(paramName), std::move(paramValue));
471 } else {
472 // We have some unit tests that make sure we always return the last
473 // value when there are duplicate parameters. I don't think this
474 // really matters, but for now we might as well maintain the same
475 // behavior.
476 it->second = std::move(paramValue);
477 }
478 });
479 }
480
unparseQueryParams()481 void HTTPMessage::unparseQueryParams() {
482 queryParams_.clear();
483 parsedQueryParams_ = false;
484 }
485
getQueryParamPtr(const string & name) const486 const string* HTTPMessage::getQueryParamPtr(const string& name) const {
487 // Parse the query parameters if we haven't done so yet
488 if (!parsedQueryParams_) {
489 parseQueryParams();
490 }
491
492 auto it = queryParams_.find(name);
493 if (it == queryParams_.end()) {
494 return nullptr;
495 }
496 return &it->second;
497 }
498
hasQueryParam(const string & name) const499 bool HTTPMessage::hasQueryParam(const string& name) const {
500 return getQueryParamPtr(name) != nullptr;
501 }
502
getQueryParam(const string & name) const503 const string& HTTPMessage::getQueryParam(const string& name) const {
504 const string* ret = getQueryParamPtr(name);
505 return ret ? *ret : empty_string;
506 }
507
getIntQueryParam(const std::string & name) const508 int HTTPMessage::getIntQueryParam(const std::string& name) const {
509 return folly::to<int>(getQueryParam(name));
510 }
511
getIntQueryParam(const std::string & name,int defval) const512 int HTTPMessage::getIntQueryParam(const std::string& name, int defval) const {
513 try {
514 return getIntQueryParam(name);
515 } catch (const std::exception&) {
516 }
517
518 return defval;
519 }
520
getDecodedQueryParam(const std::string & name) const521 std::string HTTPMessage::getDecodedQueryParam(const std::string& name) const {
522 auto val = getQueryParam(name);
523
524 std::string result;
525 try {
526 folly::uriUnescape(val, result, folly::UriEscapeMode::QUERY);
527 } catch (const std::exception& ex) {
528 LOG(WARNING) << "Invalid escaped query param: " << folly::exceptionStr(ex);
529 }
530 return result;
531 }
532
getQueryParams() const533 const std::map<std::string, std::string>& HTTPMessage::getQueryParams() const {
534 // Parse the query parameters if we haven't done so yet
535 if (!parsedQueryParams_) {
536 parseQueryParams();
537 }
538 return queryParams_;
539 }
540
setQueryString(const std::string & query,bool strict)541 bool HTTPMessage::setQueryString(const std::string& query, bool strict) {
542 return setQueryStringImpl(query, true, strict);
543 }
544
setQueryStringImpl(const std::string & query,bool unparse,bool strict)545 bool HTTPMessage::setQueryStringImpl(const std::string& query,
546 bool unparse,
547 bool strict) {
548 // No need to strictly verify the URL when reparsing it
549 auto u = ParseURL::parseURL(request().url_, /*strict=*/false);
550
551 if (u) {
552 // Recreate the URL by just changing the query string
553 auto res = setURLImpl(createUrl(u->scheme(),
554 u->authority(),
555 u->path(),
556 query, // new query string
557 u->fragment()),
558 unparse,
559 strict);
560 return !strict || res.valid();
561 }
562
563 VLOG(4) << "Error parsing URL during setQueryString: " << request().url_;
564 return false;
565 }
566
removeQueryParam(const std::string & name)567 bool HTTPMessage::removeQueryParam(const std::string& name) {
568 // Parse the query parameters if we haven't done so yet
569 if (!parsedQueryParams_) {
570 parseQueryParams();
571 }
572
573 if (!queryParams_.erase(name)) {
574 // Query param was not found.
575 return false;
576 }
577
578 auto query = createQueryString(queryParams_, request().query_.size());
579 return setQueryStringImpl(query, false, /*strict=*/false);
580 }
581
setQueryParam(const std::string & name,const std::string & value,bool strict)582 bool HTTPMessage::setQueryParam(const std::string& name,
583 const std::string& value,
584 bool strict) {
585 // Parse the query parameters if we haven't done so yet
586 if (!parsedQueryParams_) {
587 parseQueryParams();
588 }
589
590 queryParams_[name] = value;
591 auto query = createQueryString(queryParams_, request().query_.size());
592 return setQueryStringImpl(query, false, strict);
593 }
594
createQueryString(const std::map<std::string,std::string> & params,uint32_t maxLength)595 std::string HTTPMessage::createQueryString(
596 const std::map<std::string, std::string>& params, uint32_t maxLength) {
597 std::string query;
598 query.reserve(maxLength);
599 for (auto it = params.begin(); it != params.end(); it++) {
600 if (it != params.begin()) {
601 query.append("&");
602 }
603 query.append(it->first + "=" + it->second);
604 }
605 query.shrink_to_fit();
606 return query;
607 }
608
createUrl(const folly::StringPiece scheme,const folly::StringPiece authority,const folly::StringPiece path,const folly::StringPiece query,const folly::StringPiece fragment)609 std::string HTTPMessage::createUrl(const folly::StringPiece scheme,
610 const folly::StringPiece authority,
611 const folly::StringPiece path,
612 const folly::StringPiece query,
613 const folly::StringPiece fragment) {
614 std::string url;
615 url.reserve(scheme.size() + authority.size() + path.size() + query.size() +
616 fragment.size() + 5); // 5 chars for ://,? and #
617 if (!scheme.empty()) {
618 folly::toAppend(scheme.str(), "://", &url);
619 }
620 folly::toAppend(authority, path, &url);
621 if (!query.empty()) {
622 folly::toAppend('?', query, &url);
623 }
624 if (!fragment.empty()) {
625 folly::toAppend('#', fragment, &url);
626 }
627 url.shrink_to_fit();
628 return url;
629 }
630
splitNameValuePieces(folly::StringPiece sp,char pairDelim,char valueDelim,std::function<void (StringPiece,StringPiece)> callback)631 void HTTPMessage::splitNameValuePieces(
632 folly::StringPiece sp,
633 char pairDelim,
634 char valueDelim,
635 std::function<void(StringPiece, StringPiece)> callback) {
636
637 while (!sp.empty()) {
638 size_t pairDelimPos = sp.find(pairDelim);
639 StringPiece keyValue;
640
641 if (pairDelimPos == string::npos) {
642 keyValue = sp;
643 sp.advance(sp.size());
644 } else {
645 keyValue = sp.subpiece(0, pairDelimPos);
646 // Skip '&' char
647 sp.advance(pairDelimPos + 1);
648 }
649
650 if (keyValue.empty()) {
651 continue;
652 }
653
654 size_t valueDelimPos = keyValue.find(valueDelim);
655 if (valueDelimPos == string::npos) {
656 // Key only query param
657 callback(trim(keyValue), StringPiece());
658 } else {
659 auto name = keyValue.subpiece(0, valueDelimPos);
660 auto value = keyValue.subpiece(valueDelimPos + 1);
661 callback(trim(name), trim(value));
662 }
663 }
664 }
665
trim(StringPiece sp)666 StringPiece HTTPMessage::trim(StringPiece sp) {
667 // TODO: use a library function from boost?
668 for (; !sp.empty() && sp.front() == ' '; sp.pop_front()) {
669 }
670 for (; !sp.empty() && sp.back() == ' '; sp.pop_back()) {
671 }
672 return sp;
673 }
674
splitNameValue(folly::StringPiece input,char pairDelim,char valueDelim,std::function<void (string &&,string &&)> callback)675 void HTTPMessage::splitNameValue(
676 folly::StringPiece input,
677 char pairDelim,
678 char valueDelim,
679 std::function<void(string&&, string&&)> callback) {
680
681 folly::StringPiece sp(input);
682 while (!sp.empty()) {
683 size_t pairDelimPos = sp.find(pairDelim);
684 folly::StringPiece keyValue;
685
686 if (pairDelimPos == string::npos) {
687 keyValue = sp;
688 sp.advance(sp.size());
689 } else {
690 keyValue = sp.subpiece(0, pairDelimPos);
691 // Skip '&' char
692 sp.advance(pairDelimPos + 1);
693 }
694
695 if (keyValue.empty()) {
696 continue;
697 }
698
699 size_t valueDelimPos = keyValue.find(valueDelim);
700 if (valueDelimPos == string::npos) {
701 // Key only query param
702 string name = keyValue.str();
703 string value;
704
705 boost::trim(name, defaultLocale);
706 callback(std::move(name), std::move(value));
707 } else {
708 string name = keyValue.subpiece(0, valueDelimPos).str();
709 string value = keyValue.subpiece(valueDelimPos + 1).str();
710
711 boost::trim(name, defaultLocale);
712 boost::trim(value, defaultLocale);
713 callback(std::move(name), std::move(value));
714 }
715 }
716 }
717
operator <<(std::ostream & os,const HTTPMessage & msg)718 std::ostream& operator<<(std::ostream& os, const HTTPMessage& msg) {
719 msg.describe(os);
720 return os;
721 }
722
dumpMessage(int vlogLevel) const723 void HTTPMessage::dumpMessage(int vlogLevel) const {
724 VLOG(vlogLevel) << *this;
725 }
726
describe(std::ostream & os) const727 void HTTPMessage::describe(std::ostream& os) const {
728 os << ", chunked: " << chunked_ << ", upgraded: " << upgraded_
729 << ", scheme: " << getScheme() << ", Fields for message:" << std::endl;
730
731 // Common fields to both requests and responses.
732 std::vector<std::pair<const char*, folly::StringPiece>> fields{{
733 {"local_ip", localIP_},
734 {"version", versionStr_},
735 {"dst_ip", dstIP_},
736 {"dst_port", dstPort_},
737 }};
738
739 std::string pushStatusMessage;
740 if (isRequest()) {
741 // Request fields.
742 const Request& req = request();
743 pushStatusMessage = getPushStatusStr();
744 fields.insert(fields.end(),
745 {{"client_ip",
746 req.clientIPPort_ ? req.clientIPPort_->ip : empty_string},
747 {"client_port",
748 req.clientIPPort_ ? req.clientIPPort_->port : empty_string},
749 {"method", getMethodString()},
750 {"path", req.path_},
751 {"query", req.query_},
752 {"url", req.url_},
753 {"push_status", pushStatusMessage}});
754
755 } else if (isResponse()) {
756 // Response fields.
757 const Response& resp = response();
758 fields.insert(
759 fields.end(),
760 {{"status", resp.statusStr_}, {"status_msg", resp.statusMsg_}});
761 }
762
763 for (auto& field : fields) {
764 if (!field.second.empty()) {
765 os << " " << field.first << ":" << stripCntrlChars(field.second)
766 << std::endl;
767 }
768 }
769
770 // This little loop prints the headers and (if present) any per-hop headers
771 // that were stripped. It executes at most twice.
772 bool first = true;
773 const HTTPHeaders* hdrs = &headers_;
774 while (hdrs) {
775 if (!first && hdrs->size() != 0) {
776 os << "Per-Hop Headers" << std::endl;
777 }
778 hdrs->forEach([&os](const string& h, const string& v) {
779 os << " " << stripCntrlChars(h) << ": " << stripCntrlChars(v)
780 << std::endl;
781 });
782 if (first) {
783 hdrs = strippedPerHopHeaders_.get();
784 first = false;
785 } else {
786 hdrs = nullptr;
787 }
788 }
789 }
790
atomicDumpMessage(int vlogLevel) const791 void HTTPMessage::atomicDumpMessage(int vlogLevel) const {
792 std::lock_guard<std::mutex> g(mutexDump_);
793 dumpMessage(vlogLevel);
794 }
795
dumpMessageToSink(google::LogSink * logSink) const796 void HTTPMessage::dumpMessageToSink(google::LogSink* logSink) const {
797 LOG_TO_SINK(logSink, INFO) << *this;
798 }
799
computeKeepalive() const800 bool HTTPMessage::computeKeepalive() const {
801 if (version_.first < 1) {
802 return false;
803 }
804
805 // RFC 2616 isn't explicitly clear about whether "close" is case-sensitive.
806 // Section 2.1 states that literal tokens in the BNF are case-insensitive
807 // unless stated otherwise. The "close" token isn't explicitly mentioned
808 // in the BNF, but other header fields such as the character set and
809 // content coding are explicitly called out as being case insensitive.
810 //
811 // We'll treat the "close" token case-insensitively. This is the most
812 // conservative approach, since disabling keepalive when it was requested
813 // is better than enabling keepalive for a client that didn't expect it.
814 //
815 // Note that we only perform ASCII lowering here. This is good enough,
816 // since the token we are looking for is ASCII.
817 if (checkForHeaderToken(HTTP_HEADER_CONNECTION, "close", false)) {
818 // The Connection header contained a "close" token, so keepalive
819 // is disabled.
820 return false;
821 }
822
823 const std::string kKeepAliveConnToken = "keep-alive";
824 if (version_ == kHTTPVersion10) {
825 // HTTP 1.0 persistent connections require a Connection: Keep-Alive
826 // header to be present for the connection to be persistent.
827 if (checkForHeaderToken(
828 HTTP_HEADER_CONNECTION, kKeepAliveConnToken.c_str(), false) ||
829 (strippedPerHopHeaders_ &&
830 doHeaderTokenCheck(*strippedPerHopHeaders_,
831 HTTP_HEADER_CONNECTION,
832 kKeepAliveConnToken.c_str(),
833 false))) {
834 return true;
835 }
836 return false;
837 }
838
839 // It's a keepalive connection.
840 return true;
841 }
842
checkForHeaderToken(const HTTPHeaderCode headerCode,char const * token,bool caseSensitive) const843 bool HTTPMessage::checkForHeaderToken(const HTTPHeaderCode headerCode,
844 char const* token,
845 bool caseSensitive) const {
846 return doHeaderTokenCheck(headers_, headerCode, token, caseSensitive);
847 }
848
doHeaderTokenCheck(const HTTPHeaders & headers,const HTTPHeaderCode headerCode,char const * token,bool caseSensitive) const849 bool HTTPMessage::doHeaderTokenCheck(const HTTPHeaders& headers,
850 const HTTPHeaderCode headerCode,
851 char const* token,
852 bool caseSensitive) const {
853 return headers.forEachValueOfHeader(headerCode, [&](const string& value) {
854 std::vector<folly::StringPiece> tokens;
855 folly::split(",", value, tokens);
856 for (auto t : tokens) {
857 t = trim(t);
858 if (caseSensitive) {
859 if (t == token) {
860 return true;
861 }
862 } else if (caseInsensitiveEqual(t, token)) {
863 return true;
864 }
865 }
866 return false;
867 });
868 }
869
getDefaultReason(uint16_t status)870 const char* HTTPMessage::getDefaultReason(uint16_t status) {
871 switch (status) {
872 case 100:
873 return "Continue";
874 case 101:
875 return "Switching Protocols";
876 case 200:
877 return "OK";
878 case 201:
879 return "Created";
880 case 202:
881 return "Accepted";
882 case 203:
883 return "Non-Authoritative Information";
884 case 204:
885 return "No Content";
886 case 205:
887 return "Reset Content";
888 case 206:
889 return "Partial Content";
890 case 300:
891 return "Multiple Choices";
892 case 301:
893 return "Moved Permanently";
894 case 302:
895 return "Found";
896 case 303:
897 return "See Other";
898 case 304:
899 return "Not Modified";
900 case 305:
901 return "Use Proxy";
902 case 307:
903 return "Temporary Redirect";
904 case 400:
905 return "Bad Request";
906 case 401:
907 return "Unauthorized";
908 case 402:
909 return "Payment Required";
910 case 403:
911 return "Forbidden";
912 case 404:
913 return "Not Found";
914 case 405:
915 return "Method Not Allowed";
916 case 406:
917 return "Not Acceptable";
918 case 407:
919 return "Proxy Authentication Required";
920 case 408:
921 return "Request Timeout";
922 case 409:
923 return "Conflict";
924 case 410:
925 return "Gone";
926 case 411:
927 return "Length Required";
928 case 412:
929 return "Precondition Failed";
930 case 413:
931 return "Request Entity Too Large";
932 case 414:
933 return "Request-URI Too Long";
934 case 415:
935 return "Unsupported Media Type";
936 case 416:
937 return "Requested Range Not Satisfiable";
938 case 417:
939 return "Expectation Failed";
940 case 418:
941 return "I'm a teapot";
942 case 426:
943 return "Upgrade Required";
944 case 428:
945 return "Precondition Required";
946 case 429:
947 return "Too Many Requests";
948 case 431:
949 return "Request Header Fields Too Large";
950 case 451:
951 return "Unavailable For Legal Reasons";
952 case 500:
953 return "Internal Server Error";
954 case 501:
955 return "Not Implemented";
956 case 502:
957 return "Bad Gateway";
958 case 503:
959 return "Service Unavailable";
960 case 504:
961 return "Gateway Timeout";
962 case 505:
963 return "HTTP Version Not Supported";
964 }
965
966 // Note: Some Microsoft clients behave badly if the reason string
967 // is left empty. Therefore return a non-empty string here.
968 return "-";
969 }
970
setURLImplInternal(bool unparse,bool strict)971 ParseURL HTTPMessage::setURLImplInternal(bool unparse, bool strict) {
972 auto& req = request();
973 auto u = ParseURL::parseURLMaybeInvalid(req.url_, strict);
974 if (u.valid()) {
975 VLOG(9) << "set path: " << u.path() << " query:" << u.query();
976 req.path_ = u.path();
977 req.query_ = u.query();
978 } else {
979 VLOG(4) << "Error in parsing URL: " << req.url_;
980 req.path_.clear();
981 req.query_.clear();
982 }
983 req.pathStr_.reset();
984 req.queryStr_.reset();
985 if (unparse) {
986 unparseQueryParams();
987 }
988 return u;
989 }
990
setHTTPPriority(uint8_t urgency,bool incremental)991 void HTTPMessage::setHTTPPriority(uint8_t urgency, bool incremental) {
992 headers_.set(HTTP_HEADER_PRIORITY,
993 httpPriorityToString(urgency, incremental));
994 }
995
setHTTPPriority(HTTPPriority httpPriority)996 void HTTPMessage::setHTTPPriority(HTTPPriority httpPriority) {
997 headers_.set(
998 HTTP_HEADER_PRIORITY,
999 httpPriorityToString(httpPriority.urgency, httpPriority.incremental));
1000 }
1001
1002 } // namespace proxygen
1003