租房小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

463 lines
13 KiB

3 months ago
  1. const debug = require('debug')('ali-oss');
  2. const xml = require('xml2js');
  3. const AgentKeepalive = require('agentkeepalive');
  4. const merge = require('merge-descriptors');
  5. const platform = require('platform');
  6. const utility = require('utility');
  7. const urllib = require('urllib');
  8. const pkg = require('./version');
  9. const bowser = require('bowser');
  10. const signUtils = require('../common/signUtils');
  11. const _initOptions = require('../common/client/initOptions');
  12. const { createRequest } = require('../common/utils/createRequest');
  13. const { encoder } = require('../common/utils/encoder');
  14. const { getReqUrl } = require('../common/client/getReqUrl');
  15. const { setSTSToken } = require('../common/utils/setSTSToken');
  16. const { retry } = require('../common/utils/retry');
  17. const { isFunction } = require('../common/utils/isFunction');
  18. const { getStandardRegion } = require('../common/utils/getStandardRegion');
  19. const globalHttpAgent = new AgentKeepalive();
  20. function _unSupportBrowserTip() {
  21. const { name, version } = platform;
  22. if (name && name.toLowerCase && name.toLowerCase() === 'ie' && version.split('.')[0] < 10) {
  23. // eslint-disable-next-line no-console
  24. console.warn('ali-oss does not support the current browser');
  25. }
  26. }
  27. // check local web protocol,if https secure default set true , if http secure default set false
  28. function isHttpsWebProtocol() {
  29. // for web worker not use window.location.
  30. // eslint-disable-next-line no-restricted-globals
  31. return location && location.protocol === 'https:';
  32. }
  33. function Client(options, ctx) {
  34. _unSupportBrowserTip();
  35. if (!(this instanceof Client)) {
  36. return new Client(options, ctx);
  37. }
  38. if (options && options.inited) {
  39. this.options = options;
  40. } else {
  41. this.options = Client.initOptions(options);
  42. }
  43. this.options.cancelFlag = false; // cancel flag: if true need to be cancelled, default false
  44. // support custom agent and urllib client
  45. if (this.options.urllib) {
  46. this.urllib = this.options.urllib;
  47. } else {
  48. this.urllib = urllib;
  49. this.agent = this.options.agent || globalHttpAgent;
  50. }
  51. this.ctx = ctx;
  52. this.userAgent = this._getUserAgent();
  53. this.stsTokenFreshTime = new Date();
  54. // record the time difference between client and server
  55. this.options.amendTimeSkewed = 0;
  56. }
  57. /**
  58. * Expose `Client`
  59. */
  60. module.exports = Client;
  61. Client.initOptions = function initOptions(options) {
  62. if (!options.stsToken) {
  63. console.warn(
  64. 'Please use STS Token for safety, see more details at https://help.aliyun.com/document_detail/32077.html'
  65. );
  66. }
  67. const opts = Object.assign(
  68. {
  69. secure: isHttpsWebProtocol(),
  70. // for browser compatibility disable fetch.
  71. useFetch: false
  72. },
  73. options
  74. );
  75. return _initOptions(opts);
  76. };
  77. /**
  78. * prototype
  79. */
  80. const proto = Client.prototype;
  81. // mount debug on proto
  82. proto.debug = debug;
  83. /**
  84. * Object operations
  85. */
  86. merge(proto, require('./object'));
  87. /**
  88. * Bucket operations
  89. */
  90. merge(proto, require('./bucket'));
  91. merge(proto, require('../common/bucket/getBucketWebsite'));
  92. merge(proto, require('../common/bucket/putBucketWebsite'));
  93. merge(proto, require('../common/bucket/deleteBucketWebsite'));
  94. // lifecycle
  95. merge(proto, require('../common/bucket/getBucketLifecycle'));
  96. merge(proto, require('../common/bucket/putBucketLifecycle'));
  97. merge(proto, require('../common/bucket/deleteBucketLifecycle'));
  98. // multiversion
  99. merge(proto, require('../common/bucket/putBucketVersioning'));
  100. merge(proto, require('../common/bucket/getBucketVersioning'));
  101. // inventory
  102. merge(proto, require('../common/bucket/getBucketInventory'));
  103. merge(proto, require('../common/bucket/deleteBucketInventory'));
  104. merge(proto, require('../common/bucket/listBucketInventory'));
  105. merge(proto, require('../common/bucket/putBucketInventory'));
  106. // worm
  107. merge(proto, require('../common/bucket/abortBucketWorm'));
  108. merge(proto, require('../common/bucket/completeBucketWorm'));
  109. merge(proto, require('../common/bucket/extendBucketWorm'));
  110. merge(proto, require('../common/bucket/getBucketWorm'));
  111. merge(proto, require('../common/bucket/initiateBucketWorm'));
  112. // multipart upload
  113. merge(proto, require('./managed-upload'));
  114. /**
  115. * common multipart-copy support node and browser
  116. */
  117. merge(proto, require('../common/multipart-copy'));
  118. /**
  119. * Multipart operations
  120. */
  121. merge(proto, require('../common/multipart'));
  122. /**
  123. * Common module parallel
  124. */
  125. merge(proto, require('../common/parallel'));
  126. /**
  127. * get OSS signature
  128. * @param {String} stringToSign
  129. * @return {String} the signature
  130. */
  131. proto.signature = function signature(stringToSign) {
  132. this.debug('authorization stringToSign: %s', stringToSign, 'info');
  133. return signUtils.computeSignature(this.options.accessKeySecret, stringToSign, this.options.headerEncoding);
  134. };
  135. proto._getReqUrl = getReqUrl;
  136. /**
  137. * get author header
  138. *
  139. * "Authorization: OSS " + Access Key Id + ":" + Signature
  140. *
  141. * Signature = base64(hmac-sha1(Access Key Secret + "\n"
  142. * + VERB + "\n"
  143. * + CONTENT-MD5 + "\n"
  144. * + CONTENT-TYPE + "\n"
  145. * + DATE + "\n"
  146. * + CanonicalizedOSSHeaders
  147. * + CanonicalizedResource))
  148. *
  149. * @param {String} method
  150. * @param {String} resource
  151. * @param {Object} header
  152. * @return {String}
  153. *
  154. * @api private
  155. */
  156. proto.authorization = function authorization(method, resource, subres, headers) {
  157. const stringToSign = signUtils.buildCanonicalString(method.toUpperCase(), resource, {
  158. headers,
  159. parameters: subres
  160. });
  161. return signUtils.authorization(
  162. this.options.accessKeyId,
  163. this.options.accessKeySecret,
  164. stringToSign,
  165. this.options.headerEncoding
  166. );
  167. };
  168. /**
  169. * get authorization header v4
  170. *
  171. * @param {string} method
  172. * @param {Object} requestParams
  173. * @param {Object} requestParams.headers
  174. * @param {(string|string[]|Object)} [requestParams.queries]
  175. * @param {string} [bucketName]
  176. * @param {string} [objectName]
  177. * @param {string[]} [additionalHeaders]
  178. * @return {string}
  179. *
  180. * @api private
  181. */
  182. proto.authorizationV4 = function authorizationV4(method, requestParams, bucketName, objectName, additionalHeaders) {
  183. return signUtils.authorizationV4(
  184. this.options.accessKeyId,
  185. this.options.accessKeySecret,
  186. getStandardRegion(this.options.region),
  187. method,
  188. requestParams,
  189. bucketName,
  190. objectName,
  191. additionalHeaders,
  192. this.options.headerEncoding
  193. );
  194. };
  195. /**
  196. * request oss server
  197. * @param {Object} params
  198. * - {String} object
  199. * - {String} bucket
  200. * - {Object} [headers]
  201. * - {Object} [query]
  202. * - {Buffer} [content]
  203. * - {Stream} [stream]
  204. * - {Stream} [writeStream]
  205. * - {String} [mime]
  206. * - {Boolean} [xmlResponse]
  207. * - {Boolean} [customResponse]
  208. * - {Number} [timeout]
  209. * - {Object} [ctx] request context, default is `this.ctx`
  210. *
  211. * @api private
  212. */
  213. proto.request = async function (params) {
  214. if (this.options.retryMax) {
  215. return await retry(request.bind(this), this.options.retryMax, {
  216. errorHandler: err => {
  217. const _errHandle = _err => {
  218. if (params.stream) return false;
  219. const statusErr = [-1, -2].includes(_err.status);
  220. const requestErrorRetryHandle = this.options.requestErrorRetryHandle || (() => true);
  221. return statusErr && requestErrorRetryHandle(_err);
  222. };
  223. if (_errHandle(err)) return true;
  224. return false;
  225. }
  226. })(params);
  227. } else {
  228. return request.call(this, params);
  229. }
  230. };
  231. async function request(params) {
  232. if (this.options.stsToken && isFunction(this.options.refreshSTSToken)) {
  233. await setSTSToken.call(this);
  234. }
  235. const reqParams = createRequest.call(this, params);
  236. if (!this.options.useFetch) {
  237. reqParams.params.mode = 'disable-fetch';
  238. }
  239. let result;
  240. let reqErr;
  241. const useStream = !!params.stream;
  242. try {
  243. result = await this.urllib.request(reqParams.url, reqParams.params);
  244. this.debug(
  245. 'response %s %s, got %s, headers: %j',
  246. params.method,
  247. reqParams.url,
  248. result.status,
  249. result.headers,
  250. 'info'
  251. );
  252. } catch (err) {
  253. reqErr = err;
  254. }
  255. let err;
  256. if (result && params.successStatuses && params.successStatuses.indexOf(result.status) === -1) {
  257. err = await this.requestError(result);
  258. // not use stream
  259. if (err.code === 'RequestTimeTooSkewed' && !useStream) {
  260. this.options.amendTimeSkewed = +new Date(err.serverTime) - new Date();
  261. return await this.request(params);
  262. }
  263. err.params = params;
  264. } else if (reqErr) {
  265. err = await this.requestError(reqErr);
  266. }
  267. if (err) {
  268. throw err;
  269. }
  270. if (params.xmlResponse) {
  271. const parseData = await this.parseXML(result.data);
  272. result.data = parseData;
  273. }
  274. return result;
  275. }
  276. proto._getResource = function _getResource(params) {
  277. let resource = '/';
  278. if (params.bucket) resource += `${params.bucket}/`;
  279. if (params.object) resource += encoder(params.object, this.options.headerEncoding);
  280. return resource;
  281. };
  282. proto._escape = function _escape(name) {
  283. return utility.encodeURIComponent(name).replace(/%2F/g, '/');
  284. };
  285. /*
  286. * Get User-Agent for browser & node.js
  287. * @example
  288. * aliyun-sdk-nodejs/4.1.2 Node.js 5.3.0 on Darwin 64-bit
  289. * aliyun-sdk-js/4.1.2 Safari 9.0 on Apple iPhone(iOS 9.2.1)
  290. * aliyun-sdk-js/4.1.2 Chrome 43.0.2357.134 32-bit on Windows Server 2008 R2 / 7 64-bit
  291. */
  292. proto._getUserAgent = function _getUserAgent() {
  293. const agent = process && process.browser ? 'js' : 'nodejs';
  294. const sdk = `aliyun-sdk-${agent}/${pkg.version}`;
  295. let plat = platform.description;
  296. if (!plat && process) {
  297. plat = `Node.js ${process.version.slice(1)} on ${process.platform} ${process.arch}`;
  298. }
  299. return this._checkUserAgent(`${sdk} ${plat}`);
  300. };
  301. proto._checkUserAgent = function _checkUserAgent(ua) {
  302. const userAgent = ua.replace(/\u03b1/, 'alpha').replace(/\u03b2/, 'beta');
  303. return userAgent;
  304. };
  305. /*
  306. * Check Browser And Version
  307. * @param {String} [name] browser name: like IE, Chrome, Firefox
  308. * @param {String} [version] browser major version: like 10(IE 10.x), 55(Chrome 55.x), 50(Firefox 50.x)
  309. * @return {Bool} true or false
  310. * @api private
  311. */
  312. proto.checkBrowserAndVersion = function checkBrowserAndVersion(name, version) {
  313. return bowser.name === name && bowser.version.split('.')[0] === version;
  314. };
  315. /**
  316. * thunkify xml.parseString
  317. * @param {String|Buffer} str
  318. *
  319. * @api private
  320. */
  321. proto.parseXML = function parseXMLThunk(str) {
  322. return new Promise((resolve, reject) => {
  323. if (Buffer.isBuffer(str)) {
  324. str = str.toString();
  325. }
  326. xml.parseString(
  327. str,
  328. {
  329. explicitRoot: false,
  330. explicitArray: false
  331. },
  332. (err, result) => {
  333. if (err) {
  334. reject(err);
  335. } else {
  336. resolve(result);
  337. }
  338. }
  339. );
  340. });
  341. };
  342. /**
  343. * generater a request error with request response
  344. * @param {Object} result
  345. *
  346. * @api private
  347. */
  348. proto.requestError = async function requestError(result) {
  349. let err = null;
  350. const setError = async message => {
  351. let info;
  352. try {
  353. info = (await this.parseXML(message)) || {};
  354. } catch (error) {
  355. this.debug(message, 'error');
  356. error.message += `\nraw xml: ${message}`;
  357. error.status = result.status;
  358. error.requestId = result.headers['x-oss-request-id'];
  359. return error;
  360. }
  361. let msg = info.Message || `unknow request error, status: ${result.status}`;
  362. if (info.Condition) {
  363. msg += ` (condition: ${info.Condition})`;
  364. }
  365. err = new Error(msg);
  366. err.name = info.Code ? `${info.Code}Error` : 'UnknownError';
  367. err.status = result.status;
  368. err.code = info.Code;
  369. err.ecCode = info.EC;
  370. err.requestId = info.RequestId;
  371. err.hostId = info.HostId;
  372. err.serverTime = info.ServerTime;
  373. };
  374. if (!result.data || !result.data.length) {
  375. if (result.status === -1 || result.status === -2) {
  376. // -1 is net error , -2 is timeout
  377. err = new Error(result.message);
  378. err.name = result.name;
  379. err.status = result.status;
  380. err.code = result.name;
  381. } else {
  382. // HEAD not exists resource
  383. if (result.status === 404) {
  384. err = new Error('Object not exists');
  385. err.name = 'NoSuchKeyError';
  386. err.status = 404;
  387. err.code = 'NoSuchKey';
  388. } else if (result.status === 412) {
  389. err = new Error('Pre condition failed');
  390. err.name = 'PreconditionFailedError';
  391. err.status = 412;
  392. err.code = 'PreconditionFailed';
  393. } else {
  394. err = new Error(`Unknow error, status: ${result.status}`);
  395. err.name = 'UnknownError';
  396. err.status = result.status;
  397. err.res = result;
  398. const ossErr = result.headers['x-oss-err'];
  399. if (ossErr) {
  400. const message = atob(ossErr);
  401. await setError(message);
  402. }
  403. }
  404. err.requestId = result.headers['x-oss-request-id'];
  405. err.host = '';
  406. }
  407. } else {
  408. const message = String(result.data);
  409. this.debug('request response error data: %s', message, 'error');
  410. await setError(message);
  411. }
  412. this.debug('generate error %j', err, 'error');
  413. return err;
  414. };