const express = require("express"); const request = require("request"); const app = express(); app.use((req, res, next) => { let data = ''; req.setEncoding('utf8'); req.on('data', (chunk) => data += chunk); req.on('end', () => { req.rawBody = data; next(); }); }); app.get('/', (_req, res) => res.send('Proxy requests via a POST call to /ajax')); const port = 4202; app.listen(port, () => console.log(`App listening on port ${port}!`)) app.post('/ajax', (req, res) => ajaxRequest(req, res)); function formDataUrlEncoded(data) { var str = []; if (!data) return; Object.keys(data).forEach(key =>str.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))); return str.join("&"); } function formData(data) { var str = []; if (!data) return; Object.keys(data).forEach(key => str.push(key + "=" + data[key])); return str.join("&"); } function ajaxRequest(req, res) { let settings = {}; if (req.rawBody) { settings = JSON.parse(req.rawBody); } else { throw "the body is empty."; } return new Promise((resolve, reject) => { if (!settings.url) throw "the url-property must be specified."; settings.method = settings.method || "GET"; settings.headers = settings.headers || []; settings.responseType = settings.responseType || "text"; settings.body = settings.body || settings.data || {}; settings.proxy = ''; // config.settings.proxyUrl // Check valid data format let isFormData = false; let isFormDataUrlEncoded = false; //var isXml = false; let isJson = false; let contentTypeSet = false; if (settings.contentType) { contentTypeSet = true; isFormData = settings.contentType.includes('form-data'); isFormDataUrlEncoded = settings.contentType.includes('form-urlencoded '); //isXml = settings.contentType.includes('xml'); isJson = settings.contentType.includes('json'); } // Validate data and try to fix errors. let postData = null; if (settings.method.toLowerCase() !== 'get') { if (isFormData) { postData = formData(settings.data); } else if (isFormDataUrlEncoded) { postData = formDataUrlEncoded(settings.data); } else if (isJson) { postData = settings.data; if ('string' !== typeof settings.data) { postData = JSON.stringify(settings.data); } } else if (!contentTypeSet) { // default to JSON settings.contentType = 'application/json'; // if (stringHelper.isValidJson(settings.data)) { // postData = settings.data; // } else if (stringHelper.canConvertToJsonString(settings.data)) { // postData = JSON.stringify(settings.data); // } else { // throw "mismatch between contenttype and data property."; // } } } settings.body = postData; let result = request(settings, (error) => { console.info("completed", settings.url); if (error) { console.error("error_a", error); reject(new Error(error)); } else { resolve(); } }); result.on('response', response => { result.pipe(res); response.on('end', resolve); }); result.on('error', err => { console.error("error_b", err); reject(err); }); }) .catch((err) => res.status(400).send(String(err))); }
var config = rfr("config"); router.route('/data/stream/:requestUrl?') .all((req, res) => { var settings = { method: req.method, url: req.params.requestUrl, headers: req.headers, proxy: config.settings.proxyUrl || '' }; var webRequest = request(settings); req.pipe(webRequest); webRequest.pipe(res); var headersSend = false; webRequest.on('data', function() { if (!headersSend) { headersSend = true; try { res.writeHead(webRequest.response.statusCode, webRequest.response.headers); } catch (e) { // can not write headers, ignore! log.info("can not write headers when they are already send"); } } }); // webRequest.on('end', function (response) { // }); });
service.proxyURL = function(url) { let proxyUrl = window.location.origin + '/api/data/stream/'; if (proxyUrl.indexOf('://') > -1) { if (url.indexOf(proxyUrl) !== -1) { // url already is a NAPI streaming url return url; } let baseUrl = ''; if (url.indexOf('://') !== -1) { // url is full url } else if (url[0] === '/') { // url is relative to root baseUrl = window.location.origin; } else { baseUrl = window.location.href + '/'; // url is relative to current url } url = baseUrl + url; let encodedUrl = encodeURIComponent(url); return proxyUrl + encodedUrl; } return url; };
var xhrService = {}; var Promise = require("bluebird"); var rfr = require("rfr"); var log = rfr("services/logService").getLogger(); var request = require("request"); var config = rfr("services/configService").getConfig(); var stringHelper = rfr("helpers/stringHelper"); // global var XMLHttpRequest = require('xhr2'); xhrService.xhr = function () { return new XMLHttpRequest(); }; var formDataUrlEncoded = function (data) { var str = []; if (data) { Object.keys(data).forEach(function (key) { str.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key])); }); } return str.join("&"); }; var formData = function (data) { var str = []; if (data) { Object.keys(data).forEach(function (key) { str.push(key + "=" + data[key]); }); } return str.join("&"); }; var authErrorHandler = null; var onAuthError = function (xhr, event) { if (authErrorHandler) { authErrorHandler(xhr, event); } }; xhrService.setAuthErrorHandler = function (handler) { authErrorHandler = handler; }; var timeoutHandler = null; var onTimeout = function (xhr, event) { if (timeoutHandler) { timeoutHandler(xhr, event); } }; xhrService.setTimeoutHandler = function (handler) { timeoutHandler = handler; }; var networkErrorHandler = null; var onNetworkError = function (xhr, event) { if (networkErrorHandler) { networkErrorHandler(xhr, event); } }; xhrService.setNetworkErrorHandler = function (handler) { networkErrorHandler = handler; }; var networkSuccessHandler = null; var onNetworkSuccess = function (xhr, event) { if (networkSuccessHandler) { networkSuccessHandler(xhr, event); } }; xhrService.setNetworkSuccessHandler = function (handler) { networkSuccessHandler = handler; }; xhrService.request = function (settings) { settings = settings || {}; return new Promise(function (resolve, reject) { var method = (settings.method || "GET").toUpperCase(); var url = settings.url || ""; var data = settings.data; var contentType = settings.contentType || "x-www-form-urlencoded"; var headers = settings.headers || []; var responseType = settings.responseType || "text"; var postData = null; if (method === "POST" || method === "PUT") { if (contentType === "x-www-form-urlencoded") { postData = formDataUrlEncoded(data); } else if (typeof data !== "string") { postData = JSON.stringify(data); } else { postData = data; } } else { url += "?" + formDataUrlEncoded(data); } var xhr = xhrService.xhr(); if (!xhr) { reject("Error: could not create a XMLHttpRequest"); return; } xhr.open(method, url, true); if (headers) { Object.keys(headers).forEach(function (key) { xhr.setRequestHeader(key, headers[key]); }); } //xhr.timeout = 30000; // 30 seconds xhr.addEventListener("error", function (event) { onNetworkError(xhr, event); reject("Error: network error"); }); xhr.addEventListener("timeout", function (event) { onTimeout(xhr, event); reject("Error: timeout error"); }); xhr.responseType = responseType; xhr.addEventListener("load", function (event) { log.info("Request completed", url, xhr.status); if (xhr.status != 200 && xhr.status != 201 && xhr.status != 304) { reject(xhr); } else { //onNetworkSuccess(xhr, event); resolve(xhr, xhr.response); } }, false); // log.info({ // method: method, // url: url, // headers: headers, // data: postdata // }); xhr.send(postData); }); }; xhrService.requestProxyStream = function (req, resp, settings) { return new Promise(function (resolve, reject) { settings.proxy = config.settings.proxyUrl || ''; var result = request(settings); req.pipe(result); result.pipe(resp); result.on('response', function (response) { response.on('end', resolve); }); result.on('error', reject); }); }; xhrService.requestProxy = function (settings) { settings = settings || {}; return new Promise(function (resolve, reject) { /* * In contrast to the xhrService.request function * this function doesn't use the xhr2 package. * This function uses the request package instead. * * The request object does not have te * properties "response" or "responseText". * Instead it has a property namend "body". * * Also when sending data there is nog "data" property * on the settingsobject. Instad a "body" property is used. * * The property contains the UNCOMPRESSED data for the request. * To keep the data valid I used the onData event to return the * COMPRESSED data instead. * */ // Make sure some properties are set or set default values. if (!settings.url) throw 'the url-property must be spedificed. '; settings.method = settings.method || "GET"; settings.headers = settings.headers || []; settings.responseType = settings.responseType || "text"; settings.body = settings.body || settings.data || {}; settings.proxy = config.settings.proxyUrl || ''; // Check valid data format var isFormData = false; var isFormDataUrlEncoded = false; //var isXml = false; var isJson = false; var contentTypeSet = false; if (settings.contentType) { contentTypeSet = true; isFormData = -1 !== settings.contentType.indexOf('form-data'); isFormDataUrlEncoded = -1 !== settings.contentType.indexOf('form-urlencoded '); //isXml = -1 !== settings.contentType.indexOf('xml'); isJson = -1 !== settings.contentType.indexOf('json'); } // Validate data and try to fix errors. var postData = null; if (settings.method.toLowerCase() !== 'get') { if (isFormData) { postData = formData(settings.data); } else if (isFormDataUrlEncoded) { postData = formDataUrlEncoded(settings.data); } // else if (isXml) { // // } else if (isJson) { postData = settings.data; if ('string' !== typeof settings.data) { postData = JSON.stringify(settings.data); } } else if (!contentTypeSet) { // default to JSON settings.contentType = 'application/json'; if (stringHelper.isValidJson(settings.data)) { postData = settings.data; } else if (stringHelper.canConvertToJsonString(settings.data)) { postData = JSON.stringify(settings.data); } else { throw 'mismatch between contenttype and data property.'; } } } settings.body = postData; // Do the request request(settings, function (error, response, body) { log.info("RequestProxy completed", settings.url); // response and body are unused if (error) { reject(new Error(error)); } }) .on('response', function (response) { // Unmodified http.IncomingMessage object response.on('data', function (data) { // Compressed data as it is received if (typeof response.rawData == 'undefined') { response.rawData = data; // create new property } else { if (data instanceof Buffer) { response.rawData = Buffer.concat([response.rawData, data]); } else { response.rawData += data; } } }) .on('end', function () { resolve(response); }); });//.pipe(fs.createWriteStream('doodle.png')); }); }; xhrService.requestProxy2 = function (req, res, settings) { settings = settings || {}; return new Promise(function (resolve, reject) { /* * In contrast to the xhrService.request function * this function doesn't use the xhr2 package. * This function uses the request package instead. * * The request object does not have te * properties "response" or "responseText". * Instead it has a property namend "body". * * Also when sending data there is nog "data" property * on the settingsobject. Instad a "body" property is used. * * The property contains the UNCOMPRESSED data for the request. * To keep the data valid I used the onData event to return the * COMPRESSED data instead. * */ // Make sure some properties are set or set default values. if (!settings.url) throw 'the url-property must be spedificed. '; settings.method = settings.method || "GET"; settings.headers = settings.headers || []; settings.responseType = settings.responseType || "text"; settings.body = settings.body || settings.data || {}; settings.proxy = config.settings.proxyUrl || ''; // Check valid data format var isFormData = false; var isFormDataUrlEncoded = false; //var isXml = false; var isJson = false; var contentTypeSet = false; if (settings.contentType) { contentTypeSet = true; isFormData = -1 !== settings.contentType.indexOf('form-data'); isFormDataUrlEncoded = -1 !== settings.contentType.indexOf('form-urlencoded '); //isXml = -1 !== settings.contentType.indexOf('xml'); isJson = -1 !== settings.contentType.indexOf('json'); } // Validate data and try to fix errors. var postData = null; if (settings.method.toLowerCase() !== 'get') { if (isFormData) { postData = formData(settings.data); } else if (isFormDataUrlEncoded) { postData = formDataUrlEncoded(settings.data); } // else if (isXml) { // // } else if (isJson) { postData = settings.data; if ('string' !== typeof settings.data) { postData = JSON.stringify(settings.data); } } else if (!contentTypeSet) { // default to JSON settings.contentType = 'application/json'; if (stringHelper.isValidJson(settings.data)) { postData = settings.data; } else if (stringHelper.canConvertToJsonString(settings.data)) { postData = JSON.stringify(settings.data); } else { throw 'mismatch between contenttype and data property.'; } } } settings.body = postData; // Do the request var result = request(settings, function (error, response, body) { log.info("RequestProxy_1 completed", settings.url); // response and body are unused if (error) { log.error("RequestProxy_1 error_1", error); reject(new Error(error)); } }); var myTimeout = setTimeout(function() { //res.status(500).send({ error: 'something blew up' }); result = request(settings, function (error, response, body) { log.error("RequestProxy_2 completed", settings.url); // response and body are unused if (error) { log.error("RequestProxy_1 error_2", error); reject(new Error(error)); } }); result.on('response', function (response) { result.pipe(res); response.on('end', resolve); }); result.on('error', function(err) { log.error("RequestProxy_2 error_2", err); reject(err); }); }, 3000); result.on('response', function (response) { clearTimeout(myTimeout); result.pipe(res); response.on('end', resolve); }); result.on('error', function(err) { log.error("RequestProxy_1 error_2", err); reject(err); }); }); }; module.exports = xhrService;
// Packages to use var Promise = require("bluebird"); var rfr = require("rfr"); var log = rfr("services/logService").getLogger(); var xhrService = rfr("services/xhrService"); var dataService = {}; dataService.delegateRequest = function (req, res) { var requestType = req.params.requestType; var asyncResult = null;//Promise.reject('invalid type: ' + requestType); switch (requestType) { case 'ajax': var settings = {}; if (req.rawBody) { settings = JSON.parse(req.rawBody); } asyncResult = dataService.ajax(req, res, settings); break; case 'url': asyncResult = dataService.url(req); break; case 'stream': asyncResult = dataService.stream(req, res); break; case 'local': asyncResult = dataService.local(req); break; default: //default code block asyncResult = Promise.reject('invalid type: ' + requestType); log.info("delegateRequest(): request type unknown ", requestType); } return asyncResult; }; dataService.ajax = (req, res, settings) => { return new Promise(function (resolve, reject) { // all ajax request use the proxy if proxyUrl is set in the config; /* var request = xhrService.requestProxy(settings); request.then((xhr) => { var result = { xhr: xhr, contentBuffer: '' }; if (xhr.rawData && xhr.rawData instanceof Buffer) { result.contentBuffer = xhr.rawData; } else { var arrayBuffer = xhr.body; var byteArray = new Uint8Array(arrayBuffer); result.contentBuffer = new Buffer(byteArray, 'binary'); } resolve(result); }) .catch((xhr) => { log.error(xhr); reject(xhr); }); */ return xhrService.requestProxy2(req, res, settings); }); }; dataService.url = (request) => { return new Promise(function (resolve, reject) { var settings = setupSettingsObjectFromRequest(request); dataService.ajax(settings) .then((result) => { resolve(result); }) .catch((xhr) => { reject(xhr); }); }); }; dataService.stream = (req, resp) => { return new Promise(function (resolve, reject) { var settings = setupSettingsObjectFromRequest(req); return xhrService.requestProxyStream(req, resp, settings); }); }; dataService.local = (settings) => { return new Promise(function (resolve, reject) { reject('not implemented yet'); }); }; module.exports = dataService; /* Define some helper functions for this service */ function setupSettingsObjectFromRequest(request) { // setup settings from current request var settings = {}; settings.headers = request.headers || {}; var url = request.params.requestUrl || throwError('requestUrlNotSet'); settings.headers.host = url; settings.method = request.method; settings.url = url; settings.responseType = "arraybuffer"; if (request.rawBody) { var body = JSON.parse(request.rawBody) || {}; settings.data = body; } // add some default properties settings.crossDomain = true; settings.headers['cache-control'] = 'no-cache'; return settings; } function throwError(errorMessage) { throw new Error(errorMessage); }
59800cookie-checkNode: Express: proxy request