core
Version:
Core Libs
849 lines (731 loc) • 17.7 kB
JavaScript
/**
* @overview Request
* @copyright Dreamlab Onet.pl sp. z. o o 2012
*/
var http = require('http');
var urlParser = require('uriparser');
var Types = require('../common/Types.js').Types;
var Message = require('./Message.js').Message;
var BinaryData = require('../data/BinaryData.js').BinaryData;
var FormData = require('../data/FormData.js').FormData;
/**
* Http Request
* @class Request
* @classdesc Object that represents http request
* @extends Message
* @throw Request.Exception.REQUEST_CANNOT_BE_A_BUFFER
* @param {Object|String|Request|Buffer|http.IncomingMessage} params
* @return {Request}
*/
var Request = function (params) {
Message.call(this, params);
this._url = null;
this._search = null;
this._method = 'GET';
this._host = null;
this._port = null;
this._defaultPortChanged = false;
this._path = '/';
this._proto = 'http:';
this._authUser = null;
this._authPassword = null;
this._querySuffixes = {};
this._querySeparator = '&';
this._query = {};
this._clientAddress = null;
this._clientPort = null;
this._timeStart = null;
if (Buffer.isBuffer(params)) {
throw Request.Exception.REQUEST_CANNOT_BE_A_BUFFER;
} else if (Types.isString(params)) {
this.setUrl(params);
} else if (Types.isInstanceOf(params, Request)) {
this.setUrl(params.getUrl());
this.setMethod(params.getMethod());
this.setHeaders(params.getHeaders());
this.setBody(params.getBody());
this.setHttpVersion(params.getHttpVersion());
//connection host
this.setConnectionHost(params.getConnectionHost());
} else if (Types.isInstanceOf(params, http.IncomingMessage)) {
// 1831 - 1886
// 1500 - 1618
this.setMethod(params.method);
// 1483 - 1572
this.setHeaders(params.headers);
// 1416 - 1552
var httpVer = params.httpVersion;
// 1381 - 1483
this.setHttpVersion(httpVer);
// 1381 - 1483
// 1362 - 1488 (bez return this) 1388 - 1492
this.setBody(params.data);
// 1340 - 1453
this.setUrl(params.url);
this._clientAddress = params.client.remoteAddress;
this._clientPort = params.client.remotePort;
this._timeStart = (params.__timeStart || Date.now());
} else {
this.init(params);
}
};
Request.prototype = Object.create(Message.prototype);
Request.prototype._cookieRegExpCache = {};
Request.prototype._preCookieRegexp = '((?:^|;)\\s?)';
Request.prototype._postCookieRegExp = '=([^;]+)(;\\s?|$)';
/**
* Checks if given string is already encoded
* @method
* @private
* @param {String} str
* @return {Boolean}
*/
Request.prototype._isEncoded = function (str) {
return (str.indexOf("%") != -1 || str.indexOf("+") != -1);
};
/**
* Set cookie
* @method
* @param {String} cookie
* @return {Request}
*/
Request.prototype.setCookie = function (cookie) {
var cookies = this.headers.getHeader("cookie");
if (!cookies) {
cookies = '';
}
if (cookies.length) {
cookies += '; ' + cookie;
} else {
cookies = cookie;
}
this.headers.setHeader("cookie", cookies, true);
return this;
};
/**
* Get cookie
* @method
* @param {String} cookieName
* @return {String|null}
*/
Request.prototype.getCookie = function (cookieName) {
var cookies = this.headers.getHeader('cookie');
if (!cookies) {
return null;
}
var result = cookies.match(this._getCookieRegexp(cookieName));
if (result && result[2]) {
return result[2];
}
return null;
};
/**
* Remove cookie
* @method
* @param {String} cookieName
* @return {null|Boolean}
*/
Request.prototype.removeCookie = function (cookieName) {
var cookies = this.headers.getHeader('cookie');
if (!cookies) {
return null;
}
cookies = cookies.replace(this._getCookieRegexp(cookieName), '$1').replace(/[; ]+$/, '');
this.headers.setHeader('cookie', cookies, true);
return true;
};
/**
* Returns cookie cached regexp
*
* @param {string} cookieName
* @return {RegExp} - regexp pattern for cookie
*/
Request.prototype._getCookieRegexp = function (cookieName) {
if (!this._cookieRegExpCache[cookieName]) {
this._cookieRegExpCache[cookieName] = new RegExp(this._preCookieRegexp + cookieName + this._postCookieRegExp);
}
return this._cookieRegExpCache[cookieName];
};
/**
* Sets url of a service endpoint
* @method
* @param {String} url
* @returns {UrlRequest}
*/
Request.prototype.setUrl = function (url) {
this._url = url;
var matches = urlParser.parse(url);
if (matches.hasOwnProperty('auth')) {
this.setAuth(matches.auth);
} else {
this.setAuth('');
}
if (matches.hasOwnProperty('host')) {
this.setConnectionHost(matches.host);
} else {
this.setConnectionHost(this.getHost());
}
if (matches.hasOwnProperty('port')) {
this._defaultPortChanged = true;
this.setPort(parseInt(matches.port, 10));
} else {
this._defaultPortChanged = false;
this.setPort(80);
}
if (matches.hasOwnProperty('protocol')) {
this.setProto(matches.protocol);
} else {
this.setProto('http:');
}
if (matches.hasOwnProperty('path')) {
var path = matches.path;
if (path[0] != '/') {
path = '/' + path;
}
this.setPath(path);
}
if (matches.hasOwnProperty('query')) {
this.setQuery(matches.query);
if (matches.hasOwnProperty('queryArraySuffix')) {
this._querySuffixes = matches.queryArraySuffix;
}
} else {
this.setQuery({});
}
if (matches.hasOwnProperty('querySeparator')) {
this._querySeparator = matches.querySeparator || '&';
}
if (matches.hasOwnProperty('search')) {
this._search = matches.search;
}
return this;
};
/**
* Returns url of a service endpoint
* @method
* @returns {String}
*/
Request.prototype.getUrl = function () {
if (this._url) {
return this._url;
}
var url = '';
var proto = this.getProto();
if (proto) {
url += proto + '//';
}
var auth = this.getAuth();
if (auth) {
url += auth + '@';
}
var host = this.getConnectionHost(),
port = this.getPort();
if (this.getHost()) {
host = this.getHost();
if (host.lastIndexOf(':') > 0) {
port = null;
}
}
//jak mamy port i nie mamy pary http:80 i https:443 to doklejamy do hosta port
//dla http/https jesli port jest default to go nie zwracamy
if (port && !((proto === 'http:' && port == 80) || (proto === 'https:' && port == 443))) {
url += host + ':' + port;
} else {
url += host;
}
var path = this.getPath();
if (path) {
url += path;
} else {
url += '/';
}
url += this.getQueryString();
this._url = url;
return url;
};
/**
* Returns query string component of an url
* @method
* @returns {String}
*/
Request.prototype.getQueryString = function () {
var url = '';
var query = this.getQuery();
if (this._search) {
return this._search;
}
if (query) {
url += '?';
var tmp = '';
// to nie zadziala dla case test ącki
for (var key in query) {
if (query.hasOwnProperty(key)) {
if (Array.isArray(query[key])) {
for (var i = 0, max = query[key].length; i < max; i++) {
var value = query[key][i];
tmp += key;
tmp += this._querySuffixes[key] || '';
tmp += '=' + value;
tmp += this._querySeparator;
}
} else {
tmp += key;
if (query[key]) {
tmp += '=' + query[key];
} else if (query[key] === '') {
tmp += '=';
}
tmp += this._querySeparator;
}
}
}
url += tmp.substring(0, tmp.length - 1);
}
if (url == '?') {
url = '';
}
return url;
};
/**
* Sets method of communicating with a service
* @method
* @param {String} method
* @returns {Request}
*/
Request.prototype.setMethod = function (method) {
this._method = method;
return this;
};
/**
* Returns method of communicating with a service
* @method
* @returns {String}
*/
Request.prototype.getMethod = function () {
return this._method;
};
/**
* Sets connection host
* @method
* @param {String} host
* @returns {Request}
*/
Request.prototype.setConnectionHost = function (host) {
this._url = null;
if (host) {
this._host = host.toLowerCase();
} else {
this._host = host;
}
return this;
};
/**
* Returns connection host
* @method
* @returns {String}
*/
Request.prototype.getConnectionHost = function () {
if (this._host) {
return this._host.toLowerCase();
} else {
return this._host;
}
};
/**
* Sets Host http header
* @method
* @param {String} host
* @returns {Request}
*/
Request.prototype.setHost = function (host) {
this._url = null;
if (host) {
this.headers.setHeader('host', host.toLowerCase(), true);
} else {
this.headers.setHeader('host', host, true);
}
return this;
};
/**
* Returns Host http header
* @method
* @returns {String|null}
*/
Request.prototype.getHost = function () {
var host = this.headers.getHeader('host');
if (host) {
return host.toLowerCase();
} else {
return host;
}
};
/**
* Sets connection port
* @method
* @param {Number} port
* @returns {Request}
*/
Request.prototype.setPort = function (port) {
this._url = null;
this._port = port;
return this;
};
/**
* Returns connection port
* @method
* @returns {Number|null}
*/
Request.prototype.getPort = function () {
return this._port;
};
/**
* Sets url path
* @method
* @param {String} path
* @returns {Request}
*/
Request.prototype.setPath = function (path) {
this._url = null;
this._path = path;
return this;
};
/**
* Returns url path
* @method
* @returns {String|null}
*/
Request.prototype.getPath = function () {
return this._path;
};
/**
* Sets protocol
* @method
* @param {String} proto
* @returns {Request}
*/
Request.prototype.setProto = function (proto) {
this._url = null;
this._proto = proto;
if (!this._defaultPortChanged) {
if (proto === 'https:') {
this.setPort(443);
} else if (proto === 'http:') {
this.setPort(80);
}
}
return this;
};
/**
* Returns protocol
* @method
* @returns {String|null}
*/
Request.prototype.getProto = function () {
return this._proto;
};
/**
* Sets http simple auth digest in format: user:pass
* @method
* @param {String} auth
* @returns {Request}
*/
Request.prototype.setAuth = function (auth) {
this._url = null;
var data;
if (Types.isString(auth)) {
var tmp = auth.split(':');
data = {
user: tmp[0],
password: tmp[1]
};
} else {
data = auth;
}
this.setAuthUser(data.user);
this.setAuthPassword(data.password);
return this;
};
/**
* Returns http simple auth digest in format: user:pass
* @method
* @returns {String|null}
*/
Request.prototype.getAuth = function () {
var user = this.getAuthUser(),
password = this.getAuthPassword();
if (user && password) {
return user + ':' + password;
}
return null;
};
/**
* Sets username for http simple auth digest
* @method
* @param {String} user
* @returns {Request}
*/
Request.prototype.setAuthUser = function (user) {
this._url = null;
this._authUser = user;
return this;
};
/**
* Returns username for http simple auth digest
* @method
* @returns {String|null}
*/
Request.prototype.getAuthUser = function () {
return this._authUser;
};
/**
* Sets password for http simple auth digest
* @method
* @param {String} password
* @returns {Request}
*/
Request.prototype.setAuthPassword = function (password) {
this._url = null;
this._authPassword = password;
return this;
};
/**
* Returns password for http simple auth digest
* @method
* @returns {String|null}
*/
Request.prototype.getAuthPassword = function () {
return this._authPassword;
};
/**
* Sets url query string
* @method
* @param {String} query
* @returns {Request}
*/
Request.prototype.setQuery = function (query) {
this._url = null;
this._search = null;
this._query = query;
return this;
};
/**
* Returns url query string
* @method
* @returns {String|null}
*/
Request.prototype.getQuery = function () {
return this._query;
};
/**
* Sets url query component
* @param {String} name
* @param {String} value
* @returns {Request}
* @method
*/
Request.prototype.setQueryParam = function (name, value) {
this._url = null;
this._search = null;
if (!this._query) {
this._query = {};
}
this._query[name] = value;
if (Types.isArray(value)) {
this._querySuffixes[name] = '[]';
}
return this;
};
Request.prototype.hasQueryParam = function (name) {
if (!this._query) {
return false;
}
return this._query.hasOwnProperty(name);
};
/**
* Returns url query component
* @method
* @param {String} name
* @returns {String|null}
*/
Request.prototype.getQueryParam = function (name) {
if (!this._query) {
return null;
}
if (this._query.hasOwnProperty(name)) {
// FIXME remove in next majaor
if (this._query[name] === null) {
return '';
}
/// END
return this._query[name];
}
return null;
};
/**
* Returns client ip address
* @method
* @returns {String|null}
*/
Request.prototype.getClientAddress = function () {
return (this._clientAddress || null);
};
/**
* Returns client port number
* @method
* @returns {Number|null}
*/
Request.prototype.getClientPort = function () {
return (this._clientPort || null);
};
/**
* Returns request processing time
* @method
* @returns {Number}
*/
Request.prototype.getProcessingTime = function () {
return this._timeStart ? (Date.now() - this._timeStart) : 0;
};
/**
* Sets request body (post data)
* @method
* @param {BinaryData|String|null|undefined} body
* @returns {Request}
*/
Request.prototype.setBody = function (body) {
if (Types.isInstanceOf(body, FormData)) {
this.setHeader('content-length', body.length(), true);
var boundary = body.getBoundary();
if (boundary) {
this.setHeader('content-type', 'multipart/form-data; boundary=' + boundary, true);
}
Message.prototype.setBody.call(this, body);
} else if (Types.isInstanceOf(body, BinaryData)) {
Message.prototype.setBody.call(this, body);
} else {
Message.prototype.setBody.call(this, new BinaryData(body ? body : '', BinaryData.Encoding.TEXT, BinaryData.CharacterEncoding.UTF8));
}
return this;
};
/**
* Determines if caching is disabled
* @method
* @returns {Boolean}
*/
Request.prototype.isCachingDisabled = function () {
if (this.getMethod() == Request.POST) {
return true;
}
return false;
};
/**
* Determines if request is conditional (can return 304 response)
* @method
* @returns {Boolean}
*/
Request.prototype.isConditional = function () {
return this.getHeader('if-modified-since');
};
/**
* Determines if request client allow receiving compressed data
* @method
* @returns {Boolean}
*/
Request.prototype.isCompressedContentAccepted = function () {
var ae = this.getHeader('accept-encoding');
if (ae) {
return ae.indexOf('gzip') > -1;
}
return false;
};
/**
* Determines if client requests for regenerate content
* @method
* @returns {Boolean}
*/
Request.prototype.isGenerating = function () {
return this.getHeader('x-onet-regenerate');
};
/**
* Returns JSON representation of a request
* @method
* @returns {Object}
*/
Request.prototype.toJson = function () {
var body = this.getBody();
return {
"url": this.getUrl(),
"method": this._method,
"headers": this.headers.getHeaders(),
"body": body ? body.toString() : body,
"httpVersion": this._httpVersion
};
};
/**
* @constant
* @type {String}
* @default
*/
Request.OPTIONS = "OPTIONS";
/**
* @constant
* @type {String}
* @default
*/
Request.GET = "GET";
/**
* @constant
* @type {String}
* @default
*/
Request.HEAD = "HEAD";
/**
* @constant
* @type {String}
* @default
*/
Request.POST = "POST";
/**
* @constant
* @type {String}
* @default
*/
Request.PUT = "PUT";
/**
* @constant
* @type {String}
* @default
*/
Request.DELETE = "DELETE";
/**
* @constant
* @type {String}
* @default
*/
Request.TRACE = "TRACE";
/**
* @constant
* @type {String}
* @default
*/
Request.CONNECT = "CONNECT";
/**
* @static
* @constant
* @namespace
*/
Request.Exception = {};
/**
* @static
* @constant
* @type {String}
* @default
*/
Request.Exception.WRONG_BODY = "Body has to be a instance of BinaryData";
/**
* @static
* @constant
* @type {String}
* @default
*/
Request.Exception.REQUEST_CANNOT_BE_A_BUFFER = "Request cannot be a Buffer";
exports.Request = Request;