geckodriver: Add some basic error handling

Source-Repo: https://github.com/mozilla/geckodriver
Source-Revision: f40a1e3fca56aad095dab6613fab0f93159aa9e4

--HG--
extra : rebase_source : 73e242bb60c55dd66c6ae4ce432b1bc21873df52
This commit is contained in:
James Graham 2014-10-19 23:44:58 +01:00
Родитель 69b3a54514
Коммит 778dcf4d40
8 изменённых файлов: 225 добавлений и 77 удалений

1
testing/geckodriver/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/target

Просмотреть файл

@ -7,8 +7,10 @@ use regex::Captures;
use hyper::method;
use hyper::method::Method;
use common::{WebDriverResult, WebDriverError, Status, UnknownError};
use messagebuilder::{MessageBuilder, MatchType, MatchNewSession, MatchGet, MatchGetCurrentUrl};
#[deriving(PartialEq)]
pub enum WebDriverCommand {
GetMarionetteId,
@ -50,24 +52,27 @@ impl WebDriverMessage {
}
}
pub fn from_http(match_type: MatchType, params: &Captures, body: &str) -> WebDriverMessage {
pub fn from_http(match_type: MatchType, params: &Captures, body: &str) -> WebDriverResult<WebDriverMessage> {
let session_id = WebDriverMessage::get_session_id(params);
let command = match match_type {
MatchNewSession => {
NewSession
},
MatchNewSession => NewSession,
MatchGet => {
let parameters: GetParameters = json::decode(body).unwrap();
Get(parameters)
let parameters_result: Result<GetParameters, json::DecoderError> = json::decode(body);
match parameters_result {
Ok(parameters) => Get(parameters),
Err(_) => {
return Err(WebDriverError::new(None,
UnknownError,
"Failed to decode request body"));
}
}
},
MatchGetCurrentUrl => {
GetCurrentUrl
}
MatchGetCurrentUrl => GetCurrentUrl
};
WebDriverMessage {
Ok(WebDriverMessage {
session_id: session_id,
command: command
}
})
}
fn get_session_id(params: &Captures) -> Option<String> {

Просмотреть файл

@ -0,0 +1,44 @@
use std::collections::TreeMap;
use serialize::json;
use serialize::json::ToJson;
#[deriving(PartialEq)]
pub enum Status {
Success,
Timeout,
UnknownError,
UnknownCommand,
}
pub type WebDriverResult<T> = Result<T, WebDriverError>;
pub struct WebDriverError {
pub session_id: Option<String>,
pub status: Status,
pub message: String
}
impl WebDriverError {
pub fn new(session_id: Option<String>, status: Status, message: &str) -> WebDriverError {
WebDriverError {
session_id: session_id,
status: status,
message: message.to_string().clone()
}
}
pub fn http_status(&self) -> int {
match self.status {
UnknownCommand => 404,
_ => 200
}
}
}
impl ToJson for WebDriverError {
fn to_json(&self) -> json::Json {
let mut data = TreeMap::new();
data.insert("error".to_string(), self.message.to_json());
json::Object(data)
}
}

Просмотреть файл

@ -4,35 +4,57 @@ use regex::Regex;
use serialize::json;
use hyper;
use hyper::status::StatusCode;
use hyper::header::common::ContentLength;
use hyper::method::Post;
use hyper::server::{Server, Incoming};
use hyper::uri::AbsolutePath;
use common::WebDriverResult;
use response::WebDriverResponse;
use messagebuilder::{get_builder};
use marionette::{MarionetteSession, MarionetteConnection};
use marionette::MarionetteConnection;
fn handle(mut incoming: Incoming) {
let mut marionette = MarionetteConnection::new();
marionette.connect();
if marionette.connect().is_err() {
fail!("Failed to connect to marionette. Start marionette client before proxy");
};
let builder = get_builder();
for (mut req, mut resp) in incoming {
println!("{}", req.uri);
let body = req.read_to_string().unwrap();
println!("{}", req.uri);;
let body = match req.method {
Post => req.read_to_string().unwrap(),
_ => "".to_string()
};
match req.uri {
AbsolutePath(path) => {
let message = builder.from_http(req.method, path[], body[]);
//Should return a Result instead
if message.is_some() {
let response = marionette.send_message(&message.unwrap());
if response.is_some() {
let body = response.unwrap().to_json().to_string();
resp.headers_mut().set(ContentLength(body.len()));
let mut stream = resp.start();
stream.write_str(body.as_slice());
stream.unwrap().end();
let (status, resp_data) = match builder.from_http(req.method, path[], body[]) {
Ok(message) => {
match marionette.send_message(&message) {
Ok(response) => {
if response.is_none() {
continue;
}
(200, response.unwrap())
}
Err(err) => (err.http_status(), WebDriverResponse::from_err(&err))
}
},
Err(err) => {
(err.http_status(), WebDriverResponse::from_err(&err))
}
};
let body = resp_data.to_json().to_string();
{
let mut status_code = resp.status_mut();
*status_code = FromPrimitive::from_int(status).unwrap();
}
resp.headers_mut().set(ContentLength(body.len()));
let mut stream = resp.start();
stream.write_str(body.as_slice());
stream.unwrap().end();
},
_ => {}
};

Просмотреть файл

@ -0,0 +1,19 @@
extern crate hyper;
extern crate serialize;
extern crate uuid;
extern crate regex;
use std::io::net::ip::Ipv4Addr;
use httpserver::start;
mod common;
mod command;
mod httpserver;
mod marionette;
mod messagebuilder;
mod response;
fn main() {
start(Ipv4Addr(127, 0, 0, 1), 1337);
}

Просмотреть файл

@ -1,12 +1,14 @@
use std::io::{IoResult, TcpStream};
use serialize::json;
use serialize::json::ToJson;
use command::{WebDriverMessage, GetMarionetteId, NewSession};
use response::WebDriverResponse;
use serialize::json;
use std::collections::TreeMap;
use std::io::{IoResult, TcpStream, IoError};
use uuid::Uuid;
use command::{WebDriverMessage, GetMarionetteId, NewSession};
use response::WebDriverResponse;
use common::{WebDriverResult, WebDriverError, UnknownError};
pub struct MarionetteSession {
pub session_id: String,
pub to: String,
@ -22,6 +24,10 @@ impl MarionetteSession {
}
}
pub fn id(&self) -> String {
self.session_id.clone()
}
pub fn update(&mut self, msg: &WebDriverMessage, from: &json::Json, session_id: &json::Json) {
match msg.command {
GetMarionetteId => {
@ -66,8 +72,7 @@ impl MarionetteSession {
}
pub fn msg_to_json(&self, msg: &WebDriverMessage) -> json::Json {
//needing a clone here seems unfortunate
let mut data = msg.to_json().as_object().expect("Message was not a map").clone();
let mut data = msg.to_json().as_object().unwrap().clone();
let session_id = self.id_to_marionette(msg);
if session_id.is_some() {
data.insert("sessionId".to_string(), session_id.unwrap());
@ -91,13 +96,14 @@ impl MarionetteConnection {
}
}
pub fn connect(&mut self) {
self.read_resp();
pub fn connect(&mut self) -> Result<(), IoError> {
try!(self.read_resp());
//Would get traits and application type here
self.send_message(&WebDriverMessage::new(GetMarionetteId, None));
Ok(())
}
fn encode_msg(&self, msg: &WebDriverMessage) -> String {
fn encode_msg(&self, msg: &WebDriverMessage) -> String {
let data = format!("{}", self.session.msg_to_json(msg));
let len = data.len().to_string();
let mut message = len;
@ -106,21 +112,34 @@ impl MarionetteConnection {
message
}
pub fn send_message(&mut self, msg: &WebDriverMessage) -> Option<WebDriverResponse> {
pub fn send_message(&mut self, msg: &WebDriverMessage) -> WebDriverResult<Option<WebDriverResponse>> {
let data = self.encode_msg(msg);
println!("{}", data);
//TODO: Error handling
self.stream.write_str(data.as_slice()).unwrap();
let resp = self.read_resp();
println!("{}", resp);
WebDriverResponse::from_json(&mut self.session, msg, resp.as_slice())
match self.stream.write_str(data.as_slice()) {
Ok(_) => {},
Err(_) => {
return Err(WebDriverError::new(Some(self.session.session_id.clone()),
UnknownError,
"Failed to write response to stream"))
}
}
match self.read_resp() {
Ok(resp) => {
println!("{}", resp);
Ok(WebDriverResponse::from_json(&mut self.session, msg, resp.as_slice()))
},
Err(_) => {
Err(WebDriverError::new(Some(self.session.id()),
UnknownError,
"Failed to decode response from marionette"))
}
}
}
fn read_resp(&mut self) -> String {
fn read_resp(&mut self) -> Result<String, IoError> {
let mut bytes = 0 as uint;
loop {
//TODO: better error handling here
let byte = self.stream.read_byte().unwrap() as char;
let byte = try!(self.stream.read_byte()) as char;
match byte {
'0'...'9' => {
bytes = bytes * 10;
@ -132,7 +151,8 @@ impl MarionetteConnection {
_ => {}
}
}
let data = self.stream.read_exact(bytes).unwrap();
String::from_utf8(data).unwrap()
let data = try!(self.stream.read_exact(bytes));
//Need to handle the error here
Ok(String::from_utf8(data).unwrap())
}
}

Просмотреть файл

@ -5,6 +5,7 @@ use serialize::json;
use hyper::method::{Method, Get, Post};
use command::{WebDriverMessage, WebDriverCommand};
use common::{WebDriverResult, WebDriverError, UnknownCommand};
#[deriving(Clone)]
pub enum MatchType {
@ -31,7 +32,7 @@ impl RequestMatcher {
}
pub fn get_match<'t>(&'t self, method: Method, path: &'t str) -> Option<Captures> {
println!("{}", path);
println!("{} {}", method, path);
if method == self.method {
self.path_regexp.captures(path)
} else {
@ -56,7 +57,7 @@ impl RequestMatcher {
//Remove the trailing /
rv.pop();
rv.push_str("$");
println!("{}", rv);
//This will fail at runtime if the regexp is invalid
Regex::new(rv[]).unwrap()
}
}
@ -72,18 +73,22 @@ impl MessageBuilder {
}
}
pub fn from_http(&self, method: Method, path: &str, body: &str) -> Option<WebDriverMessage> {
pub fn from_http(&self, method: Method, path: &str, body: &str) -> WebDriverResult<WebDriverMessage> {
println!("{} {}", method, path)
for &(ref match_method, ref matcher) in self.http_matchers.iter() {
println!("{} {}", match_method, matcher.path_regexp);
if method == *match_method {
let captures = matcher.get_match(method.clone(), path);
if captures.is_some() {
return Some(WebDriverMessage::from_http(matcher.match_type,
&captures.unwrap(),
body))
return WebDriverMessage::from_http(matcher.match_type,
&captures.unwrap(),
body)
}
}
}
None
Err(WebDriverError::new(None,
UnknownCommand,
format!("{} did not match a known command", path)[]))
}
pub fn add(&mut self, method: Method, path: &str, match_type: MatchType) {
@ -98,6 +103,7 @@ pub fn get_builder() -> MessageBuilder {
(Post, "/session/{sessionId}/url", MatchGet),
(Get, "/session/{sessionId}/url", MatchGetCurrentUrl)];
for &(ref method, ref url, ref match_type) in matchers.iter() {
println!("{} {}", method, url);
builder.add(method.clone(), *url, *match_type);
}
builder

Просмотреть файл

@ -5,29 +5,43 @@ use serialize::json::{decode, ToJson, Builder};
use command::{WebDriverMessage, GetMarionetteId, NewSession, Get, GetCurrentUrl};
use marionette::{MarionetteSession};
pub struct WebDriverResponse {
session_id: String,
status: Status,
value: TreeMap<String,String>
}
use common::{Status, Success, Timeout, UnknownError, UnknownCommand, WebDriverError};
#[deriving(PartialEq)]
enum Status {
Success,
Timeout,
UnknownError
pub struct WebDriverResponse {
session_id: Option<String>,
status: Status,
value: json::Json
}
impl WebDriverResponse {
pub fn new(session_id: Option<String>, status: Status, value: json::Json) -> WebDriverResponse {
WebDriverResponse {
session_id: session_id,
status: status,
value: value
}
}
pub fn from_json(session: &mut MarionetteSession,
message: &WebDriverMessage,
data: &str) -> Option<WebDriverResponse> {
println!("Decoding json data");
let decoded = json::from_str(data).unwrap();
println!("Decoded json data");
let decoded = match json::from_str(data) {
Ok(data) => data,
Err(msg) => {
let error = WebDriverError::new(Some(session.id()),
UnknownError,
"Failed to decode marionette data as json");
return Some(WebDriverResponse::from_err(&error));
}
};
let json_data = match decoded {
json::Object(x) => x,
_ => fail!("Expected an object")
_ => {
let error = WebDriverError::new(Some(session.id()),
UnknownError,
"Expected a json object");
return Some(WebDriverResponse::from_err(&error));
}
};
let status = if json_data.contains_key(&"error".to_string()) {
UnknownError
@ -42,28 +56,44 @@ impl WebDriverResponse {
json_data.find(&"from".to_string()).unwrap(),
json_data.find(&"value".to_string()).unwrap());
};
Some(WebDriverResponse {status: status,
session_id: session.session_id.clone(),
value: TreeMap::new()})
Some(WebDriverResponse::new(Some(session.session_id.clone()), status,
json::Null))
},
Get(_) => {
Some(WebDriverResponse {status: status,
session_id: session.session_id.clone(),
value: TreeMap::new()})
Some(WebDriverResponse::new(Some(session.session_id.clone()), status,
json::Null))
},
GetCurrentUrl => {
Some(WebDriverResponse {status: status,
session_id: session.session_id.clone(),
value: TreeMap::new()})
let value = match json_data.find(&"value".to_string()) {
Some(ref data) => {
data.clone()
},
None => {
let error = WebDriverError::new(Some(session.session_id.clone()),
UnknownError,
"Failed to find value field");
return Some(WebDriverResponse::from_err(&error));
}
};
Some(WebDriverResponse::new(Some(session.id()),
status,
value.clone()))
}
}
}
pub fn from_err(error_data: &WebDriverError) -> WebDriverResponse {
WebDriverResponse::new(error_data.session_id.clone(),
error_data.status,
error_data.to_json())
}
fn status_string(&self) -> String {
match self.status {
Success => "success".to_string(),
Timeout => "timeout".to_string(),
UnknownError => "unknown error".to_string()
UnknownError => "unknown error".to_string(),
UnknownCommand => "unknown command".to_string()
}
}
@ -71,7 +101,8 @@ impl WebDriverResponse {
let mut data = TreeMap::new();
data.insert("sessionId".to_string(), self.session_id.to_json());
data.insert("status".to_string(), self.status_string().to_json());
data.insert("capabilties".to_string(), self.value.to_json());
data.insert("value".to_string(), self.value.to_json());
json::Object(data)
}
}