Computer

NodeJS XMLHttpRequest

A simple implementation to use XMLHttpRequest on NodeJS.

TODO:
  1. Implement XMLHttpRequest.responseURL;
  2. Implement XMLHttpRequest.overrideMimeType() method;
  3. Get the XMLHttpRequest.responseType;
  4. Implement the parser for XMLHttpRequest.response.
// v.1   - 2016-07-08
// v.1.1 - 2016-07-13
// v.1.2 - 2016-07-25
// v.1.3 - 2017-09-04

global.XMLHttpRequest = function () {
	var _local = {
		xhr                : null,
		request            : null,
		headers            : {
			request        : { },
			response       : { }
		},

		readyState         : 0,
		responseText       : null,
		responseType       : "text", // "", "arraybuffer", "blob", "document", "json", "text"
		responseURL        : null,
		responseXML        : undefined,
		status             : 0,
		statusText         : '',
		timeout            : '',
		upload             : '',
		withCredentials    : false,

		onreadystatechange : null,
		ontimeout          : null,

		setState           : function (status) {
			_local.readyState = status;
			if(_local.onreadystatechange !== null) {
				_local.onreadystatechange();
			}
		},
		runTimeout         : function (e) {
			if(_local.ontimeout !== null) {
				_local.ontimeout(e);
			}
		}
	};
	_local.xhr = this;

	Object.defineProperties(this, {
		UNSENT           : { enumerable : true, writable : false, value : 0 },
		OPENED           : { enumerable : true, writable : false, value : 1 },
		HEADERS_RECEIVED : { enumerable : true, writable : false, value : 2 },
		LOADING          : { enumerable : true, writable : false, value : 3 },
		DONE             : { enumerable : true, writable : false, value : 4 },

		readyState       : { enumerable : true, get : function () { return _local.readyState     ; } },
		response         : { enumerable : true, get : function () { return _local.response       ; } },
		responseText     : { enumerable : true, get : function () { return _local.responseText   ; } },
		responseURL      : { enumerable : true, get : function () { return _local.responseURL    ; } },
		responseXML      : { enumerable : true, get : function () { return _local.responseXML    ; } },
		status           : { enumerable : true, get : function () { return _local.status         ; } },
		statusText       : { enumerable : true, get : function () { return _local.statusText     ; } },
		upload           : { enumerable : true, get : function () { return _local.upload         ; } },

		responseType     : { enumerable : true, get : function () { return _local.responseType   ; },
			set : function (type) {
				_local.responseType    = type;
			}
		},
		timeout          : { enumerable : true, get : function () { return _local.timeout        ; },
			set : function (timeout) {
				_local.timeout         = Math.min(1, 0 + timeout);
			}
		},
		withCredentials  : { enumerable : true, get : function () { return _local.withCredentials; },
			set : function (withCredentials) {
				_local.withCredentials = (withCredentials === true);
			}
		},

		onreadystatechange : {
			enumerable : true,
			get : function () {
				return _local.onreadystatechange;
			},
			set : function (value) {
				if(typeof(value) == "function"
				||        value === null
				) _local.onreadystatechange = value;
			}
		},
		timeout : {
			enumerable : true,
			get : function () {
				return _local.timeout;
			},
			set : function (value) {
				if(typeof(value) == "function"
				||        value === null
				) _local.timeout = value;
			}
		},

		open : {
			enumerable : true,
			writable   : false,
			value      : function (method, url, async, username, password) {
				// async is useless. it always will be asyncronous.
				var url_data = require("url").parse(url);
				_local.withCredentials = (username && password);
				_local.request = require(url_data.protocol == "https:" ? "https" : "http");
				if(_local.timeout !== null) {
					_local.request.setTimeout(_local.timeout, _local.runTimeout);
				}
				_local.request.request(
					{
						method   : method,
						hostname : url_data.host,
						port     : url_data.port || ( url_data.protocol == "https:" ? 443 : 80 ),
						path     : url_data.path,
						auth     : ( _local.withCredentials ? username + ':' + password : null )
					},
					function (result) {
						_local.headers.response = result.headers;
						_local.status           = result.statusCode;
						_local.statusText       = result.statusMessage;
						_local.responseURL      = result.URL;
						_local.responseText     = '';
						_local.setState(_local.xhr.HEADERS_RECEIVED);
						result.on("data", function (data) {
							_local.responseText+= data;
							_local.setState(_local.xhr.LOADING);
						});
						result.on("end" , function () {
							// TODO:
							// - set responseType
							// - set response
							_local.setState(_local.xhr.DONE);
						});
					}
				);
			}
		},
		send : {
			enumerable : true,
			writable   : false,
			value : function (data) {
				if(_local.request === null) {
					_local.setState(_local.xhr.UNSENT);
				} else {
					if(       data          !== undefined
					&&        data          !== null
					&& typeof(data.toString) == "function"
					) data =  data.toString();
					for(var i in _local.headers.request) {
						_local.request.setHeader(i, _local.headers.request[i]);
					}
					if(typeof(data) == "string") {
						if(!_local.headers.request["Content-Type"])
						_local.headers.request["Content-Type"  ] = "application/octet-stream";
						_local.headers.request["Content-Length"] =  data.length;
						_local.request.write(data);
					}
					_local.request.end();
					_local.setState(_local.xhr.OPENED);
				}
			}
		},
		abort : {
			enumerable : true,
			writable   : false,
			value      : function () {
				if(_local.request != null) {
					_local.request.abort();
				}
			}
		},
		overrideMimeType : {
			enumerable : true,
			writable   : false,
			value      : function () {
				// TODO
			}
		},
		setRequestHeader : {
			enumerable : true,
			writable   : false,
			value      : function (key, value) { _local.headers.request[key] = value; }
		},
		getResponseHeader : {
			enumerable : true,
			writable   : false,
			value      : function (key       ) { return _local.headers.response[key]; }
		},
		getAllResponseHeaders : {
			enumerable : true,
			writable   : false,
			value      : function (          ) { return _local.headers.response     ; }
		}
	});

	Object.defineProperties(this,  {
	// Non standard
		getAllRequestHeaders : {
			enumerable : true,
			writable   : false,
			value      : function (          ) { return _local.headers.request      ; }
		}
	});
};
USE IT AT YOUR OWN RISK!
Kudos: 0 [Give a kudo]

12r6j5xasef5Z6Sm4A6oBpMLn2ESPPX6ym
Ads: