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

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