flyio
Version:
Lightweigh Promise based HTTP client for the browser and node.js
182 lines (153 loc) • 6.24 kB
JavaScript
/*
* author: wendu
* email: 824783146@qq.com
**/
let util = require('./utils/utils')
let isBrowser = typeof document !== "undefined";
//EngineWrapper can help generating a http engine quickly through a adapter
function EngineWrapper(adapter) {
class AjaxEngine {
constructor() {
this.requestHeaders = {};
this.readyState = 0;
this.timeout = 0;// 0 stands for no timeout
this.responseURL = "";
this.responseHeaders = {};
}
_call(name) {
this[name] && this[name].apply(this, [].splice.call(arguments, 1))
}
_changeReadyState(state) {
this.readyState = state;
this._call("onreadystatechange")
}
open(method, url) {
this.method = method;
if (!url) {
url = location.href;
} else {
url = util.trim(url);
if (url.indexOf("http") !== 0) {
// Normalize the request url
if (isBrowser) {
let t = document.createElement("a");
t.href = url;
url = t.href;
}
}
}
this.responseURL = url;
this._changeReadyState(1)
}
send(arg) {
arg = arg || null;
let self = this;
if (adapter) {
let request = {
method: self.method,
url: self.responseURL,
headers: self.requestHeaders || {},
body: arg
}
util.merge(request, self._options || {})
if (request.method === "GET") {
request.body = null;
}
self._changeReadyState(3)
let timer;
self.timeout = self.timeout || 0;
if (self.timeout > 0) {
timer = setTimeout(() => {
if (self.readyState === 3) {
this._call("onloadend");
self._changeReadyState(0);
self._call("ontimeout");
}
}, self.timeout);
}
request.timeout = self.timeout;
adapter(request, function (response) {
function getAndDelete(key) {
let t = response[key]
delete response[key]
return t;
}
// If the request has already timeout, return
if (self.readyState !== 3) return;
clearTimeout(timer)
// Make sure the type of status is integer
self.status = getAndDelete("statusCode") - 0;
let responseText = getAndDelete("responseText")
let statusMessage = getAndDelete("statusMessage")
// Network error, set the status code 0
if (!self.status) {
self.statusText = responseText;
self._call("onerror", {msg: statusMessage});
} else {
// Parsing the response headers to array in a object, because
// there may be multiple values with the same header name
let responseHeaders = getAndDelete("headers")
let headers = {};
for (let field in responseHeaders) {
let value = responseHeaders[field];
let key = field.toLowerCase();
// Is array
if (typeof value === "object") {
headers[key] = value;
} else {
headers[key] = headers[key] || []
headers[key].push(value)
}
}
let cookies = headers["set-cookie"];
if (isBrowser && cookies) {
cookies.forEach((e) => {
// Remove the http-Only property of the cookie
// so that JavaScript can operate it.
document.cookie = e.replace(/;\s*httpOnly/ig, "")
})
}
self.responseHeaders = headers;
// Set the fields of engine from response
self.statusText = statusMessage || "";
self.response = self.responseText = responseText;
self._response = response;
self._changeReadyState(4);
self._call("onload");
}
self._call("onloadend");
});
} else {
console.error("Ajax require adapter")
}
}
setRequestHeader(key, value) {
this.requestHeaders[util.trim(key)] = value;
}
getResponseHeader(key) {
return (this.responseHeaders[key.toLowerCase()] || "").toString() || null
}
getAllResponseHeaders() {
let str = "";
for (let key in this.responseHeaders) {
str += key + ":" + this.getResponseHeader(key) + "\r\n";
}
return str || null;
}
abort(msg) {
this._changeReadyState(0)
this._call("onerror", {msg});
this._call("onloadend");
}
static setAdapter(requestAdapter) {
adapter = requestAdapter
}
}
return AjaxEngine;
}
// learn more about keep-loader: https://github.com/wendux/keep-loader
KEEP("cdn||cdn-min", () => {
// This code block will be removed besides the "CDN" and "cdn-min" build environment
window.EngineWrapper = EngineWrapper
})
module.exports = EngineWrapper;