Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add reconnection capability for persisted WebDriver sessions #276

Merged
merged 3 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,32 @@
/// should generally be one that supports HTTPS, as that is commonly required by WebDriver
/// implementations.
///
/// Calls `with_capabilities_and_connector` with an empty capabilities list.
pub(crate) async fn new_with_connector<C>(
webdriver: &str,
connector: C,
) -> Result<Self, error::NewSessionError>
where
C: connect::Connect + Unpin + 'static + Clone + Send + Sync,
{
Self::with_capabilities_and_connector(
webdriver,
&webdriver::capabilities::Capabilities::new(),
connector,
)
.await
let (client, wdb) = Session::create_client_and_parse_url(webdriver, connector).await?;
Session::setup_session(client, wdb, None).await
}

Check warning on line 72 in src/client.rs

View check run for this annotation

Codecov / codecov/patch

src/client.rs#L70-L72

Added lines #L70 - L72 were not covered by tests

/// Reconnect to a previously established WebDriver session using its ID.
///
/// Ideal for resuming operations without losing session data after a disconnect
/// or process restart, ensuring that the session can be reused without creating a new one.
///
pub async fn with_existing_session<C>(
webdriver: &str,
session_id: &str,
connector: C,
) -> Result<Self, error::NewSessionError>
where
C: connect::Connect + Unpin + 'static + Clone + Send + Sync,
{
let (client, wdb) = Session::create_client_and_parse_url(webdriver, connector).await?;
Session::setup_session(client, wdb, Some(session_id)).await

Check warning on line 88 in src/client.rs

View check run for this annotation

Codecov / codecov/patch

src/client.rs#L79-L88

Added lines #L79 - L88 were not covered by tests
}

/// Connect to the WebDriver host running the given address.
Expand Down
89 changes: 62 additions & 27 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,24 @@
where
C: connect::Connect + Unpin + 'static + Clone + Send + Sync,
{
fn new(
rx: mpsc::UnboundedReceiver<Task>,
client: hyper_util::client::legacy::Client<C, BoxBody<hyper::body::Bytes, Infallible>>,
wdb_url: url::Url,
session_id: Option<impl Into<String>>,
) -> Self {
Session {
ongoing: Ongoing::None,
rx,
client,
wdb: wdb_url,
session: session_id.map(Into::into),
is_legacy: false,
ua: None,
persist: false,
}
}

fn shutdown(&mut self, ack: Option<Ack>) {
// session was not created
if self.session.is_none() {
Expand Down Expand Up @@ -607,40 +625,58 @@
}
}

pub(crate) async fn with_capabilities_and_connector(
pub(crate) async fn create_client_and_parse_url(
webdriver: &str,
cap: &webdriver::capabilities::Capabilities,
connector: C,
) -> Result<Client, error::NewSessionError> {
) -> Result<
(
hyper_util::client::legacy::Client<C, BoxBody<hyper::body::Bytes, Infallible>>,
url::Url,
),
error::NewSessionError,
> {
// Where is the WebDriver server?
let wdb = webdriver.parse::<url::Url>();
let wdb = wdb.map_err(error::NewSessionError::BadWebdriverUrl)?;
let wdb = webdriver
.parse::<url::Url>()
.map_err(error::NewSessionError::BadWebdriverUrl)?;

// We want a tls-enabled client
let client = hyper_util::client::legacy::Client::builder(TokioExecutor::new())
.build::<_, BoxBody<hyper::body::Bytes, Infallible>>(connector);

let mut cap = cap.to_owned();
Ok((client, wdb))
}

pub(crate) async fn setup_session(
client: hyper_util::client::legacy::Client<C, BoxBody<hyper::body::Bytes, Infallible>>,
wdb: url::Url,
session_id: Option<&str>,
) -> Result<Client, error::NewSessionError> {
// We're going to need a channel for sending requests to the WebDriver host
let (tx, rx) = mpsc::unbounded_channel();

// Set up our WebDriver session.
tokio::spawn(Session {
tokio::spawn(Session::new(
rx,
ongoing: Ongoing::None,
client,
wdb,
session: None,
is_legacy: false,
ua: None,
persist: false,
});
session_id.map(|id| id.to_string()),
));

// now that the session is running, let's do the handshake
let client = Client {
tx: tx.clone(),
Ok(Client {
tx,
is_legacy: false,
new_session_response: None,
};
})
}

pub(crate) async fn with_capabilities_and_connector(
webdriver: &str,
cap: &webdriver::capabilities::Capabilities,
connector: C,
) -> Result<Client, error::NewSessionError> {
let (client, wdb) = Self::create_client_and_parse_url(webdriver, connector).await?;
let mut cap = cap.to_owned();

// Create a new session for this client
// https://www.w3.org/TR/webdriver/#dfn-new-session
Expand All @@ -659,6 +695,8 @@
.insert("w3c".to_string(), Json::from(true));
}

let mut client = Self::setup_session(client, wdb, None).await?;

let session_config = webdriver::capabilities::SpecNewSessionParameters {
alwaysMatch: cap.clone(),
firstMatch: vec![webdriver::capabilities::Capabilities::new()],
Expand All @@ -670,11 +708,11 @@
.map(Self::map_handshake_response)
.await
{
Ok(new_session_response) => Ok(Client {
tx,
is_legacy: false,
new_session_response: Some(wd::NewSessionResponse::from_wd(new_session_response)),
}),
Ok(new_session_response) => {
client.new_session_response =
Some(wd::NewSessionResponse::from_wd(new_session_response));
Ok(client)
}

Check warning on line 715 in src/session.rs

View check run for this annotation

Codecov / codecov/patch

src/session.rs#L714-L715

Added lines #L714 - L715 were not covered by tests
Err(error::NewSessionError::NotW3C(json)) => {
// maybe try legacy mode?
let mut legacy = false;
Expand Down Expand Up @@ -717,11 +755,8 @@
.map(Self::map_handshake_response)
.await?;

Ok(Client {
tx,
is_legacy: true,
new_session_response: None,
})
client.is_legacy = true;
Ok(client)

Check warning on line 759 in src/session.rs

View check run for this annotation

Codecov / codecov/patch

src/session.rs#L759

Added line #L759 was not covered by tests
}
Err(e) => Err(e),
}
Expand Down
Loading