webdriver: Merge pull request #30 from jgraham/new_session_capabilities
authorJames Graham <james@hoppipolla.co.uk>
Mon, 23 May 2016 14:07:57 +0100
changeset 428064 e9a2a2cedf426b2c8efdf9ebc851759b06db2e9e
parent 428063 3851a3cafa271a7aad5c838ac75a994558a818ed
child 428065 cee7d0686a28a04d182a809b4aa46d1ef233287d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
webdriver: Merge pull request #30 from jgraham/new_session_capabilities Add support for capabilites in New Session command Source-Repo: https://github.com/mozilla/webdriver-rust Source-Revision: a47ba3d93ab667259c839df4e5d11bef778455a4
testing/webdriver/src/command.rs
testing/webdriver/src/error.rs
testing/webdriver/src/server.rs
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -4,17 +4,17 @@ use std::collections::BTreeMap;
 
 use common::{Date, Nullable, WebElement, FrameId, LocatorStrategy};
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 use httpapi::{Route, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
 
 
 #[derive(PartialEq)]
 pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
-    NewSession,
+    NewSession(NewSessionParameters),
     DeleteSession,
     Get(GetParameters),
     GetCurrentUrl,
     GoBack,
     GoForward,
     Refresh,
     GetTitle,
     GetPageSource,
@@ -99,17 +99,20 @@ impl <U: WebDriverExtensionRoute> WebDri
                                                         "Body was not a json object")),
                 Err(_) => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                          format!("Failed to decode request body as json: {}", body)))
             }
         } else {
             Json::Null
         };
         let command = match match_type {
-            Route::NewSession => WebDriverCommand::NewSession,
+            Route::NewSession => {
+                let parameters: NewSessionParameters = try!(Parameters::from_json(&body_data));
+                WebDriverCommand::NewSession(parameters)
+            },
             Route::DeleteSession => WebDriverCommand::DeleteSession,
             Route::Get => {
                 let parameters: GetParameters = try!(Parameters::from_json(&body_data));
                 WebDriverCommand::Get(parameters)
             },
             Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
             Route::GoBack => WebDriverCommand::GoBack,
             Route::GoForward => WebDriverCommand::GoForward,
@@ -308,17 +311,17 @@ impl <U: WebDriverExtensionRoute> WebDri
     fn get_session_id(params: &Captures) -> Option<String> {
         params.name("sessionId").map(|x| x.to_string())
     }
 }
 
 impl <U:WebDriverExtensionRoute> ToJson for WebDriverMessage<U> {
     fn to_json(&self) -> Json {
         let parameters = match self.command {
-            WebDriverCommand::NewSession |
+            WebDriverCommand::NewSession(_) |
             WebDriverCommand::DeleteSession | WebDriverCommand::GetCurrentUrl |
             WebDriverCommand::GoBack | WebDriverCommand::GoForward | WebDriverCommand::Refresh |
             WebDriverCommand::GetTitle | WebDriverCommand::GetPageSource |
             WebDriverCommand::GetWindowHandle | WebDriverCommand::GetWindowHandles |
             WebDriverCommand::Close | WebDriverCommand::GetWindowSize | WebDriverCommand::MaximizeWindow |
             WebDriverCommand::SwitchToParentFrame | WebDriverCommand::GetActiveElement |
             WebDriverCommand::IsDisplayed(_) | WebDriverCommand::IsSelected(_) |
             WebDriverCommand::GetElementAttribute(_, _) | WebDriverCommand::GetCSSValue(_, _) |
@@ -357,31 +360,75 @@ impl <U:WebDriverExtensionRoute> ToJson 
     }
 }
 
 pub trait Parameters: Sized {
     fn from_json(body: &Json) -> WebDriverResult<Self>;
 }
 
 #[derive(PartialEq)]
+pub struct NewSessionParameters {
+    pub desired: BTreeMap<String, Json>,
+    pub required: BTreeMap<String, Json>,
+}
+
+impl Parameters for NewSessionParameters {
+    fn from_json(body: &Json) -> WebDriverResult<NewSessionParameters> {
+        let data = try_opt!(body.as_object(),
+                            ErrorStatus::UnknownError,
+                            "Message body was not an object");
+
+        let desired_capabilities =
+            if let Some(capabilities) = data.get("desiredCapabilities") {
+                try_opt!(capabilities.as_object(),
+                         ErrorStatus::InvalidArgument,
+                         "'desiredCapabilities' parameter is not an object").clone()
+            } else {
+                BTreeMap::new()
+            };
+
+        let required_capabilities =
+            if let Some(capabilities) = data.get("requiredCapabilities") {
+                try_opt!(capabilities.as_object(),
+                         ErrorStatus::InvalidArgument,
+                         "'requiredCapabilities' parameter is not an object").clone()
+            } else {
+                BTreeMap::new()
+            };
+
+        Ok(NewSessionParameters {
+            desired: desired_capabilities,
+            required: required_capabilities
+        })
+    }
+}
+
+impl NewSessionParameters {
+    pub fn get(&self, name: &str) -> Option<&Json> {
+        debug!("Getting {} from capabilities", name);
+        self.required.get(name).or_else(|| self.desired.get(name))
+    }
+}
+
+#[derive(PartialEq)]
 pub struct GetParameters {
     pub url: String
 }
 
 impl Parameters for GetParameters {
     fn from_json(body: &Json) -> WebDriverResult<GetParameters> {
         let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
                             "Message body was not an object");
         let url = try_opt!(
             try_opt!(data.get("url"),
                      ErrorStatus::InvalidArgument,
                      "Missing 'url' parameter").as_string(),
             ErrorStatus::InvalidArgument,
             "'url' not a string");
-        return Ok(GetParameters {
+        Ok(GetParameters {
             url: url.to_string()
         })
     }
 }
 
 impl ToJson for GetParameters {
     fn to_json(&self) -> Json {
         let mut data = BTreeMap::new();
--- a/testing/webdriver/src/error.rs
+++ b/testing/webdriver/src/error.rs
@@ -1,9 +1,10 @@
 use hyper::status::StatusCode;
+use rustc_serialize::base64::FromBase64Error;
 use rustc_serialize::json::{Json, ToJson, ParserError, DecoderError};
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::convert::From;
 use std::error::Error;
 use std::fmt;
 use std::io::Error as IoError;
 
@@ -159,25 +160,39 @@ impl Error for WebDriverError {
     fn cause(&self) -> Option<&Error> {
         None
     }
 }
 
 impl From<ParserError> for WebDriverError {
     fn from(err: ParserError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError,
-                            err.description().to_string())
+            err.description().to_string())
     }
 }
 
 impl From<IoError> for WebDriverError {
     fn from(err: IoError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError,
-                            err.description().to_string())
+            err.description().to_string())
     }
 }
 
 impl From<DecoderError> for WebDriverError {
     fn from(err: DecoderError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError,
-                            format!("Could not decode json string:\n{}", err.description()))
+                            err.description().to_string())
     }
 }
+
+impl From<FromBase64Error> for WebDriverError {
+    fn from(err: FromBase64Error) -> WebDriverError {
+        WebDriverError::new(ErrorStatus::UnknownError,
+                            err.description().to_string())
+    }
+}
+
+impl From<Box<Error>> for WebDriverError {
+    fn from(err: Box<Error>) -> WebDriverError {
+        WebDriverError::new(ErrorStatus::UnknownError,
+                            err.description().to_string())
+    }
+}
--- a/testing/webdriver/src/server.rs
+++ b/testing/webdriver/src/server.rs
@@ -1,16 +1,16 @@
 use std::marker::PhantomData;
-use std::io::{Write, Read};
+use std::io::Read;
 use std::sync::Mutex;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use std::net::SocketAddr;
 
-use hyper::header::{ContentLength, ContentType};
+use hyper::header::ContentType;
 use hyper::method::Method;
 use hyper::server::{Server, Handler, Request, Response};
 use hyper::uri::RequestUri::AbsolutePath;
 use hyper::status::StatusCode;
 
 use command::{WebDriverMessage, WebDriverCommand};
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 use httpapi::{WebDriverHttpApi, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
@@ -113,33 +113,33 @@ impl <T: WebDriverHandler<U>,
                     },
                     None => Ok(())
                 }
             },
             None => {
                 match self.session {
                     Some(_) => {
                         match msg.command {
-                            WebDriverCommand::NewSession => {
+                            WebDriverCommand::NewSession(_) => {
                                 Err(WebDriverError::new(
                                     ErrorStatus::UnsupportedOperation,
                                     "Session is already started"))
                             },
                             _ => {
                                 //This should be impossible
                                 error!("Got a message with no session id");
                                 Err(WebDriverError::new(
                                     ErrorStatus::UnknownError,
                                     "Got a command with no session?!"))
                             }
                         }
                     },
                     None => {
                         match msg.command {
-                            WebDriverCommand::NewSession => Ok(()),
+                            WebDriverCommand::NewSession(_) => Ok(()),
 
                             _ => Err(WebDriverError::new(
                                 ErrorStatus::InvalidSessionId,
                                 "Tried to run a command before creating a session"))
                         }
                     }
                 }
             }
@@ -216,36 +216,34 @@ impl <U: WebDriverExtensionRoute> Handle
                     }
                 };
                 debug!("Returning status {:?}", status);
                 debug!("Returning body {}", resp_body);
                 {
                     let resp_status = res.status_mut();
                     *resp_status = status;
                 }
-                res.headers_mut().set(ContentLength(resp_body.len() as u64));
                 res.headers_mut().set(ContentType::json());
-                let mut stream = res.start().unwrap();
-                stream.write_all(&resp_body.as_bytes()).unwrap();
-                stream.end().unwrap();
+                res.send(&resp_body.as_bytes()).unwrap();
             },
             _ => {}
         }
     }
 }
 
 pub fn start<T: 'static+WebDriverHandler<U>,
              U: 'static+WebDriverExtensionRoute>(address: SocketAddr,
                                                  handler: T,
                                                  extension_routes:Vec<(Method, &str, U)>) {
     let (msg_send, msg_recv) = channel();
 
     let api = WebDriverHttpApi::new(extension_routes);
     let http_handler = HttpHandler::new(api, msg_send);
-    let server = Server::http(address).unwrap();
+    let mut server = Server::http(address).unwrap();
+    server.keep_alive(None);
 
     let builder = thread::Builder::new().name("webdriver dispatcher".to_string());
     builder.spawn(move || {
         let mut dispatcher = Dispatcher::new(handler);
         dispatcher.run(msg_recv)
     }).unwrap();
     server.handle(http_handler).unwrap();
 }