commit 4fb388d6a1f8f0703dcd4e942d5175bc6d7f1f6d Author: boudin Date: Thu Aug 13 00:43:00 2015 +0200 Initial commit diff --git a/lib/httpclient.js b/lib/httpclient.js new file mode 100644 index 0000000..4945038 --- /dev/null +++ b/lib/httpclient.js @@ -0,0 +1,263 @@ +"use strict"; + +var http = require("http"); +var https = require("https"); +var url = require("url"); + + +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) { + case "Path" : + cookie.path = value; + break; + case "Domain" : + cookie.domain = value; + break; + case "Expires" : + this.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.HttpClient = function Browser (_baseUrl, _headers) { + var baseUrl = null; + var instance = this; + + //CookieJar and custom headers + var cookieJar = new CookieJar(); + this.headers = {}; + + if (_baseUrl) { + baseUrl = url.parse(_baseUrl); + } + + if (_headers) { + this.headers = _headers; + } + + if (this.headers["User-Agent"] === undefined) { + this.headers["User-Agent"] = "HttpClient/1.0.0 (" + os.type() + " " + os.release()+")"; + } + + if (this.headers["Accept-Language"] === undefined) { + this.headers["Accept-Language"] = "en-US"; + } + + if (this.headers["Accept"] === undefined) { + this.headers["Accept"] = "text/plain,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; + } + + if (this.headers["Accept-Encoding"] === undefined) { + this.headers["Accept-Encoding"] = "gzip,deflate"; + } + + this.get = function (_path, _querystring) { + return query({ + method : "GET", + path : _path, + querystring : _querystring, + }); + }; + + this.post = function(_path, _data) { + return query({ + method : "POST", + path : _path, + data : _data + }); + }; + + this.put = function(_path, _data) { + return query({ + method : "PUT", + path : _path, + data : _data + }); + }; + + this.delete = function(_path, _querystring) { + return query({ + method : "DELETE", + path : _path, + querystring : _querystring + }); + }; + + this.patch = function(_path, _data) { + return query({ + method : "PATCH", + path : _path, + data : _data + }); + }; + + function query(_request) { + var pathUrl = url.parse(_request.path); + + pathUrl.headers = instance.headers; + + var cookies = cookieJar.get(pathUrl.pathname); + + if ( pathUrl.pathname !== "/") { + cookies += cookieJar.get("/"); + } + + if (cookies) { + pathUrl.headers.Cookies = cookies; + } + + //Complete the url with the base one if a path was provided + if (baseUrl !== null && pathUrl.protocol === null) { + pathUrl.protocol = baseUrl.protocol; + pathUrl.hostname = baseUrl.hostname; + pathUrl.auth = baseUrl.auth; + pathUrl.port = baseUrl.port; + } + + return new Promise(function(fulfill, reject) { + var protocol; + + if (pathUrl.protocol === "https:") { + protocol = https; + + if (pathUrl.rejectUnauthorized === null || pathUrl.rejectUnauthorized === undefined) { + pathUrl.rejectUnauthorized = false; + } + + if (!pathUrl.port) { + pathUrl.port = 443; + } + } + else { + protocol = http; + + if (!pathUrl.port) { + pathUrl.port = 80; + } + } + + pathUrl.method = _request.method; + + var request = protocol.request(pathUrl, function(response) { + if (response.statusCode < 300) { + if (response.headers["set-cookie"]) { + for (let i = 0 ; i < response.headers["set-cookie"].length ; ++i) { + cookieJar.update(response.headers["set-cookie"][i]); + } + } + + 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; + response.setEncoding("utf8"); + } + + + fulfill(decoder); + } + else if (response.statusCode < 400) { + instance.get(response.headers.location) + .then(fulfill) + .catch(reject); + } + else { + reject(response.statusCode, response); + } + }); + + request.on("error", function(error) { + reject(error); + }); + + if (_request.data) { + request.write(_request.data); + } + + request.end(); + }); + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..739ac38 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "httpclient", + "version": "1.0.0", + "description": "Wrapper around http and https request with cookie container", + "main": "lib/httpclient.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://lab.cherboiche.org/iojs/httpclient.git" + }, + "keywords": [ + "http", + "https", + "request", + "cookies" + ], + "author": "boudin", + "license": "GPL-3.0" +}