Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions source/common/http/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,22 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "http_service_headers_lib",
srcs = ["http_service_headers.cc"],
hdrs = ["http_service_headers.h"],
deps = [
":headers_lib",
"//envoy/formatter:substitution_formatter_interface",
"//envoy/http:header_map_interface",
"//source/common/formatter:substitution_format_string_lib",
"//source/common/formatter:substitution_formatter_lib",
"//source/common/stream_info:stream_info_lib",
"//source/server:generic_factory_context_lib",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "user_agent_lib",
srcs = ["user_agent.cc"],
Expand Down
52 changes: 52 additions & 0 deletions source/common/http/http_service_headers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "source/common/http/http_service_headers.h"

#include "source/common/formatter/substitution_format_string.h"
#include "source/common/formatter/substitution_formatter.h"
#include "source/server/generic_factory_context.h"

namespace Envoy {
namespace Http {

HttpServiceHeadersApplicator::HttpServiceHeadersApplicator(
const envoy::config::core::v3::HttpService& http_service,
Server::Configuration::ServerFactoryContext& server_context, absl::Status& creation_status)
: stream_info_(server_context.timeSource(), nullptr,
StreamInfo::FilterState::LifeSpan::FilterChain) {

// Formatters can only be instantiated on the main thread because some create thread local
// storage.
ASSERT_IS_MAIN_OR_TEST_THREAD();

Server::GenericFactoryContextImpl generic_context{server_context,
server_context.messageValidationVisitor()};

auto commands = Formatter::SubstitutionFormatStringUtils::parseFormatters(
http_service.formatters(), generic_context);
SET_AND_RETURN_IF_NOT_OK(commands.status(), creation_status);

for (const auto& header_value_option : http_service.request_headers_to_add()) {
const auto& header = header_value_option.header();
if (!header.value().empty()) {
auto formatter_or_error = Formatter::FormatterImpl::create(header.value(), false, *commands);
SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
formatted_headers_.emplace_back(LowerCaseString(header.key()),
std::move(formatter_or_error.value()));
} else {
static_headers_.emplace_back(LowerCaseString(header.key()), header.raw_value());
}
}
}

void HttpServiceHeadersApplicator::apply(RequestHeaderMap& headers) const {
for (const auto& header_pair : static_headers_) {
headers.setReference(header_pair.first, header_pair.second);
}
if (!formatted_headers_.empty()) {
for (const auto& header_pair : formatted_headers_) {
headers.setCopy(header_pair.first, header_pair.second->format({}, stream_info_));
}
}
}

} // namespace Http
} // namespace Envoy
44 changes: 44 additions & 0 deletions source/common/http/http_service_headers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include "envoy/config/core/v3/http_service.pb.h"
#include "envoy/formatter/substitution_formatter_base.h"
#include "envoy/http/header_map.h"
#include "envoy/server/factory_context.h"

#include "source/common/http/headers.h"
#include "source/common/stream_info/stream_info_impl.h"

namespace Envoy {
namespace Http {

/**
* Parses and applies request_headers_to_add from an HTTP service configuration.
*
* Separates headers into static (plain string value) and formatted (substitution
* formatter) groups. Static headers are evaluated once at construction time.
* Formatted headers are re-evaluated on each apply() call, so that
* runtime updates such as SDS secret rotation are reflected in outgoing requests.
*/
class HttpServiceHeadersApplicator {
public:
HttpServiceHeadersApplicator(const envoy::config::core::v3::HttpService& http_service,
Server::Configuration::ServerFactoryContext& server_context,
absl::Status& creation_status);

/**
* Apply all parsed headers to the outgoing request message.
*/
void apply(RequestHeaderMap& headers) const;

private:
std::vector<std::pair<const LowerCaseString, std::string>> static_headers_;
std::vector<std::pair<const LowerCaseString, Formatter::FormatterPtr>> formatted_headers_;

// A `StreamInfo` is required, but in this context we don't have one, so create an empty one.
// This allows formatters that don't require any stream info to succeed, such as extensions that
// load data externally for API keys and similar.
const StreamInfo::StreamInfoImpl stream_info_;
};

} // namespace Http
} // namespace Envoy
1 change: 1 addition & 0 deletions source/extensions/stat_sinks/open_telemetry/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ envoy_cc_library(
"//source/common/http:async_client_lib",
"//source/common/http:async_client_utility_lib",
"//source/common/http:header_map_lib",
"//source/common/http:http_service_headers_lib",
"//source/common/http:message_lib",
"//source/common/http:utility_lib",
"//source/common/protobuf",
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/stat_sinks/open_telemetry/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ OpenTelemetrySinkFactory::createStatsSink(const Protobuf::Message& config,
case SinkConfig::ProtocolSpecifierCase::kHttpService: {
std::shared_ptr<OtlpMetricsExporter> http_metrics_exporter =
std::make_shared<OpenTelemetryHttpMetricsExporter>(server.clusterManager(),
sink_config.http_service());
sink_config.http_service(), server);

return std::make_unique<OpenTelemetrySink>(
otlp_metrics_flusher, http_metrics_exporter,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "source/extensions/stat_sinks/open_telemetry/open_telemetry_http_impl.h"

#include "source/common/common/enum_to_int.h"
#include "source/common/common/fmt.h"
#include "source/common/http/headers.h"
#include "source/common/http/message_impl.h"
#include "source/common/http/utility.h"
Expand All @@ -14,12 +15,16 @@ namespace OpenTelemetry {

OpenTelemetryHttpMetricsExporter::OpenTelemetryHttpMetricsExporter(
Upstream::ClusterManager& cluster_manager,
const envoy::config::core::v3::HttpService& http_service)
const envoy::config::core::v3::HttpService& http_service,
Server::Configuration::ServerFactoryContext& server_context)
: cluster_manager_(cluster_manager), http_service_(http_service) {
// Parse headers at construction time to avoid copies per request.
for (const auto& header_value_option : http_service_.request_headers_to_add()) {
parsed_headers_to_add_.push_back({Http::LowerCaseString(header_value_option.header().key()),
header_value_option.header().value()});
// Create headers applicator to handle substitution formatters.
absl::Status creation_status;
headers_applicator_ = std::make_unique<Http::HttpServiceHeadersApplicator>(
http_service_, server_context, creation_status);
if (!creation_status.ok()) {
throw EnvoyException(fmt::format("Failed to create HttpServiceHeadersApplicator: {}",
creation_status.message()));
}
}

Expand Down Expand Up @@ -49,10 +54,8 @@ void OpenTelemetryHttpMetricsExporter::send(MetricsExportRequestPtr&& metrics) {
// User-Agent header follows the OTLP specification.
message->headers().setReferenceUserAgent(AccessLoggers::OpenTelemetry::getOtlpUserAgentHeader());

// Add custom headers from config.
for (const auto& header_pair : parsed_headers_to_add_) {
message->headers().setReference(header_pair.first, header_pair.second);
}
// Apply custom headers from config using the common helper.
headers_applicator_->apply(message->headers());
message->body().add(request_body);

const auto options =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "source/common/http/async_client_impl.h"
#include "source/common/http/async_client_utility.h"
#include "source/common/http/http_service_headers.h"
#include "source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.h"

namespace Envoy {
Expand All @@ -21,7 +22,8 @@ class OpenTelemetryHttpMetricsExporter : public OtlpMetricsExporter,
public Logger::Loggable<Logger::Id::stats> {
public:
OpenTelemetryHttpMetricsExporter(Upstream::ClusterManager& cluster_manager,
const envoy::config::core::v3::HttpService& http_service);
const envoy::config::core::v3::HttpService& http_service,
Server::Configuration::ServerFactoryContext& server_context);

// OtlpMetricsExporter
void send(MetricsExportRequestPtr&& metrics) override;
Expand All @@ -36,7 +38,7 @@ class OpenTelemetryHttpMetricsExporter : public OtlpMetricsExporter,
envoy::config::core::v3::HttpService http_service_;
// Track active HTTP requests to cancel them on destruction.
Http::AsyncClientRequestTracker active_requests_;
std::vector<std::pair<const Http::LowerCaseString, const std::string>> parsed_headers_to_add_;
std::unique_ptr<Http::HttpServiceHeadersApplicator> headers_applicator_;
};

} // namespace OpenTelemetry
Expand Down
1 change: 1 addition & 0 deletions source/extensions/tracers/zipkin/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ envoy_cc_library(
"//source/common/config:utility_lib",
"//source/common/http:async_client_utility_lib",
"//source/common/http:header_map_lib",
"//source/common/http:http_service_headers_lib",
"//source/common/http:message_lib",
"//source/common/http:utility_lib",
"//source/common/json:json_loader_lib",
Expand Down
24 changes: 16 additions & 8 deletions source/extensions/tracers/zipkin/zipkin_tracer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "envoy/config/trace/v3/zipkin.pb.h"

#include "source/common/common/enum_to_int.h"
#include "source/common/common/fmt.h"
#include "source/common/config/utility.h"
#include "source/common/http/headers.h"
#include "source/common/http/message_impl.h"
Expand Down Expand Up @@ -70,10 +71,13 @@ Driver::Driver(const envoy::config::trace::v3::ZipkinConfig& zipkin_config,
collector_->endpoint_ = path;
}

// Parse headers from HttpService
for (const auto& header_option : http_service.request_headers_to_add()) {
const auto& header_value = header_option.header();
collector_->request_headers_.emplace_back(header_value.key(), header_value.value());
// Create headers applicator using the common helper for substitution formatter support
absl::Status creation_status;
collector_->headers_applicator_ = std::make_unique<Http::HttpServiceHeadersApplicator>(
http_service, context, creation_status);
if (!creation_status.ok()) {
throw EnvoyException(fmt::format("Failed to create HttpServiceHeadersApplicator: {}",
creation_status.message()));
}
} else {
if (zipkin_config.collector_cluster().empty() || zipkin_config.collector_endpoint().empty()) {
Expand Down Expand Up @@ -210,10 +214,14 @@ void ReporterImpl::flushSpans() {
? Http::Headers::get().ContentTypeValues.Protobuf
: Http::Headers::get().ContentTypeValues.Json);

// Add custom headers from collector configuration
for (const auto& header : collector_->request_headers_) {
// Replace any existing header with the configured value
message->headers().setCopy(header.first, header.second);
// Apply custom headers from collector configuration using common helper
if (collector_->headers_applicator_) {
collector_->headers_applicator_->apply(message->headers());
} else {
// Legacy header application for backward compatibility
for (const auto& header : collector_->request_headers_) {
message->headers().setCopy(header.first, header.second);
}
}

message->body().add(request_body);
Expand Down
4 changes: 4 additions & 0 deletions source/extensions/tracers/zipkin/zipkin_tracer_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "envoy/upstream/cluster_manager.h"

#include "source/common/http/async_client_utility.h"
#include "source/common/http/http_service_headers.h"
#include "source/common/upstream/cluster_update_tracker.h"
#include "source/extensions/tracers/zipkin/span_buffer.h"
#include "source/extensions/tracers/zipkin/tracer.h"
Expand Down Expand Up @@ -63,6 +64,9 @@ struct CollectorInfo {
// Only available when using HttpService configuration via request_headers_to_add.
// Legacy configuration does not support custom headers.
std::vector<std::pair<Http::LowerCaseString, std::string>> request_headers_;

// Headers applicator for substitution formatter support when using HttpService configuration
std::unique_ptr<Http::HttpServiceHeadersApplicator> headers_applicator_;
};

using CollectorInfoConstSharedPtr = std::shared_ptr<const CollectorInfo>;
Expand Down