Skip to content

Commit 50f8084

Browse files
committed
Add regex parsing bound_address to URL
* Style changes per code review
1 parent 214ef0a commit 50f8084

File tree

3 files changed

+72
-17
lines changed

3 files changed

+72
-17
lines changed

elasticsearch/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ bytes = "^0.5"
2727
dyn-clone = "~1"
2828
percent-encoding = "2.1.0"
2929
reqwest = { version = "~0.10", default-features = false, features = ["gzip", "json"] }
30+
lazy_static = "^1.4"
3031
url = "^2.1"
32+
regex = "1.3"
3133
serde = { version = "~1", features = ["derive"] }
3234
serde_json = "~1"
3335
serde_with = "~1"

elasticsearch/src/http/transport.rs

Lines changed: 67 additions & 17 deletions
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 regex::Regex;
4041
use serde::Serialize;
4142
use serde_json::Value;
4243
use std::{
@@ -101,6 +102,10 @@ impl fmt::Display for BuildError {
101102

102103
/// Default address to Elasticsearch running on `http://localhost:9200`
103104
pub static DEFAULT_ADDRESS: &str = "http://localhost:9200";
105+
lazy_static! {
106+
static ref ADDRESS_REGEX: Regex =
107+
Regex::new(r"((?P<fqdn>[^/]+)/)?(?P<ip>[^:]+|\[[\da-fA-F:\.]+\]):(?P<port>\d+)$").unwrap();
108+
}
104109

105110
/// Builds a HTTP transport to make API calls to Elasticsearch
106111
pub struct TransportBuilder {
@@ -389,7 +394,7 @@ impl Transport {
389394
Ok(transport)
390395
}
391396

392-
pub fn request_builder<B, Q>(
397+
fn request_builder<B, Q>(
393398
&self,
394399
connection: &Connection,
395400
method: Method,
@@ -464,6 +469,28 @@ impl Transport {
464469
Ok(request_builder)
465470
}
466471

472+
fn parse_to_url(address: &str, scheme: &str) -> Result<Url, Error> {
473+
if address.is_empty() {
474+
return Err(crate::error::lib("Bound Address is empty"));
475+
}
476+
477+
let matches = ADDRESS_REGEX
478+
.captures(address)
479+
.ok_or_else(|| crate::lib(format!("error parsing address into url: {}", address)))?;
480+
481+
let host = matches
482+
.name("fqdn")
483+
.or_else(|| Some(matches.name("ip").unwrap()))
484+
.unwrap()
485+
.as_str()
486+
.trim();
487+
let port = matches.name("port").unwrap().as_str().trim();
488+
489+
Ok(Url::parse(
490+
format!("{}://{}:{}", scheme, host, port).as_str(),
491+
)?)
492+
}
493+
467494
/// Creates an asynchronous request that can be awaited
468495
pub async fn send<B, Q>(
469496
&self,
@@ -486,7 +513,7 @@ impl Transport {
486513
let node_request = self.request_builder(
487514
&connection,
488515
Method::Get,
489-
"_nodes/_all/http",
516+
"_nodes/http?filter_path=nodes.*.http",
490517
headers.clone(),
491518
None::<&Q>,
492519
None::<B>,
@@ -499,12 +526,17 @@ impl Transport {
499526
.unwrap()
500527
.iter()
501528
.map(|h| {
502-
let url = format!(
503-
"{}://{}",
504-
scheme,
505-
h.1["http"]["publish_address"].as_str().unwrap()
506-
);
507-
let url = Url::parse(&url).unwrap();
529+
let address = h.1["http"]["publish_address"]
530+
.as_str()
531+
.or_else(|| {
532+
Some(
533+
h.1["http"]["bound_address"].as_array().unwrap()[0]
534+
.as_str()
535+
.unwrap(),
536+
)
537+
})
538+
.unwrap();
539+
let url = Self::parse_to_url(address, scheme).unwrap();
508540
Connection::new(url)
509541
})
510542
.collect();
@@ -708,10 +740,10 @@ impl ConnectionPool for CloudConnectionPool {
708740

709741
/// A Connection Pool that manages a static connection of nodes
710742
#[derive(Debug, Clone)]
711-
pub struct MultiNodeConnectionPool<LoadBalancing = RoundRobin> {
743+
pub struct MultiNodeConnectionPool<ConnSelector = RoundRobin> {
712744
inner: Arc<RwLock<MultiNodeConnectionPoolInner>>,
713745
reseed_frequency: Option<Duration>,
714-
load_balancing_strategy: LoadBalancing,
746+
load_balancing_strategy: ConnSelector,
715747
reseeding: Arc<AtomicBool>,
716748
}
717749

@@ -721,9 +753,9 @@ pub struct MultiNodeConnectionPoolInner {
721753
connections: Vec<Connection>,
722754
}
723755

724-
impl<LoadBalancing> ConnectionPool for MultiNodeConnectionPool<LoadBalancing>
756+
impl<ConnSelector> ConnectionPool for MultiNodeConnectionPool<ConnSelector>
725757
where
726-
LoadBalancing: LoadBalancingStrategy + Clone,
758+
ConnSelector: ConnectionSelector + Clone,
727759
{
728760
fn next(&self) -> Connection {
729761
let inner = self.inner.read().expect("lock poisoned");
@@ -787,9 +819,9 @@ impl MultiNodeConnectionPool<RoundRobin> {
787819
}
788820

789821
/** The strategy selects an address from a given collection. */
790-
pub trait LoadBalancingStrategy: Send + Sync + Debug {
822+
pub trait ConnectionSelector: Send + Sync + Debug {
791823
/** Try get the next connection. */
792-
fn try_next<'a>(&self, connections: &'a [Connection]) -> Result<Connection, Error>;
824+
fn try_next(&self, connections: &[Connection]) -> Result<Connection, Error>;
793825
}
794826

795827
/** A round-robin strategy cycles through nodes sequentially. */
@@ -806,8 +838,8 @@ impl Default for RoundRobin {
806838
}
807839
}
808840

809-
impl LoadBalancingStrategy for RoundRobin {
810-
fn try_next<'a>(&self, connections: &'a [Connection]) -> Result<Connection, Error> {
841+
impl ConnectionSelector for RoundRobin {
842+
fn try_next(&self, connections: &[Connection]) -> Result<Connection, Error> {
811843
if connections.is_empty() {
812844
Err(crate::error::lib("Connection list empty"))
813845
} else {
@@ -823,7 +855,7 @@ pub mod tests {
823855
use crate::auth::ClientCertificate;
824856
use crate::http::transport::{
825857
CloudId, Connection, ConnectionPool, MultiNodeConnectionPool, SingleNodeConnectionPool,
826-
TransportBuilder,
858+
Transport, TransportBuilder,
827859
};
828860
use std::{
829861
sync::atomic::Ordering,
@@ -853,6 +885,24 @@ pub mod tests {
853885
assert!(res.is_err());
854886
}
855887

888+
#[test]
889+
fn test_url_parsing_where_hostname_and_ip_present() {
890+
let url = Transport::parse_to_url("localhost/127.0.0.1:9200", "http").unwrap();
891+
assert_eq!(url.into_string(), "http://localhost:9200/");
892+
}
893+
894+
#[test]
895+
fn test_url_parsing_where_only_ip_present() {
896+
let url = Transport::parse_to_url("127.0.0.1:9200", "http").unwrap();
897+
assert_eq!(url.into_string(), "http://127.0.0.1:9200/");
898+
}
899+
900+
#[test]
901+
fn test_url_parsing_where_only_hostname_present() {
902+
let url = Transport::parse_to_url("localhost:9200", "http").unwrap();
903+
assert_eq!(url.into_string(), "http://localhost:9200/");
904+
}
905+
856906
#[test]
857907
fn can_parse_cloud_id() {
858908
let base64 = base64::encode("cloud-endpoint.example$3dadf823f05388497ea684236d918a1a$3f26e1609cf54a0f80137a80de560da4");

elasticsearch/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ type _DoctestReadme = ();
353353
#[macro_use]
354354
extern crate dyn_clone;
355355

356+
#[macro_use]
357+
extern crate lazy_static;
358+
356359
pub mod auth;
357360
pub mod cert;
358361
pub mod http;

0 commit comments

Comments
 (0)