24 using std::unique_ptr;
29 const char CRLF[] =
"\r\n";
40 char*
next = (
char*)dst;
43 *next++ =
'0' + (value % 10);
46 unsigned length = next -
start;
50 while (next > start) {
63 size_t encodedLen = u64toa(value, buf);
64 queue.
append(buf, encodedLen);
68 #define appendLiteral(queue, len, str) (len) += (sizeof(str) - 1); \ 69 (queue).append(str, sizeof(str) - 1) 73 queue.
append(str.data(), str.size());
87 transportDirection_(direction),
89 forceUpstream1_1_(forceUpstream1_1),
94 requestPending_(false),
95 responsePending_(false),
96 egressChunked_(false),
98 lastChunkWritten_(false),
100 disableKeepalivePending_(false),
101 connectRequest_(false),
103 expectNoResponseBody_(false),
104 mayChunkEgress_(false),
105 is1xxResponse_(false),
106 inRecvLastChunk_(false),
107 ingressUpgrade_(false),
108 ingressUpgradeComplete_(false),
109 egressUpgrade_(false),
110 nativeUpgrade_(false),
111 headersComplete_(false) {
120 LOG(FATAL) <<
"Unknown transport direction.";
173 return &parserSettings;
193 msg_->setStatusCode(200);
201 (
const char*)buf.
data(),
261 what ? what : folly::to<std::string>(
262 "Error parsing message: ",
320 std::array<unsigned char, 16> arr;
331 std::array<unsigned char, 20> arr;
342 appendString(writeBuf, len, kUpgradeToken.str());
348 appendString(writeBuf, len, key);
354 appendString(writeBuf, len, kUpgradeToken.str());
373 const bool downstream = !upstream;
389 LOG(DFATAL) <<
"Out of order, duplicate or premature HTTP response";
417 }
else if (statusCode == 101) {
448 DCHECK_NE(statusCode, 0);
449 if (version.first == 0 && version.second == 9) {
453 appendUint(writeBuf, len, version.first);
455 appendUint(writeBuf, len, version.second);
457 appendUint(writeBuf, len, statusCode);
459 appendString(writeBuf, len, statusMessage);
471 appendString(writeBuf, len, msg.
getURL());
473 appendUint(writeBuf, len, version.first);
475 appendUint(writeBuf, len, version.second);
478 LOG(DFATAL) <<
"Attempted to pipeline HTTP request with pending upgrade";
497 if (version.first == 0 && version.second == 9) {
502 const string* deferredContentLength =
nullptr;
503 bool hasTransferEncodingChunked =
false;
504 bool hasDateHeader =
false;
505 bool hasUpgradeHeader =
false;
506 std::vector<StringPiece> connectionTokens;
507 size_t lastConnectionToken = 0;
509 bool hasUpgradeTokeninConnection =
false;
511 const string& header,
512 const string& value) {
515 deferredContentLength = &
value;
518 egressWebsocketUpgrade)) {
519 static const string kClose =
"close";
520 static const string kKeepAlive =
"keep-alive";
522 for (
auto curConnectionToken = lastConnectionToken;
523 curConnectionToken < connectionTokens.size();
524 curConnectionToken++) {
525 auto token =
trimWhitespace(connectionTokens[curConnectionToken]);
527 hasUpgradeTokeninConnection =
true;
532 connectionTokens[lastConnectionToken++] = token;
535 connectionTokens.resize(lastConnectionToken);
539 hasUpgradeHeader =
true;
544 }
else if (!hasTransferEncodingChunked &&
549 hasTransferEncodingChunked =
true;
554 hasDateHeader =
true;
555 }
else if (egressWebsocketUpgrade &&
559 }
else if (egressWebsocketUpgrade &&
564 size_t lineLen = header.length() + value.length() + 4;
567 char* dst = (
char*)writable.first;
568 memcpy(dst, header.data(), header.length());
569 dst += header.length();
572 memcpy(dst, value.data(), value.length());
573 dst += value.length();
576 DCHECK_EQ(
size_t(++dst - (
char*)writable.first), lineLen);
595 appendLiteral(writeBuf, len,
"Transfer-Encoding: chunked\r\n");
601 if (downstream && !hasDateHeader) {
607 if (!hasUpgradeHeader && txn == 1) {
610 if (!hasUpgradeTokeninConnection) {
611 connectionTokens.push_back(kUpgradeConnectionToken);
612 lastConnectionToken++;
615 LOG(
ERROR) << folly::to<string>(
"Not serializing headers. " 616 "Upgrade headers present/txn: ",
617 hasUpgradeHeader, txn);
625 if (connectionTokens.size() > 0) {
626 appendString(writeBuf, len,
folly::join(
", ", connectionTokens));
629 if (connectionTokens.size() > 0) {
630 appendString(writeBuf, len,
", ");
641 if (deferredContentLength) {
643 appendString(writeBuf, len, *deferredContentLength);
659 unique_ptr<IOBuf> chain,
667 size_t totLen = buflen;
676 char chunkLenBuf[32];
677 int rc = snprintf(chunkLenBuf,
sizeof(chunkLenBuf),
"%zx\r\n", buflen);
679 CHECK_LT(
size_t(rc),
sizeof(chunkLenBuf));
681 writeBuf.
append(chunkLenBuf, rc);
685 writeBuf.
append(
"\r\n", 2);
703 CHECK(length) <<
"use sendEOM to terminate the message using the " 704 <<
"standard zero-length chunk. Don't " 705 <<
"send zero-length chunks using this API.";
709 char chunkLenBuf[32];
710 int rc = snprintf(chunkLenBuf,
sizeof(chunkLenBuf),
"%zx\r\n", length);
712 CHECK_LT(
size_t(rc),
sizeof(chunkLenBuf));
714 writeBuf.
append(chunkLenBuf, rc);
725 writeBuf.
append(
"\r\n", 2);
742 trailers.
forEach([&] (
const string& trailer,
const string& value) {
743 appendString(writeBuf, len, trailer);
745 appendString(writeBuf, len, value);
795 std::unique_ptr<folly::IOBuf>) {
804 for (
const auto& proto: protocols) {
841 url_.append(buf, len);
937 LOG(
ERROR) <<
"Invalid Transfer-Encoding header. Value =" << headerVal;
946 bool error = hdrs.forEachValueOfHeader(
956 LOG(
ERROR) <<
"Invalid message, multiple Content-Length headers";
983 VLOG(4) <<
"Adding inferred host header: " << hostAndPort;
1000 upgradeHeader_.clear();
1014 if (serverUpgrade.empty() ||
1016 LOG(
ERROR) <<
"Invalid 101 response, empty upgrade headers";
1040 LOG(
ERROR) <<
"Invalid 101 response, client/server upgrade mismatch " 1079 msg_->setIngressWebsocketUpgrade();
1085 LOG(
ERROR) <<
"Mismatch in expected ws accept key: " <<
1097 bool msgKeepalive =
msg_->computeKeepalive();
1098 if (!msgKeepalive) {
1103 if (msgKeepalive &&
msg_->isHTTP1_0() &&
1140 return (ignoreBody) ? 1 : 0;
1150 DCHECK_GE(buf, dataStart);
1151 DCHECK_LE(buf + len, dataEnd);
1153 clone->trimStart(buf - dataStart);
1154 clone->trimEnd(dataEnd - (buf + len));
1155 DCHECK_EQ(len, clone->computeChainDataLength());
1164 VLOG(5) <<
"Suppressed onChunkHeader callback for final zero length " 1238 DCHECK(codec !=
nullptr);
1239 DCHECK_EQ(&codec->
parser_, parser);
1243 }
catch (
const std::exception& ex) {
1252 DCHECK(codec !=
nullptr);
1253 DCHECK_EQ(&codec->
parser_, parser);
1256 return codec->
onURL(buf, len);
1257 }
catch (
const std::exception& ex) {
1266 DCHECK(codec !=
nullptr);
1267 DCHECK_EQ(&codec->
parser_, parser);
1271 }
catch (
const std::exception& ex) {
1280 DCHECK(codec !=
nullptr);
1281 DCHECK_EQ(&codec->
parser_, parser);
1285 }
catch (
const std::exception& ex) {
1294 DCHECK(codec !=
nullptr);
1295 DCHECK_EQ(&codec->
parser_, parser);
1299 }
catch (
const std::exception& ex) {
1309 DCHECK(codec !=
nullptr);
1310 DCHECK_EQ(&codec->
parser_, parser);
1314 }
catch (
const std::exception& ex) {
1323 DCHECK(codec !=
nullptr);
1324 DCHECK_EQ(&codec->
parser_, parser);
1327 return codec->
onBody(buf, len);
1328 }
catch (
const std::exception& ex) {
1342 DCHECK(codec !=
nullptr);
1343 DCHECK_EQ(&codec->
parser_, parser);
1347 }
catch (
const std::exception& ex) {
1355 DCHECK(codec !=
nullptr);
1356 DCHECK_EQ(&codec->
parser_, parser);
1360 }
catch (
const std::exception& ex) {
1369 DCHECK(codec !=
nullptr);
1370 DCHECK_EQ(&codec->
parser_, parser);
1374 }
catch (
const std::exception& ex) {
1381 return npn.length() == 8 && (npn ==
"http/1.0" || npn ==
"http/1.1");
std::pair< CodecProtocol, std::string > upgradeResult_
size_t generateTrailers(folly::IOBufQueue &writeBuf, StreamID txn, const HTTPHeaders &trailers) override
int onBody(const char *buf, size_t len)
void setHttpStatusCode(uint32_t statusCode)
std::string currentHeaderValue_
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
bool responseBodyMustBeEmpty(unsigned status)
TransportDirection transportDirection_
spdy::GoawayStatusCode statusCode
std::unique_ptr< HTTPMessage > msg_
void serializeWebsocketHeader(folly::IOBufQueue &writeBuf, size_t &len, bool upstream)
virtual void onError(StreamID stream, const HTTPException &error, bool newTxn=false)=0
void setCurrentIngressBuf(std::unique_ptr< folly::IOBuf > buf)
bool isBusy() const override
virtual void onMessageBegin(StreamID stream, HTTPMessage *msg)=0
constexpr folly::StringPiece kUpgradeToken
http_data_cb on_header_field
bool caseInsensitiveEqual(folly::StringPiece s, folly::StringPiece t)
static const std::pair< uint8_t, uint8_t > kHTTPVersion10
void hash_init(const EVP_MD *md)
unsigned short http_minor
const std::string & getAllowedUpgradeProtocols()
std::unique_ptr< HTTPMessage > upgradeRequest_
static std::enable_if< std::is_integral< T >::value &&!std::is_same< T, bool >::value, T >::type secureRandom()
virtual void onChunkHeader(StreamID, size_t)
void setAllowedUpgradeProtocols(std::list< std::string > protocols)
#define appendLiteral(queue, len, str)
const std::string & getStatusMessage() const
HeaderParseState headerParseState_
virtual void onMessageComplete(StreamID stream, bool upgrade)=0
uint16_t getStatusCode() const
void generateHeader(folly::IOBufQueue &writeBuf, StreamID txn, const HTTPMessage &msg, bool eom=false, HTTPHeaderSize *size=nullptr) override
std::string upgradeHeader_
virtual void onTrailersComplete(StreamID stream, std::unique_ptr< HTTPHeaders > trailers)=0
static int onChunkHeaderCB(http_parser *parser)
static bool supportsNextProtocol(const std::string &npn)
constexpr detail::Map< Move > move
bool disableKeepalivePending_
size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings, const char *data, size_t len)
void advance(size_type n)
folly::StringPiece currentHeaderNameStringPiece_
int onHeaderValue(const char *buf, size_t len)
void setPartialMsg(std::unique_ptr< HTTPMessage > partialMsg)
constexpr size_type size() const
bool expectNoResponseBody_
const uint8_t * data() const
void setProxygenError(ProxygenError proxygenError)
static std::string formatDateHeader()
bool is1xxResponse() const
static int onMessageCompleteCB(http_parser *parser)
std::unique_ptr< IOBuf > clone() const
static int onHeaderValueCB(http_parser *parser, const char *buf, size_t len)
std::string generateWebsocketKey() const
static int onMessageBeginCB(http_parser *parser)
std::string hostAndPort() const
static http_parser * parser
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
void split(const Delim &delimiter, const String &input, std::vector< OutputType > &out, bool ignoreEmpty)
static int onHeadersCompleteCB(http_parser *parser, const char *buf, size_t len)
std::pair< void *, std::size_t > preallocate(std::size_t min, std::size_t newAllocationSize, std::size_t max=std::numeric_limits< std::size_t >::max())
std::string currentHeaderName_
unsigned short status_code
static int onChunkCompleteCB(http_parser *parser)
static int onUrlCB(http_parser *parser, const char *buf, size_t len)
HTTPCodec::Callback * callback_
const char * http_method_str(enum http_method m)
void http_parser_pause(http_parser *parser, int paused)
constexpr auto size(C const &c) -> decltype(c.size())
constexpr bool empty() const
void hash_final(MutableByteRange out)
void writeBuf(const Buf &buf, folly::io::Appender &out)
#define HTTP_PARSER_ERRNO(p)
HTTPHeaderSize headerSize_
const std::string & methodToString(HTTPMethod method)
bool getIsChunked() const
void hash_update(ByteRange data)
folly::Optional< std::pair< CodecProtocol, std::string > > checkForProtocolUpgrade(const std::string &clientUpgrade, const std::string &serverUpgrade, bool serverMode)
static const std::pair< uint8_t, uint8_t > kHTTPVersion11
std::size_t length() const
const std::string & getCodecProtocolString(CodecProtocol proto)
const char * http_errno_description(enum http_errno err)
unsigned short http_major
void addDateHeader(folly::IOBufQueue &writeBuf, size_t &len)
FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept
http_data_cb on_headers_complete
bool isReusable() const override
size_t onIngress(const folly::IOBuf &buf) override
static int onReasonCB(http_parser *parser, const char *buf, size_t len)
std::unique_ptr< IOBuf > cloneOne() const
static int onBodyCB(http_parser *parser, const char *buf, size_t len)
static const char *const value
HTTPHeaders & getHeaders()
constexpr folly::StringPiece kWSMagicString
bool isParsingHeaderOrTrailerName() const
void http_parser_init(http_parser *parser, enum http_parser_type t)
bool isParsingHeaders() const
int onHeaderField(const char *buf, size_t len)
constexpr Iter end() const
int onHeadersComplete(size_t len)
http_data_cb on_header_value
std::size_t computeChainDataLength() const
void onParserError(const char *what=nullptr)
constexpr Iter begin() const
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
std::string allowedNativeUpgrades_
virtual void onHeadersComplete(StreamID stream, std::unique_ptr< HTTPMessage > msg)=0
http_cb on_chunk_complete
StringPiece trimWhitespace(StringPiece sp)
const std::string & getMethodString() const
void setParserPaused(bool paused) override
std::string websockAcceptKey_
int onReason(const char *buf, size_t len)
void pushHeaderNameAndValue(HTTPHeaders &hdrs)
size_t generateRstStream(folly::IOBufQueue &writeBuf, StreamID txn, ErrorCode statusCode) override
void join(const Delim &delimiter, Iterator begin, Iterator end, String &output)
FOLLY_CPP14_CONSTEXPR const Value & value() const &
constexpr folly::StringPiece kUpgradeConnectionToken
KeepaliveRequested keepaliveRequested_
folly::Function< void()> callback_
static int onHeaderFieldCB(http_parser *parser, const char *buf, size_t len)
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
virtual void onChunkComplete(StreamID)
static const char * getDefaultReason(uint16_t status)
folly::Optional< HTTPMethod > getMethod() const
std::string generateWebsocketAccept(const std::string &acceptKey) const
Range< const char * > StringPiece
bool ingressUpgradeComplete_
StreamID createStream() override
const std::string & getURL() const
size_t generateEOM(folly::IOBufQueue &writeBuf, StreamID txn) override
int onURL(const char *buf, size_t len)
void postallocate(std::size_t n)
std::unique_ptr< HTTPHeaders > trailers_
virtual void onBody(StreamID stream, std::unique_ptr< folly::IOBuf > chain, uint16_t padding)=0
const folly::IOBuf * currentIngressBuf_
size_t generateGoaway(folly::IOBufQueue &writeBuf, StreamID lastStream, ErrorCode statusCode, std::unique_ptr< folly::IOBuf > debugData=nullptr) override
static std::string encode(folly::ByteRange buffer)
int onChunkHeader(size_t len)
bool isEgressWebsocketUpgrade() const
HTTP1xCodec(TransportDirection direction, bool forceUpstream1_1=false)
void onIngressEOF() override
http_cb on_message_complete
static const http_parser_settings * getParserSettings()
void reset(Iter start, size_type size)
size_t generateChunkHeader(folly::IOBufQueue &writeBuf, StreamID txn, size_t length) override
size_t generateBody(folly::IOBufQueue &writeBuf, StreamID txn, std::unique_ptr< folly::IOBuf > chain, folly::Optional< uint8_t > padding, bool eom) override
bool wantsKeepalive() const
size_t generateChunkTerminator(folly::IOBufQueue &writeBuf, StreamID txn) override
virtual bool onNativeProtocolUpgrade(StreamID, CodecProtocol, const std::string &, HTTPMessage &)
const std::pair< uint8_t, uint8_t > & getHTTPVersion() const
NetworkSocket accept(NetworkSocket s, sockaddr *addr, socklen_t *addrlen)