UNPKG

fiftyonedegreescore

Version:
191 lines (190 loc) 7.72 kB
/* ********************************************************************* * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. * Copyright 2017 51Degrees Mobile Experts Limited, 5 Charlotte Close, * Caversham, Reading, Berkshire, United Kingdom RG4 7BY * * This Source Code Form is the subject of the following patents and patent * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: * European Patent No. 2871816; * European Patent Application No. 17184134.9; * United States Patent Nos. 9,332,086 and 9,350,823; and * United States Patent Application No. 15/686,066. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. * * If a copy of the MPL was not distributed with this file, You can obtain * one at http://mozilla.org/MPL/2.0/. * * This Source Code Form is "Incompatible With Secondary Licenses", as * defined by the Mozilla Public License, v. 2.0. ********************************************************************** */ var zlib = require("zlib"); var https = require("https"); // Share usage object to return. var shareUsage = {}; // Is a share usage worker already running? var running = 0; // Queue to store new device information. var queue = [], // Maximum length of the queue. newDeviceQueueLength = 50; // Array of local addresses to check client address against. var local = ['127.0.0.1', '0:0:0:0:0:0:0:1']; // 51Degrees log to send messages to. var log; // Product and version used when sharing usage. var version, product; // Boolean value used to stop sharing usage if there is a connection // problem. var stop = false; // Url to send device information to. var requestOptions = { host: 'devices.51degrees.com', path: '/new.ashx', method: 'POST', port: '443', headers: { 'Content-Type': 'text/xml; charset=utf-8', 'Content-Encoding': 'gzip' } }; // Indicates if the device is local. var isLocal = function (address) { var isLocal = false; local.forEach(function (localAddress) { if (address.indexOf(localAddress) !== -1) { isLocal = true; } }); return isLocal; }; // Sends all the data in the queue. var sendData = function (outputStream) { log.emit('debug', 'Sending usage data to ' + requestOptions.host); var request = https.request(requestOptions, function (response) { switch (response.statusCode) { case 200: // OK // Ok response, do nothing break; case 408: // Request Timeout // Could be temporary, do nothing. log.emit('debug', "Response code is 408 : " + response.statusMessage); break; default: // Turn off functionality. log.emit('error', 'Stopping usage sharing as remote ' + 'name ' + requestOptions.host + ' returned status ' + 'description ' + response.statusMessage); stop = true; break; } }).on('error', function (err) { if (err.code === 'ENOTFOUND') { // The address was not found, stop sharing. stop = true; log.emit('error', 'Stopping usage sharing as remote name ' + requestOptions.host + ' could not be resolved ' + 'and generated ENOTFOUND exception.'); } else if (err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') { // The certificate was not trusted, stop sharing. stop = true; log.emit('error', 'Stopping usage sharing as secure connection to remote ' + requestOptions.host + ' could not ' + 'be established and threw error ' + err.code); } else { // Some other error occured, stop sharing. stop = true; log.emit('error', 'Stopping usage shareing after ' + err.code + ' exception.'); } }) // Set the timeout to 1 second. .setTimeout(1000); // Begin the devices XML string var xmlString = '<?xml version="1.0" encoding="UTF-8"?>'; xmlString += '<Devices>'; // Add each device to the XML and remove from the queue. while (queue.length > 0) { // Using pop instead of shift lists devices in reverse order // but is quicker. xmlString += queue.pop(); } // End the devices XML string. xmlString += '</Devices>'; // Compress the XML string and send it. zlib.gzip(xmlString, function (err, result) { request.write(result); request.end(); log.emit('debug', 'Usage data sent successfully'); }); }; // Adds the request to the queue to be processed. shareUsage.recordNewDevice = function (request) { if (stop === false) { // Usage sharing has not been stopped, so get the device information. var device = getContent(request); // Add the device information to the queue. queue.push(device); if (queue.length === newDeviceQueueLength) { // The queue has reached is maximum length, so send the data. sendData(queue); } } }; // Replaces symbols that may be interpreted as part of the xml. var charEscape = function (str) { return str.replace(/\</g, "&lt;").replace(/\>/g, "&gt;").replace(/\"/g, "&quot;"); }; // Records the information as XML data and converts to a string for storage. var getContent = function (request) { // Begin the XML. var device = '<Device>'; // Add the sender information. device += '<DateSent>' + new Date().toISOString() + '</DateSent>'; device += '<Version>' + version + '</Version>'; device += '<Product>' + product + '</Product>'; // Get the remote address. var remoteAddress = request.connection.remoteAddress; // Are you local? if (isLocal(remoteAddress.toString()) === true) { device += '<ClientIP>' + remoteAddress.toString().replace(/^.*:/, '') + '</ClientIP>'; } // Add the local address, removing the leading part. var localAddress = request.connection.localAddress.replace(/^.*:/, ''); device += '<ServerIP>' + localAddress + '</ServerIP>'; // Add the headers that are useful. Object.keys(request.headers).forEach(function (header) { if (header === 'user-agent' || header === 'host' || header.indexOf('profile') !== -1) { device += '<Header Name="' + header + '">' + charEscape(request.headers[header]) + '</Header>'; } }); // End the XML. device += '</Device>'; return device; }; // Module constructor. Sets the error log, product version and product name. module.exports = function (provider, FOD) { log = FOD.log; if (provider.config.UsageSharingDebug === true) { // Provider has been created as part of a test, so send the usage // data to localhost after one request to be tested. requestOptions.host = 'localhost'; requestOptions.port = 1234; newDeviceQueueLength = 1; } // Get the version of the data set e.g. "3.2". version = provider.getDataSetFormat(); // Get the product name e.g. "Node js : Trie" product = 'Node js : ' + provider.config.Type; if (running !== 1) { log.emit('info', '[' + provider.Id + '] ' + 'Usage sharer started'); // The usage sharer is started. running = 1; } else { log.emit('info', '[' + provider.Id + '] ' + 'Updated pre-existing usage sharer.'); } // Return the share usage object. return shareUsage; };