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