in proxygen/lib/http/codec/compress/QPACKCodec.cpp [58:172]
std::unique_ptr<folly::IOBuf> QPACKCodec::encodeHTTP(
folly::IOBufQueue& controlQueue,
const HTTPMessage& msg,
bool includeDate,
uint64_t streamId,
uint32_t maxEncoderStreamBytes,
const folly::Optional<HTTPHeaders>& extraHeaders) noexcept {
auto baseIndex = encoder_.startEncode(controlQueue, 0, maxEncoderStreamBytes);
uint32_t requiredInsertCount = 0;
auto prevSize = controlQueue.chainLength();
auto uncompressed = 0;
if (msg.isRequest()) {
if (msg.isEgressWebsocketUpgrade()) {
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_METHOD),
methodToString(HTTPMethod::CONNECT),
baseIndex,
requiredInsertCount);
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_PROTOCOL),
headers::kWebsocketString,
baseIndex,
requiredInsertCount);
} else {
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_METHOD),
msg.getMethodString(),
baseIndex,
requiredInsertCount);
}
if (msg.getMethod() != HTTPMethod::CONNECT ||
msg.isEgressWebsocketUpgrade()) {
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_SCHEME),
msg.getScheme(),
baseIndex,
requiredInsertCount);
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_PATH),
msg.getURL(),
baseIndex,
requiredInsertCount);
}
const HTTPHeaders& headers = msg.getHeaders();
const std::string& host = headers.getSingleOrEmpty(HTTP_HEADER_HOST);
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_AUTHORITY),
host,
baseIndex,
requiredInsertCount);
} else {
if (msg.isEgressWebsocketUpgrade()) {
uncompressed +=
encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_COLON_STATUS),
headers::kStatus200,
baseIndex,
requiredInsertCount);
} else {
uncompressed += encoder_.encodeHeaderQ(
HPACKHeaderName(HTTP_HEADER_COLON_STATUS),
folly::to<folly::fbstring>(msg.getStatusCode()),
baseIndex,
requiredInsertCount);
}
// HEADERS frames do not include a version or reason string.
}
bool hasDateHeader = false;
// Add the HTTP headers supplied by the caller, but skip
// any per-hop headers that aren't supported in HTTP/2.
auto headerEncodeHelper = [&](HTTPHeaderCode code,
folly::StringPiece name,
folly::StringPiece value) {
if (CodecUtil::perHopHeaderCodes()[code] || name.empty() ||
name[0] == ':') {
DCHECK(!name.empty()) << "Empty header";
DCHECK_NE(name[0], ':') << "Invalid header=" << name;
return;
}
// Note this code will not drop headers named by Connection. That's the
// caller's job
// see HTTP/2 spec, 8.1.2
DCHECK(name != "TE" || value == "trailers");
if ((!name.empty() && name[0] != ':') && code != HTTP_HEADER_HOST) {
if (code == HTTP_HEADER_OTHER) {
uncompressed += encoder_.encodeHeaderQ(
HPACKHeaderName(name), value, baseIndex, requiredInsertCount);
} else {
uncompressed += encoder_.encodeHeaderQ(
HPACKHeaderName(code), value, baseIndex, requiredInsertCount);
}
}
hasDateHeader |= ((code == HTTP_HEADER_DATE) ? 1 : 0);
};
msg.getHeaders().forEachWithCode(headerEncodeHelper);
if (extraHeaders) {
extraHeaders->forEachWithCode(headerEncodeHelper);
}
if (includeDate && msg.isResponse() && !hasDateHeader) {
uncompressed += encoder_.encodeHeaderQ(HPACKHeaderName(HTTP_HEADER_DATE),
HTTPMessage::formatDateHeader(),
baseIndex,
requiredInsertCount);
}
auto result =
encoder_.completeEncode(streamId, baseIndex, requiredInsertCount);
encodedSize_.uncompressed = uncompressed;
recordCompressedSize(result.get(), controlQueue.chainLength() - prevSize);
return result;
}