@@ -37,6 +37,7 @@ use crate::{
37
37
} ;
38
38
use base64:: write:: EncoderWriter as Base64Encoder ;
39
39
use bytes:: BytesMut ;
40
+ use lazy_static:: lazy_static;
40
41
use serde:: Serialize ;
41
42
use std:: {
42
43
error, fmt,
@@ -97,6 +98,37 @@ impl fmt::Display for BuildError {
97
98
/// Default address to Elasticsearch running on `http://localhost:9200`
98
99
pub static DEFAULT_ADDRESS : & str = "http://localhost:9200" ;
99
100
101
+ lazy_static ! {
102
+ /// Client metadata header: service, language, transport, followed by additional information
103
+ static ref CLIENT_META : String = build_meta( ) ;
104
+ }
105
+
106
+ fn build_meta ( ) -> String {
107
+ let mut version_parts = env ! ( "CARGO_PKG_VERSION" ) . split ( & [ '.' , '-' ] [ ..] ) ;
108
+ let mut version = String :: new ( ) ;
109
+
110
+ // major.minor.patch followed with an optional 'p' for preliminary versions
111
+ version. push_str ( version_parts. next ( ) . unwrap ( ) ) ;
112
+ version. push ( '.' ) ;
113
+ version. push_str ( version_parts. next ( ) . unwrap ( ) ) ;
114
+ version. push ( '.' ) ;
115
+ version. push_str ( version_parts. next ( ) . unwrap ( ) ) ;
116
+ if version_parts. next ( ) . is_some ( ) {
117
+ version. push ( 'p' ) ;
118
+ }
119
+
120
+ let rustc = env ! ( "RUSTC_VERSION" ) ;
121
+ let mut meta = format ! ( "es={},rs={},t={}" , version, rustc, version) ;
122
+
123
+ if cfg ! ( feature = "native-tls" ) {
124
+ meta. push_str ( ",tls=n" ) ;
125
+ } else if cfg ! ( feature = "rustls-tls" ) {
126
+ meta. push_str ( ",tls=r" ) ;
127
+ }
128
+
129
+ meta
130
+ }
131
+
100
132
/// Builds a HTTP transport to make API calls to Elasticsearch
101
133
pub struct TransportBuilder {
102
134
client_builder : reqwest:: ClientBuilder ,
@@ -108,6 +140,7 @@ pub struct TransportBuilder {
108
140
proxy_credentials : Option < Credentials > ,
109
141
disable_proxy : bool ,
110
142
headers : HeaderMap ,
143
+ meta_header : bool ,
111
144
timeout : Option < Duration > ,
112
145
}
113
146
@@ -128,6 +161,7 @@ impl TransportBuilder {
128
161
proxy_credentials : None ,
129
162
disable_proxy : false ,
130
163
headers : HeaderMap :: new ( ) ,
164
+ meta_header : true ,
131
165
timeout : None ,
132
166
}
133
167
}
@@ -187,6 +221,16 @@ impl TransportBuilder {
187
221
self
188
222
}
189
223
224
+ /// Whether to send a `x-elastic-client-meta` header that describes the runtime environment.
225
+ ///
226
+ /// This header contains information that is similar to what could be found in `User-Agent`. Using a separate
227
+ /// header allows applications to use `User-Agent` for their own needs, e.g. to identify application version
228
+ /// or other environment information. Defaults to `true`.
229
+ pub fn enable_meta_header ( mut self , enable : bool ) -> Self {
230
+ self . meta_header = enable;
231
+ self
232
+ }
233
+
190
234
/// Sets a global request timeout for the client.
191
235
///
192
236
/// The timeout is applied from when the request starts connecting until the response body has finished.
@@ -270,6 +314,7 @@ impl TransportBuilder {
270
314
client,
271
315
conn_pool : self . conn_pool ,
272
316
credentials : self . credentials ,
317
+ send_meta : self . meta_header ,
273
318
} )
274
319
}
275
320
}
@@ -309,6 +354,7 @@ pub struct Transport {
309
354
client : reqwest:: Client ,
310
355
credentials : Option < Credentials > ,
311
356
conn_pool : Box < dyn ConnectionPool > ,
357
+ send_meta : bool ,
312
358
}
313
359
314
360
impl Transport {
@@ -396,13 +442,20 @@ impl Transport {
396
442
}
397
443
398
444
// default headers first, overwrite with any provided
399
- let mut request_headers = HeaderMap :: with_capacity ( 3 + headers. len ( ) ) ;
445
+ let mut request_headers = HeaderMap :: with_capacity ( 4 + headers. len ( ) ) ;
400
446
request_headers. insert ( CONTENT_TYPE , HeaderValue :: from_static ( DEFAULT_CONTENT_TYPE ) ) ;
401
447
request_headers. insert ( ACCEPT , HeaderValue :: from_static ( DEFAULT_ACCEPT ) ) ;
402
448
request_headers. insert ( USER_AGENT , HeaderValue :: from_static ( DEFAULT_USER_AGENT ) ) ;
403
449
for ( name, value) in headers {
404
450
request_headers. insert ( name. unwrap ( ) , value) ;
405
451
}
452
+ // if meta header enabled, send it last so that it's not overridden.
453
+ if self . send_meta {
454
+ request_headers. insert (
455
+ "x-elastic-client-meta" ,
456
+ HeaderValue :: from_static ( CLIENT_META . as_str ( ) ) ,
457
+ ) ;
458
+ }
406
459
407
460
request_builder = request_builder. headers ( request_headers) ;
408
461
@@ -601,9 +654,10 @@ impl ConnectionPool for CloudConnectionPool {
601
654
602
655
#[ cfg( test) ]
603
656
pub mod tests {
657
+ use super :: * ;
604
658
#[ cfg( any( feature = "native-tls" , feature = "rustls-tls" ) ) ]
605
659
use crate :: auth:: ClientCertificate ;
606
- use crate :: http :: transport :: { CloudId , Connection , SingleNodeConnectionPool , TransportBuilder } ;
660
+ use regex :: Regex ;
607
661
use url:: Url ;
608
662
609
663
#[ test]
@@ -742,4 +796,12 @@ pub mod tests {
742
796
let conn = Connection :: new ( url) ;
743
797
assert_eq ! ( conn. url. as_str( ) , "http://10.1.2.3/" ) ;
744
798
}
799
+
800
+ #[ test]
801
+ pub fn test_meta_header ( ) {
802
+ let re = Regex :: new ( r"^es=[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,3}p?,rs=[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,3}p?,t=[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,3}p?(,tls=[rn])?$" ) . unwrap ( ) ;
803
+ let x: & str = CLIENT_META . as_str ( ) ;
804
+ println ! ( "{}" , x) ;
805
+ assert ! ( re. is_match( x) ) ;
806
+ }
745
807
}
0 commit comments