std::unique_ptr QPACKCodec::encodeHTTP()

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;
}