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

Code before:
void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
  if (stream_id_ == -1) {
    // Handle the case where client streams are reset before headers are created.
    // For example, if we send local reply after the stream is created but before
    // headers are sent, we will end up here.
    ENVOY_CONN_LOG(trace, "Stream {} reset before headers sent.", parent_.connection_, stream_id_);
    Status status = parent_.onStreamClose(this, 0);
    ASSERT(status.ok());
    return;
  }
  if (codec_callbacks_) {
    // TODO(wbpcode): this ensure that onCodecLowLevelReset is only called once. But
    // we should replace this with a better design later.
    // See https://github.com/envoyproxy/envoy/issues/42264 for why we need this.
    if (!codec_low_level_reset_is_called_) {
      codec_low_level_reset_is_called_ = true;
      codec_callbacks_->onCodecLowLevelReset();
    }
  }
  parent_.adapter_->SubmitRst(stream_id_,
                              static_cast<http2::adapter::Http2ErrorCode>(reasonToReset(reason)));
}

NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() {
  if (metadata_encoder_ == nullptr) {
    metadata_encoder_ = std::make_unique<NewMetadataEncoder>();
  }
  return *metadata_encoder_;
}

MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
  if (metadata_decoder_ == nullptr) {
    auto cb = [this](MetadataMapPtr&& metadata_map_ptr) {
      this->onMetadataDecoded(std::move(metadata_map_ptr));
    };
    metadata_decoder_ = std::make_unique<MetadataDecoder>(cb, parent_.max_metadata_size_);
  }
  return *metadata_decoder_;
}

void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr) {
  // Empty metadata maps should not be decoded.
  if (metadata_map_ptr->empty()) {
    ENVOY_CONN_LOG(debug, "decode metadata called with empty map, skipping", parent_.connection_);
    parent_.stats_.metadata_empty_frames_.inc();
  } else {
    StreamDecoder* stream_decoder = decoder();
    if (stream_decoder) {
      stream_decoder->decodeMetadata(std::move(metadata_map_ptr));
    }
  }

Review comment (suggestion):
This PR changed the default stream reset code and will enlarger the problem in the https://github.com/envoyproxy/envoy/issues/42264. So, I made a quick work around to resolve problem here.

Code after:
  // the cleanup logic to run which will reset the stream in all cases if all data frames could not
  // be sent.
  if (parent_.sendPendingFramesAndHandleError()) {
    // Intended to check through coverage that this error case is tested
    return;
  }
}

void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
  if (stream_id_ == -1) {
    // Handle the case where client streams are reset before headers are created.
    // For example, if we send local reply after the stream is created but before
    // headers are sent, we will end up here.
    ENVOY_CONN_LOG(trace, "Stream {} reset before headers sent.", parent_.connection_, stream_id_);
    Status status = parent_.onStreamClose(this, 0);
    ASSERT(status.ok());
    return;
  }
  if (codec_callbacks_) {
    // TODO(wbpcode): this ensure that onCodecLowLevelReset is only called once. But
    // we should replace this with a better design later.
    // See https://github.com/envoyproxy/envoy/issues/42264 for why we need this.
    if (!codec_low_level_reset_is_called_) {
      codec_low_level_reset_is_called_ = true;
      codec_callbacks_->onCodecLowLevelReset();
    }
  }

  const bool response_end_stream_sent =
      parent_.adapter_->IsServerSession() ? local_end_stream_sent_ : false;
  parent_.adapter_->SubmitRst(stream_id_, static_cast<http2::adapter::Http2ErrorCode>(
                                              reasonToReset(reason, response_end_stream_sent)));
}

NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() {
  if (metadata_encoder_ == nullptr) {
    metadata_encoder_ = std::make_unique<NewMetadataEncoder>();
  }
  return *metadata_encoder_;
}

MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
  if (metadata_decoder_ == nullptr) {
    auto cb = [this](MetadataMapPtr&& metadata_map_ptr) {
      this->onMetadataDecoded(std::move(metadata_map_ptr));
    };
    metadata_decoder_ = std::make_unique<MetadataDecoder>(cb, parent_.max_metadata_size_);
  }
  return *metadata_decoder_;
}