Added "extends" key to the jshint config files, so there is less repetition.

Mocha now runs from grunt, just run "grunt"

Copied es-php's README.md, will modify later

More logging around sending a request, including stack traces for debug messages

Connections now manage their own state, and emit a "status changed" event which
the connection pool listens for

Fixed the custom errors

Stream loggers will dump their buffered output to stderr when the process exits
so that log messages will be sort of saved, File logger overrides this and
writes to the file syncronously

Added _.handler(), _.scheduled(), and _.makeBoundMethods() to the utils
This commit is contained in:
Spencer Alger
2013-10-21 10:09:12 -07:00
parent 8cc87637e2
commit b063dfdca7
24 changed files with 371 additions and 159 deletions

View File

@ -1,6 +1,5 @@
{
"node": true,
"white": true,
"bitwise": false,
@ -9,7 +8,7 @@
"eqeqeq": true,
"forin": true,
"immed": true,
"expr": false,
"expr": true,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
@ -24,5 +23,6 @@
"laxcomma": true,
"validthis": true,
"sub": true,
"maxlen": 140
}
"maxlen": 140,
"-W084": true
}

View File

@ -3,6 +3,8 @@
module.exports = function (grunt) {
var argv = require('optimist').argv;
var pre = [
'src/pre.js',
'src/shared.js',
@ -34,15 +36,17 @@ module.exports = function (grunt) {
dest: 'dist/elasticsearch-node.js'
}
},
simplemocha: {
mochaTest: {
unit: [
'test/unit/*.test.js'
'test/unit/**/*.test.js'
],
integration: [
'test/integration/*.test.js'
'yaml-suite': [
'test/integration/yaml-suite/index.js'
],
options: {
reporter: 'spec'
colors: true,
require: 'should',
reporter: 'dot'
}
},
jshint: {
@ -50,18 +54,11 @@ module.exports = function (grunt) {
src: [
'Gruntfile.js',
'src/**/*.js',
],
options: {
jshintrc: 'src/.jshintrc'
}
},
tests: {
src: [
'test/**/*.js'
],
options: {
jshintrc: 'test/.jshintrc'
}
]
},
options: {
jshintrc: true
}
},
yuidoc: {
@ -84,13 +81,11 @@ module.exports = function (grunt) {
});
// load plugins
grunt.loadNpmTasks('grunt-simple-mocha');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-yuidoc');
// Default task.
grunt.registerTask('default', ['jshint', 'simple-mocha']);
grunt.registerTask('test', ['simplemocha:integration']);
};
grunt.registerTask('default', ['jshint', 'mochaTest']);
};

162
README.md
View File

@ -1,4 +1,162 @@
elasticsearch-js
================
=================
Official *low-level* client for Elasticsearch.
Elasticsearch javascript client library
This project's goal it to give the JavaScript community a solif foundation for all Elasticsearch-related code. It features a complete API, provides a module for use in Node.js as well as several different build for use in the browser. We have tried to be opinion-free and very plugable.
To maintain consistency across all the low-level clients (Ruby, Python, etc), clients accept all of their parameters via a single object, along with a single callback.
Features
--------
- One-to-one mapping with REST API and other language clients
- Configurable, automatic discovery of cluster nodes
- Persistent, Keep-Alive connections (within the lifetime of the script)
- Load balancing (with pluggable selection strategy) across all availible nodes. Defaults to round-robin
- Pluggable connection pools to offer different connection strategies
- Generalized, pluggable architecture - most components can be replaced with your own custom class if specialized behavior is required
Documentation
--------------
[Full documentation can be found here.](http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/index.html) Docs are stored within the repo under /docs/, so if you see a typo or problem, please submit a PR to fix it!
Installation via Composer
-------------------------
The recommended method to install _Elasticsearch-PHP_ is through [Composer](http://getcomposer.org).
1. Add ``elasticsearch/elasticsearch`` as a dependency in your project's ``composer.json`` file:
```json
{
"require": {
"elasticsearch/elasticsearch": "~0.4"
}
}
```
2. Download and install Composer:
curl -s http://getcomposer.org/installer | php
3. Install your dependencies:
php composer.phar install
4. Require Composer's autoloader
Composer also prepares an autoload file that's capable of autoloading all of the classes in any of the libraries that it downloads. To use it, just add the following line to your code's bootstrap process:
```php
<?php
require 'vendor/autoload.php';
$client = new Elasticsearch\Client();
```
You can find out more on how to install Composer, configure autoloading, and other best-practices for defining dependencies at [getcomposer.org](http://getcomposer.org).
Index a document
-----
In elasticsearch-php, almost everything is configured by associative arrays. The REST endpoint, document and optional parameters - everything is an associative array.
To index a document, we simply specify a `body` that contains the document that we wish to index. Each field in the document is represented by a different key/value pair in the associative array.
The index, type and ID are also specified in the parameters assoc. array:
```php
$params = array();
$params['body'] = array('testField' => 'abc');
$params['index'] = 'my_index';
$params['type'] = 'my_type';
$params['id'] = 'my_id';
$ret = $client->index($params);
```
Get a document
-----
Let's get the document that we just indexed:
```php
$getParams = array();
$getParams['index'] = 'my_index';
$getParams['type'] = 'my_type';
$getParams['id'] = 'my_id';
$retDoc = $client->get($getParams);
```
Search for a document
-----
Searching is a hallmark of elasticsearch (no surprise there!), so let's perform a basic search. We are going to use the Match query as a demonstration:
```php
$searchParams['index'] = 'my_index';
$searchParams['type'] = 'my_type';
$searchParams['body']['query']['match']['testField'] = 'abc';
$queryResponse = $client->search($searchParams);
echo $queryResponse['hits']['hits'][0]['_id']; // Outputs 'abc'
```
Delete a document
-----
Alright, let's go ahead and delete the document that we added previously:
```php
$deleteParams = array();
$deleteParams['index'] = 'my_index';
$deleteParams['type'] = 'my_type';
$deleteParams['id'] = 'my_id';
$retDelete = $client->delete($deleteParams);
```
Delete an index
-----
Due to the dynamic nature of elasticsearch, the first document we added automatically built an index with some default settings. Let's delete that index because we want to specify our own settings later:
```php
$deleteParams['index'] = 'my_index';
$client->indices()->delete($deleteParams);
```
Create an index
-----
Ok, now that we are starting fresh, let's add a new index with some custom settings:
```php
$indexParams['index'] = 'my_index';
$indexParams['body']['settings']['number_of_shards'] = 2;
$indexParams['body']['settings']['number_of_replicas'] = 0;
$client->indices()->create($indexParams);
```
Wrap up
=======
That was just a crash-course overview of the client and it's syntax. If you are familiar with elasticsearch, you'll notice that the methods are named just like REST endpoints.
You'll also notice that the client is configured in a manner that facilitates easy discovery via the IDE. All core actions are available under the $client object (indexing, searching, getting, etc). Index and cluster management are located under the $client->indices() and $client->cluster() objects, respectively.
Check out the rest of the [Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/index.html) to see how the entire client works.
License
-------
Copyright 2013 Elasticsearch
Licensed 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.

View File

@ -16,12 +16,11 @@
"mkdirp": "~0.3.5",
"yuidoc-bootstrap-theme": "git://github.com/spenceralger/yuidoc-bootstrap-theme.git#master",
"grunt-contrib-yuidoc": "~0.5.0",
"grunt-simple-mocha": "~0.4.0",
"mocha": "~1.13.0",
"mocha-as-promised": "~1.4.0",
"moment": "~2.2.1",
"should": "~2.0.1",
"sinon": "~1.7.3"
"sinon": "~1.7.3",
"grunt-mocha-test": "~0.7.0"
},
"license": "Apache License",
"dependencies": {

View File

@ -20,8 +20,6 @@ var argv = require('optimist')
.argv;
// Error.stackTraceLimit = Infinity;
// console.log(argv);
// process.exit();
var count = parseInt(argv._[0] || 14000, 10),

View File

@ -1,28 +0,0 @@
{
"node": true,
"white": true,
"bitwise": false,
"curly": true,
"eqnull": true,
"eqeqeq": true,
"forin": true,
"immed": true,
"expr": false,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"quotmark": "single",
"plusplus": false,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"validthis": true,
"sub": true,
"maxlen": 140
}

4
src/api/.jshintrc Normal file
View File

@ -0,0 +1,4 @@
{
"extends": "../../.jshintrc",
"maxlen": 0
}

View File

@ -222,7 +222,7 @@ Log.prototype.info = function (/* ...msg */) {
*/
Log.prototype.debug = function (/* ...msg */) {
if (EventEmitter.listenerCount(this, 'debug')) {
return this.emit('debug', Log.join(arguments));
return this.emit('debug', Log.join(arguments) + _.getStackTrace(Log.prototype.debug));
}
};

View File

@ -63,12 +63,15 @@ Transport.prototype.request = function (params, cb) {
function sendRequestWithConnection(err, _connection) {
if (err) {
log.error(err);
cb(err);
} else if (_connection) {
connection = _connection;
log.info('Selected', _connection.status, 'Connection, making request');
connection.request(params, checkRespForFailure);
} else {
cb(new errors.ConnectionFault('No active connections.'));
log.warning('No living connections');
cb(new errors.ConnectionFault('No living connections.'));
}
}
@ -81,9 +84,10 @@ Transport.prototype.request = function (params, cb) {
if (err && remainingRetries) {
remainingRetries--;
log.info('Retrying request after connection error');
log.info('connection error, retrying');
connectionPool.select(sendRequestWithConnection);
} else {
log.info('Request complete');
cb(err, reqParams, body, status);
}
}

View File

@ -46,7 +46,9 @@ var defaultConfig = {
sniffOnStart: false,
sniffAfterRequests: null,
sniffOnConnectionFail: false,
maxRetries: 3
maxRetries: 3,
timeout: 10000,
deadTimeout: 60000
};
function ClientConfig(config) {

View File

@ -12,12 +12,11 @@ var _ = require('./utils'),
* @class ConnectionAbstract
* @constructor
*/
function ConnectionAbstract(config) {
function ConnectionAbstract(config, nodeInfo) {
EventEmitter.call(this);
this.config = config;
this.hostname = config.hostname || 'localhost';
this.port = config.port || 9200;
this.timeout = config.timeout || 10000;
this.hostname = nodeInfo.hostname || 'localhost';
this.port = nodeInfo.port || 9200;
_.makeBoundMethods(this);
}
@ -37,12 +36,17 @@ ConnectionAbstract.prototype.request = function () {
throw new Error('Connection#request must be overwritten by the Connector');
};
ConnectionAbstract.prototype.ping = function () {
ConnectionAbstract.prototype.ping = function (params, cb) {
if (typeof params === 'function') {
cb = params;
} else if (typeof cb !== 'function') {
throw new TypeError('Callback must be a function');
}
return this.request({
path: '/',
method: 'HEAD',
timeout: '100'
});
}, cb);
};
ConnectionAbstract.prototype.setStatus = function (status) {

View File

@ -30,10 +30,14 @@ ConnectionPool.prototype.select = function (cb) {
if (this.config.selector.length > 1) {
this.config.selector(this.connections.alive, cb);
} else {
cb(null, this.config.selector(this.connections.alive));
try {
cb(null, this.config.selector(this.connections.alive));
} catch (e) {
cb(e);
}
}
} else {
cb(new errors.ConnectionFault('No active connections'));
cb();
}
};
@ -46,10 +50,10 @@ ConnectionPool.prototype.empty = function () {
});
};
ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, connection) {
var origStatus = connection.status, from, to, index;
ConnectionPool.prototype.onStatusChanged = _.handler(function (status, oldStatus, connection) {
var from, to, index;
if (origStatus === status) {
if (oldStatus === status) {
return true;
} else {
this.config.log.info('connection to', _.formatUrl(connection), 'is', status);
@ -65,8 +69,8 @@ ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, conn
to = this.connections.dead;
break;
case 'closed':
from = this.connections[origStatus];
connection.removeListener('status changed', this.bound.setStatus);
from = this.connections[oldStatus];
connection.removeListener('status changed', this.bound.onStatusChanged);
break;
}
@ -88,7 +92,7 @@ ConnectionPool.prototype.setStatus = _.handler(function (status, oldStatus, conn
ConnectionPool.prototype.add = function (connection) {
if (!~this.connections.alive.indexOf(connection) && !~this.connections.dead.indexOf(connection)) {
connection.status = 'alive';
connection.on('status changed', this.bound.setStatus);
connection.on('status changed', this.bound.onStatusChanged);
this.connections.alive.push(connection);
}
};

View File

@ -18,10 +18,10 @@ var http = require('http'),
};
function HttpConnection(config) {
ConnectionAbstract.call(this, config);
function HttpConnection(config, nodeInfo) {
ConnectionAbstract.call(this, config, nodeInfo);
this.protocol = config.protocol || 'http:';
this.protocol = nodeInfo.protocol || 'http:';
if (this.protocol[this.protocol.length - 1] !== ':') {
this.protocol = this.protocol + ':';
}
@ -36,7 +36,7 @@ function HttpConnection(config) {
this.on('closed', this.bound.onClosed);
this.once('alive', this.bound.onAlive);
this.requestCount = 0;
}
_.inherits(HttpConnection, ConnectionAbstract);
@ -51,12 +51,15 @@ HttpConnection.prototype.onAlive = _.handler(function () {
});
HttpConnection.prototype.request = function (params, cb) {
var request,
response,
status = 0,
timeout = params.timeout || this.timeout,
timeoutId,
log = this.config.log;
var incoming;
var timeoutId;
var log = this.config.log;
var request;
var requestId = this.requestCount;
var response;
var responseStarted = false;
var status = 0;
var timeout = params.timeout || this.config.timeout;
var reqParams = _.defaults({
protocol: this.protocol,
@ -67,15 +70,20 @@ HttpConnection.prototype.request = function (params, cb) {
headers: _.defaults(params.headers || {}, defaultHeaders)
});
log.debug('starting request', requestId);
// general clean-up procedure to run after the request, can only run once
var cleanUp = function (err) {
cleanUp = _.noop;
clearTimeout(timeoutId);
if (request) {
request.removeAllListeners();
}
request && request.removeAllListeners();
incoming && incoming.removeAllListeners();
log.debug('calling back request', requestId, err ? 'with error "' + err.message + '"' : '');
_.nextTick(cb, err, reqParams, response, status);
// override so this doesn't get called again
cleanUp = _.noop;
};
// ensure that "get" isn't being used with a request body
@ -88,7 +96,8 @@ HttpConnection.prototype.request = function (params, cb) {
request = http.request(reqParams);
request.on('response', function (incoming) {
request.on('response', function (_incoming) {
incoming = _incoming;
status = incoming.statusCode;
incoming.setEncoding('utf8');
response = '';
@ -98,7 +107,6 @@ HttpConnection.prototype.request = function (params, cb) {
});
incoming.on('end', function requestComplete() {
incoming.removeAllListeners();
cleanUp();
});
});
@ -113,5 +121,9 @@ HttpConnection.prototype.request = function (params, cb) {
request.emit('error', new errors.RequestTimeout('Request timed out at ' + timeout + 'ms'));
}, timeout);
request.setNoDelay(true);
request.setSocketKeepAlive(true);
request.end(params.body);
this.requestCount++;
};

View File

@ -1,11 +1,11 @@
var _ = require('./utils'),
errors = module.exports;
function ErrorAbstract(msg) {
Error.call(this, msg);
Error.captureStackTrace(this, this.constructor);
function ErrorAbstract(msg, constructor) {
this.message = msg;
Error.call(this, this.message);
Error.captureStackTrace(this, constructor);
}
_.inherits(ErrorAbstract, Error);
@ -14,8 +14,7 @@ _.inherits(ErrorAbstract, Error);
* @param {String} [msg] - An error message that will probably end up in a log.
*/
errors.ConnectionFault = function ConnectionFault(msg) {
return new Error(msg || 'Connection Failure');
ErrorAbstract.call(this, msg || 'Connection Failure');
ErrorAbstract.call(this, msg || 'Connection Failure', errors.ConnectionFault);
};
_.inherits(errors.ConnectionFault, ErrorAbstract);
@ -24,8 +23,7 @@ _.inherits(errors.ConnectionFault, ErrorAbstract);
* @param {String} [msg] - An error message that will probably end up in a log.
*/
errors.Generic = function Generic(msg) {
return new Error(msg || 'Generic Error');
ErrorAbstract.call(this, msg || 'Generic Error');
ErrorAbstract.call(this, msg || 'Generic Error', errors.Generic);
};
_.inherits(errors.Generic, ErrorAbstract);
@ -34,8 +32,7 @@ _.inherits(errors.Generic, ErrorAbstract);
* @param {String} [msg] - An error message that will probably end up in a log.
*/
errors.RequestTimeout = function RequestTimeout(msg) {
return new Error(msg || 'RequestTimeout');
ErrorAbstract.call(this, msg || 'Request Timeout');
ErrorAbstract.call(this, msg || 'Request Timeout', errors.RequestTimeout);
};
_.inherits(errors.RequestTimeout, ErrorAbstract);
@ -44,8 +41,7 @@ _.inherits(errors.RequestTimeout, ErrorAbstract);
* @param {String} [msg] - An error message that will probably end up in a log.
*/
errors.Serialization = function RequestTimeout(msg) {
return new Error(msg || 'ParseError');
ErrorAbstract.call(this, msg || 'Unable to parse response body');
ErrorAbstract.call(this, msg || 'Unable to parse response body', errors.RequestTimeout);
};
_.inherits(errors.RequestTimeout, ErrorAbstract);
@ -105,8 +101,7 @@ _.each(statusCodes, function (name, status) {
var className = _.studlyCase(name);
function StatusCodeError(msg) {
return new Error(msg || name);
ErrorAbstract.call(this, msg || name);
ErrorAbstract.call(this, msg || name, errors.StatusCodeError);
}
_.inherits(StatusCodeError, ErrorAbstract);

View File

@ -13,8 +13,6 @@ function LoggerAbstract(config, bridge) {
_.makeBoundMethods(this);
console.log(this.bound);
// when the bridge closes, remove our event listeners
this.bridge.on('closing', this.bound.cleanUpListeners);

View File

@ -16,6 +16,8 @@ var StreamLogger = require('./stream'),
fs = require('fs');
function File(config, bridge) {
this.path = config.path;
config.stream = fs.createWriteStream(config.path, {
flags: 'a',
encoding: 'utf8'
@ -24,3 +26,15 @@ function File(config, bridge) {
File.callSuper(this, arguments);
}
_.inherits(File, StreamLogger);
File.prototype.onProcessExit = _.handler(function () {
// flush the write buffer to disk
var writeBuffer = this.stream._writableState.buffer;
var out = '';
if (writeBuffer) {
writeBuffer.forEach(function (buffered) {
out += buffered.chunk.toString();
});
fs.appendFileSync(this.path, out);
}
});

View File

@ -51,9 +51,9 @@ Stdio.prototype.write = function (to, label, colorize, message) {
* @param {Error} e - The Error object to log
* @return {undefined}
*/
Stdio.prototype.onError = function (e) {
Stdio.prototype.onError = _.handler(function (e) {
this.write(process.stderr, e.name === 'Error' ? 'ERROR' : e.name, clc.red.bold, e.stack);
};
});
/**
* Handler for the bridges "warning" event
@ -63,9 +63,9 @@ Stdio.prototype.onError = function (e) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Stdio.prototype.onWarning = function (msg) {
Stdio.prototype.onWarning = _.handler(function (msg) {
this.write(process.stderr, 'WARNING', clc.yellow.bold, msg);
};
});
/**
* Handler for the bridges "info" event
@ -75,9 +75,9 @@ Stdio.prototype.onWarning = function (msg) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Stdio.prototype.onInfo = function (msg) {
Stdio.prototype.onInfo = _.handler(function (msg) {
this.write(process.stdout, 'INFO', clc.cyan.bold, msg);
};
});
/**
* Handler for the bridges "debug" event
@ -87,9 +87,9 @@ Stdio.prototype.onInfo = function (msg) {
* @param {String} msg - The message to be logged
* @return {undefined}
*/
Stdio.prototype.onDebug = function (msg) {
Stdio.prototype.onDebug = _.handler(function (msg) {
this.write(process.stdout, 'DEBUG', clc.magentaBright.bold, msg);
};
});
/**
* Handler for the bridges "trace" event
@ -98,7 +98,7 @@ Stdio.prototype.onDebug = function (msg) {
* @private
* @return {undefined}
*/
Stdio.prototype.onTrace = function (method, url, body, responseBody, responseStatus) {
Stdio.prototype.onTrace = _.handler(function (method, url, body, responseBody, responseStatus) {
var message = 'curl "' + url.replace(/"/g, '\\"') + '" -X' + method.toUpperCase();
if (body) {
message += ' -d "' + body.replace(/"/g, '\\"') + '"';
@ -116,4 +116,4 @@ Stdio.prototype.onTrace = function (method, url, body, responseBody, responseSta
message += '\n' + responseBody;
this.write(process.stdout, 'TRACE', clc.cyanBright.bold, message);
};
});

View File

@ -19,15 +19,29 @@ var LoggerAbstract = require('../Logger'),
function Stream(config, bridge) {
Stream.callSuper(this, arguments);
_.makeBoundMethods(this);
if (config.stream instanceof nodeStreams.Writable) {
this.stream = config.stream;
} else {
throw new TypeError('Invalid stream, use an instance of stream.Writeable');
}
process.on('exit', this.bound.onProcessExit);
}
_.inherits(Stream, LoggerAbstract);
// flush the write buffer to stderr synchronously
Stream.prototype.onProcessExit = _.handler(function () {
var writeBuffer = this.stream._writableState.buffer;
if (writeBuffer && writeBuffer.length) {
console.error('Log stream did not get to finish writing. Flushing to stderr');
writeBuffer.forEach(function (buffered) {
console.error(buffered.chunk.toString());
});
}
});
Stream.prototype.write = function (label, message) {
this.stream.write(this.format(label, message), 'utf8');
};

View File

@ -450,7 +450,6 @@ utils.makeBoundMethods = function (obj, methods) {
for (var prop in obj) {
// dearest maintainer, we want to look through the prototype
if (typeof obj[prop] === 'function' && obj[prop]._provideBound === true) {
console.log('binding', prop);
obj.bound[prop] = utils.bind(obj[prop], obj);
}
}
@ -463,4 +462,10 @@ utils.makeBoundMethods = function (obj, methods) {
utils.noop = function () {};
utils.getStackTrace = function (callee) {
var e = {};
Error.captureStackTrace(e, callee || utils.getStackTrace);
return '\n' + e.stack.split('\n').slice(1).join('\n');
};
module.exports = utils;

View File

@ -1,28 +1,6 @@
{
"node": true,
"extends": "../.jshintrc",
"bitwise": false,
"curly": true,
"eqnull": true,
"eqeqeq": true,
"forin": true,
"immed": true,
"expr": false,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"quotmark": "single",
"plusplus": false,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"validthis": true,
"sub": true,
"maxlen": 140,
"-W030": true,
"-W068": true,

View File

@ -0,0 +1,61 @@
var EsServer = require('../../mocks/es_server');
var HttpConnection = require('../../../src/lib/connections/http');
var errors = require('../../../src/lib/errors');
describe('overall timeout for the network connections', function () {
var server;
var connection;
before(function (done) {
server = new EsServer();
server.routes.get['/timeout'] = function (req, res) {
// wait for 10 seconds before responding, or the value in the timeout param
var timeout = parseInt(req.parsedUrl.query.timeout, 10);
if (isNaN(timeout)) {
timeout = 10000;
}
res.writeHead(200);
res.on('close', function() {
clearInterval(dataInterval);
clearTimeout(finTimeout);
});
var dataInterval = setInterval(function () {
res.write('.');
}, 100);
var finTimeout = setTimeout(function () {
clearInterval(dataInterval);
res.end('good bye');
}, timeout);
};
server.on('online', function (port) {
connection = new HttpConnection({
hostname: 'localhost',
port: port
});
done();
});
});
it('should bail quickly', function (done) {
this.timeout(1000);
connection.request({
path: '/timeout?timeout=1000',
timeout: 100
}, function (err, resp, status) {
err.should.be.an.instanceof(errors.RequestTimeout);
done();
});
});
});

View File

@ -13,10 +13,9 @@ var argv = require('optimist')
.default('dataPath', '/tmp/yaml-test-runner')
.argv;
if (argv.hostname || argv.port) {
console.log('working with ES instance at ' + argv.hostname + ':' + argv.port);
}
// if (argv.hostname || argv.port) {
// console.log('working with ES instance at ' + argv.hostname + ':' + argv.port);
// }
// Where do the tests live?
var TEST_DIR = path.resolve(__dirname, '../../../es_api_spec/test/');
@ -31,7 +30,7 @@ var esServer = null;
var client = null;
// location that the logger will write to
var logFile = path.resolve(__dirname, '../integration-test.log');
var logFile = path.resolve(__dirname, './log');
// empty all of the indices in ES please
function clearIndices (done) {
@ -67,6 +66,7 @@ before(function (done) {
});
before(function () {
Error.stackTraceLimit = Infinity;
// create the client
client = new es.Client({
hosts: [
@ -77,7 +77,7 @@ before(function () {
],
log: {
type: 'file',
level: ['error', 'debug', 'trace'],
level: ['error', 'warning', 'trace'],
path: logFile
}
});
@ -391,7 +391,7 @@ ActionRunner.prototype = {
rangeMatchesCurrentVersion(args.version, _.bind(function (match) {
if (match) {
this.skipping = true;
console.log('skipping the rest of these actions' + (args.reason ? ' because ' + args.reason : ''));
// console.log('skipping the rest of these actions' + (args.reason ? ' because ' + args.reason : ''));
} else {
this.skipping = false;
}

View File

View File

@ -30,12 +30,7 @@ var pingResp = JSON.stringify({
'tagline': 'You Know, for Search'
});
function EsServer(config) {
this.config = _.defaults(config || {}, {
port: 0,
data: '/'
});
function EsServer() {
var self = this;
var server = http.createServer();
@ -77,7 +72,7 @@ function EsServer(config) {
};
process.nextTick(function () {
server.listen(self.config.port, function () {
server.listen(0, function () {
self.emit('online', server.address().port);
});
});