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 | |
| 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')
3 files changed, 503 insertions, 0 deletions
diff --git a/node_modules/algoliasearch/src/server/builds/AlgoliaSearchServer.js b/node_modules/algoliasearch/src/server/builds/AlgoliaSearchServer.js new file mode 100644 index 00000000..c6e5dfec --- /dev/null +++ b/node_modules/algoliasearch/src/server/builds/AlgoliaSearchServer.js @@ -0,0 +1,78 @@ +'use strict'; + +// Some methods only accessible server side + +module.exports = AlgoliaSearchServer; + +var inherits = require('inherits'); + +var AlgoliaSearch = require('../../AlgoliaSearch'); + +function AlgoliaSearchServer(applicationID, apiKey, opts) { + // Default protocol is https: on the server, to avoid leaking admin keys + if (opts.protocol === undefined) { + opts.protocol = 'https:'; + } + + AlgoliaSearch.apply(this, arguments); +} + +inherits(AlgoliaSearchServer, AlgoliaSearch); + +/* + * Allow to use IP rate limit when you have a proxy between end-user and Algolia. + * This option will set the X-Forwarded-For HTTP header with the client IP and the X-Forwarded-API-Key with the API Key having rate limits. + * @param adminAPIKey the admin API Key you can find in your dashboard + * @param endUserIP the end user IP (you can use both IPV4 or IPV6 syntax) + * @param rateLimitAPIKey the API key on which you have a rate limit + */ +AlgoliaSearchServer.prototype.enableRateLimitForward = function(adminAPIKey, endUserIP, rateLimitAPIKey) { + this._forward = { + adminAPIKey: adminAPIKey, + endUserIP: endUserIP, + rateLimitAPIKey: rateLimitAPIKey + }; +}; + +/* + * Disable IP rate limit enabled with enableRateLimitForward() function + */ +AlgoliaSearchServer.prototype.disableRateLimitForward = function() { + this._forward = null; +}; + +/* + * Specify the securedAPIKey to use with associated information + */ +AlgoliaSearchServer.prototype.useSecuredAPIKey = function(securedAPIKey, securityTags, userToken) { + this._secure = { + apiKey: securedAPIKey, + securityTags: securityTags, + userToken: userToken + }; +}; + +/* + * If a secured API was used, disable it + */ +AlgoliaSearchServer.prototype.disableSecuredAPIKey = function() { + this._secure = null; +}; + +AlgoliaSearchServer.prototype._computeRequestHeaders = function(additionalUA) { + var headers = AlgoliaSearchServer.super_.prototype._computeRequestHeaders.call(this, additionalUA); + + if (this._forward) { + headers['x-algolia-api-key'] = this._forward.adminAPIKey; + headers['x-forwarded-for'] = this._forward.endUserIP; + headers['x-forwarded-api-key'] = this._forward.rateLimitAPIKey; + } + + if (this._secure) { + headers['x-algolia-api-key'] = this._secure.apiKey; + headers['x-algolia-tagfilters'] = this._secure.securityTags; + headers['x-algolia-usertoken'] = this._secure.userToken; + } + + return headers; +}; diff --git a/node_modules/algoliasearch/src/server/builds/get-agent.js b/node_modules/algoliasearch/src/server/builds/get-agent.js new file mode 100644 index 00000000..cbe003b4 --- /dev/null +++ b/node_modules/algoliasearch/src/server/builds/get-agent.js @@ -0,0 +1,107 @@ +'use strict'; + +// this module will either get a regular http keepalive agent or +// a proxying agent if `HTTP_PROXY` or `HTTPS_PROXY` environment variables +// are set +// request/request module could have been used but it does not do any +// keepalive on POST requests https://github.com/request/request/issues/236#issuecomment-94587587 + +module.exports = getAgent; + +function getAgent(protocol) { + var agent; + + if (protocol !== 'http:' && protocol !== 'https:') { + throw new Error('get-agent: `protocol` must be `http:` or `https:`'); + } + + var parsedProxy = getParsedProxy(); + + if (parsedProxy.protocol) { + agent = getProxyingAgent(protocol, parsedProxy); + } else { + // no proxy given, let's use regular keepalived agents + agent = getKeepaliveAgent(protocol); + } + + return agent; +} + +function getParsedProxy() { + var url = require('url'); + var proxy = process.env.HTTP_PROXY || + process.env.HTTPS_PROXY || + ''; + + return url.parse(proxy); +} + +function getKeepaliveAgent(protocol) { + var http = require('http'); + var https = require('https'); + + var HttpsAgent = require('agentkeepalive').HttpsAgent; + var HttpAgent = require('agentkeepalive'); + var semver = require('semver'); + + var keepAliveAgent; + + // node 0.10 => agentkeepalive + // node >= 0.12 => native keepalive + // iojs => native keepalive + // node >= 0.11.4 has good keepAlive https://github.com/joyent/node/commit/b5b841 + if (semver.satisfies(process.version, '<0.11.4')) { + if (protocol === 'http:') { + keepAliveAgent = new HttpAgent({ + maxSockets: Infinity + }); + } else if (protocol === 'https:') { + keepAliveAgent = new HttpsAgent({ + maxSockets: Infinity + }); + } + } else if (protocol === 'http:') { + keepAliveAgent = new http.Agent({ + keepAlive: true, + maxSockets: Infinity + }); + } else if (protocol === 'https:') { + keepAliveAgent = new https.Agent({ + keepAlive: true, + maxSockets: Infinity + }); + } + + return keepAliveAgent; +} + +function getProxyingAgent(protocol, parsedProxy) { + var tunnel = require('tunnel-agent'); + + var agentSettings = { + maxSockets: Infinity, + proxy: { + host: parsedProxy.hostname, + port: parseInt(parsedProxy.port, 10), + proxyAuth: parsedProxy.auth + } + }; + + // httpOverHttps + // httpsOverHttps + // https://github.com/mikeal/tunnel-agent/blob/912a7a6d00e10ec76baf9c9369de280fa5badef3/index.js#L12-L15 + var tunnelType = protocol.replace(':', '') + + 'Over' + + parsedProxy.protocol.replace(':', '').replace('h', 'H'); + + var agent = new tunnel[tunnelType](agentSettings); + + // `tunnel-agent` does not have a destroy method + agent.destroy = function() { + this.sockets.forEach(function(socket) { + socket.destroy(); + }); + }; + + return agent; +} 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'); +}; |
