@@ -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,28 @@ 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" . to_string ( ) ,
130+ ) ) ;
131+ }
132+ }
133+ crate :: Compression :: Zstd => {
134+ #[ cfg( not( feature = "zstd-http" ) ) ]
135+ {
136+ return Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm (
137+ "zstd compression requested but zstd-http feature not enabled" . to_string ( ) ,
138+ ) ) ;
139+ }
140+ }
141+ }
142+ }
143+
122144 let timeout = resolve_timeout ( signal_timeout_var, self . exporter_config . timeout . as_ref ( ) ) ;
123145
124146 #[ allow( unused_mut) ] // TODO - clippy thinks mut is not needed, but it is
@@ -284,8 +306,10 @@ pub(crate) struct OtlpHttpClient {
284306}
285307
286308impl 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 > {
309+ /// Compress data using gzip or zstd if the user has requested it and the relevant feature
310+ /// has been enabled. If the user has requested it but the feature has not been enabled,
311+ /// we should catch this at exporter build time and never get here.
312+ fn process_body ( & self , body : Vec < u8 > ) -> Result < ( Vec < u8 > , Option < & ' static str > ) , String > {
289313 match self . compression {
290314 #[ cfg( feature = "gzip-http" ) ]
291315 Some ( crate :: Compression :: Gzip ) => {
@@ -352,8 +376,8 @@ impl OtlpHttpClient {
352376 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
353377 } ;
354378
355- let ( compressed_body , content_encoding) = self . compress_body ( body) ?;
356- Ok ( ( compressed_body , content_type, content_encoding) )
379+ let ( processed_body , content_encoding) = self . process_body ( body) ?;
380+ Ok ( ( processed_body , content_type, content_encoding) )
357381 }
358382
359383 #[ cfg( feature = "logs" ) ]
@@ -374,7 +398,7 @@ impl OtlpHttpClient {
374398 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
375399 } ;
376400
377- let ( compressed_body, content_encoding) = self . compress_body ( body) ?;
401+ let ( compressed_body, content_encoding) = self . process_body ( body) ?;
378402 Ok ( ( compressed_body, content_type, content_encoding) )
379403 }
380404
@@ -399,7 +423,7 @@ impl OtlpHttpClient {
399423 _ => ( req. encode_to_vec ( ) , "application/x-protobuf" ) ,
400424 } ;
401425
402- match self . compress_body ( body) {
426+ match self . process_body ( body) {
403427 Ok ( ( compressed_body, content_encoding) ) => {
404428 Some ( ( compressed_body, content_type, content_encoding) )
405429 }
@@ -839,7 +863,7 @@ mod tests {
839863
840864 // Test with some sample data
841865 let test_data = b"Hello, world! This is test data for compression." ;
842- let result = client. compress_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
866+ let result = client. process_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
843867 let ( compressed_body, content_encoding) = result;
844868
845869 // Verify encoding header is set
@@ -870,7 +894,7 @@ mod tests {
870894
871895 // Test with some sample data
872896 let test_data = b"Hello, world! This is test data for zstd compression." ;
873- let result = client. compress_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
897+ let result = client. process_body ( test_data. to_vec ( ) ) . unwrap ( ) ;
874898 let ( compressed_body, content_encoding) = result;
875899
876900 // Verify encoding header is set
@@ -897,7 +921,7 @@ mod tests {
897921 ) ;
898922
899923 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
900- let result = client. compress_body ( body. clone ( ) ) . unwrap ( ) ;
924+ let result = client. process_body ( body. clone ( ) ) . unwrap ( ) ;
901925 let ( result_body, content_encoding) = result;
902926
903927 // Body should be unchanged and no encoding header
@@ -918,7 +942,7 @@ mod tests {
918942 ) ;
919943
920944 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
921- let result = client. compress_body ( body) ;
945+ let result = client. process_body ( body) ;
922946
923947 // Should return error when gzip requested but feature not enabled
924948 assert ! ( result. is_err( ) ) ;
@@ -940,7 +964,7 @@ mod tests {
940964 ) ;
941965
942966 let body = vec ! [ 1 , 2 , 3 , 4 ] ;
943- let result = client. compress_body ( body) ;
967+ let result = client. process_body ( body) ;
944968
945969 // Should return error when zstd requested but feature not enabled
946970 assert ! ( result. is_err( ) ) ;
@@ -1199,5 +1223,33 @@ mod tests {
11991223 } ,
12001224 ) ;
12011225 }
1226+
1227+ #[ cfg( all( feature = "trace" , not( feature = "gzip-http" ) ) ) ]
1228+ #[ test]
1229+ fn test_build_span_exporter_with_gzip_without_feature ( ) {
1230+ use super :: super :: HttpExporterBuilder ;
1231+ use crate :: { WithHttpConfig , ExporterBuildError } ;
1232+
1233+ let builder = HttpExporterBuilder :: default ( )
1234+ . with_compression ( crate :: Compression :: Gzip ) ;
1235+
1236+ let result = builder. build_span_exporter ( ) ;
1237+ // This test will fail until the issue is fixed: compression validation should happen at build time
1238+ assert ! ( matches!( result, Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm ( _) ) ) ) ;
1239+ }
1240+
1241+ #[ cfg( all( feature = "trace" , not( feature = "zstd-http" ) ) ) ]
1242+ #[ test]
1243+ fn test_build_span_exporter_with_zstd_without_feature ( ) {
1244+ use super :: super :: HttpExporterBuilder ;
1245+ use crate :: { WithHttpConfig , ExporterBuildError } ;
1246+
1247+ let builder = HttpExporterBuilder :: default ( )
1248+ . with_compression ( crate :: Compression :: Zstd ) ;
1249+
1250+ let result = builder. build_span_exporter ( ) ;
1251+ // This test will fail until the issue is fixed: compression validation should happen at build time
1252+ assert ! ( matches!( result, Err ( ExporterBuildError :: UnsupportedCompressionAlgorithm ( _) ) ) ) ;
1253+ }
12021254 }
12031255}
0 commit comments