# Source: core/request.js

    var Promise = require("./promise").Promise;

    /**
     * Makes an XHR request.
     *
     * @example
     * request("http://example.com")
     *
     * @param  {string|object} request A url, or request object
     * @param {string} request.url          The URL to request.
     * @param {string} [request.method]     The request method, such as "GET" or "POST"
     * @param {object} [request.headers]    An object mapping from header name to value.
     * The value can be an array to set the same header multiple times.
     * @param {any} [request.body]          The body of the request to send.
     * @param {string} [request.overrideMimeType] Override the return MIME-type of the request
     * @param {object} [request.options]    An object of properties to set on the XHR object, such as `responseType` or `withCredentials`
     * @param {object} [request.xhr]        An existing XMLHttpRequest object to use.
     * @returns {Promise.<object>}           A promise for a response object
     * containing `status`, `headers`, `body` and `xhr` properties.
     */
     module.exports = function doRequest(request) {
        request = exports.normalizeRequest(request);
        var done = new Promise(function(resolve, reject) {
            var xhr = request.xhr || new XMLHttpRequest();
            xhr.open(request.method, request.url, true);

            xhr.onload = function() {
                var response = {
                    status: xhr.status,
                    headers: exports.parseResponseHeaders(xhr.getAllResponseHeaders()),
                    body: xhr.response,

                    xhr: xhr
                };

                resolve(response);
            };
            xhr.onerror = function() {
                reject(new Error("Could not load"));
            };

            var headers = request.headers;
            //jshint -W089
            for (var name in headers) {
                var value = headers[name];
                // The header value can be an array, in which case set all of them
                if (Array.isArray(value)) {
                    for (var i = 0; i < value.length; i++) {
                        xhr.setRequestHeader(name, value[i]);
                    }
                } else {
                    xhr.setRequestHeader(name, value);
                }
            }

            // Allow any options to be set on the XHR
            var options = request.options;
            for (var o in options) {
                xhr[o] = options[o];
            }

            // Method can't be passed into options
            if (request.overrideMimeType) {
                xhr.overrideMimeType(request.overrideMimeType);
            }
            //jshint +W089

            xhr.send(request.body);
        });

        return done;
    };

    /* global exports: true */
    exports =  module.exports;
    exports.request = exports;

    exports.makeOk = function (next) {
        return function ok(request) {
            request = exports.normalizeRequest(request);
            var url = request.url;

            return Promise.resolve(next(request)).then(function (response) {
                if (response.status >= 200 && response.status < 300) {
                    return response;
                } else {
                    var error = new Error("Could not load " + JSON.stringify(url) + ": " + response.status + " " + response.xhr.statusText);
                    error.response = response;
                    throw error;
                }
            });
        };
    };
    /**
     * Makes an XHR request and only resolves the promise if the response status
     * is 2xx, otherwise it is rejected. The rejected Error object has a `response`
     * property containing the response.
     * @param  {string|object} request See documentation for `request`
     * @returns {Promise.<object>}      See documentation for `request`
     */
    exports.ok = exports.makeOk(exports.request);

    exports.makeJson = function (next) {
        return function json(request) {
            request = exports.normalizeRequest(request);
            var url = request.url;

            request.headers.accept = request.headers.accept || "application/json";
            request.headers["content-type"] = request.headers["content-type"] || "application/json";

            if (typeof request.body === "object") {
                request.body = JSON.stringify(request.body);
            }

            request.overrideMimeType = request.overrideMimeType || "application/json";
            request.options.responseType = request.options.responseType || "json";

            return Promise.resolve(next(request)).then(function (response) {
                // If response.body is null then the JSON.parse failed, so do it
                // ourselves to get a informative error
                if (response.body === null || typeof response.body === "string") {
                    try {
                        response.body = JSON.parse(response.body);
                    } catch (error) {
                        throw new Error("Could not parse JSON from " + JSON.stringify(url) + ": " + error.message);
                    }
                }

                return response;
            });
        };
    };

    exports.json = exports.makeJson(exports.request);

    exports.normalizeRequest = function (request) {
        if (typeof request === "string") {
            request = {
                url: request
            };
        }
        request.method = request.method || "GET";
        request.headers = request.headers || {};
        request.options = request.options || {};

        return request;
    };

    exports.parseResponseHeaders = function (headerString) {
        var headers = {};
        if (!headerString) {
            return headers;
        }

        headerString.replace(/^([^:]+):(.*)$/gm, function (_, name, value) {
            name = name.trim().toLowerCase();
            value = value.trim();

            if (name in headers) {
                // Put multiple headers of the same name into an array
                if (typeof headers[name] === "string") {
                    headers[name] = [headers[name]];
                }
                headers[name].push(value);
            } else {
                headers[name] = value;
            }
        });

        return headers;
    };