int HTTP1xCodec::onHeadersComplete()

in proxygen/lib/http/codec/HTTP1xCodec.cpp [960:1191]


int HTTP1xCodec::onHeadersComplete(size_t len) {
  if (headerParseState_ == HeaderParseState::kParsingHeaderValue) {
    if (!pushHeaderNameAndValue(msg_->getHeaders())) {
      return -1;
    }
  }

  // discard messages with folded or multiple valued Transfer-Encoding headers
  // ex : "chunked , zorg\r\n" or "\r\n chunked \r\n" (t12767790)
  HTTPHeaders& hdrs = msg_->getHeaders();
  const std::string& headerVal =
      hdrs.getSingleOrEmpty(HTTP_HEADER_TRANSFER_ENCODING);
  if (!headerVal.empty() && !caseInsensitiveEqual(headerVal, kChunked)) {
    LOG(ERROR) << "Invalid Transfer-Encoding header. Value =" << headerVal;
    return -1;
  }

  // discard messages with multiple content-length headers (t12767790)
  if (hdrs.getNumberOfValues(HTTP_HEADER_CONTENT_LENGTH) > 1) {
    // Only reject the message if the Content-Length headers have different
    // values
    folly::Optional<folly::StringPiece> contentLen;
    bool error = hdrs.forEachValueOfHeader(
        HTTP_HEADER_CONTENT_LENGTH, [&](folly::StringPiece value) -> bool {
          if (!contentLen.has_value()) {
            contentLen = value;
            return false;
          }
          return (contentLen.value() != value);
        });

    if (error) {
      LOG(ERROR) << "Invalid message, multiple Content-Length headers";
      return -1;
    }
  }

  // Update the HTTPMessage with the values parsed from the header
  msg_->setHTTPVersion(parser_.http_major, parser_.http_minor);
  msg_->setIsChunked((parser_.flags & F_CHUNKED));

  if (transportDirection_ == TransportDirection::DOWNSTREAM) {
    // Set the method type
    msg_->setMethod(http_method_str(static_cast<http_method>(parser_.method)));

    connectRequest_ = (msg_->getMethod() == HTTPMethod::CONNECT);

    // If this is a headers-only request, we shouldn't send
    // an entity-body in the response.
    headRequest_ = (msg_->getMethod() == HTTPMethod::HEAD);

    ParseURL parseUrl = msg_->setURL(std::move(url_), strictValidation_);
    if (strictValidation_ && !parseUrl.valid()) {
      LOG(ERROR) << "Invalid URL: " << msg_->getURL();
      return -1;
    }
    url_.clear();

    if (parseUrl.hasHost()) {
      // RFC 2616 5.2.1 states "If Request-URI is an absoluteURI, the host
      // is part of the Request-URI. Any Host header field value in the
      // request MUST be ignored."
      auto hostAndPort = parseUrl.hostAndPort();
      VLOG(4) << "Adding inferred host header: " << hostAndPort;
      msg_->getHeaders().set(HTTP_HEADER_HOST, hostAndPort);
    }

    // If the client sent us an HTTP/1.x with x >= 1, we may send
    // chunked responses.
    mayChunkEgress_ = ((parser_.http_major == 1) && (parser_.http_minor >= 1));
  } else {
    msg_->setStatusCode(parser_.status_code);
    msg_->setStatusMessage(std::move(reason_));
    reason_.clear();
  }

  auto g = folly::makeGuard([this] {
    // Always clear the outbound upgrade header after we receive a response
    if (transportDirection_ == TransportDirection::UPSTREAM &&
        parser_.status_code != 100) {
      upgradeHeader_.clear();
    }
  });
  headerParseState_ = HeaderParseState::kParsingHeadersComplete;
  if (transportDirection_ == TransportDirection::UPSTREAM) {
    if (connectRequest_ &&
        (parser_.status_code >= 200 && parser_.status_code < 300)) {
      // Enable upgrade if this is a 200 response to a CONNECT
      // request we sent earlier
      ingressUpgrade_ = true;
    } else if (parser_.status_code == 101) {
      // Set the upgrade flags if the server has upgraded.
      const std::string& serverUpgrade =
          msg_->getHeaders().getSingleOrEmpty(HTTP_HEADER_UPGRADE);
      if (serverUpgrade.empty() || upgradeHeader_.empty()) {
        LOG(ERROR) << "Invalid 101 response, empty upgrade headers";
        return -1;
      }
      auto result = checkForProtocolUpgrade(
          upgradeHeader_, serverUpgrade, false /* client mode */);
      if (result) {
        ingressUpgrade_ = true;
        egressUpgrade_ = true;
        if (result->first != CodecProtocol::HTTP_1_1) {
          bool success = callback_->onNativeProtocolUpgrade(
              ingressTxnID_, result->first, result->second, *msg_);
          if (success) {
            nativeUpgrade_ = true;
            msg_->setIsUpgraded(ingressUpgrade_);
            return 1; // no message body if successful
          }
        } else if (result->second == getCodecProtocolString(result->first)) {
          // someone upgraded to http/1.1?  Reset upgrade flags
          ingressUpgrade_ = false;
          egressUpgrade_ = false;
        }
        // else, there's some non-native upgrade
      } else {
        LOG(ERROR) << "Invalid 101 response, client/server upgrade mismatch "
                      "client="
                   << upgradeHeader_ << " server=" << serverUpgrade;
        return -1;
      }
    } else if (parser_.upgrade || parser_.flags & F_UPGRADE) {
      // Ignore upgrade header for upstream response messages with status code
      // different from 101 in case if it was not a response to CONNECT.
      parser_.upgrade = false;
      parser_.flags &= ~F_UPGRADE;
    }
  } else {
    if (connectRequest_) {
      // Enable upgrade by default for the CONNECT requests.
      // If we locally reject CONNECT, we will disable this flag while
      // sending the reject response. If we forward the req to upstream proxy,
      // we will start forwarding data to the proxy without waiting for
      // the response from the proxy server.
      ingressUpgrade_ = true;
    } else if (!allowedNativeUpgrades_.empty() && ingressTxnID_ == 1) {
      upgradeHeader_ = msg_->getHeaders().getSingleOrEmpty(HTTP_HEADER_UPGRADE);
      if (!upgradeHeader_.empty() && !allowedNativeUpgrades_.empty()) {
        auto result = checkForProtocolUpgrade(
            upgradeHeader_, allowedNativeUpgrades_, true /* server mode */);
        if (result && result->first != CodecProtocol::HTTP_1_1) {
          nativeUpgrade_ = true;
          upgradeResult_ = *result;
          // unfortunately have to copy because msg_ is passed to
          // onHeadersComplete
          upgradeRequest_ = std::make_unique<HTTPMessage>(*msg_);
        }
      }
    }
  }
  msg_->setIsUpgraded(ingressUpgrade_);

  const std::string& upgrade = hdrs.getSingleOrEmpty(HTTP_HEADER_UPGRADE);
  if (kUpgradeToken.equals(upgrade, folly::AsciiCaseInsensitive())) {
    msg_->setIngressWebsocketUpgrade();
    if (transportDirection_ == TransportDirection::UPSTREAM) {
      // response.
      const std::string& accept =
          hdrs.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT);
      if (accept != websockAcceptKey_) {
        LOG(ERROR) << "Mismatch in expected ws accept key: "
                   << "upstream: " << accept
                   << " expected: " << websockAcceptKey_;
        return -1;
      }
    } else {
      // request.
      // If the websockAcceptKey is already set, we error out.
      // Currently, websockAcceptKey is never cleared, which means
      // that only one Websocket upgrade attempt can be made on the
      // connection. If that upgrade request is not successful for any
      // reason, the connection is no longer usable. At some point, we
      // may want to change this to clear the websockAcceptKey if
      // the request doesn't succeed keeping the connection usable.
      if (!websockAcceptKey_.empty()) {
        LOG(ERROR) << "ws accept key already set: '" << websockAcceptKey_
                   << "'";
        return -1;
      }
      auto key = hdrs.getSingleOrEmpty(HTTP_HEADER_SEC_WEBSOCKET_KEY);
      websockAcceptKey_ = generateWebsocketAccept(key);
    }
  }

  bool msgKeepalive = msg_->computeKeepalive();
  if (!msgKeepalive) {
    keepalive_ = false;
  }
  if (transportDirection_ == TransportDirection::DOWNSTREAM) {
    // Remember whether this was an HTTP 1.0 request with keepalive enabled
    if (msgKeepalive && msg_->isHTTP1_0() &&
        (keepaliveRequested_ == KeepaliveRequested::UNSET ||
         keepaliveRequested_ == KeepaliveRequested::ENABLED)) {
      keepaliveRequested_ = KeepaliveRequested::ENABLED;
    } else {
      keepaliveRequested_ = KeepaliveRequested::DISABLED;
    }
  }

  // Determine whether the HTTP parser should ignore any headers
  // that indicate the presence of a message body.  This is needed,
  // for example, if the message is a response to a request with
  // method==HEAD.
  bool ignoreBody;
  if (transportDirection_ == TransportDirection::DOWNSTREAM) {
    ignoreBody = false;
  } else {
    is1xxResponse_ = msg_->is1xxResponse();
    if (expectNoResponseBody_) {
      ignoreBody = true;
    } else {
      ignoreBody = RFC2616::responseBodyMustBeEmpty(msg_->getStatusCode());
    }
  }

  headersComplete_ = true;
  headerSize_.uncompressed += len;
  headerSize_.compressed += len;
  msg_->setIngressHeaderSize(headerSize_);

  if (userAgent_.empty()) {
    userAgent_ = msg_->getHeaders().getSingleOrEmpty(HTTP_HEADER_USER_AGENT);
  }
  callback_->onHeadersComplete(ingressTxnID_, std::move(msg_));

  // 1 is a magic value that tells the http_parser not to expect a
  // message body even if the message header implied the presence
  // of one (e.g., via a Content-Length)
  return (ignoreBody) ? 1 : 0;
}