Skip to content

Commit 954de29

Browse files
committed
Add x-elastic-client-meta header
1 parent 805d756 commit 954de29

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

Diff for: elasticsearch/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ rustls-tls = ["reqwest/rustls-tls"]
2929
base64 = "^0.11"
3030
bytes = "^1.0"
3131
dyn-clone = "~1"
32+
lazy_static = "1.4"
3233
percent-encoding = "2.1.0"
3334
reqwest = { version = "~0.11", default-features = false, features = ["gzip", "json"] }
3435
url = "^2.1"
@@ -45,6 +46,7 @@ futures = "0.3.1"
4546
http = "0.2"
4647
hyper = { version = "0.14", default-features = false, features = ["tcp", "stream", "server"] }
4748
os_type = "2.2"
49+
regex="1.4"
4850
sysinfo = "0.12.0"
4951
textwrap = "^0.11"
5052
tokio = { version = "1.0", default-features = false, features = ["macros", "net", "time", "rt-multi-thread"] }

Diff for: elasticsearch/build.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ extern crate rustc_version;
2020
use rustc_version::{version_meta, Channel};
2121

2222
fn main() {
23-
match version_meta().unwrap().channel {
23+
let version = version_meta().unwrap();
24+
match version.channel {
2425
Channel::Stable => {
2526
println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
2627
}
@@ -34,4 +35,11 @@ fn main() {
3435
println!("cargo:rustc-cfg=RUSTC_IS_DEV");
3536
}
3637
}
38+
39+
let semver = version.semver;
40+
let mut rustcv: String = format!("{}.{}.{}", semver.major, semver.minor, semver.patch);
41+
if version.channel != Channel::Stable {
42+
rustcv.push('p');
43+
}
44+
println!("cargo:rustc-env=RUSTC_VERSION={}", rustcv);
3745
}

Diff for: elasticsearch/src/http/transport.rs

+64-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::{
3737
};
3838
use base64::write::EncoderWriter as Base64Encoder;
3939
use bytes::BytesMut;
40+
use lazy_static::lazy_static;
4041
use serde::Serialize;
4142
use std::{
4243
error, fmt,
@@ -97,6 +98,37 @@ impl fmt::Display for BuildError {
9798
/// Default address to Elasticsearch running on `http://localhost:9200`
9899
pub static DEFAULT_ADDRESS: &str = "http://localhost:9200";
99100

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+
100132
/// Builds a HTTP transport to make API calls to Elasticsearch
101133
pub struct TransportBuilder {
102134
client_builder: reqwest::ClientBuilder,
@@ -108,6 +140,7 @@ pub struct TransportBuilder {
108140
proxy_credentials: Option<Credentials>,
109141
disable_proxy: bool,
110142
headers: HeaderMap,
143+
meta_header: bool,
111144
timeout: Option<Duration>,
112145
}
113146

@@ -128,6 +161,7 @@ impl TransportBuilder {
128161
proxy_credentials: None,
129162
disable_proxy: false,
130163
headers: HeaderMap::new(),
164+
meta_header: true,
131165
timeout: None,
132166
}
133167
}
@@ -187,6 +221,16 @@ impl TransportBuilder {
187221
self
188222
}
189223

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+
190234
/// Sets a global request timeout for the client.
191235
///
192236
/// The timeout is applied from when the request starts connecting until the response body has finished.
@@ -270,6 +314,7 @@ impl TransportBuilder {
270314
client,
271315
conn_pool: self.conn_pool,
272316
credentials: self.credentials,
317+
send_meta: self.meta_header,
273318
})
274319
}
275320
}
@@ -309,6 +354,7 @@ pub struct Transport {
309354
client: reqwest::Client,
310355
credentials: Option<Credentials>,
311356
conn_pool: Box<dyn ConnectionPool>,
357+
send_meta: bool,
312358
}
313359

314360
impl Transport {
@@ -396,13 +442,20 @@ impl Transport {
396442
}
397443

398444
// 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());
400446
request_headers.insert(CONTENT_TYPE, HeaderValue::from_static(DEFAULT_CONTENT_TYPE));
401447
request_headers.insert(ACCEPT, HeaderValue::from_static(DEFAULT_ACCEPT));
402448
request_headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
403449
for (name, value) in headers {
404450
request_headers.insert(name.unwrap(), value);
405451
}
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+
}
406459

407460
request_builder = request_builder.headers(request_headers);
408461

@@ -601,9 +654,10 @@ impl ConnectionPool for CloudConnectionPool {
601654

602655
#[cfg(test)]
603656
pub mod tests {
657+
use super::*;
604658
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
605659
use crate::auth::ClientCertificate;
606-
use crate::http::transport::{CloudId, Connection, SingleNodeConnectionPool, TransportBuilder};
660+
use regex::Regex;
607661
use url::Url;
608662

609663
#[test]
@@ -742,4 +796,12 @@ pub mod tests {
742796
let conn = Connection::new(url);
743797
assert_eq!(conn.url.as_str(), "http://10.1.2.3/");
744798
}
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+
}
745807
}

0 commit comments

Comments
 (0)