Skip to content

Commit 9e3a12d

Browse files
committed
test: user_agent in http requests
1 parent 63476f9 commit 9e3a12d

File tree

9 files changed

+91
-67
lines changed

9 files changed

+91
-67
lines changed

beacon_node/builder_client/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use eth2::types::{
77
};
88
use eth2::types::{FullPayloadContents, SignedBlindedBeaconBlock};
99
use eth2::{
10-
CONSENSUS_VERSION_HEADER, CONTENT_TYPE_HEADER, HttpClientBuilderWithUserAgent,
11-
JSON_CONTENT_TYPE_HEADER, SSZ_CONTENT_TYPE_HEADER, StatusCode, ok_or_error,
10+
CONSENSUS_VERSION_HEADER, CONTENT_TYPE_HEADER, JSON_CONTENT_TYPE_HEADER,
11+
SSZ_CONTENT_TYPE_HEADER, StatusCode, ok_or_error,
1212
};
13+
1314
use reqwest::header::{ACCEPT, HeaderMap, HeaderValue};
1415
use reqwest::{IntoUrl, Response};
1516
use sensitive_url::SensitiveUrl;
@@ -74,13 +75,18 @@ impl BuilderHttpClient {
7475
builder_header_timeout: Option<Duration>,
7576
disable_ssz: bool,
7677
) -> Result<Self, Error> {
77-
let user_agent = user_agent.unwrap_or(lighthouse_version::user_agent());
78-
let client = HttpClientBuilderWithUserAgent::new(Some(user_agent.clone())).build()?;
78+
let user_agent = user_agent
79+
.as_deref()
80+
.unwrap_or(lighthouse_version::DEFAULT_USER_AGENT);
81+
let client = eth2::create_client_with_user_agent(user_agent)
82+
.build()
83+
.unwrap_or_else(|_| reqwest::Client::new());
84+
7985
Ok(Self {
8086
client,
8187
server,
8288
timeouts: Timeouts::new(builder_header_timeout),
83-
user_agent,
89+
user_agent: user_agent.into(),
8490
disable_ssz,
8591
ssz_available: Arc::new(false.into()),
8692
})

beacon_node/http_api/tests/tests.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use eth2::{
88
Error::ServerMessage,
99
StatusCode, Timeouts,
1010
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
11+
reqwest::Method,
1112
reqwest::RequestBuilder,
1213
types::{
1314
BlockId as CoreBlockId, ForkChoiceNode, ProduceBlockV3Response, StateId as CoreStateId, *,
@@ -35,6 +36,7 @@ use state_processing::per_slot_processing;
3536
use state_processing::state_advance::partial_state_advance;
3637
use std::convert::TryInto;
3738
use std::sync::Arc;
39+
use std::sync::Mutex;
3840
use tokio::time::Duration;
3941
use tree_hash::TreeHash;
4042
use types::application_domain::ApplicationDomain;
@@ -43,6 +45,7 @@ use types::{
4345
MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, SingleAttestation, Slot,
4446
attestation::AttestationBase,
4547
};
48+
use warp::Filter;
4649

4750
type E = MainnetEthSpec;
4851

@@ -6733,6 +6736,33 @@ impl ApiTester {
67336736
}
67346737
self
67356738
}
6739+
6740+
async fn test_user_agent(self) -> Self {
6741+
let captured: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
6742+
let captured_filter = captured.clone();
6743+
6744+
// Warp route: capture User-Agent header
6745+
let route = warp::any()
6746+
.and(warp::header::optional::<String>("user-agent"))
6747+
.map(move |ua: Option<String>| {
6748+
*captured_filter.lock().unwrap() = ua;
6749+
warp::reply()
6750+
});
6751+
6752+
// Start ephemeral server
6753+
let (addr, server) = warp::serve(route).bind_ephemeral(([127, 0, 0, 1], 0));
6754+
tokio::spawn(server);
6755+
6756+
// Make a request (simulate Lighthouse client)
6757+
let url = format!("http://{}/", addr);
6758+
let _client = self.client.create_request(Method::GET, &url).await;
6759+
6760+
// Check captured User-Agent
6761+
let ua = captured.lock().unwrap().clone().unwrap();
6762+
assert_eq!(ua, lighthouse_version::DEFAULT_USER_AGENT);
6763+
6764+
self
6765+
}
67366766
}
67376767

67386768
async fn poll_events<S: Stream<Item = Result<EventKind<E>, eth2::Error>> + Unpin, E: EthSpec>(
@@ -7882,3 +7912,8 @@ async fn get_beacon_rewards_blocks_electra() {
78827912
.test_beacon_block_rewards_electra()
78837913
.await;
78847914
}
7915+
7916+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
7917+
async fn test_user_agent() {
7918+
ApiTester::new().await.test_user_agent().await;
7919+
}

common/eth2/src/lib.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use futures_util::StreamExt;
2323
use libp2p_identity::PeerId;
2424
use lighthouse_version;
2525
use pretty_reqwest_error::PrettyReqwestError;
26-
use reqwest;
26+
pub use reqwest;
2727
use reqwest::{
28-
Body, IntoUrl, RequestBuilder, Response,
28+
Body, IntoUrl, Method, RequestBuilder, Response,
2929
header::{HeaderMap, HeaderValue},
3030
};
3131
pub use reqwest::{StatusCode, Url};
@@ -239,7 +239,7 @@ impl AsRef<str> for BeaconNodeHttpClient {
239239
impl BeaconNodeHttpClient {
240240
pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self {
241241
let client = reqwest::ClientBuilder::new()
242-
.user_agent(lighthouse_version::user_agent())
242+
.user_agent(lighthouse_version::DEFAULT_USER_AGENT)
243243
.build()
244244
.unwrap_or_else(|_| reqwest::Client::new());
245245

@@ -2891,6 +2891,14 @@ impl BeaconNodeHttpClient {
28912891
self.post_with_timeout_and_response(path, &selections, self.timeouts.sync_aggregators)
28922892
.await
28932893
}
2894+
2895+
pub async fn create_request(&self, method: Method, url: &str) -> Result<Response, Error> {
2896+
self.client
2897+
.request(method, url)
2898+
.send()
2899+
.await
2900+
.map_err(Error::from)
2901+
}
28942902
}
28952903

28962904
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an
@@ -2910,12 +2918,12 @@ pub async fn ok_or_error(response: Response) -> Result<Response, Error> {
29102918
}
29112919
}
29122920

2913-
/// A wrapper around `reqwest::ClientBuilder` which adds a user agent for client identification
2914-
pub struct HttpClientBuilderWithUserAgent;
2915-
2916-
impl HttpClientBuilderWithUserAgent {
2917-
pub fn new(user_agent: Option<String>) -> reqwest::ClientBuilder {
2918-
let user_agent = user_agent.unwrap_or(lighthouse_version::user_agent());
2919-
reqwest::ClientBuilder::new().user_agent(user_agent)
2920-
}
2921+
/// A helper function to create a `reqwest::Client` with a user agent
2922+
pub fn create_client_with_user_agent<'a>(
2923+
user_agent: impl Into<Option<&'a str>>,
2924+
) -> reqwest::ClientBuilder {
2925+
let user_agent = user_agent
2926+
.into()
2927+
.unwrap_or(lighthouse_version::DEFAULT_USER_AGENT);
2928+
reqwest::ClientBuilder::new().user_agent(user_agent)
29212929
}

common/eth2/src/lighthouse_vc/http_client.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl ValidatorClientHttpClient {
4747
/// Create a new client pre-initialised with an API token.
4848
pub fn new(server: SensitiveUrl, secret: String) -> Result<Self, Error> {
4949
let client = reqwest::ClientBuilder::new()
50-
.user_agent(lighthouse_version::user_agent())
50+
.user_agent(lighthouse_version::DEFAULT_USER_AGENT)
5151
.build()
5252
.unwrap_or_else(|_| reqwest::Client::new());
5353

@@ -64,7 +64,7 @@ impl ValidatorClientHttpClient {
6464
/// A token can be fetched by using `self.get_auth`, and then reading the token from disk.
6565
pub fn new_unauthenticated(server: SensitiveUrl) -> Result<Self, Error> {
6666
let client = reqwest::ClientBuilder::new()
67-
.user_agent(lighthouse_version::user_agent())
67+
.user_agent(lighthouse_version::DEFAULT_USER_AGENT)
6868
.build()
6969
.unwrap_or_else(|_| reqwest::Client::new());
7070

@@ -692,8 +692,17 @@ impl ValidatorClientHttpClient {
692692
self.delete(url).await
693693
}
694694

695-
pub async fn create_request<U: IntoUrl>(&self, url: U) -> Result<Response, Error> {
696-
self.client.get(url).send().await.map_err(Error::from)
695+
/// Used internally by the `ValidatorClientHttpClient` to create a request.
696+
pub async fn create_request<U: IntoUrl>(
697+
&self,
698+
method: reqwest::Method,
699+
url: U,
700+
) -> Result<Response, Error> {
701+
self.client
702+
.request(method, url)
703+
.send()
704+
.await
705+
.map_err(Error::from)
697706
}
698707
}
699708

common/lighthouse_version/src/lib.rs

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ pub const VERSION: &str = git_version!(
2121
fallback = "Lighthouse/v7.1.0"
2222
);
2323

24+
/// Returns `VERSION` used as default User-Agent for HTTP requests.
25+
pub const DEFAULT_USER_AGENT: &str = VERSION;
26+
2427
/// Returns the first eight characters of the latest commit hash for this build.
2528
///
2629
/// No indication is given if the tree is dirty. This is part of the standard
@@ -48,15 +51,6 @@ pub fn version_with_platform() -> String {
4851
format!("{}/{}-{}", VERSION, consts::ARCH, consts::OS)
4952
}
5053

51-
/// Returns `CLIENT_NAME/VERSION` used as default User-Agent for HTTP requests.
52-
///
53-
/// ## Example
54-
///
55-
/// `Lighthouse/v7.1.0-67da032+`
56-
pub fn user_agent() -> String {
57-
format!("{}", VERSION)
58-
}
59-
6054
/// Returns semantic versioning information only.
6155
///
6256
/// ## Example
@@ -100,34 +94,4 @@ mod test {
10094
version()
10195
);
10296
}
103-
104-
#[test]
105-
fn user_agent_formatting() {
106-
let ua = user_agent();
107-
108-
// User agent should match the VERSION format
109-
let re = Regex::new(
110-
r"^Lighthouse/v[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta).[0-9])?(-[[:xdigit:]]{7})?\+?$",
111-
)
112-
.unwrap();
113-
114-
assert!(
115-
re.is_match(&ua),
116-
"user agent doesn't match expected format: {}",
117-
ua
118-
);
119-
120-
// User agent should be equal to VERSION
121-
assert_eq!(ua, VERSION, "user_agent() should return VERSION");
122-
}
123-
124-
#[test]
125-
fn user_agent_non_empty() {
126-
let ua = user_agent();
127-
assert!(!ua.is_empty(), "user agent should not be empty");
128-
assert!(
129-
ua.starts_with("Lighthouse/"),
130-
"user agent should start with 'Lighthouse/'"
131-
);
132-
}
13397
}

testing/node_test_rig/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use beacon_node::ProductionBeaconNode;
66
use environment::RuntimeContext;
7-
use eth2::{BeaconNodeHttpClient, Timeouts, HttpClientBuilderWithUserAgent};
7+
use eth2::{BeaconNodeHttpClient, Timeouts};
88
use sensitive_url::SensitiveUrl;
99
use std::path::PathBuf;
1010
use std::time::Duration;
@@ -81,7 +81,7 @@ impl<E: EthSpec> LocalBeaconNode<E> {
8181
format!("http://{}:{}", listen_addr.ip(), listen_addr.port()).as_str(),
8282
)
8383
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?;
84-
let beacon_node_http_client = HttpClientBuilderWithUserAgent::new(None)
84+
let beacon_node_http_client = eth2::create_client_with_user_agent(None)
8585
.timeout(HTTP_TIMEOUT)
8686
.build()
8787
.map_err(|e| format!("Unable to build HTTP client: {:?}", e))?;

validator_client/http_api/src/test_utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use account_utils::{
66
};
77
use deposit_contract::decode_eth1_tx_data;
88
use doppelganger_service::DoppelgangerService;
9+
use eth2::reqwest::Method;
910
use eth2::{
1011
Error as ApiError,
1112
lighthouse_vc::{http_client::ValidatorClientHttpClient, types::*},
@@ -456,11 +457,11 @@ impl ApiTester {
456457

457458
// Make a request (simulate Lighthouse client)
458459
let url = format!("http://{}/", addr);
459-
let _client = self.client.create_request(&url).await;
460+
let _client = self.client.create_request(Method::GET, &url).await;
460461

461462
// Check captured User-Agent
462463
let ua = captured.lock().unwrap().clone().unwrap();
463-
assert_eq!(ua, lighthouse_version::user_agent());
464+
assert_eq!(ua, lighthouse_version::DEFAULT_USER_AGENT);
464465

465466
self
466467
}

validator_client/http_api/src/tests.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use account_utils::{
1111
random_password_string, validator_definitions::ValidatorDefinitions,
1212
};
1313
use deposit_contract::decode_eth1_tx_data;
14+
use eth2::reqwest::Method;
1415
use eth2::{
1516
Error as ApiError,
1617
lighthouse_vc::{http_client::ValidatorClientHttpClient, types::*},
@@ -420,11 +421,11 @@ impl ApiTester {
420421

421422
// Make a request (simulate Lighthouse client)
422423
let url = format!("http://{}/", addr);
423-
let _client = self.client.create_request(&url).await;
424+
let _client = self.client.create_request(Method::GET, &url).await;
424425

425426
// Check captured User-Agent
426427
let ua = captured.lock().unwrap().clone().unwrap();
427-
assert_eq!(ua, lighthouse_version::user_agent());
428+
assert_eq!(ua, lighthouse_version::DEFAULT_USER_AGENT);
428429

429430
self
430431
}

validator_client/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use beacon_node_fallback::{
1717
use clap::ArgMatches;
1818
use doppelganger_service::DoppelgangerService;
1919
use environment::RuntimeContext;
20-
use eth2::{BeaconNodeHttpClient, HttpClientBuilderWithUserAgent, StatusCode, Timeouts};
20+
use eth2::{BeaconNodeHttpClient, StatusCode, Timeouts};
2121
use initialized_validators::Error::UnableToOpenVotingKeystore;
2222
use lighthouse_validator_store::LighthouseValidatorStore;
2323
use parking_lot::RwLock;
@@ -272,7 +272,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
272272
let url = x.1;
273273
let slot_duration = Duration::from_secs(context.eth2_config.spec.seconds_per_slot);
274274

275-
let mut beacon_node_http_client_builder = HttpClientBuilderWithUserAgent::new(None);
275+
let mut beacon_node_http_client_builder = eth2::create_client_with_user_agent(None);
276276

277277
// Add new custom root certificates if specified.
278278
if let Some(certificates) = &config.beacon_nodes_tls_certs {

0 commit comments

Comments
 (0)