// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. goog.provide('webdriver.http.CorsClient'); goog.require('goog.Promise'); goog.require('webdriver.http.Client'); goog.require('webdriver.http.Response'); /** * Communicates with a WebDriver server, which may be on a different domain, * using the cross-origin resource sharing * (CORS) extension to WebDriver's JSON wire protocol. * *

Each command from the standard JSON protocol will be encoded in a * JSON object with the following form: * {method:string, path:string, data:!Object} * *

The encoded command is then sent as a POST request to the server's /xdrpc * endpoint. The server will decode the command, re-route it to the appropriate * handler, and then return the command's response as a standard JSON response * object. The JSON responses will always be returned with a 200 * response from the server; clients must rely on the response's "status" field * to determine whether the command succeeded. * *

This client cannot be used with the standard wire protocol due to * limitations in the various browser implementations of the CORS specification: *

* * @param {string} url URL for the WebDriver server to send commands to. * @constructor * @implements {webdriver.http.Client} * @see CORS Spec * @see * JSON wire protocol */ webdriver.http.CorsClient = function(url) { if (!webdriver.http.CorsClient.isAvailable()) { throw Error('The current environment does not support cross-origin ' + 'resource sharing'); } /** @private {string} */ this.url_ = url + webdriver.http.CorsClient.XDRPC_ENDPOINT; }; /** * Resource URL to send commands to on the server. * @type {string} * @const */ webdriver.http.CorsClient.XDRPC_ENDPOINT = '/xdrpc'; /** * Tests whether the current environment supports cross-origin resource sharing. * @return {boolean} Whether cross-origin resource sharing is supported. * @see http://www.w3.org/TR/cors/ */ webdriver.http.CorsClient.isAvailable = function() { return typeof XDomainRequest !== 'undefined' || (typeof XMLHttpRequest !== 'undefined' && goog.isBoolean(new XMLHttpRequest().withCredentials)); }; /** @override */ webdriver.http.CorsClient.prototype.send = function(request) { var url = this.url_; return new goog.Promise(function(fulfill, reject) { var xhr = new (typeof XDomainRequest !== 'undefined' ? XDomainRequest : XMLHttpRequest); xhr.open('POST', url, true); xhr.onload = function() { fulfill(webdriver.http.Response.fromXmlHttpRequest( /** @type {!XMLHttpRequest} */ (xhr))); }; xhr.onerror = function() { reject(Error([ 'Unable to send request: POST ', url, '\nPerhaps the server did not respond to the preflight request ', 'with valid access control headers?' ].join(''))); }; // Define event handlers for all events on the XDomainRequest. Apparently, // if we don't do this, IE9+10 will silently abort our request. Yay IE. // Note, we're not using goog.nullFunction, because it tends to get // optimized away by the compiler, which leaves us where we were before. xhr.onprogress = xhr.ontimeout = function() {}; xhr.send(JSON.stringify({ 'method': request.method, 'path': request.path, 'data': request.data })); }); };