@@ -47,10 +47,10 @@ pub struct HttpConfig {
4747 /// Select the HTTP client
4848 client : Option < Arc < dyn HttpClient > > ,
4949
50- /// Additional headers to send to the collector .
50+ /// Additional headers to send to the OTLP endpoint .
5151 headers : Option < HashMap < String , String > > ,
5252
53- /// The compression algorithm to use when communicating with the collector .
53+ /// The compression algorithm to use when communicating with the OTLP endpoint .
5454 compression : Option < crate :: Compression > ,
5555}
5656
@@ -119,6 +119,30 @@ impl HttpExporterBuilder {
119119
120120 let compression = self . resolve_compression ( signal_compression_var) ?;
121121
122+ // Validate compression is supported at build time
123+ if let Some ( compression_alg) = & compression {
124+ match compression_alg {
125+ crate :: Compression :: Gzip => {
126+ #[ cfg( not( feature = "gzip-http" ) ) ]
127+ {
128+ return Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm (
129+ "gzip compression requested but gzip-http feature not enabled"
130+ . to_string ( ) ,
131+ ) ) ;
132+ }
133+ }
134+ crate :: Compression :: Zstd => {
135+ #[ cfg( not( feature = "zstd-http" ) ) ]
136+ {
137+ return Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm (
138+ "zstd compression requested but zstd-http feature not enabled"
139+ . to_string ( ) ,
140+ ) ) ;
141+ }
142+ }
143+ }
144+ }
145+
122146 let timeout = resolve_timeout ( signal_timeout_var, self . exporter_config . timeout . as_ref ( ) ) ;
123147
124148 #[ allow( unused_mut) ] // TODO - clippy thinks mut is not needed, but it is
@@ -284,8 +308,10 @@ pub(crate) struct OtlpHttpClient {
284308}
285309
286310impl OtlpHttpClient {
287- /// Compress data using gzip if compression is enabled and the feature is available
288- fn compress_body ( & self , body : Vec < u8 > ) -> Result < ( Vec < u8 > , Option < & ' static str > ) , String > {
311+ /// Compress data using gzip or zstd if the user has requested it and the relevant feature
312+ /// has been enabled. If the user has requested it but the feature has not been enabled,
313+ /// we should catch this at exporter build time and never get here.
314+ fn process_body ( & self , body : Vec < u8 > ) -> Result < ( Vec < u8 > , Option < & ' static str > ) , String > {
289315 match self . compression {
290316 #[ cfg( feature = "gzip-http" ) ]
291317 Some ( crate :: Compression :: Gzip ) => {
@@ -352,8 +378,8 @@ impl OtlpHttpClient {
352378 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
353379 } ;
354380
355- let ( compressed_body , content_encoding) = self . compress_body ( body) ?;
356- Ok ( ( compressed_body , content_type, content_encoding) )
381+ let ( processed_body , content_encoding) = self . process_body ( body) ?;
382+ Ok ( ( processed_body , content_type, content_encoding) )
357383 }
358384
359385 #[ cfg( feature = "logs" ) ]
@@ -374,8 +400,8 @@ impl OtlpHttpClient {
374400 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
375401 } ;
376402
377- let ( compressed_body , content_encoding) = self . compress_body ( body) ?;
378- Ok ( ( compressed_body , content_type, content_encoding) )
403+ let ( processed_body , content_encoding) = self . process_body ( body) ?;
404+ Ok ( ( processed_body , content_type, content_encoding) )
379405 }
380406
381407 #[ cfg( feature = "metrics" ) ]
@@ -399,9 +425,9 @@ impl OtlpHttpClient {
399425 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
400426 } ;
401427
402- match self . compress_body ( body) {
403- Ok ( ( compressed_body , content_encoding) ) => {
404- Some ( ( compressed_body , content_type, content_encoding) )
428+ match self . process_body ( body) {
429+ Ok ( ( processed_body , content_encoding) ) => {
430+ Some ( ( processed_body , content_type, content_encoding) )
405431 }
406432 Err ( e) => {
407433 otel_debug ! ( name: "CompressionFailed" , error = e) ;
@@ -839,7 +865,7 @@ mod tests {
839865
840866 // Test with some sample data
841867 let test_data = b"Hello, world! This is test data for compression." ;
842- let result = client. compress_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
868+ let result = client. process_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
843869 let ( compressed_body, content_encoding) = result;
844870
845871 // Verify encoding header is set
@@ -870,7 +896,7 @@ mod tests {
870896
871897 // Test with some sample data
872898 let test_data = b"Hello, world! This is test data for zstd compression." ;
873- let result = client. compress_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
899+ let result = client. process_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
874900 let ( compressed_body, content_encoding) = result;
875901
876902 // Verify encoding header is set
@@ -897,7 +923,7 @@ mod tests {
897923 ) ;
898924
899925 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
900- let result = client. compress_body ( body. clone ( ) ) . unwrap ( ) ;
926+ let result = client. process_body ( body. clone ( ) ) . unwrap ( ) ;
901927 let ( result_body, content_encoding) = result;
902928
903929 // Body should be unchanged and no encoding header
@@ -918,7 +944,7 @@ mod tests {
918944 ) ;
919945
920946 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
921- let result = client. compress_body ( body) ;
947+ let result = client. process_body ( body) ;
922948
923949 // Should return error when gzip requested but feature not enabled
924950 assert ! ( result. is_err( ) ) ;
@@ -940,7 +966,7 @@ mod tests {
940966 ) ;
941967
942968 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
943- let result = client. compress_body ( body) ;
969+ let result = client. process_body ( body) ;
944970
945971 // Should return error when zstd requested but feature not enabled
946972 assert ! ( result. is_err( ) ) ;
@@ -1199,5 +1225,37 @@ mod tests {
11991225 } ,
12001226 ) ;
12011227 }
1228+
1229+ #[ cfg( all( feature = "trace" , not( feature = "gzip-http" ) ) ) ]
1230+ #[ test]
1231+ fn test_build_span_exporter_with_gzip_without_feature ( ) {
1232+ use super :: super :: HttpExporterBuilder ;
1233+ use crate :: { ExporterBuildError , WithHttpConfig } ;
1234+
1235+ let builder = HttpExporterBuilder :: default ( ) . with_compression ( crate :: Compression :: Gzip ) ;
1236+
1237+ let result = builder. build_span_exporter ( ) ;
1238+ // This test will fail until the issue is fixed: compression validation should happen at build time
1239+ assert ! ( matches!(
1240+ result,
1241+ Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm ( _) )
1242+ ) ) ;
1243+ }
1244+
1245+ #[ cfg( all( feature = "trace" , not( feature = "zstd-http" ) ) ) ]
1246+ #[ test]
1247+ fn test_build_span_exporter_with_zstd_without_feature ( ) {
1248+ use super :: super :: HttpExporterBuilder ;
1249+ use crate :: { ExporterBuildError , WithHttpConfig } ;
1250+
1251+ let builder = HttpExporterBuilder :: default ( ) . with_compression ( crate :: Compression :: Zstd ) ;
1252+
1253+ let result = builder. build_span_exporter ( ) ;
1254+ // This test will fail until the issue is fixed: compression validation should happen at build time
1255+ assert ! ( matches!(
1256+ result,
1257+ Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm ( _) )
1258+ ) ) ;
1259+ }
12021260 }
12031261}
0 commit comments