Repository: envoyproxy/envoy
File: source/common/http/conn_manager_impl.cc (C++)

Code before:
  checkForDeferredClose(false);
}

void ConnectionManagerImpl::sendGoAwayAndClose(bool immediate_close) {
  ENVOY_CONN_LOG(trace, "connection manager sendGoAwayAndClose was triggerred from filters.",
                 read_callbacks_->connection());
  if (go_away_sent_) {
    return;
  }

  // For HTTP/2 connections, use graceful drain sequence for RFC-compliant shutdown
  // unless immediate_close is requested
  if (codec_->protocol() >= Protocol::Http2 && !immediate_close) {
    if (drain_state_ == DrainState::NotDraining) {
      startDrainSequence();
    }
    // For HTTP/2, consider the "go away" process started once draining begins.
    // The actual GOAWAY frame will be sent in onDrainTimeout(), but we want to
    // prevent multiple calls to sendGoAwayAndClose() from starting multiple drain sequences.
    go_away_sent_ = true;
  } else {
    // For HTTP/1.x connections or immediate close requested, use immediate GOAWAY and close
    codec_->goAway();
    go_away_sent_ = true;
    doConnectionClose(Network::ConnectionCloseType::FlushWriteAndDelay, absl::nullopt,
                      "forced_goaway");
  }
}

void ConnectionManagerImpl::chargeTracingStats(const Tracing::Reason& tracing_reason,
                                               ConnectionManagerTracingStats& tracing_stats) {
  switch (tracing_reason) {
  case Tracing::Reason::ClientForced:
    tracing_stats.client_enabled_.inc();
    break;
  case Tracing::Reason::Sampling:
    tracing_stats.random_sampling_.inc();
    break;
  case Tracing::Reason::ServiceForced:
    tracing_stats.service_forced_.inc();
    break;
  default:
    tracing_stats.not_traceable_.inc();
    break;
  }
}

absl::optional<absl::string_view>
ConnectionManagerImpl::HttpStreamIdProviderImpl::toStringView() const {
  if (parent_.request_headers_ == nullptr) {
    return {};

Review comment (suggestion):
Although I think it makes no actual practical sense, but will adding a codec_->shutdownNotice(); here more in line with spec?

```suggestion
    codec_->shutdownNotice();
    codec_->goAway();
```

Code after:
  checkForDeferredClose(false);
}

void ConnectionManagerImpl::sendGoAwayAndClose(bool graceful) {
  ENVOY_CONN_LOG(trace, "connection manager sendGoAwayAndClose was triggerred from filters.",
                 read_callbacks_->connection());
  if (go_away_sent_) {
    return;
  }

  // Use graceful drain sequence if graceful shutdown is requested.
  // startDrainSequence() works for both HTTP/1 and HTTP/2:
  // - HTTP/1: shutdownNotice() + goAway() provides graceful close
  // - HTTP/2: shutdownNotice() sends GOAWAY with high stream ID, then goAway() sends final GOAWAY
  if (graceful) {
    if (drain_state_ == DrainState::NotDraining) {
      startDrainSequence();
    }
    // Consider the "go away" process started once draining begins.
    // The actual GOAWAY frame will be sent in onDrainTimeout(), but we want to
    // prevent multiple calls to sendGoAwayAndClose() from starting multiple drain sequences.
    go_away_sent_ = true;
  } else {
    // Immediate close - send GOAWAY and close immediately
    codec_->shutdownNotice();
    codec_->goAway();
    go_away_sent_ = true;
    doConnectionClose(Network::ConnectionCloseType::FlushWriteAndDelay, absl::nullopt,
                      "forced_goaway");
  }
}

void ConnectionManagerImpl::chargeTracingStats(const Tracing::Reason& tracing_reason,
                                               ConnectionManagerTracingStats& tracing_stats) {
  switch (tracing_reason) {
  case Tracing::Reason::ClientForced:
    tracing_stats.client_enabled_.inc();
    break;
  case Tracing::Reason::Sampling:
    tracing_stats.random_sampling_.inc();
    break;
  case Tracing::Reason::ServiceForced:
    tracing_stats.service_forced_.inc();
    break;
  default:
    tracing_stats.not_traceable_.inc();
    break;
  }
}

absl::optional<absl::string_view>