/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usecrate::command::{WebDriverCommand, WebDriverMessage}; usecrate::error::{ErrorStatus, WebDriverError, WebDriverResult}; usecrate::httpapi::{
standard_routes, Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute,
}; usecrate::response::{CloseWindowResponse, WebDriverResponse}; usecrate::Parameters; use bytes::Bytes; use http::{Method, StatusCode}; use std::marker::PhantomData; use std::net::{SocketAddr, TcpListener as StdTcpListener}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::thread; use tokio::net::TcpListener; use tokio_stream::wrappers::TcpListenerStream; use url::{Host, Url}; use warp::{Buf, Filter, Rejection};
// Silence warning about Quit being unused for now. #[allow(dead_code)] enum DispatchMessage<U: WebDriverExtensionRoute> {
HandleWebDriver(
WebDriverMessage<U>,
Sender<WebDriverResult<WebDriverResponse>>,
),
Quit,
}
#[derive(Clone, Debug, PartialEq)] /// Representation of whether we managed to successfully send a DeleteSession message /// and read the response during session teardown. pubenum SessionTeardownKind { /// A DeleteSession message has been sent and the response handled.
Deleted, /// No DeleteSession message has been sent, or the response was not received.
NotDeleted,
}
match resp {
Ok(WebDriverResponse::NewSession(ref new_session)) => { self.session = Some(Session::new(new_session.session_id.clone()));
}
Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(ref handles))) => { if handles.is_empty() {
debug!("Last window was closed, deleting session"); // The teardown_session implementation is responsible for actually // sending the DeleteSession message in this case self.teardown_session(SessionTeardownKind::NotDeleted);
}
}
Ok(WebDriverResponse::DeleteSession) => { self.teardown_session(SessionTeardownKind::Deleted);
}
Err(ref x) if x.delete_session => { // This includes the case where we failed during session creation self.teardown_session(SessionTeardownKind::NotDeleted)
}
_ => {}
}
if resp_chan.send(resp).is_err() {
error!("Sending response to the main thread failed");
};
}
Ok(DispatchMessage::Quit) => break,
Err(e) => panic!("Error receiving message in handler: {:?}", e),
}
}
}
impl Drop for Listener { fn drop(&mutself) { let _ = self.guard.take().map(|j| j.join());
}
}
pubfn start<T, U>( mut address: SocketAddr,
allow_hosts: Vec<Host>,
allow_origins: Vec<Url>,
handler: T,
extension_routes: Vec<(Method, &'static str, U)>,
) -> ::std::io::Result<Listener> where
T: 'static + WebDriverHandler<U>,
U: 'static + WebDriverExtensionRoute + Send + Sync,
{ let listener = StdTcpListener::bind(address)?;
listener.set_nonblocking(true)?; let addr = listener.local_addr()?; if address.port() == 0 { // If we passed in 0 as the port number the OS will assign an unused port; // we want to update the address to the actual used port
address.set_port(addr.port())
} let (msg_send, msg_recv) = channel();
let builder = thread::Builder::new().name("webdriver server".to_string()); let handle = builder.spawn(move || { let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap(); let listener = rt.block_on(async { TcpListener::from_std(listener).unwrap() }); let wroutes = build_warp_routes(
address,
allow_hosts,
allow_origins,
&extension_routes,
msg_send.clone(),
); let fut = warp::serve(wroutes).run_incoming(TcpListenerStream::new(listener));
rt.block_on(fut);
})?;
fn is_host_allowed(server_address: &SocketAddr, allow_hosts: &[Host], host_header: &str) -> bool { // Validate that the Host header value has a hostname in allow_hosts and // the port matches the server configuration let header_host_url = match Url::parse(&format!("http://{}", &host_header)) {
Ok(x) => x,
Err(_) => { returnfalse;
}
};
let host = match header_host_url.host() {
Some(host) => host.to_owned(),
None => { // This shouldn't be possible since http URL always have a // host, but conservatively return false here, which will cause // an error response returnfalse;
}
}; let port = match header_host_url.port_or_known_default() {
Some(port) => port,
None => { // This shouldn't be possible since http URL always have a // default port, but conservatively return false here, which will cause // an error response returnfalse;
}
};
let host_matches = match host {
Host::Domain(_) => allow_hosts.contains(&host),
Host::Ipv4(_) | Host::Ipv6(_) => true,
}; let port_matches = server_address.port() == port;
host_matches && port_matches
}
fn is_origin_allowed(allow_origins: &[Url], origin_url: Url) -> bool { // Validate that the Origin header value is in allow_origins
allow_origins.contains(&origin_url)
}
fn build_route<U: 'static + WebDriverExtensionRoute + Send + Sync>(
server_address: SocketAddr,
allow_hosts: Vec<Host>,
allow_origins: Vec<Url>,
method: Method,
path: &'static str,
route: Route<U>,
chan: Arc<Mutex<Sender<DispatchMessage<U>>>>,
) -> warp::filters::BoxedFilter<(impl warp::Reply,)> { // Create an empty filter based on the provided method and append an empty hashmap to it. The // hashmap will be used to store path parameters. letmut subroute = match method {
Method::GET => warp::get().boxed(),
Method::POST => warp::post().boxed(),
Method::DELETE => warp::delete().boxed(),
Method::OPTIONS => warp::options().boxed(),
Method::PUT => warp::put().boxed(),
_ => panic!("Unsupported method"),
}
.or(warp::head())
.unify()
.map(Parameters::new)
.boxed();
// For each part of the path, if it's a normal part, just append it to the current filter, // otherwise if it's a parameter (a named enclosed in { }), we take that parameter and insert // it into the hashmap created earlier. for part in path.split('/') { if part.is_empty() { continue;
} elseif part.starts_with('{') {
assert!(part.ends_with('}'));
#[cfg(test)] mod tests { usesuper::*; use std::net::IpAddr; use std::str::FromStr;
#[test] fn test_host_allowed() { let addr_80 = SocketAddr::new(IpAddr::from_str("127.0.0.1").unwrap(), 80); let addr_8000 = SocketAddr::new(IpAddr::from_str("127.0.0.1").unwrap(), 8000); let addr_v6_80 = SocketAddr::new(IpAddr::from_str("::1").unwrap(), 80); let addr_v6_8000 = SocketAddr::new(IpAddr::from_str("::1").unwrap(), 8000);
// We match the host ip address to the server, so we can only use hosts that actually resolve let localhost_host = Host::Domain("localhost".to_string()); let test_host = Host::Domain("example.test".to_string()); let subdomain_localhost_host = Host::Domain("subdomain.localhost".to_string());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.