2020#include " opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
2121#include " opentelemetry/proto/collector/logs/v1/logs_service.pb.h"
2222
23-
2423OPENTELEMETRY_BEGIN_NAMESPACE
2524namespace exporter
2625{
2726namespace otlp
2827{
2928
30- // Send this header along with unique random number, which we'll reject if received back .
31- // This is to avoid loops due to misconfugration
32- static std::string kFwProxyIdHeader { " otelcpp-otlp-grpc-forward- proxy-id " };
29+ // Detect loops, by sending a random identifier for each forward proxy instance .
30+ // All identifiers received are collected, and send as individual http headers.
31+ static std::string kFwProxyRidHeader { " otel-fw- proxy-rid " };
3332
3433using namespace opentelemetry ;
3534
@@ -89,11 +88,41 @@ struct OtlpGrpcForwardProxy::Impl
8988
9089 std::string fw_proxy_id;
9190
92- bool LoopDetected ( const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata ) const
91+ static inline char ascii_tolower ( const char c ) noexcept
92+ {
93+ const unsigned char letter = c - ' A' ;
94+ return letter < 26 ? ( c | 0x20 ) : c;
95+ }
96+
97+ static inline bool ascii_strieq ( const char *s0, const char *s1 ) noexcept
98+ {
99+ assert (s0);
100+ assert (s1);
101+ for ( ;; )
102+ {
103+ const auto c0{ ascii_tolower ( *s0 ++ ) };
104+ const auto c1{ ascii_tolower ( *s1 ++ ) };
105+ if ( c0 != c1 )
106+ return false ;
107+ if ( c0 == 0 ) // reached end, also c0 == c1
108+ return true ;
109+ }
110+ }
111+
112+ bool CheckForLoop ( grpc::ClientContext* context, const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata ) const
93113 {
94- const auto key{ grpc::string_ref ( kFwProxyIdHeader ) };
95- const auto it{ client_metadata.find (key) };
96- return it != client_metadata.cend () && it->second .compare ( grpc::string_ref ( fw_proxy_id ) ) == 0 ;
114+ assert (context != nullptr );
115+ const auto matching_keys{ client_metadata.equal_range ( grpc::string_ref ( kFwProxyRidHeader ) ) };
116+ for ( auto i = matching_keys.first ; i != matching_keys.second ; ++i )
117+ if ( ascii_strieq ( i->second .data (), fw_proxy_id.c_str () ) )
118+ // Loop detected.
119+ return true ;
120+ else
121+ // Add what we've got so far
122+ context->AddMetadata (kFwProxyRidHeader , i->second .data () );
123+ // Add ourselves too.
124+ context->AddMetadata (kFwProxyRidHeader , fw_proxy_id);
125+ return false ;
97126 }
98127
99128 Impl (const OtlpGrpcClientOptions&);
@@ -192,13 +221,12 @@ grpc::ServerUnaryReactor* OtlpGrpcForwardProxy::Impl::Finish(grpc::CallbackServe
192221 explicit NAME (OtlpGrpcForwardProxy::Impl& impl_): impl(impl_){} \
193222 grpc::ServerUnaryReactor* Export (grpc::CallbackServerContext* cbServerContext, const REQUEST* req, RESPONSE* resp) override { \
194223 auto exportResult{ sdk::common::ExportResult::kFailure }; \
195- if ( impl.LoopDetected (cbServerContext->client_metadata ()) ) \
196- return impl.Finish (cbServerContext, exportResult, grpc::Status (grpc::StatusCode::FAILED_PRECONDITION, " OtlpGrpcForwardProxy detected loop." )); \
197224 if ( impl.active ) { \
225+ auto context{ impl.client ->MakeClientContext (impl.clientOptions ) }; \
226+ if ( impl.CheckForLoop (context.get (), cbServerContext->client_metadata ()) ) \
227+ return impl.Finish (cbServerContext, exportResult, grpc::Status (grpc::StatusCode::FAILED_PRECONDITION, " OtlpGrpcForwardProxy detected loop." )); \
198228 OTEL_INTERNAL_LOG_DEBUG (" [otlp_grpc_forward_proxy] " TEXT " export" ); \
199229 auto syncStatus{ grpc::Status (grpc::Status (grpc::StatusCode::DO_NOT_USE, " " )) }; \
200- auto context{ impl.client ->MakeClientContext (impl.clientOptions ) }; \
201- context->AddMetadata (kFwProxyIdHeader , impl.fw_proxy_id ); \
202230 auto arena{ std::make_unique<google::protobuf::Arena>(impl.arenaOptions ) }; \
203231 auto request{ *req }; \
204232 exportResult = impl.client ->DelegateAsyncExport ( impl.clientOptions , impl.STUB .get (), std::move (context), std::move (arena), std::move (request), \
@@ -221,12 +249,11 @@ grpc::ServerUnaryReactor* OtlpGrpcForwardProxy::Impl::Finish(grpc::CallbackServe
221249 OtlpGrpcForwardProxy::Impl& impl; \
222250 explicit NAME (OtlpGrpcForwardProxy::Impl& impl_): impl(impl_){} \
223251 grpc::Status Export (grpc::ServerContext* serverContext, const REQUEST* req, RESPONSE* resp) override { \
224- if ( impl.LoopDetected (serverContext->client_metadata ()) ) \
225- return grpc::Status (grpc::StatusCode::FAILED_PRECONDITION, " OtlpGrpcForwardProxy detected loop." ); \
226252 if ( impl.active ) { \
227- OTEL_INTERNAL_LOG_DEBUG (" [otlp_grpc_forward_proxy] " TEXT " export" ); \
228253 auto context{ impl.client ->MakeClientContext (impl.clientOptions ) }; \
229- context->AddMetadata (kFwProxyIdHeader , impl.fw_proxy_id ); \
254+ if ( impl.CheckForLoop ( context.get (), serverContext->client_metadata ()) ) \
255+ return grpc::Status (grpc::StatusCode::FAILED_PRECONDITION, " OtlpGrpcForwardProxy detected loop." ); \
256+ OTEL_INTERNAL_LOG_DEBUG (" [otlp_grpc_forward_proxy] " TEXT " export" ); \
230257 return impl.STUB ->Export ( context.get (), *req, resp ); \
231258 } \
232259 return grpc::Status (grpc::StatusCode::FAILED_PRECONDITION, " OtlpGrpcForwardProxy is not active." ); \
0 commit comments