void StreamEncoderImpl::encodeHeadersBase(const RequestOrResponseHeaderMap& headers,
                                          absl::optional<uint64_t> status, bool end_stream) {
  bool saw_content_length = false;
  headers.iterate(
      [](const HeaderEntry& header, void* context) -> HeaderMap::Iterate {
        absl::string_view key_to_use = header.key().getStringView();
        uint32_t key_size_to_use = header.key().size();
        // Translate :authority -> host so that upper layers do not need to deal with this.
        if (key_size_to_use > 1 && key_to_use[0] == ':' && key_to_use[1] == 'a') {
          key_to_use = absl::string_view(Headers::get().HostLegacy.get());
          key_size_to_use = Headers::get().HostLegacy.get().size();
        }

        // Skip all headers starting with ':' that make it here.
        if (key_to_use[0] == ':') {
          return HeaderMap::Iterate::Continue;
        }

        static_cast<StreamEncoderImpl*>(context)->encodeFormattedHeader(
            key_to_use, header.value().getStringView());

        return HeaderMap::Iterate::Continue;
      },
      this);

  if (headers.ContentLength()) {
    saw_content_length = true;
  }

  ASSERT(!headers.TransferEncoding());

  // Assume we are chunk encoding unless we are passed a content length or this is a header only
  // response. Upper layers generally should strip transfer-encoding since it only applies to
  // HTTP/1.1. The codec will infer it based on the type of response.
  // for streaming (e.g. SSE stream sent to hystrix dashboard), we do not want
  // chunk transfer encoding but we don't have a content-length so disable_chunk_encoding_ is
  // consulted before enabling chunk encoding.
  //
  // Note that for HEAD requests Envoy does best-effort guessing when there is no
  // content-length. If a client makes a HEAD request for an upstream resource
  // with no bytes but the upstream response doesn't include "Content-length: 0",
  // Envoy will incorrectly assume a subsequent response to GET will be chunk encoded.
  if (saw_content_length || disable_chunk_encoding_) {
    chunk_encoding_ = false;
  } else {
    if (status && *status == 100) {
      // Make sure we don't serialize chunk information with 100-Continue headers.
      chunk_encoding_ = false;
    } else if (end_stream && !is_response_to_head_request_) {
      // If this is a headers-only stream, append an explicit "Content-Length: 0" unless it's a
      // response to a HEAD request.
      // For 204s and 1xx where content length is disallowed, don't append the content length but
      // also don't chunk encode.
      if (!status || (*status >= 200 && *status != 204)) {
        encodeFormattedHeader(Headers::get().ContentLength.get(), "0");
      }
      chunk_encoding_ = false;
    } else if (connection_.protocol() == Protocol::Http10) {
      chunk_encoding_ = false;
    } else if (status && (*status < 200 || *status == 204) &&
               connection_.strict1xxAnd204Headers()) {
      // TODO(zuercher): when the "envoy.reloadable_features.strict_1xx_and_204_response_headers"
      // feature flag is removed, this block can be coalesced with the 100 Continue logic above.

      // For 1xx and 204 responses, do not send the chunked encoding header or enable chunked
      // encoding: https://tools.ietf.org/html/rfc7230#section-3.3.1
      chunk_encoding_ = false;

      // Assert 1xx (may have content) OR 204 and end stream.
      ASSERT(*status < 200 || end_stream);
    } else {
      // For responses to connect requests, do not send the chunked encoding header:
      // https://tools.ietf.org/html/rfc7231#section-4.3.6.
      if (!is_response_to_connect_request_) {
        encodeFormattedHeader(Headers::get().TransferEncoding.get(),
                              Headers::get().TransferEncodingValues.Chunked);
      }
      // We do not apply chunk encoding for HTTP upgrades, including CONNECT style upgrades.
      // If there is a body in a response on the upgrade path, the chunks will be
      // passed through via maybeDirectDispatch so we need to avoid appending
      // extra chunk boundaries.
      //
      // When sending a response to a HEAD request Envoy may send an informational
      // "Transfer-Encoding: chunked" header, but should not send a chunk encoded body.
      chunk_encoding_ = !Utility::isUpgrade(headers) && !is_response_to_head_request_ &&
                        !is_response_to_connect_request_;
    }
  }

  connection_.addToBuffer(CRLF);

  if (end_stream) {
    endEncode();
  } else {
    connection_.flushOutput();
  }
}