# 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      var location = url.parse(response.headers['location']);9091      if (!location.hostname) {92        location.hostname = options.host;93        location.port = options.port;94      }9596      request.abort();97      sendRequest({98        method: 'GET',99        host: location.hostname,100        path: location.pathname + (location.search || ''),101        port: location.port,102        headers: {103          'Accept': 'application/json; charset=utf-8'104        }105      }, callback);106      return;107    }108109    var body = [];110    response.on('data', body.push.bind(body));111    response.on('end', function() {112      var resp = new HttpResponse(response.statusCode,113          response.headers, body.join('').replace(/\0/g, ''));114      callback(null, resp);115    });116  });117118  request.on('error', function(e) {119    if (e.code === 'ECONNRESET') {120      setTimeout(function() {121        sendRequest(options, callback, opt_data);122      }, 15);123    } else {124      var message = e.message;125      if (e.code) {126        message = e.code + ' ' + message;127      }128      callback(new Error(message));129    }130  });131132  if (opt_data) {133    request.write(opt_data);134  }135136  request.end();137};138139140// PUBLIC API141142/** @type {webdriver.http.Executor.} */143exports.Executor = base.require('webdriver.http.Executor');144145/** @type {webdriver.http.Request.} */146exports.Request = base.require('webdriver.http.Request');147148/** @type {webdriver.http.Response.} */149exports.Response = base.require('webdriver.http.Response');150151exports.HttpClient = HttpClient;