int ClientConnectionImpl::onHeadersComplete() {
  // Handle the case where the client is closing a kept alive connection (by sending a 408
  // with a 'Connection: close' header). In this case we just let response flush out followed
  // by the remote close.
  if (!pending_response_.has_value() && !resetStreamCalled()) {
    throw PrematureResponseException(static_cast<Http::Code>(parser_.status_code));
  } else if (pending_response_.has_value()) {
    ASSERT(!pending_response_done_);
    auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
    ENVOY_CONN_LOG(trace, "Client: onHeadersComplete size={}", connection_, headers->size());
    headers->setStatus(parser_.status_code);

    if (parser_.status_code >= 200 && parser_.status_code < 300 &&
        pending_response_.value().encoder_.connectRequest()) {
      ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT response.", connection_);
      handling_upgrade_ = true;

      // For responses to connect requests, do not accept the chunked
      // encoding header: https://tools.ietf.org/html/rfc7231#section-4.3.6
      if (headers->TransferEncoding() &&
          absl::EqualsIgnoreCase(headers->TransferEncoding()->value().getStringView(),
                                 Headers::get().TransferEncodingValues.Chunked)) {
        sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding);
        throw CodecProtocolException("http/1.1 protocol error: unsupported transfer encoding");
      }
    }

    if (strict_1xx_and_204_headers_ && (parser_.status_code < 200 || parser_.status_code == 204)) {
      if (headers->TransferEncoding()) {
        sendProtocolError(Http1ResponseCodeDetails::get().TransferEncodingNotAllowed);
        throw CodecProtocolException(
            "http/1.1 protocol error: transfer encoding not allowed in 1xx or 204");
      }

      if (headers->ContentLength()) {
        // Report a protocol error for non-zero Content-Length, but paper over zero Content-Length.
        if (headers->ContentLength()->value().getStringView() != "0") {
          sendProtocolError(Http1ResponseCodeDetails::get().ContentLengthNotAllowed);
          throw CodecProtocolException(
              "http/1.1 protocol error: content length not allowed in 1xx or 204");
        }

        headers->removeContentLength();
      }
    }

    if (parser_.status_code == 100) {
      // http-parser treats 100 continue headers as their own complete response.
      // Swallow the spurious onMessageComplete and continue processing.
      ignore_message_complete_for_100_continue_ = true;
      pending_response_.value().decoder_->decode100ContinueHeaders(std::move(headers));

      // Reset to ensure no information from the continue headers is used for the response headers
      // in case the callee does not move the headers out.
      headers_or_trailers_.emplace<ResponseHeaderMapPtr>(nullptr);
    } else if (cannotHaveBody() && !handling_upgrade_) {
      deferred_end_stream_headers_ = true;
    } else {
      pending_response_.value().decoder_->decodeHeaders(std::move(headers), false);
    }
  }

  // Here we deal with cases where the response cannot have a body, but http_parser does not deal
  // with it for us.
  return cannotHaveBody() ? 1 : 0;
}