# http/index.js

    1// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Defines a the {@code webdriver.http.Client} for use with17 * NodeJS.18 */1920var http = require('http'),21    url = require('url');2223var base = require('../_base'),24    HttpResponse = base.require('webdriver.http.Response');252627/**28 * A {@link webdriver.http.Client} implementation using Node's built-in http29 * module.30 * @param {string} serverUrl URL for the WebDriver server to send commands to.31 * @constructor32 * @implements {webdriver.http.Client}33 */34var HttpClient = function(serverUrl) {35  var parsedUrl = url.parse(serverUrl);36  if (!parsedUrl.hostname) {37    throw new Error('Invalid server URL: ' + serverUrl);38  }3940  /**41   * Base options for each request.42   * @private {!Object}43   */44  this.options_ = {45    host: parsedUrl.hostname,46    path: parsedUrl.pathname,47    port: parsedUrl.port48  };49};505152/** @override */53HttpClient.prototype.send = function(httpRequest, callback) {54  var data;55  httpRequest.headers['Content-Length'] = 0;56  if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {57    data = JSON.stringify(httpRequest.data);58    httpRequest.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');59    httpRequest.headers['Content-Type'] = 'application/json;charset=UTF-8';60  }6162  var path = this.options_.path;63  if (path[path.length - 1] === '/' && httpRequest.path[0] === '/') {64    path += httpRequest.path.substring(1);65  } else {66    path += httpRequest.path;67  }6869  sendRequest({70    method: httpRequest.method,71    host: this.options_.host,72    port: this.options_.port,73    path: path,74    headers: httpRequest.headers75  }, callback, data);76};777879/**80 * Sends a single HTTP request.81 * @param {!Object} options The request options.82 * @param {function(Error, !webdriver.http.Response=)} callback The function to83 *     invoke with the server's response.84 * @param {string=} opt_data The data to send with the request.85 */86var sendRequest = function(options, callback, opt_data) {87  var request = http.request(options, function(response) {88    if (response.statusCode == 302 || response.statusCode == 303) {89      try {90        var location = url.parse(response.headers['location']);91      } catch (ex) {92        callback(Error(93            'Failed to parse "Location" header for server redirect: ' +94            ex.message + '\nResponse was: \n' +95            new HttpResponse(response.statusCode, response.headers, '')));96        return;97      }9899      if (!location.hostname) {100        location.hostname = options.host;101        location.port = options.port;102      }103104      request.abort();105      sendRequest({106        method: 'GET',107        host: location.hostname,108        path: location.pathname + (location.search || ''),109        port: location.port,110        headers: {111          'Accept': 'application/json; charset=utf-8'112        }113      }, callback);114      return;115    }116117    var body = [];118    response.on('data', body.push.bind(body));119    response.on('end', function() {120      var resp = new HttpResponse(response.statusCode,121          response.headers, body.join('').replace(/\0/g, ''));122      callback(null, resp);123    });124  });125126  request.on('error', function(e) {127    if (e.code === 'ECONNRESET') {128      setTimeout(function() {129        sendRequest(options, callback, opt_data);130      }, 15);131    } else {132      var message = e.message;133      if (e.code) {134        message = e.code + ' ' + message;135      }136      callback(new Error(message));137    }138  });139140  if (opt_data) {141    request.write(opt_data);142  }143144  request.end();145};146147148// PUBLIC API149150/** @type {webdriver.http.Executor.} */151exports.Executor = base.require('webdriver.http.Executor');152153/** @type {webdriver.http.Request.} */154exports.Request = base.require('webdriver.http.Request');155156/** @type {webdriver.http.Response.} */157exports.Response = base.require('webdriver.http.Response');158159exports.HttpClient = HttpClient;