@nathanfaucett/request
Version:
http requests for the browser and node.js
213 lines (176 loc) • 6.3 kB
JavaScript
var once = require("@nathanfaucett/once"),
PromisePolyfill = require("@nathanfaucett/promise_polyfill"),
XMLHttpRequestPolyfill = require("@nathanfaucett/xmlhttprequest_polyfill"),
isFunction = require("@nathanfaucett/is_function"),
isString = require("@nathanfaucett/is_string"),
isNull = require("@nathanfaucett/is_null"),
objectForEach = require("@nathanfaucett/object-for_each"),
trim = require("@nathanfaucett/trim"),
extend = require("@nathanfaucett/extend"),
Response = require("./Response"),
defaults = require("./defaults"),
camelcaseHeader = require("./camelcaseHeader"),
parseContentType = require("./parseContentType");
var supportsFormData = typeof FormData !== "undefined";
defaults.values.XMLHttpRequest = XMLHttpRequestPolyfill;
defaults.values.xmlHttpRequestOptions = {};
module.exports = request;
function request(options) {
var plugins = request.plugins,
defer = null,
xhr,
canSetRequestHeader,
canOverrideMimeType,
isFormData,
onComplete;
options = defaults(options);
xhr = new options.XMLHttpRequest(options.xmlHttpRequestOptions);
canSetRequestHeader = isFunction(xhr.setRequestHeader);
canOverrideMimeType = isFunction(xhr.overrideMimeType);
plugins.emit("before", xhr, options);
isFormData = supportsFormData && options.data instanceof FormData;
if (options.isPromise) {
defer = PromisePolyfill.defer();
}
function onSuccess(response) {
plugins.emit("response", response, xhr, options);
plugins.emit("load", response, xhr, options);
if (options.isPromise) {
defer.resolve(response);
} else {
if (isFunction(options.success)) {
options.success(response);
}
}
}
function onError(response) {
plugins.emit("response", response, xhr, options);
plugins.emit("error", response, xhr, options);
if (options.isPromise) {
defer.reject(response);
} else {
if (isFunction(options.error)) {
options.error(response);
}
}
}
onComplete = once(function onComplete() {
var statusCode = +xhr.status,
responseText = xhr.responseText,
response = new Response();
response.url = xhr.responseURL || options.url;
response.method = options.method;
response.statusCode = statusCode;
response.responseHeaders = xhr.getAllResponseHeaders
? parseResponseHeaders(xhr.getAllResponseHeaders())
: {};
response.requestHeaders = options.headers
? extend({}, options.headers)
: {};
response.body = response.data = null;
if (responseText) {
if (options.transformResponse) {
response.body = response.data = options.transformResponse(
responseText
);
} else {
if (
parseContentType(
response.responseHeaders["Content-Type"]
) === "application/json"
) {
try {
response.body = response.data = JSON.parse(
responseText
);
} catch (e) {
response.body = response.data = e;
onError(response);
return;
}
} else if (responseText) {
response.body = response.data = responseText;
}
}
}
if ((statusCode > 199 && statusCode < 301) || statusCode === 304) {
onSuccess(response);
} else {
onError(response);
}
});
function onReadyStateChange() {
switch (+xhr.readyState) {
case 1:
plugins.emit("request", xhr, options);
break;
case 4:
onComplete();
break;
}
}
addEventListener(xhr, "readystatechange", onReadyStateChange);
if (options.withCredentials && options.async) {
xhr.withCredentials = options.withCredentials;
}
xhr.open(
options.method,
options.url,
options.async,
options.username,
options.password
);
if (canSetRequestHeader) {
if (options.headers && options.headers["Content-Type"] && isFormData) {
delete options.headers["Content-Type"];
}
objectForEach(Object(options.headers), function(value, key) {
if (isString(value)) {
if (key === "Content-Type" && canOverrideMimeType) {
xhr.overrideMimeType(value);
}
xhr.setRequestHeader(key, value);
}
});
}
if (options.transformRequest) {
options.data = options.transformRequest(options.data);
} else if (options.data) {
if (!isString(options.data) && !isFormData) {
if (options.headers["Content-Type"] === "application/json") {
options.data = JSON.stringify(options.data);
} else {
options.data = options.data + "";
}
}
}
xhr.send(options.data);
return isNull(defer) ? undefined : defer.promise;
}
function parseResponseHeaders(responseHeaders) {
var headers = {},
raw = responseHeaders.split("\n");
objectForEach(Object(raw), function(header) {
var tmp = header.split(":"),
key = tmp[0],
value = tmp[1];
if (key && value) {
key = camelcaseHeader(key);
value = trim(value);
if (key === "Content-Length") {
value = +value;
}
headers[key] = value;
}
});
return headers;
}
function addEventListener(xhr, event, listener) {
if (isFunction(xhr.addEventListener)) {
xhr.addEventListener(event, listener, false);
} else if (isFunction(xhr.attachEvent)) {
xhr.attachEvent("on" + event, listener);
} else {
xhr["on" + event] = listener;
}
}