diff --git a/scripts/.jshintrc b/.jshintrc similarity index 88% rename from scripts/.jshintrc rename to .jshintrc index 27ba910bb..d6b86007b 100644 --- a/scripts/.jshintrc +++ b/.jshintrc @@ -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 -} \ No newline at end of file + "maxlen": 140, + "-W084": true +} diff --git a/Gruntfile.js b/Gruntfile.js index 129fcecf9..8e5f6f8b4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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']); - -}; \ No newline at end of file + grunt.registerTask('default', ['jshint', 'mochaTest']); +}; diff --git a/README.md b/README.md index 01c17d5fd..f66c1a894 100644 --- a/README.md +++ b/README.md @@ -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 + '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. diff --git a/package.json b/package.json index a9f9ac47a..b2958a665 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/scripts/generate/logs/index.js b/scripts/generate/logs/index.js index 7f90773eb..a8a7544b6 100644 --- a/scripts/generate/logs/index.js +++ b/scripts/generate/logs/index.js @@ -20,8 +20,6 @@ var argv = require('optimist') .argv; // Error.stackTraceLimit = Infinity; - -// console.log(argv); // process.exit(); var count = parseInt(argv._[0] || 14000, 10), diff --git a/src/.jshintrc b/src/.jshintrc deleted file mode 100644 index 27ba910bb..000000000 --- a/src/.jshintrc +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/src/api/.jshintrc b/src/api/.jshintrc new file mode 100644 index 000000000..b79722485 --- /dev/null +++ b/src/api/.jshintrc @@ -0,0 +1,4 @@ +{ + "extends": "../../.jshintrc", + "maxlen": 0 +} diff --git a/src/lib/Log.js b/src/lib/Log.js index abfba47c7..207b4f692 100644 --- a/src/lib/Log.js +++ b/src/lib/Log.js @@ -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)); } }; diff --git a/src/lib/Transport.js b/src/lib/Transport.js index acd79ba8b..d149b99ba 100644 --- a/src/lib/Transport.js +++ b/src/lib/Transport.js @@ -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); } } diff --git a/src/lib/client_config.js b/src/lib/client_config.js index 344c3db4b..e8f5b3fe5 100644 --- a/src/lib/client_config.js +++ b/src/lib/client_config.js @@ -46,7 +46,9 @@ var defaultConfig = { sniffOnStart: false, sniffAfterRequests: null, sniffOnConnectionFail: false, - maxRetries: 3 + maxRetries: 3, + timeout: 10000, + deadTimeout: 60000 }; function ClientConfig(config) { diff --git a/src/lib/connection.js b/src/lib/connection.js index c1a5c5c89..95335a1d7 100644 --- a/src/lib/connection.js +++ b/src/lib/connection.js @@ -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) { diff --git a/src/lib/connection_pool.js b/src/lib/connection_pool.js index 2bac02d6f..1a94283a4 100644 --- a/src/lib/connection_pool.js +++ b/src/lib/connection_pool.js @@ -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); } }; diff --git a/src/lib/connections/http.js b/src/lib/connections/http.js index c5c8ac31d..20e91d765 100644 --- a/src/lib/connections/http.js +++ b/src/lib/connections/http.js @@ -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++; }; diff --git a/src/lib/errors.js b/src/lib/errors.js index efd2bfa4b..e316b0131 100644 --- a/src/lib/errors.js +++ b/src/lib/errors.js @@ -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); diff --git a/src/lib/logger.js b/src/lib/logger.js index 4da71df0a..2fbec6e8f 100644 --- a/src/lib/logger.js +++ b/src/lib/logger.js @@ -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); diff --git a/src/lib/loggers/File.js b/src/lib/loggers/File.js index 78a15faf2..6601a3fdc 100644 --- a/src/lib/loggers/File.js +++ b/src/lib/loggers/File.js @@ -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); + } +}); diff --git a/src/lib/loggers/StdIo.js b/src/lib/loggers/StdIo.js index 1692bd16d..eccb15fe3 100644 --- a/src/lib/loggers/StdIo.js +++ b/src/lib/loggers/StdIo.js @@ -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); -}; +}); diff --git a/src/lib/loggers/Stream.js b/src/lib/loggers/Stream.js index 224c073ab..149cfb8d9 100644 --- a/src/lib/loggers/Stream.js +++ b/src/lib/loggers/Stream.js @@ -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'); }; diff --git a/src/lib/utils.js b/src/lib/utils.js index 486a83ad4..8e41609fd 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -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; diff --git a/test/.jshintrc b/test/.jshintrc index 424c016e3..d1fcba292 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -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, diff --git a/test/integration/network-failures/timeout.js b/test/integration/network-failures/timeout.js index e69de29bb..238fcba6e 100644 --- a/test/integration/network-failures/timeout.js +++ b/test/integration/network-failures/timeout.js @@ -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(); + }); + }); + +}); diff --git a/test/integration/yaml-suite/index.js b/test/integration/yaml-suite/index.js index 2b79df319..787dd5a03 100644 --- a/test/integration/yaml-suite/index.js +++ b/test/integration/yaml-suite/index.js @@ -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; } diff --git a/test/integration/yaml-suite/log b/test/integration/yaml-suite/log new file mode 100644 index 000000000..e69de29bb diff --git a/test/mocks/es_server.js b/test/mocks/es_server.js index 096168e0e..e63c4fc3a 100644 --- a/test/mocks/es_server.js +++ b/test/mocks/es_server.js @@ -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); }); });