Skip to content

Commit

Permalink
Alternate server support for feature request flatiron#75
Browse files Browse the repository at this point in the history
  • Loading branch information
mikepb committed Aug 28, 2011
1 parent 1cfcb18 commit 57d78e3
Showing 1 changed file with 101 additions and 95 deletions.
196 changes: 101 additions & 95 deletions lib/cradle.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,66 +18,52 @@ cradle.Response = require('./cradle/response').Response;
cradle.Cache = require('./cradle/cache').Cache;

cradle.options = {
// Global options, unused for alternate servers
cache: true,
servers: [],
// Options overridden by alternate server options
auth: null,
host: '127.0.0.1',
port: 5984,
auth: null,
cache: true,
raw: false,
timeout: 0,
secure: false,
headers: {}
headers: {},
retry: 1
};

cradle.setup = function (settings) {
this.host = settings.host;
this.auth = settings.auth;
this.port = parseInt(settings.port);
cradle.merge(this.options, settings);
var protocolPattern = /^(https?):\/\//;

cradle.setup = function (options) {
this.options = this._parseOptions(options);
return this;
};

var protocolPattern = /^(https?):\/\//;
cradle._parseOptions = function(options, localOptions) {
if (!options) return cradle.merge({}, this.options);

cradle.Connection = function Connection(/* variable args */) {
var args = Array.prototype.slice.call(arguments),
host, port, remote, auth, options = {};

args.forEach(function (a) {
if (typeof(a) === 'number' || (typeof(a) === 'string' && /^\d{2,5}$/.test(a))) {
port = parseInt(a);
} else if (typeof(a) === 'object') {
options = a;
host = host || options.host;
port = port || options.port;
auth = options.auth;
} else {
host = a;
}
});

options.host = host || cradle.options.host;
options.port = port || cradle.options.port;
options.auth = auth || cradle.options.auth;
if (options.port) options.port = parseInt(options.port);
if (options.retry) options.retry = parseInt(options.retry);

this.options = cradle.merge({}, cradle.options, options);
this.options.secure = this.options.secure || this.options.ssl;

if (protocolPattern.test(this.options.host)) {
this.options.protocol = this.options.host.match(protocolPattern)[1];
this.options.host = this.options.host.replace(protocolPattern, '');
if (protocolPattern.test(options.host)) {
options.protocol = options.host.match(protocolPattern)[1];
options.host = options.host.replace(protocolPattern, '');
options.secure = options.protocol === 'https';
}

if (this.options.protocol === 'https') this.options.secure = true;
options.socket = this.options.secure ? https : http;

if (this.options.auth && this.options.auth.user) { // Deprecation warning
console.log('Warning: "user" & "pass" parameters ignored. Use "username" & "password"');
}
if (this.options.ssl) { // Deprecation warning
console.log('Warning: "ssl" option is deprecated. Use "secure" instead.');
if (!localOptions && options.servers) {
for (var i = 0, serverOptions; serverOptions = options.servers[i]; i++) {
serverOptions = this._parseOptions(serverOptions, true);
options.servers[i] = cradle.merge({}, this.options, serverOptions);
}
}

this.socket = (this.options.secure) ? https : http;
return cradle.merge({}, this.options, options);
};

cradle.Connection = function Connection(options) {
this.options = cradle._parseOptions(options);
};

//
Expand All @@ -95,54 +81,73 @@ cradle.Connection = function Connection(/* variable args */) {
// `retry > 0`: retry `retry` times
// `retry < 0`: always retry
//
cradle.Connection.prototype.rawRequest = function (method, path, options, data, headers) {
var promise = new(events.EventEmitter), request, retry, that = this;
cradle.Connection.prototype.rawRequest = function (method, path, query, data, headers) {
var promise = new(events.EventEmitter);

// Default to trying once
retry = retry || 1;

// HTTP Headers
headers = headers || {};

// Set HTTP Basic Auth
if (this.options.auth) {
headers['Authorization'] = "Basic " + new Buffer(this.options.auth.username + ':' + this.options.auth.password).toString('base64');
}

// Set client-wide headers
for (var h in this.options.headers) {
headers[h] = this.options.headers[h];
// Parse path
if (path) {
path = path.replace(/https?:\/\//, '').replace(/\/{2,}/g, '/');
if (path[0] !== '/') path = '/' + path;
} else {
path = '/';
}

path = (path || '/').replace(/https?:\/\//, '').replace(/\/{2,}/g, '/');
if (path.charAt(0) !== '/') { path = '/' + path }

if (options) {
for (var k in options) {
if (typeof(options[k]) === 'boolean') {
options[k] = String(options[k]);
// Add query
if (query) {
for (var k in query) {
if (typeof query[k] === 'boolean') {
query[k] = String(query[k]);
}
}
path += '?' + querystring.stringify(options);
path += '?';
path += querystring.stringify(query);
}

// Keep this connection alive for future requests
headers = headers || {};
headers['Connection'] = 'keep-alive';

// Handle data
if (data && data.on) { headers['Transfer-Encoding'] = 'chunked' }

this._rawRequest(promise, {
host: this.options.host,
port: this.options.port,
method: method.toUpperCase(),
path: path,
headers: headers
}, data, retry);
// Service this request
this._rawRequest(promise, method.toUpperCase(), path, data, headers,
this.options, this.options.retry || 0, -1);

return promise;
}

cradle.Connection.prototype._rawRequest = function (promise, options, data, retry) {
var request = this.socket.request(options), that = this;
cradle.Connection.prototype._rawRequest = function (promise, method, path, data,
headers, options, retry, server) {
var reqHeaders = {}, that = this;

// Set HTTP Basic Auth
if (options.auth) {
reqHeaders['Authorization'] = "Basic " + new Buffer(options.auth.username + ':' + options.auth.password).toString('base64');
}

// Merge headers
cradle.merge(reqHeaders, options.headers, headers);

var request = options.socket.request({
host: options.host,
port: options.port,
path: path,
method: method,
headers: reqHeaders
});

if (data) {
if (data.on) {
data.on('data', function (chunk) { request.write(chunk) });
data.on('end', function () { request.end() });
} else {
request.write(data, 'utf8');
request.end();
}
} else {
request.end();
}

request.on('response', function (res) {
promise.emit('response', res);
Expand All @@ -163,32 +168,33 @@ cradle.Connection.prototype._rawRequest = function (promise, options, data, retr
}

// Attempt to retry for supported errors
if (retry-- && (
// Ignore broken pipe
if (// Unlimited tries if negative, stop at zero otherwise
retry-- && (
// Retry for broken pipe
err.code === 'EPIPE' ||
// Ignore connection reset
err.code === 'ECONNRESET'
))
// Retry for connection reset
err.code === 'ECONNRESET'))
{
return that._rawRequest(promise, options, data, retry);
return that._rawRequest(promise, method, path, data, headers,
options, retry, server);
}

// Try alternate servers
if (// This server is dead
err.code === 'ECONNREFUSED' &&
// Are there alternate servers?
that.options.servers &&
// Is there a next alternate server to use?
// If so, use that server's options
(options = that.options.servers[++server]))
{
return that._rawRequest(promise, method, path, data, headers,
options, options.retry || 0, server);
}

promise.emit('error', err);
promise.emit('end');
});


if (data) {
if (data.on) {
data.on('data', function (chunk) { request.write(chunk) });
data.on('end', function () { request.end() });
} else {
request.write(data, 'utf8');
request.end();
}
} else {
request.end();
}
}

//
Expand Down

0 comments on commit 57d78e3

Please sign in to comment.