huaweicloud-obs-sync
Version:
sync files to huaweicloud obs.
1,700 lines (1,529 loc) • 58.8 kB
JavaScript
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const events = require('events');
const httpLib = require('http');
const httpsLib = require('https');
const xml2js = require('xml2js');
const urlLib = require('url');
const pathLib = require('path');
const streamLib = require('stream');
const obsModel = require('./obsModel');
const v2Model = require('./v2Model');
const CONTENT_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
const OBS_SDK_VERSION = '3.0.0';
const mimeTypes = {
'7z' : 'application/x-7z-compressed',
'aac' : 'audio/x-aac',
'ai' : 'application/postscript',
'aif' : 'audio/x-aiff',
'asc' : 'text/plain',
'asf' : 'video/x-ms-asf',
'atom' : 'application/atom+xml',
'avi' : 'video/x-msvideo',
'bmp' : 'image/bmp',
'bz2' : 'application/x-bzip2',
'cer' : 'application/pkix-cert',
'crl' : 'application/pkix-crl',
'crt' : 'application/x-x509-ca-cert',
'css' : 'text/css',
'csv' : 'text/csv',
'cu' : 'application/cu-seeme',
'deb' : 'application/x-debian-package',
'doc' : 'application/msword',
'docx' : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dvi' : 'application/x-dvi',
'eot' : 'application/vnd.ms-fontobject',
'eps' : 'application/postscript',
'epub' : 'application/epub+zip',
'etx' : 'text/x-setext',
'flac' : 'audio/flac',
'flv' : 'video/x-flv',
'gif' : 'image/gif',
'gz' : 'application/gzip',
'htm' : 'text/html',
'html' : 'text/html',
'ico' : 'image/x-icon',
'ics' : 'text/calendar',
'ini' : 'text/plain',
'iso' : 'application/x-iso9660-image',
'jar' : 'application/java-archive',
'jpe' : 'image/jpeg',
'jpeg' : 'image/jpeg',
'jpg' : 'image/jpeg',
'js' : 'text/javascript',
'json' : 'application/json',
'latex' : 'application/x-latex',
'log' : 'text/plain',
'm4a' : 'audio/mp4',
'm4v' : 'video/mp4',
'mid' : 'audio/midi',
'midi' : 'audio/midi',
'mov' : 'video/quicktime',
'mp3' : 'audio/mpeg',
'mp4' : 'video/mp4',
'mp4a' : 'audio/mp4',
'mp4v' : 'video/mp4',
'mpe' : 'video/mpeg',
'mpeg' : 'video/mpeg',
'mpg' : 'video/mpeg',
'mpg4' : 'video/mp4',
'oga' : 'audio/ogg',
'ogg' : 'audio/ogg',
'ogv' : 'video/ogg',
'ogx' : 'application/ogg',
'pbm' : 'image/x-portable-bitmap',
'pdf' : 'application/pdf',
'pgm' : 'image/x-portable-graymap',
'png' : 'image/png',
'pnm' : 'image/x-portable-anymap',
'ppm' : 'image/x-portable-pixmap',
'ppt' : 'application/vnd.ms-powerpoint',
'pptx' : 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'ps' : 'application/postscript',
'qt' : 'video/quicktime',
'rar' : 'application/x-rar-compressed',
'ras' : 'image/x-cmu-raster',
'rss' : 'application/rss+xml',
'rtf' : 'application/rtf',
'sgm' : 'text/sgml',
'sgml' : 'text/sgml',
'svg' : 'image/svg+xml',
'swf' : 'application/x-shockwave-flash',
'tar' : 'application/x-tar',
'tif' : 'image/tiff',
'tiff' : 'image/tiff',
'torrent' : 'application/x-bittorrent',
'ttf' : 'application/x-font-ttf',
'txt' : 'text/plain',
'wav' : 'audio/x-wav',
'webm' : 'video/webm',
'wma' : 'audio/x-ms-wma',
'wmv' : 'video/x-ms-wmv',
'woff' : 'application/x-font-woff',
'wsdl' : 'application/wsdl+xml',
'xbm' : 'image/x-xbitmap',
'xls' : 'application/vnd.ms-excel',
'xlsx' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xml' : 'application/xml',
'xpm' : 'image/x-xpixmap',
'xwd' : 'image/x-xwindowdump',
'yaml' : 'text/yaml',
'yml' : 'text/yaml',
'zip' : 'application/zip',
};
const allowedResourceParameterNames = [
'acl',
'backtosource',
'policy',
'torrent',
'logging',
'location',
'storageinfo',
'quota',
'storageclass',
'storagepolicy',
'requestpayment',
'versions',
'versioning',
'versionid',
'uploads',
'uploadid',
'partnumber',
'website',
'notification',
'replication',
'lifecycle',
'deletebucket',
'delete',
'cors',
'restore',
'tagging',
'append',
'position',
'response-content-type',
'response-content-language',
'response-expires',
'response-cache-control',
'response-content-disposition',
'response-content-encoding',
'x-image-process',
'x-oss-process'
];
const allowedResponseHttpHeaderMetadataNames = [
'content-type',
'content-md5',
'content-length',
'content-language',
'expires',
'origin',
'cache-control',
'content-disposition',
'content-encoding',
'x-default-storage-class',
'location',
'date',
'etag',
'host',
'last-modified',
'content-range',
'x-reserved',
'access-control-allow-origin',
'access-control-allow-headers',
'access-control-max-age',
'access-control-allow-methods',
'access-control-expose-headers',
'connection'
];
const commonHeaders = {
'content-length' : 'ContentLength',
'date' : 'Date',
'x-reserved' : 'Reserved'
};
const obsAllowedStorageClass = ['STANDARD', 'WARM', 'COLD'];
const v2AllowedStorageClass = ['STANDARD', 'STANDARD_IA', 'GLACIER'];
const obsAllowedAcl = ['private', 'public-read', 'public-read-write', 'public-read-delivered', 'public-read-write-delivered'];
const v2AllowedAcl = ['private', 'public-read', 'public-read-write', 'authenticated-read', 'bucket-owner-read', 'bucket-owner-full-control', 'log-delivery-write'];
const obsAllowedUri = ['Everyone'];
const v2AllowedUri = ['http://acs.amazonaws.com/groups/global/AllUsers', 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers', 'http://acs.amazonaws.com/groups/s3/LogDelivery'];
const obsAllowedEvent = ['ObjectCreated:*', 'ObjectCreated:Put', 'ObjectCreated:Post', 'ObjectCreated:Copy',
'ObjectCreated:CompleteMultipartUpload', 'ObjectRemoved:*', 'ObjectRemoved:Delete', 'ObjectRemoved:DeleteMarkerCreated'];
const v2AllowedEvent = ['s3:ObjectCreated:*', 's3:ObjectCreated:Put', 's3:ObjectCreated:Post', 's3:ObjectCreated:Copy',
's3:ObjectCreated:CompleteMultipartUpload', 's3:ObjectRemoved:*', 's3:ObjectRemoved:Delete', 's3:ObjectRemoved:DeleteMarkerCreated'];
function encodeURIWithSafe(str, safe, skipEncoding){
str = String(str);
if(str.length === 0){
return '';
}
if(skipEncoding){
return str;
}
let ret;
if(safe){
ret = [];
for(let i=0;i<str.length;i++){
ret.push(safe.indexOf(str[i]) >=0 ? str[i] : encodeURIComponent(str[i]));
}
ret = ret.join('');
}else{
ret = encodeURIComponent(str);
}
return ret.replace(/!/g, '%21')
.replace(/~/g,'%7E')
.replace(/\*/g, '%2A')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29');
}
function headerTostring(obj){
return JSON ? JSON.stringify(obj) : '';
}
function parseObjectFromHeaders(sentAs, headers){
var metadata = {};
for(let key in headers){
let k = String(key).toLowerCase();
if(k.indexOf(sentAs) === 0){
metadata[k.slice(sentAs.length)] = headers[key];
}
}
return metadata;
}
function mkdirsSync(dirname){
return fs.existsSync(dirname) ? true : (mkdirsSync(pathLib.dirname(dirname)) ? fs.mkdirSync(dirname) : false);
}
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
function isObject(obj){
return Object.prototype.toString.call(obj) === '[object Object]';
}
function utcToLocaleString(utcDate){
return utcDate ? new Date(Date.parse(utcDate)).toLocaleString() : '';
}
function makeObjFromXml(xml, bc){
xml2js.parseString(xml, {explicitArray:false, ignoreAttrs:true}, function(err, result){
if(err){
bc(err, null);
return;
}
bc(null, result);
});
}
function getExpireDate(utcDateStr){
var date = new Date(Date.parse(utcDateStr));
var hour = date.getUTCHours();
var min = date.getUTCMinutes();
var sec = date.getUTCSeconds();
var day = date.getUTCDate();
var moth = date.getUTCMonth() + 1;
var year = date.getUTCFullYear();
var shortDate = '';
var longDate = '';
var expireDate = '';
expireDate += year + '-';
if(moth < 10){
expireDate += '0';
}
expireDate += moth + '-';
if(day < 10){
expireDate += '0';
}
expireDate += day + 'T';
if(hour < 10){
expireDate += '0';
}
expireDate += hour + ':';
if(min < 10){
expireDate += '0';
}
expireDate += min + ':';
if(sec < 10){
expireDate += '0';
}
expireDate += sec + 'Z';
return expireDate;
}
function getDates(utcDateStr){
var date = new Date(Date.parse(utcDateStr));
var hour = date.getUTCHours();
var min = date.getUTCMinutes();
var sec = date.getUTCSeconds();
var day = date.getUTCDate();
var moth = date.getUTCMonth() + 1;
var year = date.getUTCFullYear();
var shortDate = '';
var longDate = '';
shortDate += year;
if(moth < 10){
shortDate += '0';
}
shortDate += moth;
if(day < 10){
shortDate += '0';
}
shortDate += day;
longDate += shortDate + 'T';
if(hour < 10){
longDate += '0';
}
longDate += hour;
if(min < 10){
longDate += '0';
}
longDate += min;
if(sec < 10){
longDate += '0';
}
longDate += sec + 'Z';
return [shortDate, longDate];
}
function getSignedAndCanonicalHeaders(header){
var arrheadKey = [];
var arrhead = {};
for(let key in header){
arrheadKey.push(key.toLowerCase());
arrhead[key.toLowerCase()] = header[key];
}
arrheadKey = arrheadKey.sort();
var signedHeaders = '';
var canonicalHeaders = '';
for(let i = 0; i < arrheadKey.length; i++){
if(i !== 0){
signedHeaders += ';';
}
signedHeaders += arrheadKey[i];
canonicalHeaders += arrheadKey[i] + ':' + arrhead[arrheadKey[i]] + '\n';
}
return [signedHeaders, canonicalHeaders];
}
function createV4Signature(shortDate, sk, region, stringToSign){
var dateKey = crypto.createHmac('sha256', 'AWS4' + sk).update(shortDate).digest();
var dateRegionKey = crypto.createHmac('sha256', dateKey).update(region).digest();
var dateRegionServiceKey = crypto.createHmac('sha256', dateRegionKey).update('s3').digest();
var signingKey = crypto.createHmac('sha256',dateRegionServiceKey).update('aws4_request').digest();
return crypto.createHmac('sha256',signingKey).update(stringToSign).digest('hex');
}
function getV4Signature(shortDate, longDate, sk, region, canonicalRequest){
var scop = shortDate + '/' + region + '/s3/aws4_request';
var stringToSign = 'AWS4-HMAC-SHA256' + '\n';
stringToSign += longDate + '\n';
stringToSign += scop + '\n';
stringToSign += crypto.createHash('sha256').update(canonicalRequest).digest('hex');
return createV4Signature(shortDate, sk, region, stringToSign);
}
function Utils(logger) {
this.log = logger;
this.ak = null;
this.sk = null;
this.securityToken = null;
this.isSecure = true;
this.server = null;
this.pathStyle = false;
this.signature = 'v2';
this.region = 'region';
this.port = null;
this.maxRetryCount = 3;
this.timeout = 60;
this.sslVerify = false;
this.httpAgent = false;
this.httpsAgent = false;
this.obsSdkVersion = OBS_SDK_VERSION;
}
Utils.prototype.encodeURIWithSafe = encodeURIWithSafe;
Utils.prototype.mimeTypes = mimeTypes;
Utils.prototype.close = function(){
if(this.httpAgent){
this.httpAgent.destroy();
}
if(this.httpsAgent){
this.httpsAgent.destroy();
}
};
Utils.prototype.refresh = function(ak, sk, securityToken){
this.ak = ak ? String(ak).trim() : null;
this.sk = sk ? String(sk).trim(): null;
this.securityToken = securityToken ? String(securityToken).trim() : null;
};
Utils.prototype.initFactory = function(ak, sk, isSecure,
server, pathStyle, signature, region, port, maxRetryCount, timeout, sslVerify, long_conn_param, securityToken){
this.refresh(ak, sk, securityToken);
if (!server) {
throw new Error('Server is not set');
}
server = String(server).trim();
if(server.indexOf('https://') === 0){
server = server.slice('https://'.length);
isSecure = true;
}else if(server.indexOf('http://') === 0){
server = server.slice('http://'.length);
isSecure = false;
}
let index = server.lastIndexOf('/');
while(index >= 0){
server = server.slice(0, index);
index = server.lastIndexOf('/');
}
index = server.indexOf(':');
if(index >= 0){
port = server.slice(index + 1);
server = server.slice(0, index);
}
this.server = server;
if(/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/.test(this.server)){
pathStyle = true;
}
if (isSecure !== undefined) {
this.isSecure = Boolean(isSecure);
}
if (pathStyle !== undefined) {
this.pathStyle = Boolean(pathStyle);
}
if (signature !== undefined) {
this.signature = String(signature).trim().toLowerCase();
}
if(this.signature === 'obs'){
this.headerPrefix = 'x-obs-';
this.headerMetaPrefix = 'x-obs-meta-';
this.authPrefix = 'OBS';
}else{
this.headerPrefix = 'x-amz-';
this.headerMetaPrefix = 'x-amz-meta-';
this.authPrefix = 'AWS';
}
if(region !== undefined){
this.region = String(region).trim();
}
this.port = port ? parseInt(port) : (this.isSecure ? 443 : 80);
if(maxRetryCount !== undefined){
this.maxRetryCount = parseInt(maxRetryCount);
}
if(timeout !== undefined){
this.timeout = parseInt(timeout);
}
if(sslVerify !== undefined){
this.sslVerify = sslVerify;
}
if(long_conn_param !== undefined && Number(long_conn_param) >= 0){
this.httpAgent = new httpLib.Agent({keepAlive : true, keepAliveMsecs : Number(long_conn_param) * 1000});
this.httpsAgent = new httpsLib.Agent({keepAlive : true, keepAliveMsecs : Number(long_conn_param) * 1000});
}
};
Utils.prototype.SseKmsAdapter = function(value){
value = value || '';
value = String(value);
let index = value.indexOf('aws:');
if(this.signature === 'obs'){
return index === 0 ? value.slice(4) : value;
}else{
return index === 0 ? value : 'aws:' + value;
}
};
Utils.prototype.BucketAdapter = function(value){
value = value || '';
value = String(value);
let index = value.indexOf('arn:aws:s3:::');
if(this.signature === 'obs'){
return index === 0 ? value.slice('arn:aws:s3:::'.length) : value;
}else{
return index === 0 ? value : 'arn:aws:s3:::' + value;
}
};
Utils.prototype.EventAdapter = function(value){
value = value || '';
value = String(value);
if(this.signature === 'obs'){
if(obsAllowedEvent.indexOf(value) >= 0){
return value;
}
if(v2AllowedEvent.indexOf(value) >= 0){
return value.substring(3);
}
}else{
if(v2AllowedEvent.indexOf(value) >= 0){
return value;
}
if(obsAllowedEvent.indexOf(value) >= 0){
return 's3:' + value;
}
}
return '';
};
Utils.prototype.URIAdapter = function(value){
value = value || '';
value = String(value);
if(this.signature === 'obs'){
if(obsAllowedUri.indexOf(value) >= 0){
return value;
}
if(value === 'AllUsers' || value === 'http://acs.amazonaws.com/groups/global/AllUsers'){
return 'Everyone';
}
}else{
if(v2AllowedUri.indexOf(value) >= 0){
return value;
}
if(value === 'Everyone' || value === 'AllUsers'){
return 'http://acs.amazonaws.com/groups/global/AllUsers';
}else if(value === 'AuthenticatedUsers'){
return 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
}else if(value === 'LogDelivery'){
return 'http://acs.amazonaws.com/groups/s3/LogDelivery';
}
}
return '';
};
Utils.prototype.StorageClassAdapter = function(value){
value = value || '';
value = String(value).toUpperCase();
if(this.signature === 'obs'){
if(obsAllowedStorageClass.indexOf(value) >= 0){
return value;
}
if(value === 'STANDARD_IA'){
return 'WARM';
}else if(value === 'GLACIER'){
return 'COLD';
}
}else{
if(v2AllowedStorageClass.indexOf(value) >= 0){
return value;
}
if(value === 'WARM'){
return 'STANDARD_IA';
}else if(value === 'COLD'){
return 'GLACIER';
}
}
return '';
};
Utils.prototype.ACLAdapter = function(value){
value = value || '';
value = String(value).toLowerCase();
if(this.signature === 'obs'){
if(obsAllowedAcl.indexOf(value) >= 0){
return value;
}
}else{
if(value === 'public-read-delivered'){
value = 'public-read';
}else if(value === 'public-read-write-delivered'){
value = 'public-read-write';
}
if(v2AllowedAcl.indexOf(value) >= 0){
return value;
}
}
return '';
};
Utils.prototype.toXml = function(mXml, xmlMeta, root, sentAs){
var xml = '';
if(root !== null){
xml += this.buildXml(mXml, xmlMeta, root, sentAs);
}else{
for (let key in xmlMeta){
if(key in mXml){
let meta = xmlMeta[key];
xml += this.buildXml(mXml, meta, key, meta.sentAs || key);
}
}
}
return xml;
};
Utils.prototype.buildXml = function(mXml, xmlMeta, key, sentAs){
var xml = '';
let type = xmlMeta.type;
if(type === 'array'){
for(let i = 0; i < mXml[key].length; i++){
if(xmlMeta.items.type === 'object'){
let result = this.toXml(mXml[key][i], xmlMeta.items.parameters, null);
if(result !== ''){
xml += '<' + sentAs + '>'+ result + '</' + sentAs + '>';
}
}else if(xmlMeta.items.type === 'adapter'){
xml += '<' + sentAs + '>' + String(this[key + 'Adapter'](mXml[key][i])).replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"') + '</' + sentAs + '>';
}else if(xmlMeta.items.type !== 'array'){
xml += '<' + sentAs + '>'+ String(mXml[key][i]).replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"') + '</' + sentAs +'>';
}
}
}else if(type === 'object'){
let result = this.toXml(mXml[key], xmlMeta.parameters, null);
if(result !== ''){
xml += '<' + sentAs;
if('data' in xmlMeta){
if('xsiNamespace' in xmlMeta.data){
xml += ' xmlns:xsi="' + xmlMeta.data.xsiNamespace + '"';
}
if('xsiType' in xmlMeta.data){
xml += ' xsi:type="' + mXml[key][xmlMeta.data.xsiType] + '"';
}
}
xml += '>';
xml += result + '</' + sentAs + '>';
}
}else if(type === 'adapter'){
xml += '<' + sentAs + '>' + String(this[key + 'Adapter'](mXml[key])).replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"') + '</' + sentAs + '>';
}else if(type !== 'ignore'){
xml += '<' + sentAs + '>' + String(mXml[key]).replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"') + '</' + sentAs + '>';
}
if(xml && xmlMeta.wrapper){
let _wrapper = xmlMeta.wrapper;
xml = '<' + _wrapper + '>' + xml + '</' + _wrapper + '>';
}
return xml;
};
Utils.prototype.jsonToObject = function(model, obj, root){
var opt = {};
if(root !== null){
this.buildObject(model, obj, root, opt);
}else{
for(let key in model){
this.buildObject(model, obj, key, opt);
}
}
return opt;
};
Utils.prototype.buildObject = function(model, obj, key, opt){
if(isObject(obj)){
let flag = true;
let wrapper = model[key].wrapper;
if(wrapper && wrapper in obj){
obj = obj[wrapper];
flag = isObject(obj);
}
if(flag){
let sentAs = model[key].sentAs || key;
if(sentAs in obj){
if(model[key].type === 'object'){
opt[key] = this.jsonToObject(model[key].parameters, obj[sentAs], null);
}else if(model[key].type === 'array'){
let arr = [];
if(!isArray(obj[sentAs])){
arr[0] = model[key].items.type === 'object' ? this.jsonToObject(model[key].items.parameters, obj[sentAs], null) : obj[sentAs];
}else{
for (let i = 0; i < obj[sentAs].length; i++ ){
arr[i] = model[key].items.type === 'object' ? this.jsonToObject(model[key].items.parameters, obj[sentAs][i], null) : obj[sentAs][i];
}
}
opt[key] = arr;
}else if(model[key].type === 'date'){
opt[key] = utcToLocaleString(obj[sentAs]);
}else{
opt[key] = obj[sentAs];
}
}
}
}
if(opt[key] === undefined){
if(model[key].type === 'object'){
opt[key] = model[key].parameters ? this.jsonToObject(model[key].parameters, null, null) : {};
}else if(model[key].type === 'array'){
opt[key] = [];
}else{
opt[key] = '';
}
}
};
Utils.prototype.makeParam = function(methodName, param){
var model = this.signature === 'obs' ? obsModel[methodName] : v2Model[methodName];
var method = model.httpMethod;
var uri = '/';
var urlPath = '';
var xml = '';
var exheaders = {};
var opt = {};
if ('urlPath' in model){
urlPath += '?';
urlPath += model.urlPath;
}
for (let key in model.parameters){
let meta = model.parameters[key];
if (meta.require && !(key in param)){
opt.err = key + ' is a required element!';
if(this.log.isLevelEnabled('warn')){
this.log.runLog('warn', methodName, opt.err);
}
return opt;
}
if(param[key] !== null && param[key] !== undefined){
if(meta.type === 'srcFile' || meta.type === 'dstFile'){
opt[meta.type] = param[key];
continue;
}
if(meta.type === 'plain'){
opt[key] = param[key];
}
let sentAs = meta.sentAs || key;
if(meta.withPrefix){
sentAs = this.headerPrefix + sentAs;
}
if(meta.location === 'uri'){
if(uri !== '/'){
uri += '/';
}
uri += param[key];
}else if(meta.location === 'header'){
let safe = meta.encodingSafe || ' ;/?:@&=+$,';
if(meta.type === 'object'){
if(this.headerMetaPrefix === sentAs){
for(let item in param[key]){
let value = param[key][item];
item = String(item).trim().toLowerCase();
exheaders[item.indexOf(sentAs) === 0 ? item: sentAs + item] = encodeURIWithSafe(value, safe);
}
}
}else if(meta.type === 'array'){
let arr = [];
for(let i=0;i<param[key].length;i++){
arr.push(encodeURIWithSafe(param[key][i], safe));
}
exheaders[sentAs] = arr;
}else if(meta.type === 'password'){
exheaders[sentAs] = Buffer.from(param[key],'utf8').toString('base64');
let pwdSentAs = sentAs + '-MD5';
exheaders[pwdSentAs] = this.bufMD5(param[key]);
}else if(meta.type === 'number' && Number(param[key])){
exheaders[sentAs] = encodeURIWithSafe(String(param[key]), safe);
}else if(meta.type === 'boolean'){
exheaders[sentAs] = encodeURIWithSafe(param[key] ? 'true' : 'false', safe);
}else if(meta.type === 'adapter'){
let val = this[key + 'Adapter'](param[key]);
if(val){
exheaders[sentAs] = encodeURIWithSafe(String(val), safe);
}
}else {
exheaders[sentAs] = encodeURIWithSafe(String(param[key]), safe, meta.skipEncoding);
}
}else if(meta.location === 'urlPath'){
let sep = urlPath === '' ? '?' : '&';
let value = param[key];
if(meta.type !== 'number' || (meta.type === 'number' && Number(value) >= 0)){
urlPath += sep + encodeURIWithSafe(sentAs, '/') + '=' + encodeURIWithSafe(String(value), '/');
}
}else if(meta.location === 'xml'){
let mxml = this.toXml(param, meta, key, sentAs);
if(mxml){
xml += mxml;
}
}else if(meta.location === 'body'){
xml = param[key];
}
}
}
if('data' in model && 'xmlRoot' in model.data){
if(xml || model.data.xmlAllowEmpty){
let xmlRoot = model.data.xmlRoot;
xml = '<' + xmlRoot + '>' + xml + '</' + xmlRoot + '>';
}
}
exheaders.Host = this.server;
if(!this.pathStyle){
let uriList = uri.split('/');
if(uriList.length >= 2 && uriList[1]){
exheaders.Host = uriList[1] + '.' + exheaders.Host;
let requestUri = uri.replace(uriList[1], '');
if(requestUri.indexOf('//') === 0){
requestUri = requestUri.slice(1);
}
if(this.signature === 'v4'){
uri = requestUri;
}else if(requestUri === '/'){
uri += '/';
}
opt.requestUri = encodeURIWithSafe(requestUri, '/');
}
}
opt.method = method;
opt.uri = encodeURIWithSafe(uri, '/');
opt.urlPath = urlPath;
if(xml){
if(!(xml instanceof streamLib.Readable)){
let body = Buffer.from(String(xml), 'utf8');
if(model.data && model.data.md5){
exheaders['Content-MD5'] = this.bufMD5(body);
}
exheaders['Content-Length'] = body.length === 0 ? '0' : String(body.length);
}
opt.xml = xml;
if(this.log.isLevelEnabled('debug')){
this.log.runLog('debug', methodName, 'request content:' + xml);
}
}
opt.headers = exheaders;
if('srcFile' in opt){
if (!fs.existsSync(opt.srcFile)) {
opt.err = 'the file [' + opt.srcFile + '] is not exist!';
if(this.log.isLevelEnabled('error')){
this.log.runLog('error', methodName, opt.err);
}
return opt;
}
let fileSize = fs.statSync(opt.srcFile).size;
if ('Content-Length' in opt.headers || 'PartSize' in opt || 'Offset' in opt) {
let offset = opt.Offset;
offset = (offset && offset >= 0 && offset < fileSize) ? offset : 0;
let partSize;
if('PartSize' in opt){
partSize = opt.PartSize;
}else if('Content-Length' in opt.headers){
partSize = parseInt(opt.headers['Content-Length']);
}else{
partSize = fileSize;
}
partSize = (partSize && partSize > 0 && partSize <= fileSize - offset) ? partSize : fileSize - offset;
opt.PartSize = partSize;
opt.Offset = offset;
opt.headers['Content-Length'] = String(opt.PartSize);
}
}
return opt;
};
Utils.prototype.sendRequest = function(funcName, opt, backcall, count){
if(count === undefined){
count = 1;
}
let that = this;
let readable = opt.xml instanceof streamLib.Readable;
that.makeRequest(funcName, opt, function(err,msg){
if(err && !readable && msg !== 'PREMATURE_END'
&& msg !== 'SELF_SIGNED_CERT_IN_CHAIN' && msg !== 'DEPTH_ZERO_SELF_SIGNED_CERT' && count <= that.maxRetryCount){
that.sendRequest(funcName, opt, backcall, count + 1);
}else if(err === 'redirect' && !readable && msg){
let uri = urlLib.parse(msg);
opt.headers.Host = uri.hostname;
opt.protocol = uri.protocol;
opt.port = uri.port || (opt.protocol.toLowerCase().indexOf('https') === 0 ? 443 : 80);
that.sendRequest(funcName, opt, backcall);
}else{
backcall(err,msg);
}
});
};
Utils.prototype.doAuth = function(opt, methodName) {
var interestHeader = ['Content-MD5', 'Content-Type', 'Date'];
var log = this.log;
var stringToSign = opt.method + '\n';
for(let i=0;i<interestHeader.length;i++){
if(interestHeader[i] in opt.headers){
stringToSign += opt.headers[interestHeader[i]];
}
stringToSign += '\n';
}
var temp = [];
for(let key in opt.headers){
if (key.toLowerCase().indexOf(this.headerPrefix) === 0){
temp.push(key.toLowerCase());
}
}
temp = temp.sort();
for(let i=0;i<temp.length;i++){
let key = temp[i];
let val = key.indexOf(this.headerMetaPrefix) === 0 ? opt.headers[key].trim() : opt.headers[key];
stringToSign += key + ':' + val + '\n';
}
var path = opt.uri;
if(opt.urlPath){
let _path = opt.urlPath.slice(1);
let arrPath = _path.split('&').sort();
let urlPath = '';
for(let i=0;i<arrPath.length;i++){
let listvar = arrPath[i].split('=');
let key = decodeURIComponent(listvar[0]);
if(allowedResourceParameterNames.indexOf(key.toLowerCase()) >= 0){
urlPath += urlPath === '' ? '?' : '&';
urlPath += key;
if(listvar.length === 2 && listvar[1]){
urlPath += '=' + decodeURIComponent(listvar[1]);
}
}
}
path += urlPath;
}
stringToSign += path;
if(log.isLevelEnabled('debug')){
log.runLog('debug',methodName, 'stringToSign:' + stringToSign);
}
opt.headers.Authorization = this.authPrefix + ' ' + this.ak + ':' + crypto.createHmac('sha1', this.sk).update(stringToSign).digest('base64');
};
Utils.prototype.v4Auth = function(opt, methodName){
opt.headers[this.headerPrefix + 'content-sha256'] = CONTENT_SHA256;
var header = opt.headers;
var log = this.log;
var shortDate = null;
var longDate = null;
if(this.headerPrefix + 'date' in header){
longDate = header[this.headerPrefix + 'date'];
shortDate = longDate.slice(0, longDate.indexOf('T'));
}else{
let dates = getDates(header.Date);
shortDate = dates[0];
longDate = dates[1];
}
var credential = this.ak + '/' + shortDate + '/' + this.region + '/s3/aws4_request';
var signedAndCanonicalHeaders = getSignedAndCanonicalHeaders(header);
var signedHeaders = signedAndCanonicalHeaders[0];
var canonicalHeaders = signedAndCanonicalHeaders[1];
var canonicalQueryString = '';
if(opt.urlPath){
let path = opt.urlPath.slice(1);
let arrPath = path.split('&').sort();
for(let i=0;i<arrPath.length;i++){
canonicalQueryString += arrPath[i];
if(arrPath[i].indexOf('=') === -1){
canonicalQueryString += '=';
}
if(i !== arrPath.length -1){
canonicalQueryString += '&';
}
}
}
var canonicalRequest = opt.method + '\n';
canonicalRequest += opt.uri + '\n';
canonicalRequest += canonicalQueryString + '\n';
canonicalRequest += canonicalHeaders + '\n';
canonicalRequest += signedHeaders + '\n';
canonicalRequest += CONTENT_SHA256;
if(log.isLevelEnabled('debug')){
log.runLog('debug',methodName, 'canonicalRequest:' + canonicalRequest);
}
var signature = getV4Signature(shortDate, longDate, this.sk, this.region, canonicalRequest);
opt.headers.Authorization = 'AWS4-HMAC-SHA256 ' + 'Credential=' + credential + ',' + 'SignedHeaders=' + signedHeaders + ',' + 'Signature=' + signature;
};
Utils.prototype.parseCommonHeaders = function(opt, headers){
for(let key in commonHeaders){
opt.InterfaceResult[commonHeaders[key]] = headers[key];
}
opt.InterfaceResult.RequestId = headers[this.headerPrefix + 'request-id'];
opt.InterfaceResult.Id2 = headers[this.headerPrefix + 'id-2'];
};
Utils.prototype.contrustCommonMsg = function(opt, obj, headers, methodName){
opt.InterfaceResult = {};
var log = this.log;
this.parseCommonHeaders(opt, headers);
if(log.isLevelEnabled('info')){
log.runLog('info', methodName, 'request finished with request id:' + opt.InterfaceResult.RequestId);
}
for (let key in obj){
if(obj[key].location === 'header'){
let sentAs = obj[key].sentAs || key;
if(obj[key].withPrefix){
sentAs = this.headerPrefix + sentAs;
}
if(obj[key].type === 'object'){
opt.InterfaceResult[key] = parseObjectFromHeaders(sentAs, headers);
}else{
let val = headers[sentAs];
if(val === undefined){
val = headers[sentAs.toLowerCase()];
}
if(val !== undefined){
opt.InterfaceResult[key] = val;
}
}
}
}
};
Utils.prototype.getRequest = function(methodName, serverback, dstFile, bc, saveAsStream, readable){
var opt = {};
var log = this.log;
var model = this.signature === 'obs' ? obsModel[methodName + 'Output'] : v2Model[methodName + 'Output'];
model = model || {};
var obj = model.parameters || {};
opt.CommonMsg = {
Status : serverback.statusCode,
Code : '',
Message : '',
HostId : '',
RequestId : '',
InterfaceResult : null
};
var headers = serverback.headers;
if(log.isLevelEnabled('info')){
log.runLog('info', methodName, 'get response start, statusCode:' + serverback.statusCode);
}
if(log.isLevelEnabled('debug')){
log.runLog('debug', methodName, 'response msg :' + 'statusCode:' + serverback.statusCode + ', headers:' + headerTostring(headers));
}
var doLog = function(){
if(log.isLevelEnabled('debug')){
let logMsg = 'Status:' + opt.CommonMsg.Status + ', Code:' + opt.CommonMsg.Code + ', Message:' + opt.CommonMsg.Message;
log.runLog('debug', methodName, 'exec interface ' + methodName + ' finish, ' + logMsg);
}
bc(null,opt);
};
var that = this;
if(serverback.statusCode >= 300 && serverback.statusCode < 400 && serverback.statusCode !== 304 && !readable){
let location = headers.location || headers.Location;
if(location){
if(log.isLevelEnabled('warn')){
let err = 'http code is 3xx, need to redirect to ' + location;
log.runLog('warn', methodName, err);
}
bc('redirect', location);
}else{
let err = 'get redirect code 3xx, but no location in headers';
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, err);
}
bc(err, null);
}
}else if(serverback.statusCode < 300){
if(dstFile){
let fileDir = pathLib.dirname(dstFile);
if(mkdirsSync(fileDir)){
let stream = fs.createWriteStream(dstFile);
serverback.on('error', function(err){
stream.end();
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'get response stream error [' + headerTostring(err) + ']');
}
bc(err, null);
}).on('end', function(){
stream.end();
that.contrustCommonMsg(opt, obj, headers, methodName);
if(log.isLevelEnabled('debug')){
log.runLog('debug', methodName, 'exec interface ' + methodName + ' finish, Status:' + opt['CommonMsg']['Status'] + ', Code: ,Message: ');
}
}).pipe(stream);
stream.on('close', function(){
if(opt.InterfaceResult && opt.InterfaceResult.ContentLength !== undefined){
if(fs.existsSync(dstFile)){
let fstat = fs.statSync(dstFile);
if(fstat.size !== parseInt(opt.InterfaceResult.ContentLength)){
bc('premature end of Content-Length delimiter message body (expected:' + opt.InterfaceResult.ContentLength + '; received:' + fstat.size + ')', 'PREMATURE_END');
return;
}
}
}
doLog();
});
}else{
bc('failed to create file:' + dstFile, null);
}
}else if(('data' in model) && saveAsStream){
that.contrustCommonMsg(opt, obj, headers, methodName);
if(log.isLevelEnabled('debug')){
let respMsg = 'Status: ' + opt.CommonMsg.Status + ', headers: ' + headerTostring(headers);
log.runLog('debug', methodName, respMsg);
}
for (let key in obj){
if(obj[key].location === 'body'){
if(opt.InterfaceResult && opt.InterfaceResult.ContentLength !== undefined){
let contentLength = parseInt(opt.InterfaceResult.ContentLength);
serverback.$readedCount = 0;
serverback.$expectedCount = contentLength;
serverback.on('data', function(data){
serverback.$readedCount += data.length;
}).on('end', function(){
let expectedCount = serverback.$expectedCount;
let readedCount = serverback.$readedCount;
delete serverback.$readedCount;
delete serverback.$expectedCount;
if(expectedCount !== readedCount){
throw new Error('premature end of Content-Length delimiter message body (expected:' + serverback.$expectedCount + '; received:' + serverback.$readedCount + ')');
}
});
}
opt.InterfaceResult[key] = serverback;
break;
}
}
doLog();
}else{
let body = [];
serverback.on('data', function(data) {
body.push(data);
}).on('error', function(err){
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'get response stream error [' + headerTostring(err) + ']');
}
bc(err, null);
}).on('end', function() {
that.contrustCommonMsg(opt, obj, headers, methodName);
body = Buffer.concat(body);
if(log.isLevelEnabled('debug')){
let respMsg = 'Status: ' + opt.CommonMsg.Status + ', headers: ' + headerTostring(headers);
if(body){
respMsg += 'body length: ' + body.length;
log.runLog('debug', methodName, 'response body length:' + body.length);
}
log.runLog('debug', methodName, respMsg);
}
if(body && ('data' in model)){
if(model.data.type === 'xml'){
makeObjFromXml(body, function(err,result){
if(err){
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'change xml to json err [' + headerTostring(err) + ']' );
}
bc(err, null);
return;
}
let tempResult = result;
if(model.data.xmlRoot && (model.data.xmlRoot in tempResult)){
tempResult = result[model.data.xmlRoot];
}
if(isObject(tempResult)){
for (let key in obj){
if(obj[key].location === 'xml'){
opt.InterfaceResult[key] = that.jsonToObject(obj, tempResult, key)[key];
}
}
}
});
}else if(model.data.type === 'body'){
for (let key in obj){
if(obj[key].location === 'body'){
opt.InterfaceResult[key] = body;
break;
}
}
}
}
doLog();
});
}
}else{
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'request error with http status code:' + serverback.statusCode);
}
let body = [];
serverback.on('data', function(data) {
body.push(data);
}).on('error', function(err){
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'get response stream error [' + headerTostring(err) + ']');
}
bc(err, null);
}).on('end', function() {
body = Buffer.concat(body);
if(log.isLevelEnabled('debug')){
let respMsg = 'Status: ' + opt.CommonMsg.Status + ', headers: ' + headerTostring(headers);
if(body !== ''){
respMsg += 'body: ' + body;
log.runLog('debug', methodName, 'response body :' + body);
}
log.runLog('debug', methodName, respMsg);
}
if(body){
makeObjFromXml(body, function(err, re){
if(err){
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'change xml to json err [' + headerTostring(err) + ']' );
}
bc(err,null);
return;
}
if(re && 'Error' in re){
let errMsg = re.Error;
opt.CommonMsg.Code = errMsg.Code;
opt.CommonMsg.Message = errMsg.Message;
opt.CommonMsg.HostId = errMsg.HostId;
opt.CommonMsg.RequestId = errMsg.RequestId;
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'request error with error code:' + opt.CommonMsg.Code + ', error message:' + opt.CommonMsg.Message + ', request id:' + opt.CommonMsg.RequestId);
}
}
doLog();
});
}else{
doLog();
}
});
}
};
Utils.prototype.makeRequest = function(methodName, opt, bc){
var log = this.log;
var body = opt.xml;
var readable = body instanceof streamLib.Readable;
var nowDate = new Date();
opt.headers.Date = nowDate.toUTCString();
delete opt.headers.Authorization;//retry bug fix
var ex = opt.headers;
var path = (opt.requestUri ? opt.requestUri : opt.uri) + opt.urlPath;
var method = opt.method;
if(this.ak && this.sk){
if(this.securityToken){
opt.headers[this.headerPrefix + 'security-token'] = this.securityToken;
}
if(this.signature === 'v4'){
this.v4Auth(opt, methodName);
}else{
this.doAuth(opt, methodName);
}
}
ex['User-Agent'] = 'obs-sdk-js/' + this.obsSdkVersion;
if(log.isLevelEnabled('info')){
log.runLog('info', methodName, 'prepare request parameters ok, then Send request to service start');
}
if(log.isLevelEnabled('debug')){
let header_msg = {};
for (let key in ex){
header_msg[key] = ex[key];
}
header_msg.Authorization = '****';
let msg = 'method:' + method + ', path:' + path + 'headers:' + headerTostring(header_msg);
if (body && !readable) {
msg += 'body:' + body;
}
log.runLog('debug', methodName, 'request msg:' + msg);
}
var reopt = {
method : method,
host : ex.Host,
port : opt.port || this.port,
path : path,
ca : !this.sslVerify || !fs.existsSync(String(this.sslVerify)) ? null : fs.readFileSync(String(this.sslVerify)),
checkServerIdentity: function (host, cert) {
return undefined;//do not verify hostname
},
rejectUnauthorized : Boolean(this.sslVerify),
headers : ex
};
var start = nowDate.getTime();
var _isSecure = opt.protocol ? opt.protocol.toLowerCase().indexOf('https') === 0 : this.isSecure;
reopt.agent = _isSecure ? this.httpsAgent : this.httpAgent;
var _http = _isSecure ? httpsLib : httpLib;
var req = _http.request(reopt);
req.setNoDelay(true);
req.setTimeout(this.timeout * 1000);
var that = this;
req.on('response', function(serverback) {
if(log.isLevelEnabled('info')){
log.runLog('info', methodName, 'http cost ' + (new Date().getTime() - start) + ' ms');
}
that.getRequest(methodName, serverback, opt.dstFile, bc, opt.SaveAsStream, readable);
}).on('error', function(err){
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'Send request to service error [' + headerTostring(err) + ']');
}
if(log.isLevelEnabled('info')){
log.runLog('info', methodName, 'http cost ' + (new Date().getTime() - start) + ' ms');
}
bc(err, null);
});
if(method in ['GET', 'HEAD', 'OPTIONS']){
req.end();
return;
}
if(body){
if(readable){
if('ContentLength' in opt){
let contentLength = parseInt(opt.ContentLength);
let writeCount = 0;
body.on('data', function(data){
if(writeCount >= contentLength){
req.end();
if(body.destroy){
body.destroy();
}
return;
}
if(contentLength - writeCount > data.length){
req.write(data, function(err){
if(err){
req.abort();
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read stream to send error [' + err + ']');
}
bc(err, null);
return;
}
writeCount += data.length;
});
}else{
req.write(Buffer.from(data, 0, contentLength - writeCount), function(err){
if(err){
req.abort();
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read stream to send error [' + err + ']');
}
bc(err, null);
return;
}
writeCount = contentLength;
});
}
});
}else{
body.pipe(req);
}
body.on('error',function(err){
req.abort();
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read file to send error [' + headerTostring(err) + ']');
}
bc(err,null);
}).on('end', function(){
req.end();
});
}else{
req.end(String(body));
}
return;
}
if(!('srcFile' in opt)){
req.end();
return;
}
let offset = opt.Offset >= 0 ? opt.Offset : 0;
if('PartSize' in opt){
this.doSend(req, opt.srcFile, offset, parseInt(opt.PartSize), methodName, bc);
}else if('ContentLength' in opt){
this.doSend(req, opt.srcFile, offset, parseInt(opt.ContentLength), methodName, bc);
}else{
let stream = fs.createReadStream(opt.srcFile);
stream.on('error', function(err){
req.abort();
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read file to send error [' + headerTostring(err) + ']');
}
bc(err, null);
}).on('end', function(){
req.end();
}).pipe(req);
}
};
Utils.prototype.doSend = function(req, srcFile, initPos, totalCount, methodName, bc){
var log = this.log;
fs.open(srcFile, 'r', function(err, fd){
if(err){
req.abort();
fs.close(fd,function(err){});
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read file to send error [' + err + ']');
}
bc(err, null);
return;
}
var buffer = Buffer.alloc(65536);
var _doSend = function(sendPosition, sendCount){
if(sendCount >= totalCount){
req.end();
fs.close(fd,function(err){});
return;
}
var sendCountOnce = (totalCount - sendCount) > buffer.length ? buffer.length : totalCount - sendCount;
fs.read(fd, buffer, 0, sendCountOnce, sendPosition, function(err, bytes){
if (err){
req.abort();
fs.close(fd,function(err){});
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read file to send error [' + err + ']');
}
bc(err, null);
return;
}
if(bytes <= 0){
req.end();
fs.close(fd, function(err){});
return;
}
req.write(buffer.slice(0, bytes), function(err){
if(err){
req.abort();
fs.close(fd,function(err){});
if(log.isLevelEnabled('error')){
log.runLog('error', methodName, 'read file to send error [' + err + ']');
}
bc(err, null);
return;
}
_doSend(sendPosition + bytes, sendCount + bytes);
});
});
};
_doSend(initPos, 0);
});
};
Utils.prototype.bufMD5 = function(buf) {
return crypto.createHash('md5').update(buf).digest('base64');
};
Utils.prototype.fileMD5 = function (filePath, bc){
var stream = fs.createReadStream(filePath);
var sha = crypto.createHash('md5');
stream.on('error',function(err){
if(err){
bc(err,null);
}
}).on('data', function(data){
sha.update(data);
}).on('end', function(){
var md5 = sha.digest('base64');
bc(null, md5);
});
};
Utils.prototype.createSignedUrlSync = function(param){
return this.signature === 'v4' ? this.createV4SignedUrlSync(param) : this.createV2SignedUrlSync(param);
};
Utils.prototype.createV2SignedUrlSync = function(param){
param = param || {};
var method = param.Method ? String(param.Method) : 'GET';
var bucketName = param.Bucket ? String(param.Bucket) : null;
var objectKey = param.Key ? String(param.Key) : null;
var specialParam = param.SpecialParam ? String(param.SpecialParam) : null;
if(this.signature === 'obs' && specialParam === 'storagePolicy'){
specialParam = 'storageClass';
}else if(this.signature === 'v2' && specialParam === 'storageClass'){
specialParam = 'storagePolicy';
}
var expires = param.Expires ? parseInt(param.Expires) : 300;
var headers = {};
if(param.Headers && (param.Headers instanceof Object) && !(param.Headers instanceof Array)){
for(let key in param.Headers){
headers[key] = param.Headers[key];
}
}
var queryParams = {};
if(param.QueryParams && (param.QueryParams instanceof Object) && !(param.QueryParams instanceof Array)){
for(let key in param.QueryParams){
queryParams[key] = param.QueryParams[key];
}
}
if(this.securityToken && !queryParams[this.headerPrefix + 'security-token']){
queryParams[this.headerPrefix + 'security-token'] = this.securityToken;
}
var result = '';
var resource = '';
var host = this.server;
if(bucketName){
resource += '/' + bucketName;
if(this.pathStyle){
result += '/' + bucketName;
}else{
host = bucketName + '.' + host;
resource += '/';
}
}
headers.Host = host;
if(objectKey){
objectKey = encodeURIWithSafe(objectKey, '/');
result += '/' + objectKey;
if(resource.lastIndexOf('/') !== resource.length - 1){
resource += '/';
}
resource += objectKey;
}
if(resource === ''){
resource = '/';
}
result += '?';
if(specialParam){
queryParams[specialParam] = '';
}
if(this.signature === 'v2'){
queryParams.AWSAccessKeyId = this.ak;
}else{
queryParams.AccessKeyId = this.ak;
}
if(expires < 0){
expires = 300;
}
expires = parseInt(new Date().getTime() / 1000) + expires;
queryParams.Expires = String(expires);
var interestHeaders = {};
for(let name in headers){
let key = String(name).toLowerCase();
if(key === 'content-type' || key === 'content-md5' || key.length > this.headerPrefix.length && key.slice(0,this.headerPrefix.length) === this.headerPrefix){
interestHeaders[key] = headers[name];
}
}
var queryParamsKeys = [];
for(let key in queryParams){
queryParamsKeys.push(key);
}
queryParamsKeys.sort();
var index = 0;
var flag = false;
var _resource = [];
for(let i=0;i<queryParamsKeys.length;i++){
let key = queryParamsKeys[i];
let val = queryParams[key];
key = encodeURIWithSafe(key, '/');
val = encodeURIWithSafe(val, '/');
result += key;
if(val){
result += '=' + val;
}
if(allowedResourceParameterNames.indexOf(key.toLowerCase())>=0 || key.toLowerCase().indexOf(this.headerPrefix) === 0){
flag = true;
let _val = val ? key + '=' + decodeURIComponent(val) : key;
_resource.push(_val);
}
result += '&';
index++;
}
_resource = _resource.join('&');
if(flag){
_resource = '?' + _resource;
}
resource += _resource;
var stringToSign = [method];
stringToSign.push('\n');
if('content-md5' in interestHeaders){
stringToSign.push(interestHeaders['content-md5']);
}
stringToSign.push('\n');
if('content-type' in interestHeaders){
stringToSign.push(interestHeaders['content-type']);
}
stringToSign.push('\n');
stringToSign.push(String(expires));
stringToSign.push('\n');
var temp = [];
var i = 0;
for(let key in interestHeaders){
if (key.length > this.headerPrefix.length && key.slice(0, this.headerPrefix.length) === this.headerPrefix){
temp[i++] = key;
}
}
temp = temp.sort();
for(let j=0;j<temp.length;j++){
stringToSign.push(temp[j]);
stringToSign.push(':');
stringToSign.push(interestHeaders[temp[j]]);
stringToSign.push('\n');
}
stringToSign.push(resource);
var hmac = crypto.createHmac('sha1', this.sk);
hmac.update(stringToSign.join(''));
result += 'Signature=' + e