blob: 4345235c9bdcce67103ff2ae60f3abc3ce33edaf [file] [log] [blame]
part of webdriver;
class CommandProcessor {
final Uri _uri;
String get _host => _uri.host;
int get _port => _uri.port;
String get path => _uri.path;
String get url => _uri.toString();
CommandProcessor([
String host = 'localhost',
int port = 4444,
String path = '/wd/hub']) :
_uri = new Uri(scheme: 'http', host: host, port: port, path: path) {
assert(!this._host.isEmpty);
}
void _failRequest(Completer completer, error, [stackTrace]) {
completer
.completeError(new WebDriverError(-1, error.toString()), stackTrace);
}
/**
* Execute a request to the WebDriver server. [httpMethod] should be
* one of 'GET', 'POST', or 'DELETE'. [command] is the text to append
* to the base URL path to get the full URL. [params] are the additional
* parameters. If a [List] or [Map] they will be posted as JSON parameters.
* If a number or string, "/params" is appended to the URL.
*/
Future _serverRequest(String httpMethod, String command, {params}) {
const successCodes = const [ HttpStatus.OK, HttpStatus.NO_CONTENT ];
var completer = new Completer();
try {
var path = command;
if (params != null) {
if (params is num || params is String) {
path = '$path/$params';
params = null;
} else if (httpMethod != 'POST') {
throw new Exception(
'The http method called for ${command} is ${httpMethod} but it '
'must be POST if you want to pass the JSON params '
'${JSON.encode(params)}');
}
}
var client = new HttpClient();
client.open(httpMethod, _host, _port, path).then((req) {
req.followRedirects = false;
req.headers.add(HttpHeaders.ACCEPT, "application/json");
req.headers.contentType = _CONTENT_TYPE_JSON;
if (params != null) {
var body = UTF8.encode(JSON.encode(params));
req.contentLength = body.length;
req.add(body);
} else {
req.contentLength = 0;
}
return req.close();
}).then((HttpClientResponse rsp) {
return rsp.transform(new Utf8Decoder())
.fold(new StringBuffer(), (buffer, data) => buffer..write(data))
.then((StringBuffer buffer) {
// For some reason we get a bunch of NULs on the end
// of the text and the json.parse blows up on these, so
// strip them.
// These NULs can be seen in the TCP packet, so it is not
// an issue with character encoding; it seems to be a bug
// in WebDriver stack.
var results = buffer.toString()
.replaceAll(new RegExp('\u{0}*\$'), '');
var status = 0;
var message = null;
var value = null;
// 4xx responses send plain text; others send JSON
if (HttpStatus.BAD_REQUEST <= rsp.statusCode
&& rsp.statusCode < HttpStatus.INTERNAL_SERVER_ERROR) {
if (rsp.statusCode == HttpStatus.NOT_FOUND) {
status = 9; // UnkownCommand
} else {
status = 13; // UnknownError
}
message = results;
} else if (!results.isEmpty) {
results = JSON.decode(results);
if (results.containsKey('status')) {
status = results['status'];
}
if (results.containsKey('value')) {
value = results['value'];
if (value is Map && value.containsKey('message')) {
message = results['message'];
}
}
}
if (status != 0) {
completer.completeError(new WebDriverError(status, message));
} else if (!successCodes.contains(rsp.statusCode)) {
completer.completeError(new WebDriverError(-1,
'Unexpected response ${rsp.statusCode}; $results'));
} else {
completer.complete(value);
}
});
}).catchError((error, s) => _failRequest(completer, error, s));
} catch (e, s) {
_failRequest(completer, e, s);
}
return completer.future;
}
Future get(String extraPath) => _serverRequest('GET', _command(extraPath));
Future post(String extraPath, [params]) =>
_serverRequest('POST', _command(extraPath), params: params);
Future delete(String extraPath) =>
_serverRequest('DELETE', _command(extraPath));
String _command(String extraPath) {
var command;
if (extraPath.startsWith('/')) {
command = '${path}$extraPath';
} else {
command = '${path}/$extraPath';
}
if (command.endsWith('/')) {
command = command.substring(0, command.length - 1);
}
return command;
}
}