diff options
| author | ruki <waruqi@gmail.com> | 2018-11-08 00:38:48 +0800 |
|---|---|---|
| committer | ruki <waruqi@gmail.com> | 2018-11-07 21:53:09 +0800 |
| commit | 26105034da4fcce7ac883c899d781f016559310d (patch) | |
| tree | c459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/algoliasearch/src/server/builds/node.js | |
| parent | 2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff) | |
| download | xmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip | |
switch to vuepress
Diffstat (limited to 'node_modules/algoliasearch/src/server/builds/node.js')
| -rw-r--r-- | node_modules/algoliasearch/src/server/builds/node.js | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/node_modules/algoliasearch/src/server/builds/node.js b/node_modules/algoliasearch/src/server/builds/node.js new file mode 100644 index 00000000..11b806ff --- /dev/null +++ b/node_modules/algoliasearch/src/server/builds/node.js @@ -0,0 +1,318 @@ +'use strict'; + +// This is the Node.JS entry point +module.exports = algoliasearch; + +var debug = require('debug')('algoliasearch:nodejs'); +var crypto = require('crypto'); +var zlib = require('zlib'); + +var inherits = require('inherits'); +var Promise = global.Promise || require('es6-promise').Promise; +var semver = require('semver'); +var isNotSupported = semver.satisfies(process.version, '<0.10'); +var isNode010 = semver.satisfies(process.version, '=0.10'); +var places = require('../../places.js'); + +var AlgoliaSearchServer = require('./AlgoliaSearchServer'); +var errors = require('../../errors'); + +// does not work on node <= 0.8 +if (isNotSupported) { + throw new errors.AlgoliaSearchError('Node.js version ' + process.version + ' is not supported'); +} + +if (process.env.NODE_ENV === 'debug') { + require('debug').enable('algoliasearch*'); +} + +debug('loaded the Node.js client'); + +function algoliasearch(applicationID, apiKey, opts) { + var cloneDeep = require('../../clone.js'); + var reduce = require('reduce'); + + if (!opts) { + opts = {}; + } + + var httpAgent = opts.httpAgent; + + opts = cloneDeep(reduce(opts, allButHttpAgent, {})); + + // as an httpAgent is an object with methods etc, we take a reference to + // it rather than cloning it like other values + function allButHttpAgent(filteredOpts, val, keyName) { + if (keyName !== 'httpAgent') { + filteredOpts[keyName] = val; + } + + return filteredOpts; + } + + opts.httpAgent = httpAgent; + + opts.timeouts = opts.timeouts || { + connect: 2 * 1000, + read: 5 * 1000, + write: 30 * 1000 + }; + + if (opts.protocol === undefined) { + opts.protocol = 'https:'; + } + + opts._ua = opts._ua || algoliasearch.ua; + opts._useCache = false; + + return new AlgoliaSearchNodeJS(applicationID, apiKey, opts); +} + +algoliasearch.version = require('../../version.js'); +algoliasearch.ua = 'Algolia for Node.js ' + algoliasearch.version; +algoliasearch.initPlaces = places(algoliasearch); + +function AlgoliaSearchNodeJS(applicationID, apiKey, opts) { + var getAgent = require('./get-agent'); + + // call AlgoliaSearchServer constructor + AlgoliaSearchServer.apply(this, arguments); + + this._Agent = opts.httpAgent || getAgent(opts.protocol); +} + +inherits(AlgoliaSearchNodeJS, AlgoliaSearchServer); + +AlgoliaSearchNodeJS.prototype._request = function request(rawUrl, opts) { + var http = require('http'); + var https = require('https'); + var url = require('url'); + + var client = this; + + return new Promise(function doReq(resolve, reject) { + opts.debug('url: %s, method: %s, timeouts: %j', rawUrl, opts.method, opts.timeouts); + + var body = opts.body; + + var parsedUrl = url.parse(rawUrl); + var requestOptions = { + hostname: parsedUrl.hostname, + port: parsedUrl.port, + method: opts.method, + path: parsedUrl.path, + agent: client._Agent + }; + + var timedOut = false; + var timeoutId; + var req; + + if (parsedUrl.protocol === 'https:') { + // we do not rely on any "smart" port computing by either node.js + // or a custom http agent, because: + // https://github.com/TooTallNate/node-https-proxy-agent/issues/7#issuecomment-119539690 + if (requestOptions.port === null) { + requestOptions.port = 443; + } + req = https.request(requestOptions); + } else { + // same reason to set the port as `https:` + if (requestOptions.port === null) { + requestOptions.port = 80; + } + req = http.request(requestOptions); + } + + req.setHeader('connection', 'keep-alive'); + req.setHeader('accept', 'application/json'); + + Object.keys(opts.headers).forEach(function setRequestHeader(headerName) { + req.setHeader(headerName, opts.headers[headerName]); + }); + + req.setHeader('accept-encoding', 'gzip,deflate'); + + // we do not use req.setTimeout because it's either an inactivity timeout + // or a global timeout given the nodejs version + timeoutId = setTimeout(timeout, opts.timeouts.connect); + + req.once('error', error); + req.once('response', response); + if (body) { + req.setHeader('content-type', 'application/json'); + req.setHeader('content-length', Buffer.byteLength(body, 'utf8')); + req.write(body); + } else if (req.method === 'DELETE') { + // Node.js was setting transfer-encoding: chunked on all DELETE requests + // which is not good since there's no body to be sent, resulting in nginx + // sending 400 on socket reuse (waiting for previous socket data) + // https://github.com/nodejs/node-v0.x-archive/issues/6164 + // https://github.com/nodejs/node-v0.x-archive/commit/aef0960 + req.setHeader('content-length', 0); + } + + req.end(); + + function response(res) { + clearTimeout(timeoutId); + timeoutId = setTimeout(onCompleteTimeout, opts.timeouts.complete); + var chunks = []; + var originalRes = res; + + // save headers and statusCode BEFORE treating the response as zlib, otherwise + // we lose them + var headers = res.headers; + var statusCode = res.statusCode; + + // Algolia answers should be gzip when asked for it, + // but a proxy might uncompress Algolia response + // So we handle both compressed and uncompressed + if (headers['content-encoding'] === 'gzip' || + headers['content-encoding'] === 'deflate') { + res = res.pipe(zlib.createUnzip()); + } + + res + .on('data', onData) + .once('error', reject) + .once('end', onEnd); + + function onData(chunk) { + chunks.push(chunk); + } + + function onEnd() { + clearTimeout(timeoutId); + + var data = Buffer.concat(chunks).toString(); + var out; + + try { + out = { + body: JSON.parse(data), + responseText: data, + statusCode: statusCode, + headers: headers + }; + } catch (e) { + out = new errors.UnparsableJSON({ + more: data + }); + } + + if (out instanceof errors.UnparsableJSON) { + reject(out); + } else { + resolve(out); + } + } + + function onCompleteTimeout() { + res.removeListener('data', onData); + res.removeListener('end', onEnd); + originalRes.destroy(); + timeout(); + } + } + + function error(err) { + opts.debug('error: %j - %s', err, rawUrl); + + if (timedOut) { + opts.debug('request had already timedout'); + return; + } + + abort(); + clearTimeout(timeoutId); + reject(new errors.Network(err.message, err)); + } + + function timeout() { + timedOut = true; + opts.debug('timeout %s', rawUrl); + abort(); + reject(new errors.RequestTimeout()); + } + + function abort() { + if (isNode010 && req.socket && req.socket.socket) { + req.socket.socket.destroy(); + } + + req.removeListener('response', response); + req.abort(); + } + }); +}; + +AlgoliaSearchNodeJS.prototype._promise = { + reject: function reject(val) { + return Promise.reject(val); + }, + resolve: function resolve(val) { + return Promise.resolve(val); + }, + delay: function delayPromise(ms) { + return new Promise(function resolveOnTimeout(resolve/* , reject */) { + setTimeout(resolve, ms); + }); + }, + all: function all(promises) { + return Promise.all(promises); + } +}; + +AlgoliaSearchNodeJS.prototype.destroy = function destroy() { + if (typeof this._Agent.destroy === 'function') { + this._Agent.destroy(); + } +}; + +/* + * Generate a secured and public API Key from an apiKey and queryParameters + * optional user token identifying the current user + * + * @param apiKey - The api key to encode as secure + * @param {Object} [queryParameters] - Any search query parameter + */ +AlgoliaSearchNodeJS.prototype.generateSecuredApiKey = function generateSecuredApiKey(privateApiKey, queryParametersOrTagFilters, userToken) { + var searchParams; + + if (Array.isArray(queryParametersOrTagFilters)) { + // generateSecuredApiKey(apiKey, ['user_42'], userToken); + + searchParams = { + tagFilters: queryParametersOrTagFilters + }; + + if (userToken) { + searchParams.userToken = userToken; + } + + searchParams = this._getSearchParams(searchParams, ''); + } else if (typeof queryParametersOrTagFilters === 'string') { + if (queryParametersOrTagFilters.indexOf('=') === -1) { + // generateSecuredApiKey(apiKey, 'user_42', userToken); + searchParams = 'tagFilters=' + queryParametersOrTagFilters; + } else { + // generateSecuredApiKey(apiKey, 'tagFilters=user_42', userToken); + searchParams = queryParametersOrTagFilters; + } + + + if (userToken) { + searchParams += '&userToken=' + encodeURIComponent(userToken); + } + } else { + searchParams = this._getSearchParams(queryParametersOrTagFilters, ''); + } + + var securedKey = crypto + .createHmac('sha256', privateApiKey) + .update(searchParams) + .digest('hex'); + + return new Buffer(securedKey + searchParams).toString('base64'); +}; |
