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