-
Notifications
You must be signed in to change notification settings - Fork 55
/
http.js
144 lines (126 loc) · 4.9 KB
/
http.js
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
import Utils from '../../../common/lib/util/utils';
import Defaults from '../../../common/lib/util/defaults';
var Http = (function() {
var noop = function() {};
function Http() {}
var now = Date.now || function() {
/* IE 8 */
return new Date().getTime();
};
function shouldFallback(err) {
var statusCode = err.statusCode;
/* 400 + no code = a generic xhr onerror. Browser doesn't give us enough
* detail to know whether it's fallback-fixable, but it may be (eg if a
* network issue), so try just in case */
return (statusCode === 408 && !err.code) ||
(statusCode === 400 && !err.code) ||
(statusCode >= 500 && statusCode <= 504);
}
function getHosts(client) {
/* If we're a connected realtime client, try the endpoint we're connected
* to first -- but still have fallbacks, being connected is not an absolute
* guarantee that a datacenter has free capacity to service REST requests. */
var connection = client.connection,
connectionHost = connection && connection.connectionManager.host;
if(connectionHost) {
return [connectionHost].concat(Defaults.getFallbackHosts(client.options));
}
return Defaults.getHosts(client.options);
}
Http._getHosts = getHosts;
Http.methods = ['get', 'delete', 'post', 'put', 'patch'];
Http.methodsWithoutBody = ['get', 'delete'];
Http.methodsWithBody = Utils.arrSubtract(Http.methods, Http.methodsWithoutBody);
/* - Http.get, Http.post, Http.put, ...
* Perform an HTTP request for a given path against prime and fallback Ably hosts
* @param rest
* @param path the full path
* @param headers optional hash of headers
* [only for methods with body: @param body object or buffer containing request body]
* @param params optional hash of params
* @param callback (err, response)
*
* - Http.getUri, Http.postUri, Http.putUri, ...
* Perform an HTTP request for a given full URI
* @param rest
* @param uri the full URI
* @param headers optional hash of headers
* [only for methods with body: @param body object or buffer containing request body]
* @param params optional hash of params
* @param callback (err, response)
*/
Utils.arrForEach(Http.methodsWithoutBody, function(method) {
Http[method] = function(rest, path, headers, params, callback) {
Http['do'](method, rest, path, headers, null, params, callback);
};
Http[method + 'Uri'] = function(rest, uri, headers, params, callback) {
Http.doUri(method, rest, uri, headers, null, params, callback);
};
});
Utils.arrForEach(Http.methodsWithBody, function(method) {
Http[method] = function(rest, path, headers, body, params, callback) {
Http['do'](method, rest, path, headers, body, params, callback);
};
Http[method + 'Uri'] = function(rest, uri, headers, body, params, callback) {
Http.doUri(method, rest, uri, headers, body, params, callback);
};
});
/* Unlike for doUri, the 'rest' param here is mandatory, as it's used to generate the hosts */
Http['do'] = function(method, rest, path, headers, body, params, callback) {
callback = callback || noop;
var uriFromHost = (typeof(path) == 'function') ? path : function(host) { return rest.baseUri(host) + path; };
var binary = (headers && headers.accept != 'application/json');
var doArgs = arguments;
var currentFallback = rest._currentFallback;
if(currentFallback) {
if(currentFallback.validUntil > now()) {
/* Use stored fallback */
Http.Request(method, rest, uriFromHost(currentFallback.host), headers, params, body, function(err) {
if(err && shouldFallback(err)) {
/* unstore the fallback and start from the top with the default sequence */
rest._currentFallback = null;
Http['do'].apply(Http, doArgs);
return;
}
callback.apply(null, arguments);
});
return;
} else {
/* Fallback expired; remove it and fallthrough to normal sequence */
rest._currentFallback = null;
}
}
var hosts = getHosts(rest);
/* if there is only one host do it */
if(hosts.length == 1) {
Http.doUri(method, rest, uriFromHost(hosts[0]), headers, body, params, callback);
return;
}
/* hosts is an array with preferred host plus at least one fallback */
var tryAHost = function(candidateHosts, persistOnSuccess) {
var host = candidateHosts.shift();
Http.doUri(method, rest, uriFromHost(host), headers, body, params, function(err) {
if(err && shouldFallback(err) && candidateHosts.length) {
tryAHost(candidateHosts, true);
return;
}
if(persistOnSuccess) {
/* RSC15f */
rest._currentFallback = {
host: host,
validUntil: now() + rest.options.timeouts.fallbackRetryTimeout
};
}
callback.apply(null, arguments);
});
};
tryAHost(hosts);
};
Http.doUri = function(method, rest, uri, headers, body, params, callback) {
Http.Request(method, rest, uri, headers, params, body, callback);
};
Http.supportsAuthHeaders = false;
Http.supportsLinkHeaders = false;
return Http;
})();
export default Http;