Path: blob/trunk/javascript/webdriver/http/corsclient.js
2868 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617goog.provide('webdriver.http.CorsClient');1819goog.require('goog.Promise');20goog.require('webdriver.http.Client');21goog.require('webdriver.http.Response');22232425/**26* Communicates with a WebDriver server, which may be on a different domain,27* using the <a href="http://www.w3.org/TR/cors/">cross-origin resource sharing28* </a> (CORS) extension to WebDriver's JSON wire protocol.29*30* <p>Each command from the standard JSON protocol will be encoded in a31* JSON object with the following form:32* {method:string, path:string, data:!Object}33*34* <p>The encoded command is then sent as a POST request to the server's /xdrpc35* endpoint. The server will decode the command, re-route it to the appropriate36* handler, and then return the command's response as a standard JSON response37* object. The JSON responses will <em>always</em> be returned with a 20038* response from the server; clients must rely on the response's "status" field39* to determine whether the command succeeded.40*41* <p>This client cannot be used with the standard wire protocol due to42* limitations in the various browser implementations of the CORS specification:43* <ul>44* <li>IE's <a href="http://goo.gl/6l3kA">XDomainRequest</a> object is only45* capable of generating the types of requests that may be generated through46* a standard <a href="http://goo.gl/vgzAU">HTML form</a> - it can not send47* DELETE requests, as is required in the wire protocol.48* <li>WebKit's implementation of CORS does not follow the spec and forbids49* redirects: https://bugs.webkit.org/show_bug.cgi?id=5760050* This limitation appears to be intentional and is documented in WebKit's51* Layout tests:52* //LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html53* <li>If the server does not return a 2xx response, IE54* implementation will fire the XDomainRequest/XMLHttpRequest object's55* onerror handler, but without the corresponding response text returned by56* the server. This renders IE incapable of handling command57* failures in the standard JSON protocol.58* </ul>59*60* @param {string} url URL for the WebDriver server to send commands to.61* @constructor62* @implements {webdriver.http.Client}63* @see <a href="http://www.w3.org/TR/cors/">CORS Spec</a>64* @see <a href="https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol">65* JSON wire protocol</a>66*/67webdriver.http.CorsClient = function(url) {68if (!webdriver.http.CorsClient.isAvailable()) {69throw Error('The current environment does not support cross-origin ' +70'resource sharing');71}7273/** @private {string} */74this.url_ = url + webdriver.http.CorsClient.XDRPC_ENDPOINT;75};767778/**79* Resource URL to send commands to on the server.80* @type {string}81* @const82*/83webdriver.http.CorsClient.XDRPC_ENDPOINT = '/xdrpc';848586/**87* Tests whether the current environment supports cross-origin resource sharing.88* @return {boolean} Whether cross-origin resource sharing is supported.89* @see http://www.w3.org/TR/cors/90*/91webdriver.http.CorsClient.isAvailable = function() {92return typeof XDomainRequest !== 'undefined' ||93(typeof XMLHttpRequest !== 'undefined' &&94goog.isBoolean(new XMLHttpRequest().withCredentials));95};969798/** @override */99webdriver.http.CorsClient.prototype.send = function(request) {100var url = this.url_;101return new goog.Promise(function(fulfill, reject) {102var xhr = new (typeof XDomainRequest !== 'undefined' ?103XDomainRequest : XMLHttpRequest);104xhr.open('POST', url, true);105106xhr.onload = function() {107fulfill(webdriver.http.Response.fromXmlHttpRequest(108/** @type {!XMLHttpRequest} */ (xhr)));109};110111xhr.onerror = function() {112reject(Error([113'Unable to send request: POST ', url,114'\nPerhaps the server did not respond to the preflight request ',115'with valid access control headers?'116].join('')));117};118119// Define event handlers for all events on the XDomainRequest. Apparently,120// if we don't do this, IE9+10 will silently abort our request. Yay IE.121// Note, we're not using goog.nullFunction, because it tends to get122// optimized away by the compiler, which leaves us where we were before.123xhr.onprogress = xhr.ontimeout = function() {};124125xhr.send(JSON.stringify({126'method': request.method,127'path': request.path,128'data': request.data129}));130});131};132133134