用工小程序前端代码
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.

464 lines
13 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 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 && 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. return err;
  374. };
  375. if (!result.data || !result.data.length) {
  376. if (result.status === -1 || result.status === -2) {
  377. // -1 is net error , -2 is timeout
  378. err = new Error(result.message);
  379. err.name = result.name;
  380. err.status = result.status;
  381. err.code = result.name;
  382. } else {
  383. // HEAD not exists resource
  384. if (result.status === 404) {
  385. err = new Error('Object not exists');
  386. err.name = 'NoSuchKeyError';
  387. err.status = 404;
  388. err.code = 'NoSuchKey';
  389. } else if (result.status === 412) {
  390. err = new Error('Pre condition failed');
  391. err.name = 'PreconditionFailedError';
  392. err.status = 412;
  393. err.code = 'PreconditionFailed';
  394. } else {
  395. err = new Error(`Unknow error, status: ${result.status}`);
  396. err.name = 'UnknownError';
  397. err.status = result.status;
  398. err.res = result;
  399. const ossErr = result.headers && result.headers['x-oss-err'];
  400. if (ossErr) {
  401. const message = atob(ossErr);
  402. err = await setError(message);
  403. }
  404. }
  405. err.requestId = result.headers && result.headers['x-oss-request-id'];
  406. err.host = '';
  407. }
  408. } else {
  409. const message = String(result.data);
  410. this.debug('request response error data: %s', message, 'error');
  411. err = await setError(message);
  412. }
  413. this.debug('generate error %j', err, 'error');
  414. return err;
  415. };