mirror of
https://github.com/facebook/proxygen.git
synced 2025-04-19 01:04:16 +03:00
309 lines
8.8 KiB
C++
309 lines
8.8 KiB
C++
/*
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#define PROXYGEN_HTTPHEADERS_IMPL
|
|
#include "proxygen/lib/http/HTTPHeaders.h"
|
|
|
|
#include <glog/logging.h>
|
|
#include <vector>
|
|
|
|
using std::bitset;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
namespace proxygen {
|
|
|
|
const string empty_string;
|
|
|
|
bitset<256>& HTTPHeaders::perHopHeaderCodes() {
|
|
static bitset<256> perHopHeaderCodes;
|
|
return perHopHeaderCodes;
|
|
}
|
|
|
|
void
|
|
HTTPHeaders::initGlobals() {
|
|
HTTPCommonHeaders::initHeaderNames();
|
|
|
|
auto& perHopHeaders = perHopHeaderCodes();
|
|
perHopHeaders[HTTP_HEADER_CONNECTION] = true;
|
|
perHopHeaders[HTTP_HEADER_KEEP_ALIVE] = true;
|
|
perHopHeaders[HTTP_HEADER_PROXY_AUTHENTICATE] = true;
|
|
perHopHeaders[HTTP_HEADER_PROXY_AUTHORIZATION] = true;
|
|
perHopHeaders[HTTP_HEADER_PROXY_CONNECTION] = true;
|
|
perHopHeaders[HTTP_HEADER_TE] = true;
|
|
perHopHeaders[HTTP_HEADER_TRAILER] = true;
|
|
perHopHeaders[HTTP_HEADER_TRANSFER_ENCODING] = true;
|
|
perHopHeaders[HTTP_HEADER_UPGRADE] = true;
|
|
}
|
|
|
|
HTTPHeaders::HTTPHeaders() :
|
|
deletedCount_(0) {
|
|
codes_.reserve(kInitialVectorReserve);
|
|
headerNames_.reserve(kInitialVectorReserve);
|
|
headerValues_.reserve(kInitialVectorReserve);
|
|
}
|
|
|
|
void HTTPHeaders::add(folly::StringPiece name, folly::StringPiece value) {
|
|
CHECK(name.size());
|
|
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(), name.size());
|
|
codes_.push_back(code);
|
|
headerNames_.push_back((code == HTTP_HEADER_OTHER)
|
|
? new std::string(name.data(), name.size())
|
|
: HTTPCommonHeaders::getPointerToHeaderName(code));
|
|
headerValues_.emplace_back(value.data(), value.size());
|
|
}
|
|
|
|
void HTTPHeaders::rawAdd(const std::string& name, const std::string& value) {
|
|
add(name, value);
|
|
}
|
|
|
|
void HTTPHeaders::addFromCodec(const char* str, size_t len, string&& value) {
|
|
const HTTPHeaderCode code = HTTPCommonHeaders::hash(str, len);
|
|
codes_.push_back(code);
|
|
headerNames_.push_back((code == HTTP_HEADER_OTHER)
|
|
? new string(str, len)
|
|
: HTTPCommonHeaders::getPointerToHeaderName(code));
|
|
headerValues_.emplace_back(std::move(value));
|
|
}
|
|
|
|
bool HTTPHeaders::exists(folly::StringPiece name) const {
|
|
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
|
|
name.size());
|
|
if (code != HTTP_HEADER_OTHER) {
|
|
return exists(code);
|
|
} else {
|
|
ITERATE_OVER_STRINGS(name, { return true; });
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool HTTPHeaders::exists(HTTPHeaderCode code) const {
|
|
return memchr((void*)codes_.data(), code, codes_.size()) != nullptr;
|
|
}
|
|
|
|
size_t HTTPHeaders::getNumberOfValues(HTTPHeaderCode code) const {
|
|
size_t count = 0;
|
|
ITERATE_OVER_CODES(code, {
|
|
++count;
|
|
});
|
|
return count;
|
|
}
|
|
|
|
size_t HTTPHeaders::getNumberOfValues(folly::StringPiece name) const {
|
|
size_t count = 0;
|
|
forEachValueOfHeader(name, [&] (folly::StringPiece value) -> bool {
|
|
++count;
|
|
return false;
|
|
});
|
|
return count;
|
|
}
|
|
|
|
bool HTTPHeaders::remove(folly::StringPiece name) {
|
|
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
|
|
name.size());
|
|
if (code != HTTP_HEADER_OTHER) {
|
|
return remove(code);
|
|
} else {
|
|
bool removed = false;
|
|
ITERATE_OVER_STRINGS(name, {
|
|
delete headerNames_[pos];
|
|
codes_[pos] = HTTP_HEADER_NONE;
|
|
removed = true;
|
|
++deletedCount_;
|
|
});
|
|
return removed;
|
|
}
|
|
}
|
|
|
|
bool HTTPHeaders::remove(HTTPHeaderCode code) {
|
|
bool removed = false;
|
|
ITERATE_OVER_CODES(code, {
|
|
codes_[pos] = HTTP_HEADER_NONE;
|
|
removed = true;
|
|
++deletedCount_;
|
|
});
|
|
return removed;
|
|
}
|
|
|
|
void HTTPHeaders::disposeOfHeaderNames() {
|
|
for (size_t i = 0; i < codes_.size(); ++i) {
|
|
if (codes_[i] == HTTP_HEADER_OTHER) {
|
|
delete headerNames_[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
HTTPHeaders::~HTTPHeaders () {
|
|
disposeOfHeaderNames();
|
|
}
|
|
|
|
HTTPHeaders::HTTPHeaders(const HTTPHeaders& hdrs) :
|
|
codes_(hdrs.codes_),
|
|
headerNames_(hdrs.headerNames_),
|
|
headerValues_(hdrs.headerValues_),
|
|
deletedCount_(hdrs.deletedCount_) {
|
|
for (size_t i = 0; i < codes_.size(); ++i) {
|
|
if (codes_[i] == HTTP_HEADER_OTHER) {
|
|
headerNames_[i] = new string(*hdrs.headerNames_[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
HTTPHeaders::HTTPHeaders(HTTPHeaders&& hdrs) noexcept :
|
|
codes_(std::move(hdrs.codes_)),
|
|
headerNames_(std::move(hdrs.headerNames_)),
|
|
headerValues_(std::move(hdrs.headerValues_)),
|
|
deletedCount_(hdrs.deletedCount_) {
|
|
hdrs.removeAll();
|
|
}
|
|
|
|
HTTPHeaders& HTTPHeaders::operator= (const HTTPHeaders& hdrs) {
|
|
if (this != &hdrs) {
|
|
disposeOfHeaderNames();
|
|
codes_ = hdrs.codes_;
|
|
headerNames_ = hdrs.headerNames_;
|
|
headerValues_ = hdrs.headerValues_;
|
|
deletedCount_ = hdrs.deletedCount_;
|
|
for (size_t i = 0; i < codes_.size(); ++i) {
|
|
if (codes_[i] == HTTP_HEADER_OTHER) {
|
|
headerNames_[i] = new string(*hdrs.headerNames_[i]);
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HTTPHeaders& HTTPHeaders::operator= (HTTPHeaders&& hdrs) {
|
|
if (this != &hdrs) {
|
|
codes_ = std::move(hdrs.codes_);
|
|
headerNames_ = std::move(hdrs.headerNames_);
|
|
headerValues_ = std::move(hdrs.headerValues_);
|
|
deletedCount_ = hdrs.deletedCount_;
|
|
|
|
hdrs.removeAll();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
void HTTPHeaders::removeAll() {
|
|
disposeOfHeaderNames();
|
|
|
|
codes_.clear();
|
|
headerNames_.clear();
|
|
headerValues_.clear();
|
|
deletedCount_ = 0;
|
|
}
|
|
|
|
size_t HTTPHeaders::size() const {
|
|
return codes_.size() - deletedCount_;
|
|
}
|
|
|
|
bool
|
|
HTTPHeaders::transferHeaderIfPresent(folly::StringPiece name,
|
|
HTTPHeaders& strippedHeaders) {
|
|
bool transferred = false;
|
|
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
|
|
name.size());
|
|
if (code == HTTP_HEADER_OTHER) {
|
|
ITERATE_OVER_STRINGS(name, {
|
|
strippedHeaders.codes_.push_back(HTTP_HEADER_OTHER);
|
|
// in the next line, ownership of pointer goes to strippedHeaders
|
|
strippedHeaders.headerNames_.push_back(headerNames_[pos]);
|
|
strippedHeaders.headerValues_.push_back(headerValues_[pos]);
|
|
codes_[pos] = HTTP_HEADER_NONE;
|
|
transferred = true;
|
|
++deletedCount_;
|
|
});
|
|
} else { // code != HTTP_HEADER_OTHER
|
|
ITERATE_OVER_CODES(code, {
|
|
strippedHeaders.codes_.push_back(code);
|
|
strippedHeaders.headerNames_.push_back(headerNames_[pos]);
|
|
strippedHeaders.headerValues_.push_back(headerValues_[pos]);
|
|
codes_[pos] = HTTP_HEADER_NONE;
|
|
transferred = true;
|
|
++deletedCount_;
|
|
});
|
|
}
|
|
return transferred;
|
|
}
|
|
|
|
void
|
|
HTTPHeaders::stripPerHopHeaders(HTTPHeaders& strippedHeaders) {
|
|
int len;
|
|
forEachValueOfHeader(HTTP_HEADER_CONNECTION, [&]
|
|
(const string& stdStr) -> bool {
|
|
// Remove all headers specified in Connection header
|
|
// look for multiple values separated by commas
|
|
char const* str = stdStr.c_str();
|
|
|
|
// skip leading whitespace
|
|
while (isLWS(*str)) str++;
|
|
|
|
while (*str != 0) {
|
|
char const* pos = strchr(str, ',');
|
|
if (pos == nullptr) {
|
|
// last (or only) token, done
|
|
|
|
// count chars in the token
|
|
len = 0;
|
|
while (str[len] != 0 && !isLWS(str[len])) len++;
|
|
if (len > 0) {
|
|
string hdr(str, len);
|
|
if (transferHeaderIfPresent(hdr, strippedHeaders)) {
|
|
VLOG(3) << "Stripped connection-named hop-by-hop header " << hdr;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
len = pos - str;
|
|
// strip trailing whitespace
|
|
while (len > 0 && isLWS(str[len - 1])) len--;
|
|
if (len > 0) {
|
|
// non-empty token
|
|
string hdr(str, len);
|
|
if (transferHeaderIfPresent(hdr, strippedHeaders)) {
|
|
VLOG(3) << "Stripped connection-named hop-by-hop header " << hdr;
|
|
}
|
|
} // else empty token, no-op
|
|
str = pos + 1;
|
|
|
|
// skip whitespace
|
|
while (isLWS(*str)) str++;
|
|
}
|
|
return false; // continue processing "connection" headers
|
|
});
|
|
|
|
// Strip hop-by-hop headers
|
|
auto& perHopHeaders = perHopHeaderCodes();
|
|
for (size_t i = 0; i < codes_.size(); ++i) {
|
|
if (perHopHeaders[codes_[i]]) {
|
|
strippedHeaders.codes_.push_back(codes_[i]);
|
|
strippedHeaders.headerNames_.push_back(headerNames_[i]);
|
|
strippedHeaders.headerValues_.push_back(headerValues_[i]);
|
|
codes_[i] = HTTP_HEADER_NONE;
|
|
++deletedCount_;
|
|
VLOG(3) << "Stripped hop-by-hop header " << *headerNames_[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void HTTPHeaders::copyTo(HTTPHeaders& hdrs) const {
|
|
for (size_t i = 0; i < codes_.size(); ++i) {
|
|
if (codes_[i] != HTTP_HEADER_NONE) {
|
|
hdrs.codes_.push_back(codes_[i]);
|
|
hdrs.headerNames_.push_back((codes_[i] == HTTP_HEADER_OTHER) ?
|
|
new string(*headerNames_[i]) : headerNames_[i]);
|
|
hdrs.headerValues_.push_back(headerValues_[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|