UNPKG

aliyun-sdk

Version:
279 lines (244 loc) 9.97 kB
var ALY = require('../core'); var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; var ipv6Regex = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; ALY.OSS = ALY.Service.defineService('oss', ['2013-10-15'], { /** * @api private */ initialize: function initialize(options) { ALY.Service.prototype.initialize.call(this, options); }, hostIsIP: function hostIsIP(hostName){ return ipv4Regex.test(hostName) || ipv6Regex.test(hostName); }, setupRequestListeners: function setupRequestListeners(request) { request.addListener('build', this.addContentType); request.addListener('build', this.populateURI); request.addListener('build', this.computeContentMd5); request.addListener('extractError', this.extractError); request.addListener('extractData', this.extractData); }, populateURI: function populateURI(req) { var httpRequest = req.httpRequest; var b = req.params.Bucket; if (b) { // support cname var cname = req.service.config.cname || false if (cname) { httpRequest.endpoint.host = httpRequest.endpoint.hostname; httpRequest.virtualHostedBucket = b; httpRequest.path = httpRequest.path.replace(new RegExp('^/' + b), ''); if (httpRequest.path[0] !== '/') { httpRequest.path = '/' + httpRequest.path; } return } // is IP if(!req.service.hostIsIP(httpRequest.endpoint.hostname)){ // 确保 host 只被 set 一次,因为 endpoint 只在 service 唯一 httpRequest.endpoint.host = httpRequest.endpoint.hostname = b + '.' + httpRequest.endpoint.hostname; httpRequest.virtualHostedBucket = b; httpRequest.path = httpRequest.path.replace(new RegExp('^/' + b), ''); } if (httpRequest.path[0] !== '/') { httpRequest.path = '/' + httpRequest.path; } } }, addContentType: function addContentType(req) { var httpRequest = req.httpRequest; if (!httpRequest.headers['Content-Type']) { // always have a Content-Type httpRequest.headers['Content-Type'] = 'application/octet-stream'; } if (ALY.util.isBrowser() && window.navigator.userAgent.match(/Firefox/)) { if (!httpRequest.headers['Content-Type'].match(/;/)) { var charset = '; charset=UTF-8'; httpRequest.headers['Content-Type'] += charset; } } }, willComputeChecksums: function willComputeChecksums(req) { // TODO: compute checksums for Stream objects //if (!ALY.util.Buffer.isBuffer(req.httpRequest.body) && // typeof req.httpRequest.body !== 'string') { // return false; //} var rules = req.service.api.operations[req.operation].input.members; if (rules.ContentMD5 && !req.params.ContentMD5) return true; }, computeContentMd5: function computeContentMd5(req) { if (req.service.willComputeChecksums(req)) { var md5 = ALY.util.crypto.md5(req.httpRequest.body, 'base64'); req.httpRequest.headers['Content-MD5'] = md5; } }, /** * OSS requires that path params not escape forward slashes. * * @api private */ escapePathParam: function escapePathParam(value) { return ALY.util.uriEscapePath(String(value)); }, /** * @return [Boolean] whether response contains an error * @api private */ successfulResponse: function successfulResponse(resp) { var req = resp.request; var httpResponse = resp.httpResponse; if (req.operation === 'completeMultipartUpload' && httpResponse.body.toString().match('<Error>')) return false; else return httpResponse.statusCode < 300; }, /** * @return [Boolean] whether the error can be retried * @api private */ retryableError: function retryableError(error, request) { if (request.operation == 'completeMultipartUpload' && error.statusCode === 200) { return true; } else { var _super = ALY.Service.prototype.retryableError; return _super.call(this, error, request); } }, /** * Provides a specialized parser for getBucketLocation -- all other * operations are parsed by the super class. * * @api private */ extractData: function extractData(resp) { var req = resp.request; if (req.operation === 'getBucketLocation') { /*jshint regexp:false*/ var match = resp.httpResponse.body.toString().match(/>(.+)<\/Location/); if (match) { delete resp.data['_']; resp.data.LocationConstraint = match[1]; } } for(var k in resp.httpResponse.headers){ if(k.indexOf('x-oss-')==0){ var arr = k.substring('x-oss-'.length).split('-'); for(var i=0;i<arr.length;i++){ arr[i] = arr[i][0].toUpperCase()+arr[i].substring(1); } resp.data[arr.join('')] = resp.httpResponse.headers[k]; } else if(k=='content-md5'){ resp.data['ContentMD5'] = resp.httpResponse.headers['content-md5']; } } // extract request id resp.data.RequestId = resp.httpResponse.headers['x-oss-request-id'] || resp.httpResponse.headers['x-oss-requestid']; }, /** * Extracts an error object from the http response. * * @api private */ extractError: function extractError(resp) { var codes = { 304: 'NotModified', 403: 'Forbidden', 400: 'BadRequest', 404: 'NotFound' }; var code = resp.httpResponse.statusCode; var body = resp.httpResponse.body; if (codes[code] && body.length === 0) { resp.error = ALY.util.error(new Error(), { code: codes[resp.httpResponse.statusCode], message: null, headers: resp.httpResponse.headers }); } else { var data; try { data = new ALY.XML.Parser({}).parse(body.toString()); resp.error = ALY.util.error(new Error(), { code: data.Code || code, message: data.Message || null, headers: resp.httpResponse.headers }); } catch(e) { data = body.toString(); resp.error = ALY.util.error(new Error(), { code: code, message: data, headers: resp.httpResponse.headers }); } } }, getSignedUrl: function getSignedUrl(operation, params, callback) { params = ALY.util.copy(params || {}); var expires = params.Expires || 900; delete params.Expires; // we can't validate this var request = this.makeRequest(operation, params); var expiresHeader = 'presigned-expires'; function signedUrlBuilder() { delete request.httpRequest.headers['User-Agent']; if (request.service.getSignerClass() === ALY.Signers.V4) { //if (expires > 604800) { // one week expiry is invalid // var message = 'getSignedUrl() does not support expiry time greater ' + // 'than a week with SigV4 signing.'; // throw ALY.util.error(new Error(), { // code: 'InvalidExpiryTime', message: message, retryable: false // }); //} request.httpRequest.headers[expiresHeader] = expires; } else { request.httpRequest.headers[expiresHeader] = parseInt( ALY.util.date.unixSeconds() + expires, 10).toString(); } } function signedUrlSigner() { var queryParams = {}; ALY.util.each(request.httpRequest.headers, function (key, value) { if (key === expiresHeader) key = 'Expires'; queryParams[key] = value; }); delete request.httpRequest.headers[expiresHeader]; var auth = queryParams['Authorization'].split(' '); if (auth[0] === 'OSS') { auth = auth[1].split(':'); queryParams['OSSAccessKeyId'] = auth[0]; queryParams['Signature'] = auth[1]; } delete queryParams['Authorization']; delete queryParams['Host']; // build URL var endpoint = request.httpRequest.endpoint; var parsedUrl = ALY.util.urlParse(request.httpRequest.path); var querystring = ALY.util.queryParamsToString(queryParams); endpoint.pathname = parsedUrl.pathname; endpoint.search = !parsedUrl.search ? querystring : parsedUrl.search + '&' + querystring; } request.on('build', signedUrlBuilder); request.on('sign', signedUrlSigner); request.removeListener('build', this.addContentType); request.removeAllListeners('afterBuild'); if (!params.Body) { // no Content-MD5/SHA-256 if body is not provided request.removeListener('build', this.computeContentMd5); } if (callback) { request.build(function() { if (request.response.error) callback(request.response.error, null); else callback(null, ALY.util.urlFormat(request.httpRequest.endpoint)); }); } else { request.build(); return ALY.util.urlFormat(request.httpRequest.endpoint); } } }); module.exports = ALY.OSS;