in proxygen/lib/http/codec/HTTP2Codec.cpp [440:542]
ErrorCode HTTP2Codec::parseHeadersImpl(
Cursor& /*cursor*/,
std::unique_ptr<IOBuf> headerBuf,
const folly::Optional<http2::PriorityUpdate>& priority,
const folly::Optional<uint32_t>& promisedStream,
const folly::Optional<ExAttributes>& exAttributes) {
curHeaderBlock_.append(std::move(headerBuf));
std::unique_ptr<HTTPMessage> msg;
uint32_t headersCompleteStream = curHeader_.stream;
// if we're not parsing CONTINUATION, then it's start of new header block
if (curHeader_.type != http2::FrameType::CONTINUATION) {
headerBlockFrameType_ = curHeader_.type;
if (promisedStream) {
parsingReq_ = true;
} else if (exAttributes) {
parsingReq_ = isRequest(curHeader_.stream);
} else {
parsingReq_ = transportDirection_ == TransportDirection::DOWNSTREAM;
}
} else if (headerBlockFrameType_ == http2::FrameType::PUSH_PROMISE) {
CHECK(promisedStream_.hasValue());
headersCompleteStream = *promisedStream_;
}
DeferredParseError parseError;
if (curHeader_.flags & http2::END_HEADERS) {
auto parseRes = parseHeadersDecodeFrames(priority, exAttributes);
if (parseRes.hasError()) {
parseError = std::move(parseRes.error());
if (parseError.connectionError) {
return parseError.errorCode;
}
} else {
msg = std::move(*parseRes);
}
}
// Report back what we've parsed
auto concurError = parseHeadersCheckConcurrentStreams(priority);
if (concurError.has_value()) {
return concurError.value();
}
bool trailers = parsingTrailers();
bool allHeaderFramesReceived =
(curHeader_.flags & http2::END_HEADERS) &&
(headerBlockFrameType_ == http2::FrameType::HEADERS);
if (allHeaderFramesReceived && !trailers) {
// Only deliver onMessageBegin once per stream.
// For responses with CONTINUATION, this will be delayed until
// the frame with the END_HEADERS flag set.
deliverCallbackIfAllowed(&HTTPCodec::Callback::onMessageBegin,
"onMessageBegin",
curHeader_.stream,
msg.get());
} else if (curHeader_.type == http2::FrameType::EX_HEADERS) {
deliverCallbackIfAllowed(&HTTPCodec::Callback::onExMessageBegin,
"onExMessageBegin",
curHeader_.stream,
exAttributes->controlStream,
exAttributes->unidirectional,
msg.get());
} else if (curHeader_.type == http2::FrameType::PUSH_PROMISE) {
DCHECK(promisedStream);
deliverCallbackIfAllowed(&HTTPCodec::Callback::onPushMessageBegin,
"onPushMessageBegin",
*promisedStream,
curHeader_.stream,
msg.get());
promisedStream_ = *promisedStream;
headersCompleteStream = *promisedStream;
}
if (curHeader_.flags & http2::END_HEADERS) {
if (!msg) {
deliverDeferredParseError(parseError);
return ErrorCode::NO_ERROR;
}
if (!(curHeader_.flags & http2::END_STREAM)) {
// If it there are DATA frames coming, consider it chunked
msg->setIsChunked(true);
}
if (trailers) {
VLOG(4) << "Trailers complete for streamId=" << headersCompleteStream
<< " direction=" << transportDirection_;
auto trailerHeaders =
std::make_unique<HTTPHeaders>(msg->extractHeaders());
msg.reset();
deliverCallbackIfAllowed(&HTTPCodec::Callback::onTrailersComplete,
"onTrailersComplete",
headersCompleteStream,
std::move(trailerHeaders));
} else {
deliverCallbackIfAllowed(&HTTPCodec::Callback::onHeadersComplete,
"onHeadersComplete",
headersCompleteStream,
std::move(msg));
promisedStream_ = folly::none;
}
}
return handleEndStream();
}