Skip to content

Commit b16d27c

Browse files
authored
upload backup region (#418)
1 parent 81166ea commit b16d27c

File tree

17 files changed

+3618
-414
lines changed

17 files changed

+3618
-414
lines changed

index.d.ts

Lines changed: 276 additions & 25 deletions
Large diffs are not rendered by default.

index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ module.exports = {
1111
httpc: {
1212
middleware: require('./qiniu/httpc/middleware'),
1313
HttpClient: require('./qiniu/httpc/client').HttpClient,
14-
ResponseWrapper: require('./qiniu/httpc/responseWrapper').ResponseWrapper
14+
ResponseWrapper: require('./qiniu/httpc/responseWrapper').ResponseWrapper,
15+
Endpoint: require('./qiniu/httpc/endpoint').Endpoint,
16+
StaticEndpointsProvider: require('./qiniu/httpc/endpointsProvider').StaticEndpointsProvider,
17+
SERVICE_NAME: require('./qiniu/httpc/region').SERVICE_NAME,
18+
Region: require('./qiniu/httpc/region').Region,
19+
StaticRegionsProvider: require('./qiniu/httpc/regionsProvider').StaticRegionsProvider,
20+
CachedRegionsProvider: require('./qiniu/httpc/regionsProvider').CachedRegionsProvider,
21+
QueryRegionsProvider: require('./qiniu/httpc/regionsProvider').QueryRegionsProvider,
22+
ChainedRegionsProvider: require('./qiniu/httpc/regionsProvider').ChainedRegionsProvider
1523
},
1624
rpc: require('./qiniu/rpc.js'),
1725
util: require('./qiniu/util.js'),
@@ -20,6 +28,6 @@ module.exports = {
2028
room: require('./qiniu/rtc/room.js'),
2129
Credentials: require('./qiniu/rtc/credentials.js'),
2230
sms: {
23-
message: require('./qiniu/sms/message.js'),
31+
message: require('./qiniu/sms/message.js')
2432
}
2533
};

qiniu/conf.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ exports.Config = function Config (options) {
5454
options = options || {};
5555
// use http or https protocol
5656
this.useHttpsDomain = !!(options.useHttpsDomain || false);
57-
// use cdn accerlated domains
57+
// use cdn accerlated domains, this is not work with auto query region
5858
this.useCdnDomain = !!(options.useCdnDomain && true);
5959
// zone of the bucket
6060
// z0 huadong, z1 huabei, z2 huanan, na0 beimei
6161
this.zone = options.zone || null;
6262
this.zoneExpire = options.zoneExpire || -1;
63+
// only available with upload for now
64+
this.regionsProvider = options.regionsProvider || null;
6365
};
6466

6567
exports.Zone = function (

qiniu/httpc/client.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ HttpClient.prototype._handleRequest = function (req) {
4949
* @property {urllib.RequestOptions} urllibOptions
5050
*/
5151

52-
/**
53-
* Wrapped result of request
54-
* @typedef {Object} RespWrapper
55-
* @property {*} data
56-
* @property {http.IncomingMessage} resp
57-
*/
58-
5952
/**
6053
*
6154
* @param {ReqOpts} requestOptions

qiniu/httpc/endpoint.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @class
3+
* @param {string} host
4+
* @param {Object} [options]
5+
* @param {string} [options.defaultScheme]
6+
* @constructor
7+
*/
8+
function Endpoint (host, options) {
9+
options = options || {};
10+
11+
this.host = host;
12+
this.defaultScheme = options.defaultScheme || 'https';
13+
}
14+
15+
/**
16+
* @param {Object} [options]
17+
* @param {string} [options.scheme]
18+
*/
19+
Endpoint.prototype.getValue = function (options) {
20+
options = options || {};
21+
22+
const scheme = options.scheme || this.defaultScheme;
23+
const host = this.host;
24+
25+
return scheme + '://' + host;
26+
};
27+
28+
exports.Endpoint = Endpoint;

qiniu/httpc/endpointsProvider.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @interface EndpointsProvider
3+
*/
4+
5+
/**
6+
* @function
7+
* @name EndpointsProvider#getEndpoints
8+
* @returns {Promise<Endpoint[]>}
9+
*/
10+
11+
/**
12+
* @interface MutableEndpointsProvider
13+
* @extends EndpointsProvider
14+
*/
15+
16+
/**
17+
* @function
18+
* @name MutableEndpointsProvider#setEndpoints
19+
* @param {endpoints: Endpoint[]} endpoints
20+
* @returns {Promise<void>}
21+
*/
22+
23+
// --- could split to files if migrate to typescript --- //
24+
25+
/**
26+
* @class
27+
* @implements EndpointsProvider
28+
* @property {Endpoint[]} endpoints
29+
* @constructor
30+
* @param {Endpoint[]} endpoints
31+
*/
32+
function StaticEndpointsProvider (endpoints) {
33+
this.endpoints = endpoints;
34+
}
35+
36+
/**
37+
* @param {Region} region
38+
* @param {string} serviceName
39+
*/
40+
StaticEndpointsProvider.fromRegion = function (region, serviceName) {
41+
return new StaticEndpointsProvider(region.services[serviceName]);
42+
};
43+
44+
/**
45+
* @returns {Promise<Endpoint[]>}
46+
*/
47+
StaticEndpointsProvider.prototype.getEndpoints = function () {
48+
return Promise.resolve(this.endpoints);
49+
};
50+
51+
exports.StaticEndpointsProvider = StaticEndpointsProvider;

qiniu/httpc/middleware/base.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ exports.Middleware = Middleware;
1919

2020
/**
2121
* @param {Middleware[]} middlewares
22-
* @param {function(ReqOpts):Promise<RespWrapper>} handler
23-
* @return {function(ReqOpts):Promise<RespWrapper>}
22+
* @param {function(ReqOpts):Promise<ResponseWrapper>} handler
23+
* @return {function(ReqOpts):Promise<ResponseWrapper>}
2424
*/
2525
exports.composeMiddlewares = function (middlewares, handler) {
2626
return middlewares.reverse()

qiniu/httpc/middleware/retryDomains.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
const middleware = require('./base');
22

3+
const URL = require('url').URL;
4+
35
/**
46
* @class
57
* @extends middleware.Middleware
68
* @param {Object} retryDomainsOptions
79
* @param {string[]} retryDomainsOptions.backupDomains
810
* @param {number} [retryDomainsOptions.maxRetryTimes]
9-
* @param {function(Error || null, RespWrapper || null, ReqOpts):boolean} [retryDomainsOptions.retryCondition]
11+
* @param {function(Error || null, ResponseWrapper || null, ReqOpts):boolean} [retryDomainsOptions.retryCondition]
1012
* @constructor
1113
*/
1214
function RetryDomainsMiddleware (retryDomainsOptions) {
@@ -23,7 +25,7 @@ RetryDomainsMiddleware.prototype.constructor = RetryDomainsMiddleware;
2325
/**
2426
* @memberOf RetryDomainsMiddleware
2527
* @param {Error || null} err
26-
* @param {RespWrapper || null} respWrapper
28+
* @param {ResponseWrapper || null} respWrapper
2729
* @param {ReqOpts} reqOpts
2830
* @return {boolean}
2931
* @private
@@ -39,7 +41,7 @@ RetryDomainsMiddleware.prototype._shouldRetry = function (err, respWrapper, reqO
3941
/**
4042
* @memberOf RetryDomainsMiddleware
4143
* @param {ReqOpts} reqOpts
42-
* @param {function(ReqOpts):Promise<RespWrapper>} next
44+
* @param {function(ReqOpts):Promise<ResponseWrapper>} next
4345
* @return {Promise<RespWrapper>}
4446
*/
4547
RetryDomainsMiddleware.prototype.send = function (reqOpts, next) {
@@ -56,7 +58,10 @@ RetryDomainsMiddleware.prototype.send = function (reqOpts, next) {
5658

5759
if (domains.length) {
5860
this._retriedTimes = 0;
59-
url.hostname = domains.shift();
61+
const domain = domains.shift();
62+
const [hostname, port] = domain.split(':');
63+
url.hostname = hostname;
64+
url.port = port || url.port;
6065
reqOpts.url = url.toString();
6166
return true;
6267
}

qiniu/httpc/region.js

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
const { Endpoint } = require('./endpoint');
2+
3+
/**
4+
* @readonly
5+
* @enum {string}
6+
*/
7+
const SERVICE_NAME = {
8+
UC: 'uc',
9+
UP: 'up',
10+
IO: 'io',
11+
RS: 'rs',
12+
RSF: 'rsf',
13+
API: 'api',
14+
S3: 's3'
15+
};
16+
17+
// --- could split to files if migrate to typescript --- //
18+
19+
/**
20+
* @typedef {SERVICE_NAME | string} ServiceKey
21+
*/
22+
23+
/**
24+
* @param {Object} options
25+
* @param {string} [options.regionId]
26+
* @param {string} [options.s3RegionId]
27+
* @param {Object.<ServiceKey, Endpoint[]>} [options.services]
28+
* @param {number} [options.ttl] seconds. default 1 day.
29+
* @param {Date} [options.createTime]
30+
* @constructor
31+
*/
32+
function Region (options) {
33+
this.regionId = options.regionId;
34+
this.s3RegionId = options.s3RegionId || options.regionId;
35+
36+
this.services = options.services || {};
37+
// use Object.values when min version of Node.js update to ≥ v7.5.0
38+
Object.keys(SERVICE_NAME).map(k => {
39+
const v = SERVICE_NAME[k];
40+
if (!Array.isArray(this.services[v])) {
41+
this.services[v] = [];
42+
}
43+
});
44+
45+
this.ttl = options.ttl || 86400;
46+
this.createTime = options.createTime || new Date();
47+
}
48+
49+
/**
50+
* This is used to be compatible with Zone.
51+
* So this function will be removed after remove Zone.
52+
* NOTE: The Region instance obtained using this method
53+
* can only be used for the following services: up, io, rs, rsf, api.
54+
* Because the Zone not support other services.
55+
* @param {conf.Zone} zone
56+
* @param {Object} [options]
57+
* @param {string} [options.regionId]
58+
* @param {string} [options.s3RegionId]
59+
* @param {number} [options.ttl]
60+
* @param {boolean} [options.isPreferCdnHost]
61+
*/
62+
Region.fromZone = function (zone, options) {
63+
options = options || {};
64+
options.ttl = options.ttl || -1;
65+
66+
const upHosts = options.isPreferCdnHost
67+
? zone.cdnUpHosts.concat(zone.srcUpHosts)
68+
: zone.srcUpHosts.concat(zone.cdnUpHosts);
69+
70+
const services = {
71+
// use array destructure if migrate to typescript
72+
[SERVICE_NAME.UP]: upHosts.map(
73+
h => new Endpoint(h)
74+
),
75+
[SERVICE_NAME.IO]: [
76+
new Endpoint(zone.ioHost)
77+
],
78+
[SERVICE_NAME.RS]: [
79+
new Endpoint(zone.rsHost)
80+
],
81+
[SERVICE_NAME.RSF]: [
82+
new Endpoint(zone.rsfHost)
83+
],
84+
[SERVICE_NAME.API]: [
85+
new Endpoint(zone.apiHost)
86+
]
87+
};
88+
89+
return new Region({
90+
regionId: options.regionId,
91+
s3RegionId: options.s3RegionId || options.regionId,
92+
services: services,
93+
ttl: options.ttl
94+
});
95+
};
96+
97+
/**
98+
* @param {string} regionId
99+
* @param {Object} [options]
100+
* @param {string} [options.s3RegionId]
101+
* @param {number} [options.ttl]
102+
* @param {Date} [options.createTime]
103+
* @param {Object.<ServiceKey, Endpoint[]>} [options.extendedServices]
104+
* @returns {Region}
105+
*/
106+
Region.fromRegionId = function (regionId, options) {
107+
options = options || {};
108+
109+
const s3RegionId = options.s3RegionId || regionId;
110+
const ttl = options.ttl;
111+
const createTime = options.createTime;
112+
113+
const isZ0 = regionId === 'z0';
114+
115+
/**
116+
* @type {Object.<ServiceKey, Endpoint[]>}
117+
*/
118+
let services = {
119+
[SERVICE_NAME.UC]: [
120+
new Endpoint('uc.qiniuapi.com')
121+
],
122+
[SERVICE_NAME.UP]: isZ0
123+
? [
124+
new Endpoint('upload.qiniup.com'),
125+
new Endpoint('up.qiniup.com'),
126+
new Endpoint('up.qbox.me')
127+
]
128+
: [
129+
new Endpoint('upload-' + regionId + '.qiniup.com'),
130+
new Endpoint('up-' + regionId + '.qiniup.com'),
131+
new Endpoint('up-' + regionId + '.qbox.me')
132+
],
133+
[SERVICE_NAME.IO]: isZ0
134+
? [
135+
new Endpoint('iovip.qiniuio.com'),
136+
new Endpoint('iovip.qbox.me')
137+
]
138+
: [
139+
new Endpoint('iovip-' + regionId + '.qiniuio.com'),
140+
new Endpoint('iovip-' + regionId + '.qbox.me')
141+
],
142+
[SERVICE_NAME.RS]: [
143+
new Endpoint('rs-' + regionId + '.qiniuapi.com'),
144+
new Endpoint('rs-' + regionId + '.qbox.me')
145+
],
146+
[SERVICE_NAME.RSF]: [
147+
new Endpoint('rsf-' + regionId + '.qiniuapi.com'),
148+
new Endpoint('rsf-' + regionId + '.qbox.me')
149+
],
150+
[SERVICE_NAME.API]: [
151+
new Endpoint('api-' + regionId + '.qiniuapi.com'),
152+
new Endpoint('api-' + regionId + '.qbox.me')
153+
],
154+
[SERVICE_NAME.S3]: [
155+
new Endpoint('s3.' + s3RegionId + '.qiniucs.com')
156+
]
157+
};
158+
159+
services = Object.assign(services, options.extendedServices || {});
160+
161+
return new Region({
162+
regionId: regionId,
163+
s3RegionId: s3RegionId,
164+
services: services,
165+
ttl: ttl,
166+
createTime: createTime
167+
});
168+
};
169+
170+
Object.defineProperty(Region.prototype, 'isLive', {
171+
get: function () {
172+
if (this.ttl < 0) {
173+
return true;
174+
}
175+
// convert ms to s
176+
const liveTime = Math.round((Date.now() - this.createTime) / 1000);
177+
return liveTime < this.ttl;
178+
},
179+
enumerable: false,
180+
configurable: true
181+
});
182+
183+
exports.SERVICE_NAME = SERVICE_NAME;
184+
exports.Region = Region;

0 commit comments

Comments
 (0)