Bug 1484462 - Update webdriver to hyper 0.12. r=ato

This commit is contained in:
Bastien Orivel 2018-08-18 17:29:05 +02:00 committed by Andreas Tolfsen
parent 4a601e2fa3
commit 963e21bf7f
8 changed files with 596 additions and 376 deletions

550
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@ publish = false
base64 = "0.6"
chrono = "^0.2"
clap = { version = "^2.19", default-features = false, features = ["suggestions", "wrap_help"] }
hyper = "0.10"
hyper = "0.12"
lazy_static = "1.0"
log = { version = "0.4", features = ["std"] }
mozprofile = { path = "../mozbase/rust/mozprofile" }

View file

@ -1,5 +1,5 @@
use base64;
use hyper::method::Method;
use hyper::Method;
use mozprofile::preferences::Pref;
use mozprofile::profile::Profile;
use mozrunner::runner::{FirefoxProcess, FirefoxRunner, Runner, RunnerProcess};
@ -67,32 +67,32 @@ const LEGACY_ELEMENT_KEY: &'static str = "ELEMENT";
pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> {
return vec![
(
Method::Get,
Method::GET,
"/session/{sessionId}/moz/context",
GeckoExtensionRoute::GetContext,
),
(
Method::Post,
Method::POST,
"/session/{sessionId}/moz/context",
GeckoExtensionRoute::SetContext,
),
(
Method::Post,
Method::POST,
"/session/{sessionId}/moz/xbl/{elementId}/anonymous_children",
GeckoExtensionRoute::XblAnonymousChildren,
),
(
Method::Post,
Method::POST,
"/session/{sessionId}/moz/xbl/{elementId}/anonymous_by_attribute",
GeckoExtensionRoute::XblAnonymousByAttribute,
),
(
Method::Post,
Method::POST,
"/session/{sessionId}/moz/addon/install",
GeckoExtensionRoute::InstallAddon,
),
(
Method::Post,
Method::POST,
"/session/{sessionId}/moz/addon/uninstall",
GeckoExtensionRoute::UninstallAddon,
),

View file

@ -10,15 +10,18 @@ readme = "README.md"
license = "MPL-2.0"
[dependencies]
cookie = { version = "0.10", default-features = false }
base64 = "0.6"
hyper = "0.10"
cookie = { version = "0.11", default-features = false }
futures = "0.1"
http = "0.1"
hyper = "0.12"
log = "0.4"
regex = "1.0"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
time = "0.1"
tokio = "0.1"
unicode-segmentation = "1.2"
url = "1"

View file

@ -1,5 +1,5 @@
use base64::DecodeError;
use hyper::status::StatusCode;
use hyper::StatusCode;
use serde::ser::{Serialize, Serializer};
use serde_json;
use std::borrow::Cow;
@ -188,37 +188,36 @@ impl ErrorStatus {
/// Returns the correct HTTP status code associated with the error type.
pub fn http_status(&self) -> StatusCode {
use self::ErrorStatus::*;
use self::StatusCode::*;
match *self {
ElementClickIntercepted => BadRequest,
ElementNotInteractable => BadRequest,
ElementNotSelectable => BadRequest,
InsecureCertificate => BadRequest,
InvalidArgument => BadRequest,
InvalidCookieDomain => BadRequest,
InvalidCoordinates => BadRequest,
InvalidElementState => BadRequest,
InvalidSelector => BadRequest,
InvalidSessionId => NotFound,
JavascriptError => InternalServerError,
MoveTargetOutOfBounds => InternalServerError,
NoSuchAlert => NotFound,
NoSuchCookie => NotFound,
NoSuchElement => NotFound,
NoSuchFrame => NotFound,
NoSuchWindow => NotFound,
ScriptTimeout => InternalServerError,
SessionNotCreated => InternalServerError,
StaleElementReference => NotFound,
Timeout => InternalServerError,
UnableToCaptureScreen => BadRequest,
UnableToSetCookie => InternalServerError,
UnexpectedAlertOpen => InternalServerError,
UnknownCommand => NotFound,
UnknownError => InternalServerError,
UnknownMethod => MethodNotAllowed,
UnknownPath => NotFound,
UnsupportedOperation => InternalServerError,
ElementClickIntercepted => StatusCode::BAD_REQUEST,
ElementNotInteractable => StatusCode::BAD_REQUEST,
ElementNotSelectable => StatusCode::BAD_REQUEST,
InsecureCertificate => StatusCode::BAD_REQUEST,
InvalidArgument => StatusCode::BAD_REQUEST,
InvalidCookieDomain => StatusCode::BAD_REQUEST,
InvalidCoordinates => StatusCode::BAD_REQUEST,
InvalidElementState => StatusCode::BAD_REQUEST,
InvalidSelector => StatusCode::BAD_REQUEST,
InvalidSessionId => StatusCode::NOT_FOUND,
JavascriptError => StatusCode::INTERNAL_SERVER_ERROR,
MoveTargetOutOfBounds => StatusCode::INTERNAL_SERVER_ERROR,
NoSuchAlert => StatusCode::NOT_FOUND,
NoSuchCookie => StatusCode::NOT_FOUND,
NoSuchElement => StatusCode::NOT_FOUND,
NoSuchFrame => StatusCode::NOT_FOUND,
NoSuchWindow => StatusCode::NOT_FOUND,
ScriptTimeout => StatusCode::INTERNAL_SERVER_ERROR,
SessionNotCreated => StatusCode::INTERNAL_SERVER_ERROR,
StaleElementReference => StatusCode::NOT_FOUND,
Timeout => StatusCode::INTERNAL_SERVER_ERROR,
UnableToCaptureScreen => StatusCode::BAD_REQUEST,
UnableToSetCookie => StatusCode::INTERNAL_SERVER_ERROR,
UnexpectedAlertOpen => StatusCode::INTERNAL_SERVER_ERROR,
UnknownCommand => StatusCode::NOT_FOUND,
UnknownError => StatusCode::INTERNAL_SERVER_ERROR,
UnknownMethod => StatusCode::METHOD_NOT_ALLOWED,
UnknownPath => StatusCode::NOT_FOUND,
UnsupportedOperation => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

View file

@ -1,7 +1,6 @@
use regex::{Captures, Regex};
use hyper::method::Method;
use hyper::method::Method::{Delete, Get, Post};
use hyper::Method;
use serde_json::Value;
use command::{VoidWebDriverExtensionCommand, WebDriverCommand, WebDriverExtensionCommand,
@ -10,214 +9,214 @@ use error::{ErrorStatus, WebDriverError, WebDriverResult};
fn standard_routes<U: WebDriverExtensionRoute>() -> Vec<(Method, &'static str, Route<U>)> {
return vec![
(Post, "/session", Route::NewSession),
(Delete, "/session/{sessionId}", Route::DeleteSession),
(Post, "/session/{sessionId}/url", Route::Get),
(Get, "/session/{sessionId}/url", Route::GetCurrentUrl),
(Post, "/session/{sessionId}/back", Route::GoBack),
(Post, "/session/{sessionId}/forward", Route::GoForward),
(Post, "/session/{sessionId}/refresh", Route::Refresh),
(Get, "/session/{sessionId}/title", Route::GetTitle),
(Get, "/session/{sessionId}/source", Route::GetPageSource),
(Get, "/session/{sessionId}/window", Route::GetWindowHandle),
(Method::POST, "/session", Route::NewSession),
(Method::DELETE, "/session/{sessionId}", Route::DeleteSession),
(Method::POST, "/session/{sessionId}/url", Route::Get),
(Method::GET, "/session/{sessionId}/url", Route::GetCurrentUrl),
(Method::POST, "/session/{sessionId}/back", Route::GoBack),
(Method::POST, "/session/{sessionId}/forward", Route::GoForward),
(Method::POST, "/session/{sessionId}/refresh", Route::Refresh),
(Method::GET, "/session/{sessionId}/title", Route::GetTitle),
(Method::GET, "/session/{sessionId}/source", Route::GetPageSource),
(Method::GET, "/session/{sessionId}/window", Route::GetWindowHandle),
(
Get,
Method::GET,
"/session/{sessionId}/window/handles",
Route::GetWindowHandles,
),
(Delete, "/session/{sessionId}/window", Route::CloseWindow),
(Method::DELETE, "/session/{sessionId}/window", Route::CloseWindow),
(
Get,
Method::GET,
"/session/{sessionId}/window/size",
Route::GetWindowSize,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/size",
Route::SetWindowSize,
),
(
Get,
Method::GET,
"/session/{sessionId}/window/position",
Route::GetWindowPosition,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/position",
Route::SetWindowPosition,
),
(
Get,
Method::GET,
"/session/{sessionId}/window/rect",
Route::GetWindowRect,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/rect",
Route::SetWindowRect,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/minimize",
Route::MinimizeWindow,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/maximize",
Route::MaximizeWindow,
),
(
Post,
Method::POST,
"/session/{sessionId}/window/fullscreen",
Route::FullscreenWindow,
),
(Post, "/session/{sessionId}/window", Route::SwitchToWindow),
(Post, "/session/{sessionId}/frame", Route::SwitchToFrame),
(Method::POST, "/session/{sessionId}/window", Route::SwitchToWindow),
(Method::POST, "/session/{sessionId}/frame", Route::SwitchToFrame),
(
Post,
Method::POST,
"/session/{sessionId}/frame/parent",
Route::SwitchToParentFrame,
),
(Post, "/session/{sessionId}/element", Route::FindElement),
(Post, "/session/{sessionId}/elements", Route::FindElements),
(Method::POST, "/session/{sessionId}/element", Route::FindElement),
(Method::POST, "/session/{sessionId}/elements", Route::FindElements),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/element",
Route::FindElementElement,
),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/elements",
Route::FindElementElements,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/active",
Route::GetActiveElement,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/displayed",
Route::IsDisplayed,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/selected",
Route::IsSelected,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/attribute/{name}",
Route::GetElementAttribute,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/property/{name}",
Route::GetElementProperty,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/css/{propertyName}",
Route::GetCSSValue,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/text",
Route::GetElementText,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/name",
Route::GetElementTagName,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/rect",
Route::GetElementRect,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/enabled",
Route::IsEnabled,
),
(
Post,
Method::POST,
"/session/{sessionId}/execute/sync",
Route::ExecuteScript,
),
(
Post,
Method::POST,
"/session/{sessionId}/execute/async",
Route::ExecuteAsyncScript,
),
(Get, "/session/{sessionId}/cookie", Route::GetCookies),
(Method::GET, "/session/{sessionId}/cookie", Route::GetCookies),
(
Get,
Method::GET,
"/session/{sessionId}/cookie/{name}",
Route::GetNamedCookie,
),
(Post, "/session/{sessionId}/cookie", Route::AddCookie),
(Delete, "/session/{sessionId}/cookie", Route::DeleteCookies),
(Method::POST, "/session/{sessionId}/cookie", Route::AddCookie),
(Method::DELETE, "/session/{sessionId}/cookie", Route::DeleteCookies),
(
Delete,
Method::DELETE,
"/session/{sessionId}/cookie/{name}",
Route::DeleteCookie,
),
(Get, "/session/{sessionId}/timeouts", Route::GetTimeouts),
(Post, "/session/{sessionId}/timeouts", Route::SetTimeouts),
(Method::GET, "/session/{sessionId}/timeouts", Route::GetTimeouts),
(Method::POST, "/session/{sessionId}/timeouts", Route::SetTimeouts),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/click",
Route::ElementClick,
),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/tap",
Route::ElementTap,
),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/clear",
Route::ElementClear,
),
(
Post,
Method::POST,
"/session/{sessionId}/element/{elementId}/value",
Route::ElementSendKeys,
),
(
Post,
Method::POST,
"/session/{sessionId}/alert/dismiss",
Route::DismissAlert,
),
(
Post,
Method::POST,
"/session/{sessionId}/alert/accept",
Route::AcceptAlert,
),
(Get, "/session/{sessionId}/alert/text", Route::GetAlertText),
(Method::GET, "/session/{sessionId}/alert/text", Route::GetAlertText),
(
Post,
Method::POST,
"/session/{sessionId}/alert/text",
Route::SendAlertText,
),
(
Get,
Method::GET,
"/session/{sessionId}/screenshot",
Route::TakeScreenshot,
),
(
Get,
Method::GET,
"/session/{sessionId}/element/{elementId}/screenshot",
Route::TakeElementScreenshot,
),
(Post, "/session/{sessionId}/actions", Route::PerformActions),
(Method::POST, "/session/{sessionId}/actions", Route::PerformActions),
(
Delete,
Method::DELETE,
"/session/{sessionId}/actions",
Route::ReleaseActions,
),
(Get, "/status", Route::Status),
(Method::GET, "/status", Route::Status),
];
}
@ -394,7 +393,7 @@ impl<U: WebDriverExtensionRoute> WebDriverHttpApi<U> {
matcher.match_type.clone(),
&captures.unwrap(),
body,
method == Post,
method == Method::POST,
);
} else {
error = ErrorStatus::UnknownMethod;

View file

@ -4,6 +4,8 @@ extern crate base64;
extern crate cookie;
#[macro_use]
extern crate log;
extern crate futures;
extern crate http;
extern crate hyper;
extern crate regex;
extern crate serde;
@ -11,6 +13,7 @@ extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate time;
extern crate tokio;
extern crate unicode_segmentation;
extern crate url;

View file

@ -1,19 +1,18 @@
use serde_json;
use std::io::Read;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::net::{SocketAddr, TcpListener as StdTcpListener};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use hyper::header::{CacheControl, CacheDirective, ContentType};
use hyper::method::Method;
use hyper::mime::{Attr, Mime, SubLevel, TopLevel, Value};
use hyper::server::{Handler, Listening, Request, Response, Server};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::Result;
use futures::{future, Future, Stream};
use hyper::{self, Body, Method, Request, Response, StatusCode};
use hyper::service::Service;
use hyper::server::conn::Http;
use http;
use tokio::runtime::current_thread::Runtime;
use tokio::reactor::Handle;
use tokio::net::TcpListener;
use command::{WebDriverCommand, WebDriverMessage};
use error::{ErrorStatus, WebDriverError, WebDriverResult};
@ -93,7 +92,7 @@ impl<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> Dispatcher<T, U> {
};
}
Ok(DispatchMessage::Quit) => break,
Err(_) => panic!("Error receiving message in handler"),
Err(e) => panic!("Error receiving message in handler: {:?}", e),
}
}
}
@ -152,96 +151,101 @@ impl<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> Dispatcher<T, U> {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
struct HttpHandler<U: WebDriverExtensionRoute> {
chan: Mutex<Sender<DispatchMessage<U>>>,
api: Mutex<WebDriverHttpApi<U>>,
chan: Arc<Mutex<Sender<DispatchMessage<U>>>>,
api: Arc<Mutex<WebDriverHttpApi<U>>>,
}
impl<U: WebDriverExtensionRoute> HttpHandler<U> {
fn new(api: WebDriverHttpApi<U>, chan: Sender<DispatchMessage<U>>) -> HttpHandler<U> {
impl <U: WebDriverExtensionRoute> HttpHandler<U> {
fn new(api: Arc<Mutex<WebDriverHttpApi<U>>>, chan: Sender<DispatchMessage<U>>) -> HttpHandler<U> {
HttpHandler {
chan: Mutex::new(chan),
api: Mutex::new(api),
chan: Arc::new(Mutex::new(chan)),
api: api,
}
}
}
impl<U: WebDriverExtensionRoute> Handler for HttpHandler<U> {
fn handle(&self, req: Request, res: Response) {
let mut req = req;
let mut res = res;
impl<U: WebDriverExtensionRoute + 'static> Service for HttpHandler<U> {
type ReqBody = Body;
type ResBody = Body;
let mut body = String::new();
if let Method::Post = req.method {
req.read_to_string(&mut body).unwrap();
}
type Error = hyper::Error;
type Future = Box<future::Future<Item=Response<Self::ResBody>, Error=hyper::Error> + Send>;
debug!("-> {} {} {}", req.method, req.uri, body);
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let uri = req.uri().clone();
let method = req.method().clone();
let api = self.api.clone();
let chan = self.chan.clone();
match req.uri {
AbsolutePath(path) => {
let msg_result = {
// The fact that this locks for basically the whole request doesn't
// matter as long as we are only handling one request at a time.
match self.api.lock() {
Ok(ref api) => api.decode_request(req.method, &path[..], &body[..]),
Err(_) => return,
}
};
let (status, resp_body) = match msg_result {
Ok(message) => {
let (send_res, recv_res) = channel();
match self.chan.lock() {
Ok(ref c) => {
let res =
c.send(DispatchMessage::HandleWebDriver(message, send_res));
match res {
Ok(x) => x,
Err(_) => {
error!("Something terrible happened");
return;
}
}
}
Err(_) => {
error!("Something terrible happened");
return;
}
}
match recv_res.recv() {
Ok(data) => match data {
Ok(response) => {
(StatusCode::Ok, serde_json::to_string(&response).unwrap())
}
Err(err) => {
(err.http_status(), serde_json::to_string(&err).unwrap())
}
},
Err(e) => panic!("Error reading response: {:?}", e),
}
}
Err(err) => (err.http_status(), serde_json::to_string(&err).unwrap()),
};
Box::new(req.into_body().concat2().and_then(move |body| {
let body = String::from_utf8(body.to_vec()).unwrap();
debug!("-> {} {} {}", method, uri, body);
debug!("<- {} {}", status, resp_body);
{
let resp_status = res.status_mut();
*resp_status = status;
let msg_result = {
// The fact that this locks for basically the whole request doesn't
// matter as long as we are only handling one request at a time.
match api.lock() {
Ok(ref api) => api.decode_request(method, &uri.path(), &body[..]),
Err(_) => panic!("Something terrible happened"),
}
res.headers_mut().set(ContentType(Mime(
TopLevel::Application,
SubLevel::Json,
vec![(Attr::Charset, Value::Utf8)],
)));
res.headers_mut()
.set(CacheControl(vec![CacheDirective::NoCache]));
};
res.send(&resp_body.as_bytes()).unwrap();
}
_ => {}
}
let (status, resp_body) = match msg_result {
Ok(message) => {
let (send_res, recv_res) = channel();
match chan.lock() {
Ok(ref c) => {
let res =
c.send(DispatchMessage::HandleWebDriver(message, send_res));
match res {
Ok(x) => x,
Err(_) => {
panic!("Something terrible happened");
}
}
}
Err(e) => panic!("Error reading response: {:?}", e),
}
match recv_res.recv() {
Ok(data) => match data {
Ok(response) => {
(StatusCode::OK, serde_json::to_string(&response).unwrap())
}
Err(err) => {
(err.http_status(), serde_json::to_string(&err).unwrap())
}
},
Err(e) => panic!("Error reading response: {:?}", e),
}
}
Err(err) => (err.http_status(), serde_json::to_string(&err).unwrap()),
};
debug!("<- {} {}", status, resp_body);
let response = Response::builder()
.status(status)
.header(http::header::CONTENT_TYPE, "application/json; charset=utf8")
.header(http::header::CACHE_CONTROL, "no-cache")
.body(resp_body.into())
.unwrap();
Ok(response)
}))
}
}
pub struct Listener {
_guard: Option<thread::JoinHandle<()>>,
pub socket: SocketAddr,
}
impl Drop for Listener {
fn drop(&mut self) {
let _ = self._guard.take().map(|j| j.join());
}
}
@ -249,17 +253,35 @@ pub fn start<T, U>(
address: SocketAddr,
handler: T,
extension_routes: &[(Method, &str, U)],
) -> Result<Listening>
) -> ::std::io::Result<Listener>
where
T: 'static + WebDriverHandler<U>,
U: 'static + WebDriverExtensionRoute,
{
let listener = StdTcpListener::bind(address)?;
let addr = listener.local_addr()?;
let (msg_send, msg_recv) = channel();
let api = WebDriverHttpApi::new(extension_routes);
let http_handler = HttpHandler::new(api, msg_send);
let mut server = Server::http(address)?;
server.keep_alive(Some(Duration::from_secs(90)));
let api = Arc::new(Mutex::new(WebDriverHttpApi::new(extension_routes)));
let builder = thread::Builder::new().name("webdriver server".to_string());
let handle = builder.spawn(move || {
let mut rt = Runtime::new().unwrap();
let listener = TcpListener::from_std(listener, &Handle::default()).unwrap();
let http_handler = HttpHandler::new(api, msg_send.clone());
let http = Http::new();
let handle = rt.handle();
let fut = listener.incoming()
.for_each(move |socket| {
let fut = http.serve_connection(socket, http_handler.clone()).map_err(|_| ());
handle.spawn(fut).unwrap();
Ok(())
});
rt.block_on(fut).unwrap();
})?;
let builder = thread::Builder::new().name("webdriver dispatcher".to_string());
builder.spawn(move || {
@ -267,5 +289,5 @@ where
dispatcher.run(msg_recv);
})?;
server.handle(http_handler)
Ok(Listener { _guard: Some(handle), socket: addr })
}