зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
69b3a54514
Коммит
778dcf4d40
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче