lib/request.js

1.
var typeis = require("type-is");
2.
var headers = require("./request/headers");
3.
var cookies = require("./request/cookies");
4.
var accepts = require("./request/accepts");
5.
const agent = require("./request/agent");
6.

			
7.
/**
8.
 * This is the request class. It is supplied as first parameter in a route handler and contains 
9.
 * information about the request that was send by the client.
10.
 * @example
11.
 * // this is an example of how to get the request class
12.
 * cerus.router().route("/")
13.
 * .then(req, res, next) {
14.
 *   // the req parameter is the request class
15.
 * });
16.
 * @class request
17.
 * @nofunction
18.
 */
19.
class request {
20.
	constructor(request, cerus) {
21.
		this._request = request;
22.
		this._cerus = cerus;
23.
		this._body = "";
24.
		this._url = require("url").parse(request.url, true);
25.

			
26.
		// Append all the incoming data to the body
27.
		request.on("data", function(data) {
28.
			this._body += data;
29.
		}.bind(this));
30.
	}
31.

			
32.
	/**
33.
	 * This function returns the body of the request. This means it contains the data the client
34.
	 * send with the request. Currently the only accepted body types are JSON and plain text.
35.
	 * This function should only be called after the "end" event was fired. Otherwise it might be
36.
	 * empty since the request wasn't finished yet.
37.
	 * @example
38.
	 * // inside a route handling function
39.
	 * req.event()
40.
	 * .on("end", function() {
41.
	 * // use req.body() here
42.
	 * });
43.
	 * @summary Returns the body of the request.
44.
	 * @function body
45.
	 */
46.
	body() {
47.
		return this._body;
48.
	}
49.

			
50.
	/**
51.
	 * With this function you can listen for all the request events. It'll return a promise that
52.
	 * will be called on a number of events. 
53.
	 * @emits data This event will be called when there was data received from the client. It will 
54.
	 * have the data as first parameter.
55.
	 * @emits end This event will be called when all data was received and there is no data left. 
56.
	 * You should for this event until working with the request data.
57.
	 * @emits close This event will be called when the response was closed.
58.
	 * @emits aborted This event will be called when it was aborted by the socket.
59.
	 * @emits error This event is thrown when there was an error.
60.
	 * @emits timeout This event will be called when the client timed out.
61.
	 * @summary Returns a promise that is called on every event.
62.
	 * @return {Promise} This function will return a promise.
63.
	 * @function event
64.
	 */
65.
	event() {
66.
		return this._cerus.promise(function(event) {
67.
			this._request.on("data", function(data) {
68.
				event("data", data);
69.
			});
70.

			
71.
			this._request.on("end", function() {
72.
				if(this._json === undefined) {
73.
					try {
74.
						this._json = JSON.parse(this._body);
75.
					} 
76.
					catch(e) {
77.
						this._json = [];
78.
					}
79.
				}
80.

			
81.
				event("end");
82.
			}.bind(this));
83.

			
84.
			this._request.on("close", function() {
85.
				event("close");
86.
			});
87.

			
88.
			this._request.on("aborted", function() {
89.
				event("aborted");
90.
			});
91.

			
92.
			this._request.socket.on("error", function() {
93.
				event("error");
94.
			});
95.

			
96.
			this._request.socket.on("timeout", function() {
97.
				event("timeout");
98.
			});
99.
		}.bind(this));
100.
	}
101.

			
102.
	/**
103.
	 * This function will return the request.remote class for this request. This class contains 
104.
	 * information about the address of the user.
105.
	 * @summary Returns the {@link request.remote.constructor} class for this request.
106.
	 * @return {Class} The {@link request.remote.constructor} class for this request.
107.
	 * @function remote
108.
	 */
109.
	remote() {
110.
		return new remote(this._request.socket);
111.
	}
112.

			
113.
	/**
114.
	 * This function will return the request.local class for this request. This class contains 
115.
	 * information about the local address that was used to connect to the server.
116.
	 * @summary Returns the {@link request.local.constructor} class for this request.
117.
	 * @return {Class} The {@link request.local.constructor} class for this request.
118.
	 * @function local
119.
	 */
120.
	local() {
121.
		return new local(this._request.socket);
122.
	}
123.

			
124.
	/**
125.
	 * This function returns if there is data readable from the request, meaning if there is data
126.
	 * available to read.
127.
	 * @summary Returns if there is data available to read.
128.
	 * @return {Boolean} If there is data available to read.
129.
	 * @function readable
130.
	 */
131.
	readable() {
132.
		return this._request.readable;
133.
	}
134.

			
135.
	/**
136.
	 * This function will return the url that was used for this request. This will be the full url 
137.
	 * the client used. Meaning that things like the hash are included.
138.
	 * @example
139.
	 * // inside a route handling function and with "/home" as url
140.
	 * req.url()
141.
	 * // -> will return "/home"
142.
	 * @summary Returns the url used for the request.
143.
	 * @alias href
144.
	 * @return {String} The url used for the request.
145.
	 * @function url
146.
	 */
147.
	url() {
148.
		return this._request.url;
149.
	}
150.

			
151.
	href() {
152.
		return this.url();
153.
	}
154.

			
155.
	/**
156.
	 * This function returns the method that was used for the request.
157.
	 * @example
158.
	 * // inside a route handling function and with "GET" as method
159.
	 * req.method()
160.
	 * // -> will return "GET"
161.
	 * @summary Returns the method used for the request.
162.
	 * @return {String} The method used for the request.
163.
	 * @function method
164.
	 */
165.
	method() {
166.
		return this._request.method;
167.
	}
168.

			
169.
	/**
170.
	 * This function will return the HTTP version sent by the client. This is often the same as the
171.
	 * version the server is running on.
172.
	 * @summary Returns the http version sent by the client.
173.
	 * @return {String} The http version sent by the client.
174.
	 * @function version
175.
	 */
176.
	version() {
177.
		return this._request.httpVersion;
178.
	}
179.

			
180.
	/**
181.
	 * This functions returns the protocol that is used for the request. This can be one of two 
182.
	 * values: "http" or "https". "https" means that the request was secure.
183.
	 * @summary Returns the protocol used for the request.
184.
	 * @return {String} The protocol used for the request.
185.
	 * @function protocol
186.
	 */
187.
	protocol() {
188.
		return this._request.connection.encrypted ? "https" : "http";
189.
	}
190.

			
191.
	/**
192.
	 * This function will return the request.headers class. With this class you can get the headers
193.
	 * that were sent in the request.
194.
	 * @summary Returns the {@link request.headers.constructor} class.
195.
	 * @return {Class} The {@link request.headers.constructor} class.
196.
	 * @function headers
197.
	 */
198.
	headers() {
199.
		return new headers(this._request);
200.
	}
201.

			
202.
	/**
203.
	 * With this function you can easily get a header. It is basically a shortcut for 
204.
	 * .headers().get(). You specify the header you want to get with the key parameter.
205.
	 * @example
206.
	 * // inside a route handling function and with the header "Host: example.com"
207.
	 * req.header("host");
208.
	 * // -> will return "example.com"
209.
	 * @summary Returns the specified header.
210.
	 * @param {String} key The name of the header you want to get.
211.
	 * @return {String} The specified header.
212.
	 * @function header
213.
	 */
214.
	header(key) {
215.
		return this.headers().get(key);
216.
	}
217.

			
218.
	/**
219.
	 * This function will return the request.cookies class. With this class you can get the cookies
220.
	 * that were sent in the request.
221.
	 * @summary Returns the {@link request.cookies.constructor} class.
222.
	 * @return {Class} The {@link request.cookies.constructor} class.
223.
	 * @function cookies
224.
	 */
225.
	cookies() {
226.
		if(this._cookies !== undefined) return this._cookies;
227.

			
228.
		return this._cookies = new cookies(this);
229.
	}
230.

			
231.
	/**
232.
	 * With this function you can easily get a cookie. It is basically a shortcut for 
233.
	 * .cookies().get(). You specify the cookie you want to get with the key parameter.
234.
	 * @example
235.
	 * // inside a route handling function and with the cookie "example=cookie"
236.
	 * req.header("example");
237.
	 * // -> will return "cookie"
238.
	 * @summary Returns the specified cookie.
239.
	 * @param {String} key The name of the cookie you want to get.
240.
	 * @return {String} The specified cookie.
241.
	 * @function cookie
242.
	 */
243.
	cookie(key) {
244.
		return this.cookies().get(key);
245.
	}
246.

			
247.
	/**
248.
	 * This function is used to get the hostname of the request. Before just returns the Host 
249.
	 * header the proxy hostname is checked since it has more importance. If there is a proxy
250.
	 * hostname it is first fixed before being returned. The fixes include removing brackets, etc.
251.
	 * @summary Returns the hostname of the request.
252.
	 * @alias host
253.
	 * @return {String} The hostname of the request.
254.
	 * @function hostname
255.
	 */
256.
	hostname() {
257.
		// Start with the proxy hostname, since it has more importance
258.
		var host = this.header("X-Forwarded-Host");
259.

			
260.
		if(!host) {
261.
			return this.header("Host");
262.
		}
263.

			
264.
		// Fix the host, if needed
265.
		var offset = (host[0] === "[") ? host.indexOf("]") + 1 : 0;
266.
		var index = host.indexOf(":", offset);
267.

			
268.
		return index !== -1 ? host.substring(0, index) : host;
269.
	}
270.

			
271.
	host() {
272.
		return this.hostname();
273.
	}
274.

			
275.
	/**
276.
	 * This function will return the path for this request. The path is different from the href and 
277.
	 * url since it doesn't contain things like the hash and domain. For example, from the url 
278.
	 * "/home#test" the path is "/home". Please make sure that when you're not using the possible 
279.
	 * arguments in the url to use pathname instead.
280.
	 * @summary Returns the path of the request.
281.
	 * @return {String} The path of the request.
282.
	 * @function path
283.
	 */
284.
	path() {
285.
		return this._url.path;
286.
	}
287.

			
288.
	/**
289.
	 * This function will return the pathname for this request. The pathname is completely 
290.
	 * different from the path, since it doesn't contain the arguments that the path might also 
291.
	 * contain. For example pathname for the url "/home?test" is "/home", where .path() will return
292.
	 * it fully.
293.
	 * @summary Returns the pathname of the request.
294.
	 * @return {String} The pathname of the request.
295.
	 * @function pathname
296.
	 */
297.
	pathname() {
298.
		return this._url.pathname;
299.
	}
300.

			
301.
	/**
302.
	 * This function will return the hash part of the url used in this request. The hash is the 
303.
	 * last bit of the url after the "#" (hashtag). For example, the hash of the url 
304.
	 * "/home#example" would be "example".
305.
	 * @summary Returns the hash part of the requested url.
306.
	 * @return {String} The hash part of the requested url.
307.
	 * @function hash
308.
	 */
309.
	hash() {
310.
		return this._url.hash;
311.
	}
312.

			
313.
	/**
314.
	 * With the class this function returns (request.accepts) you can check what the client 
315.
	 * accepts. This class is basically a wrapper for dougwilson's accepts module.
316.
	 * @summary Returns the {@link request.accepts.constructor} class.
317.
	 * @return {Class} The {@link request.accepts.constructor} class. 
318.
	 * @function accepts
319.
	 */
320.
	accepts() {
321.
		if(this._accepts !== undefined) return this._accepts;
322.

			
323.
		return this._cookies = new accepts(this._request);
324.
	}
325.

			
326.
	/**
327.
	 * This function returns true if the client is currently connecting to the server. It is set to
328.
	 * false when the client has succesfully connected.
329.
	 * @summary Returns if the client is currently connecting.
330.
	 * @return {Boolean} If the client is currently connecting.
331.
	 * @function connecting
332.
	 */
333.
	connecting() {
334.
		return this._request.socket.connecting;
335.
	}
336.

			
337.
	/**
338.
	 * With this function you can set for how long the socket can be kept alive. With the first 
339.
	 * parameter you can also set if keep alive is enabled. This needs to be set to true if you 
340.
	 * want the delay to work.
341.
	 * @summary Sets for how long the socket can be kept alive.
342.
	 * @param {Boolean} enable If keep alive may be enabled.
343.
	 * @param {Number} delay How long to keep the socket alive.
344.
	 * @function alive
345.
	 */
346.
	alive(enable, delay) {
347.
		this._request.socket.setKeepAlive(enable, delay);
348.
	}
349.

			
350.
	/**
351.
	 * Using this function you can timeout the request for a certain amount of time. By timing the 
352.
	 * connection out the request will have to wait for the specified amount of time. Since this 
353.
	 * happens asynchronous this function will return promise.
354.
	 * @summary Timeouts the request for the specified amount of time.
355.
	 * @param {Number} timeout How long to timeout the connection for.
356.
	 * @returns {Promise} This function returns a promise.
357.
	 * @function timeout
358.
	 */
359.
	timeout(timeout) {
360.
		if(typeof timeout !== "number") {
361.
			throw new TypeError("argument timeout must be a number");
362.
		}
363.

			
364.
		return this._cerus.promise(function(event) {
365.
			this._request.setTimeout(timeout, function() {
366.
				event("timeout");
367.
			});
368.
		}.bind(this));
369.
	}
370.

			
371.
	/**
372.
	 * With function you can check the content-type of the request. The content-type is the header 
373.
	 * that is sent to inform what type of data the request data is. You can insert as many 
374.
	 * parameters as you want and the content-type will be returned from those parameters. If none
375.
	 * of the paramters match the content-type false is returned. This also a wrapper for one of 
376.
	 * dougwilson's modules.
377.
	 * @example
378.
	 * // inside a route handling function and with "application/json" as content-type
379.
	 * req.is("html", "json");
380.
	 * // -> will return "json"
381.
	 * @summary Returns the matched content-type.
382.
	 * @alias type
383.
	 * @param {...String} types The types to match.
384.
	 * @return {String|Boolean} Returns the matched content-type or false.
385.
	 * @function is
386.
	 */
387.
	is(...types) {
388.
		return typeis(this._request, types);
389.
	}
390.

			
391.
	type(...types) {
392.
		return this.is(...types);
393.
	}
394.

			
395.
	/**
396.
	 * This function will return the connection header. This header contains the type of connection
397.
	 * the client requested. For example, "close" means the client would like to close the 
398.
	 * connection.
399.
	 * @summary Returns the connection header.
400.
	 * @return {String} The connection header sent by the client.
401.
	 * @function connection
402.
	 */
403.
	connection() {
404.
		return this.header("connection");
405.
	}
406.

			
407.
	/**
408.
	 * With this function you can destroy the socket that received the request. This makes the 
409.
	 * server unusable and should therefor only be used when there is an important error. You 
410.
	 * can also add an error. This error will be used as parameter in the error event.
411.
	 * @summary Destroys the socket that received the request.
412.
	 * @param {String} (error) The error you want to add.
413.
	 * @function destroy
414.
	 */
415.
	destroy(error) {
416.
		this._request.destroy(error);
417.
	}
418.

			
419.
	/**
420.
	 * This function will return the request.bytes class for this request. This class contains 
421.
	 * stats about the bytes that were read and written by the request.
422.
	 * @summary Returns the {@link request.bytes.constructor} class.
423.
	 * @return {Class} The {@link request.bytes.constructor} class.
424.
	 * @function bytes
425.
	 */
426.
	bytes() {
427.
		return new bytes(this._request.socket);
428.
	}
429.

			
430.
	/**
431.
	 * With this function you can get the specified data that was send by the request. If it was a
432.
	 * POST request the data is currently just the parsed JSON. In the future this class will also
433.
	 * accept different types of data. For the other methods the data is fetched from the query 
434.
	 * that is parsed from the url.
435.
	 * @example
436.
	 * // inside a route handling function and with "/home?data=example" as url
437.
	 * req.get("data");
438.
	 * // -> will return "example"
439.
	 * @summary Fetches the specified data from the request.
440.
	 * @param {String} key The key to specify which data to get.
441.
	 * @return {String} The specified data from the request.
442.
	 * @function get
443.
	 */
444.
	get(key) {
445.
		if(typeof key !== "string") {
446.
			throw new TypeError("argument key must be a string");
447.
		}
448.

			
449.
		// TODO: This needs to change to support more than json
450.
		// If it is a POST request use the parsed json
451.
		if(this.method() === "POST") {
452.
			return this._json[key];
453.
		}
454.

			
455.
		// Use the query for other methods
456.
		return this._url.query[key];
457.
	}
458.

			
459.
	/**
460.
	 * This function is used to determine if the request was send using XHR, a.k.a. XMLHTTPRequest.
461.
	 * It does this by checking if the X-Requested-With header matches "xmlhttprequest".
462.
	 * @summary Checks if the request was send using XHR.
463.
	 * @return {Boolean} If the request was send using XHR.
464.
	 * @function xhr
465.
	 */
466.
	xhr() {
467.
		var xhr = this.header("X-Requested-With") || "";
468.

			
469.
		return xhr.toLowerCase() === "xmlhttprequest";
470.
	}
471.

			
472.
	/**
473.
	 * This function will check if the request was send using a proxy. It does this by checking if 
474.
	 * the X-Forwarded-Proto header was set.
475.
	 * @summary Checks if the request was send using a proxy.
476.
	 * @return {Boolean} If the request was send using a proxy.
477.
	 * @function proxy
478.
	 */
479.
	proxy() {
480.
		return this.header("X-Forwarded-Proto") !== undefined;
481.
	}
482.

			
483.
	/**
484.
	 * With this function you can check if the connection is secure, meaning it is over "https".
485.
	 * @summary Checks if the connnection is secure.
486.
	 * @return {Boolean} If the connection is secure.
487.
	 * @function secure
488.
	 */
489.
	secure() {
490.
		return this.protocol() === "https";
491.
	}
492.

			
493.
	/**
494.
	 * This function returns the request.agent class. This class contains more information about 
495.
	 * the client by parsing the user-agent header. This is class is a wrapper of faisalman's 
496.
	 * ua-parser-js package.
497.
	 * @summary Returns the {@link request.agent.constructor} class.
498.
	 * @return {Class} The {@link request.agent.constructor} class.
499.
	 * @function agent
500.
	 */
501.
	agent() {
502.
		if(this._agent !== undefined) return this._agent;
503.

			
504.
		return this._agent = new agent(this);
505.
	}
506.
}
507.

			
508.
module.exports = request;
509.

			
510.
/**
511.
 * This is the remote address class. It contains information about the remote address. The remote 
512.
 * address that was used by the client.
513.
 * @class request.remote
514.
 * @nofunction
515.
 */
516.
class remote {
517.
	constructor(socket) {
518.
		this._socket = socket;
519.
	}
520.

			
521.
	/**
522.
	 * This function will return the IP address used by the client.
523.
	 * @example
524.
	 * req.remote().address();
525.
	 * // -> will return the client IP
526.
	 * @summary Returns the IP used by the client.
527.
	 * @return {String} The IP used by the client.
528.
	 * @function address
529.
	 */
530.
	address() {
531.
		return this._socket.remoteAddress;
532.
	}
533.

			
534.
	/**
535.
	 * This function will return the port used by the client.
536.
	 * @example
537.
	 * req.remote().port();
538.
	 * // -> will return the client port
539.
	 * @summary Returns the port used by the client.
540.
	 * @return {String} The port used by the client.
541.
	 * @function port
542.
	 */
543.
	port() {
544.
		return this._socket.remotePort;
545.
	}
546.

			
547.
	/**
548.
	 * This function will return the family of the IP that was returned by the client.
549.
	 * @example
550.
	 * req.remote().family();
551.
	 * // -> will return the family of the client IP
552.
	 * @summary Returns the family of the IP used by the client.
553.
	 * @return {String} The family of the IP used by the client.
554.
	 * @function family
555.
	 */
556.
	family() {
557.
		return this._socket.remoteFamily;
558.
	}
559.
}
560.

			
561.
/**
562.
 * This is the local address class. It contains information about the local address. The local 
563.
 * address is the address on the server the client connected to.
564.
 * @class request.local
565.
 * @nofunction
566.
 */
567.
class local {
568.
	constructor(socket) {
569.
		this._socket = socket;
570.
	}
571.

			
572.
	/**
573.
	 * This function will return the IP address connected to by the client.
574.
	 * @example
575.
	 * req.local().address();
576.
	 * // -> will return the IP connected to by the client.
577.
	 * @summary Returns the IP connected to by the client.
578.
	 * @return {String} The IP connected to by the client.
579.
	 * @function address
580.
	 */
581.
	address() {
582.
		return this._socket.localAddress;
583.
	}
584.

			
585.
	/**
586.
	 * This function will return the port connected to by the client.
587.
	 * @example
588.
	 * req.local().port();
589.
	 * // -> will return the port connected to by the client.
590.
	 * @summary Returns the port connected to by the client.
591.
	 * @return {String} The port connected to by the client.
592.
	 * @function port
593.
	 */
594.
	port() {
595.
		return this._socket.localPort;
596.
	}
597.
}
598.

			
599.
/**
600.
 * This is the bytes class. It contains information about the amount bytes written and read during 
601.
 * this request.
602.
 * @class request.bytes
603.
 * @nofunction
604.
 */
605.
class bytes {
606.
	constructor(socket) {
607.
		this._socket = socket;
608.
	}
609.

			
610.
	/**
611.
	 * This function will return the amount of bytes that have been read from the request.
612.
	 * @summary Returns the amount of bytes read.
613.
	 * @return {Number} The amount of bytes read.
614.
	 * @function read
615.
	 */
616.
	read() {
617.
		return this._socket.bytesRead;
618.
	}
619.

			
620.
	/**
621.
	 * This function returns the amount of bytes that have been written to the response.
622.
	 * @summary Returns the amount of bytes written.
623.
	 * @return {Number} The amount of bytes written.
624.
	 * @function written
625.
	 */
626.
	written() {
627.
		return this._socket.bytesWritten;
628.
	}
629.
}
630.