aliyun-sdk
Version:
Aliyun SDK for JavaScript
279 lines (244 loc) • 9.97 kB
JavaScript
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;