all files / montage/core/ request.js

98.65% Statements 73/74
97.37% Branches 37/38
92.31% Functions 12/13
98.65% Lines 73/74
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168                                                                                                                                                             26× 14×       26× 26× 26×   26×         10× 10×   10×                
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.
 */
exports = 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;
};
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;
};