Files
httpclient/lib/httpclient.js
2017-07-28 12:06:58 +02:00

315 lines
7.3 KiB
JavaScript

"use strict";
const http = require("http");
const https = require("https");
const { URL } = require('url');
const querystring = require("querystring");
const stream = require("stream");
const zlib = require('zlib');
function Cookie(rawCookie) {
var cookieRegex = /([^,; =]+)(?:=([^;]*)(?:[ ;]+|$))/g;
var match;
var cookie = {};
while ((match = cookieRegex.exec(rawCookie)) !== null) {
var key = match[1];
var value = match[2];
switch (key.toLowerCase()) {
case "path" :
cookie.path = value;
break;
case "domain" :
cookie.domain = value;
break;
case "expires" :
cookie.expires = Date.parse(value);
break;
case "max-age" :
cookie.maxAge = Number.parseInt(value);
break;
case "secured" :
cookie.secured = true;
break;
case "http-only" :
cookie.httpOnly = true;
break;
default :
cookie.name = key;
cookie.value = value;
break;
}
}
return cookie;
}
var CookieJar = function () {
var cookies = new Map();
this.get = function (path) {
var cookieString = "";
if (cookies.has(path)) {
var currentDate = new Date();
cookies.get(path).forEach( function (value, key, map) {
if (value.expires !== undefined && value.expires < currentDate
|| value.maxAge !== undefined && currentDate.setSeconds(currentDate.getSeconds() + value.maxAge) < new Date()) {
map.delete(key);
}
else {
cookieString += value.name + "=" + value.value +"; ";
}
}, null);
}
return cookieString;
};
this.update = function (_cookie) {
var cookie = Cookie(_cookie);
var path = cookie.path ? cookie.path.toUpperCase() : "/";
if (!cookies.has(path)) {
cookies.set(path, new Map());
}
var deleteCookie = cookie.maxAge !== undefined && cookie.maxAge <= 0
|| cookie.expires !== undefined && cookie.expires < new Date();
if (deleteCookie) {
if (cookies.get(path).has(cookie.name)) {
cookies.get(path).delete(cookie.name);
}
}
else if (!cookies.get(path).has(cookie.name)) {
cookies.get(path).set(cookie.name, cookie);
}
else {
cookies.get(path).set(cookie.name, cookie);
}
};
};
module.exports = function Browser (parameters) {
parameters = parameters || {};
var baseUrl = parameters.baseUrl ? new URL(parameters.baseUrl) : null;
//CookieJar and custom headers
var cookieJar = new CookieJar();
this.headers = parameters.headers || {};
this.redirect = parameters && parameters.redirect;
this.ignoreErrors = parameters && parameters.ignoreErrors;
if (this.headers["User-Agent"] === undefined) {
this.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0";
}
if (this.headers["Accept-Language"] === undefined) {
this.headers["Accept-Language"] = "en,*:q=0.1";
}
if (this.headers["Accept"] === undefined) {
this.headers["Accept"] = "*/*";
}
if (this.headers["Accept-Encoding"] === undefined) {
this.headers["Accept-Encoding"] = "gzip,deflate";
}
if (this.headers["Accept-Charset"] === undefined) {
this.headers["Accept-Charset"] = "us-ascii,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15,ISO-8859-16,windows-1250,windows-1251,windows-1252,windows-1256,windows-1257,cp437,cp737,cp850,cp852,cp866,x-cp866-u,x-mac,x-mac-ce,x-kam-cs,koi8-r,koi8-u,koi8-ru,TCVN-5712,VISCII,utf-8";
}
if (parameters.keepAlive) {
this.headers["Connection"] = "keep-alive";
}
this.get = function (path, querystring) {
return query({
method : "GET",
path: path,
querystring: querystring
}, this);
};
this.post = function (path, data) {
return query({
method : "POST",
path: path,
data: data
}, this);
};
this.postForm = function (path, data) {
return query({
method : "POST",
path: path,
data: querystring.stringify(data)
}, this);
};
this.put = function (path, data) {
return query({
method : "PUT",
path: path,
data: data
}, this);
};
this.delete = function (path, querystring) {
return query({
method : "DELETE",
path: path,
querystring: querystring
}, this);
};
this.patch = function (path, data) {
return query({
method : "PATCH",
path: path,
data: data
}, this);
};
function query(_request, client) {
var url = new URL(_request.path);
var isHttps = url.protocol === "https:";
var requestObj = {
hostname: url.hostname,
port: url.port ? url.port : isHttps ? 443 : 80,
path: url.pathname,
method: _request.method,
headers: client.headers
};
if (url.search) {
requestObj.path += url.search;
}
if (_request.querystring) {
let encodedQueryString = querystring.stringify(_request.querystring);
requestObj.path += url.search ? url.search + "&" + encodedQueryString : "?" + encodedQueryString;
}
var cookies = cookieJar.get(url.pathname);
if (url.pathname !== "/") {
cookies += cookieJar.get("/");
}
if (cookies) {
requestObj.headers.Cookies = cookies;
}
if (isHttps) {
requestObj.rejectUnauthorized = false;
}
//Complete the url with the base one if a path was provided
// if (client.baseUrl !== null && pathUrl.protocol === null) {
// pathUrl.protocol = baseUrl.protocol;
// pathUrl.hostname = baseUrl.hostname;
// pathUrl.auth = baseUrl.auth;
// pathUrl.port = baseUrl.port;
// }
return new Promise((fulfill, reject) => {
var protocol = isHttps ? https : http;
var request = protocol.request(requestObj, function(response) {
if (response.headers["set-cookie"]) {
for (let i = 0; i < response.headers["set-cookie"].length; ++i) {
cookieJar.update(response.headers["set-cookie"][i]);
}
}
if (response.statusCode < 300 || response.statusCode < 400 && !client.redirect || response.statusCode >= 400 && client.ignoreErrors) {
let decoder;
if (response.headers["content-encoding"] === "gzip") {
decoder = zlib.createGunzip();
response.pipe(decoder);
}
else if (response.headers["content-encoding"] === "deflate") {
decoder = zlib.createInfate();
response.pipe(decoder);
}
else {
decoder = response;
}
fulfill({
response: response,
body: decoder,
url: url.toString()
});
}
else if (response.statusCode < 400 && client.redirect) {
client.headers["Referer"] = url.protocol + "//" + url.hostname + url.pathname;
let newLocation = response.headers.location;
if (newLocation.startsWith("/")) {
newLocation = url.protocol + "//" + url.hostname + newLocation;
}
client.get(newLocation)
.then(fulfill)
.catch(reject);
}
else {
reject(response.statusCode, response);
}
});
request.on("error", (error) => {
reject(error);
});
if (_request.data) {
if (_request.data instanceof stream) {
_request.data.pipe(request);
}
else if (_request.data instanceof String || typeof _request.data === "string") {
request.write(_request.data);
}
else {
request.write(JSON.stringify(_request.data));
}
}
request.end();
});
}
};
module.exports.readPage = function (data) {
return new Promise( function ( fulfill, reject) {
var page = "";
data.on("data", function (text) {
page += text;
});
data.on("end", function () {
fulfill(page);
});
data.on("error", function (error) {
reject(error);
});
});
};