Computer
NodeJS XMLHttpRequest
A simple implementation to use XMLHttpRequest on NodeJS.
TODO:
- Implement XMLHttpRequest.responseURL;
- Implement XMLHttpRequest.overrideMimeType() method;
- Get the XMLHttpRequest.responseType;
- 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!