Skip to content

Commit 3602e9e

Browse files
chore: drop direct dependency on futures-util (#303)
1 parent 77f03d4 commit 3602e9e

File tree

3 files changed

+115
-139
lines changed

3 files changed

+115
-139
lines changed

.github/workflows/check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ jobs:
112112
# https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
113113
strategy:
114114
matrix:
115-
msrv: ["1.82.0"] # tokio
115+
msrv: ["1.88.0"] # time
116116
name: ubuntu / ${{ matrix.msrv }}
117117
steps:
118118
- uses: actions/checkout@v4

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ webdriver = { version = "0.53", default-features = false }
2828
url = "2.2.2"
2929
serde = { version = "1.0.103", features = ["derive"] }
3030
serde_json = "1.0.50"
31-
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
3231
tokio = { version = "1", features = ["sync", "rt", "time"] }
3332
hyper = { version = "1.1.0", features = ["client", "http1"] }
3433
hyper-util = { version = "0.1.3", features = ["client", "http1", "client-legacy", "tokio"] }
@@ -42,6 +41,7 @@ http = "1.0.0"
4241
time = "0.3"
4342

4443
[dev-dependencies]
44+
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
4545
tokio = { version = "1", features = ["full"] }
4646
hyper = { version = "1.1.0", features = ["server"] }
4747
hyper-util = { version = "0.1.3", features = ["server", "http1"] }

src/session.rs

Lines changed: 113 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ use crate::error::ErrorStatus;
33
use crate::wd::{self, WebDriverCompatibleCommand};
44
use crate::{error, Client};
55
use base64::Engine;
6-
use futures_util::future::{self, Either};
7-
use futures_util::{FutureExt, TryFutureExt};
86
use http_body_util::combinators::BoxBody;
97
use http_body_util::BodyExt;
108
use hyper_util::client::legacy::connect;
@@ -683,11 +681,7 @@ where
683681
capabilities: session_config,
684682
};
685683

686-
match client
687-
.issue(WebDriverCommand::NewSession(spec))
688-
.map(Self::map_handshake_response)
689-
.await
690-
{
684+
match Self::map_handshake_response(client.issue(WebDriverCommand::NewSession(spec)).await) {
691685
Ok(new_session_response) => {
692686
client.new_session_response =
693687
Some(wd::NewSessionResponse::from_wd(new_session_response));
@@ -710,150 +704,132 @@ where
710704
&self,
711705
cmd: Box<impl WebDriverCompatibleCommand + Send + 'static + ?Sized>,
712706
) -> impl Future<Output = Result<Json, error::CmdError>> {
713-
// TODO: make this an async fn
714-
// will take some doing as returned future must be independent of self
715-
let url = match cmd.endpoint(&self.wdb, self.session.as_deref()) {
716-
Ok(url) => url,
717-
Err(e) => return Either::Right(future::err(error::CmdError::from(e))),
718-
};
719-
720-
let (method, mut body) = cmd.method_and_body(&url);
721-
722-
// issue the command to the webdriver server
723-
let mut req = hyper::Request::builder();
724-
req = req.method(method).uri(url.as_str());
725-
if let Some(ref s) = self.ua {
726-
req = req.header(hyper::header::USER_AGENT, s.to_owned());
727-
}
728-
// because https://github.com/hyperium/hyper/pull/727
729-
if !url.username().is_empty() || url.password().is_some() {
730-
req = req.header(
731-
hyper::header::AUTHORIZATION,
732-
format!(
733-
"Basic {}",
734-
base64::engine::general_purpose::STANDARD.encode(&format!(
735-
"{}:{}",
736-
url.username(),
737-
url.password().unwrap_or("")
738-
))
739-
),
740-
);
741-
}
707+
let input = cmd
708+
.endpoint(&self.wdb, self.session.as_deref())
709+
.map(|url| (url, self.ua.clone(), self.client.clone()));
710+
711+
async move {
712+
let (url, ua, client) = input?;
713+
714+
let (method, mut body) = cmd.method_and_body(&url);
715+
716+
// issue the command to the webdriver server
717+
let mut req = hyper::Request::builder();
718+
req = req.method(method).uri(url.as_str());
719+
if let Some(ref s) = ua {
720+
req = req.header(hyper::header::USER_AGENT, s);
721+
}
722+
// because https://github.com/hyperium/hyper/pull/727
723+
if !url.username().is_empty() || url.password().is_some() {
724+
req = req.header(
725+
hyper::header::AUTHORIZATION,
726+
format!(
727+
"Basic {}",
728+
base64::engine::general_purpose::STANDARD.encode(&format!(
729+
"{}:{}",
730+
url.username(),
731+
url.password().unwrap_or("")
732+
))
733+
),
734+
);
735+
}
742736

743-
let json_mime: mime::Mime = "application/json; charset=utf-8"
744-
.parse::<mime::Mime>()
745-
.unwrap_or(mime::APPLICATION_JSON);
737+
let json_mime: mime::Mime = "application/json; charset=utf-8"
738+
.parse::<mime::Mime>()
739+
.unwrap_or(mime::APPLICATION_JSON);
746740

747-
let req = if let Some(body) = body.take() {
748-
req = req.header(hyper::header::CONTENT_TYPE, json_mime.as_ref());
749-
req = req.header(hyper::header::CONTENT_LENGTH, body.len());
750-
self.client.request(req.body(BoxBody::new(body)).unwrap())
751-
} else {
752-
self.client.request(
741+
let body = if let Some(body) = body.take() {
742+
req = req.header(hyper::header::CONTENT_TYPE, json_mime.as_ref());
743+
req = req.header(hyper::header::CONTENT_LENGTH, body.len());
744+
req.body(BoxBody::new(body)).unwrap()
745+
} else {
753746
req.body(BoxBody::new(http_body_util::Empty::new()))
754-
.unwrap(),
755-
)
756-
};
747+
.unwrap()
748+
};
757749

758-
let f = req
759-
.map_err(error::CmdError::from)
760-
.and_then(move |res| {
761-
// keep track of result status (.body() consumes self -- ugh)
762-
let status = res.status();
763-
764-
// check that the server sent us json
765-
let ctype = res
766-
.headers()
767-
.get(hyper::header::CONTENT_TYPE)
768-
.and_then(|ctype| ctype.to_str().ok()?.parse::<mime::Mime>().ok());
769-
770-
// What did the server send us?
771-
res.into_body()
772-
.collect()
773-
.map_ok(|body| body.to_bytes())
774-
.map_ok(move |body| (body, ctype, status))
775-
.map_err(|e| -> error::CmdError { e.into() })
776-
})
777-
.map(|r| {
778-
let (body, ctype, status) = r?;
779-
780-
// Too bad we can't stream into a String :(
781-
let body =
782-
String::from_utf8(body.to_vec()).expect("non utf-8 response from webdriver");
783-
784-
if let Some(ctype) = ctype {
785-
if ctype.type_() == mime::APPLICATION_JSON.type_()
786-
&& ctype.subtype() == mime::APPLICATION_JSON.subtype()
787-
{
788-
Ok((body, status))
789-
} else {
790-
// nope, something else...
791-
Err(error::CmdError::NotJson(body))
792-
}
793-
} else {
794-
// WebDriver host sent us something weird...
795-
Err(error::CmdError::NotJson(body))
796-
}
797-
})
798-
.map(move |r| {
799-
let (body, status) = r?;
800-
let is_success = status.is_success();
801-
802-
// https://www.w3.org/TR/webdriver/#dfn-send-a-response
803-
// NOTE: the standard specifies that even errors use the "Send a Response" steps
804-
let body = match serde_json::from_str(&*body)? {
805-
Json::Object(mut v) => v
806-
.remove("value")
807-
.ok_or(error::CmdError::NotW3C(Json::Object(v))),
808-
v => Err(error::CmdError::NotW3C(v)),
809-
}?;
810-
811-
if is_success {
812-
return Ok(body);
813-
}
750+
let res = client.request(body).await?;
751+
// keep track of result status (.body() consumes self -- ugh)
752+
let status = res.status();
814753

815-
// https://www.w3.org/TR/webdriver/#dfn-send-an-error
816-
// https://www.w3.org/TR/webdriver/#handling-errors
817-
let mut body = match body {
818-
Json::Object(o) => o,
819-
j => return Err(error::CmdError::NotW3C(j)),
820-
};
754+
// check that the server sent us json
755+
let ctype = res
756+
.headers()
757+
.get(hyper::header::CONTENT_TYPE)
758+
.and_then(|ctype| ctype.to_str().ok()?.parse::<mime::Mime>().ok());
759+
760+
// What did the server send us?
761+
let body = res.into_body().collect().await?.to_bytes();
821762

822-
// phantomjs injects a *huge* field with the entire screen contents -- remove that
823-
body.remove("screen");
763+
// Too bad we can't stream into a String :(
764+
let body = String::from_utf8(body.to_vec()).expect("non utf-8 response from webdriver");
824765

825-
if !body.contains_key("error")
826-
|| !body.contains_key("message")
827-
|| !body["error"].is_string()
828-
|| !body["message"].is_string()
766+
if let Some(ctype) = ctype {
767+
if ctype.type_() != mime::APPLICATION_JSON.type_()
768+
|| ctype.subtype() != mime::APPLICATION_JSON.subtype()
829769
{
830-
return Err(error::CmdError::NotW3C(Json::Object(body)));
770+
// nope, something else...
771+
return Err(error::CmdError::NotJson(body));
831772
}
773+
} else {
774+
// WebDriver host sent us something weird...
775+
return Err(error::CmdError::NotJson(body));
776+
}
832777

833-
let Some(es) = body["error"].as_str() else {
834-
return Err(error::CmdError::NotW3C(Json::Object(body)));
835-
};
836-
let es = es.parse()?;
778+
let is_success = status.is_success();
837779

838-
let message = match body.remove("message") {
839-
Some(Json::String(x)) => x,
840-
_ => String::new(),
841-
};
780+
// https://www.w3.org/TR/webdriver/#dfn-send-a-response
781+
// NOTE: the standard specifies that even errors use the "Send a Response" steps
782+
let body = match serde_json::from_str(&body)? {
783+
Json::Object(mut v) => v
784+
.remove("value")
785+
.ok_or(error::CmdError::NotW3C(Json::Object(v))),
786+
v => Err(error::CmdError::NotW3C(v)),
787+
}?;
842788

843-
let mut wd_error = error::WebDriver::new(es, message);
789+
if is_success {
790+
return Ok(body);
791+
}
844792

845-
// Add the stacktrace if there is one.
846-
if let Some(Json::String(x)) = body.remove("stacktrace") {
847-
wd_error = wd_error.with_stacktrace(x);
848-
}
793+
// https://www.w3.org/TR/webdriver/#dfn-send-an-error
794+
// https://www.w3.org/TR/webdriver/#handling-errors
795+
let mut body = match body {
796+
Json::Object(o) => o,
797+
j => return Err(error::CmdError::NotW3C(j)),
798+
};
849799

850-
// Some commands may annotate errors with extra data.
851-
if let Some(x) = body.remove("data") {
852-
wd_error = wd_error.with_data(x);
853-
}
854-
Err(error::CmdError::from_webdriver_error(wd_error))
855-
});
800+
// phantomjs injects a *huge* field with the entire screen contents -- remove that
801+
body.remove("screen");
802+
803+
if !body.contains_key("error")
804+
|| !body.contains_key("message")
805+
|| !body["error"].is_string()
806+
|| !body["message"].is_string()
807+
{
808+
return Err(error::CmdError::NotW3C(Json::Object(body)));
809+
}
810+
811+
let Some(es) = body["error"].as_str() else {
812+
return Err(error::CmdError::NotW3C(Json::Object(body)));
813+
};
814+
let es = es.parse()?;
815+
816+
let message = match body.remove("message") {
817+
Some(Json::String(x)) => x,
818+
_ => String::new(),
819+
};
820+
821+
let mut wd_error = error::WebDriver::new(es, message);
856822

857-
Either::Left(f)
823+
// Add the stacktrace if there is one.
824+
if let Some(Json::String(x)) = body.remove("stacktrace") {
825+
wd_error = wd_error.with_stacktrace(x);
826+
}
827+
828+
// Some commands may annotate errors with extra data.
829+
if let Some(x) = body.remove("data") {
830+
wd_error = wd_error.with_data(x);
831+
}
832+
Err(error::CmdError::from_webdriver_error(wd_error))
833+
}
858834
}
859835
}

0 commit comments

Comments
 (0)