Skip to content

Commit

Permalink
feat: add headerEncoding option
Browse files Browse the repository at this point in the history
  • Loading branch information
biejia committed Jul 29, 2020
1 parent 318809d commit 4758a2a
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 75 deletions.
7 changes: 4 additions & 3 deletions lib/browser/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const signUtils = require('../common/signUtils');
const { isIP: _isIP } = require('../common/utils/isIP');
const _initOptions = require('../common/client/initOptions');
const { createRequest } = require('../common/utils/createRequest');
const { encoder } = require('../common/utils/encoder');

const globalHttpAgent = new AgentKeepalive();

Expand Down Expand Up @@ -127,7 +128,7 @@ merge(proto, require('../common/parallel'));
proto.signature = function signature(stringToSign) {
this.debug('authorization stringToSign: %s', stringToSign, 'info');

return signUtils.computeSignature(this.options.accessKeySecret, stringToSign);
return signUtils.computeSignature(this.options.accessKeySecret, stringToSign, this.options.headerEncoding);
};

/**
Expand Down Expand Up @@ -157,7 +158,7 @@ proto.authorization = function authorization(method, resource, subres, headers)
parameters: subres
});

return signUtils.authorization(this.options.accessKeyId, this.options.accessKeySecret, stringToSign);
return signUtils.authorization(this.options.accessKeyId, this.options.accessKeySecret, stringToSign, this.options.headerEncoding);
};

/**
Expand Down Expand Up @@ -221,7 +222,7 @@ proto.request = async function request(params) {
proto._getResource = function _getResource(params) {
let resource = '/';
if (params.bucket) resource += `${params.bucket}/`;
if (params.object) resource += Buffer.from(params.object).toString('latin1');
if (params.object) resource += encoder(params.object, this.options.headerEncoding);

return resource;
};
Expand Down
2 changes: 1 addition & 1 deletion lib/browser/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ proto.signatureUrl = function signatureUrl(name, options) {
options['security-token'] = this.options.stsToken;
}

const signRes = signHelper._signatureForURL(this.options.accessKeySecret, options, resource, expires);
const signRes = signHelper._signatureForURL(this.options.accessKeySecret, options, resource, expires, this.options.headerEncoding);

const url = urlutil.parse(this._getReqUrl(params));
url.query = {
Expand Down
7 changes: 4 additions & 3 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const signUtils = require('./common/signUtils');
const { isIP: _isIP } = require('./common/utils/isIP');
const _initOptions = require('./common/client/initOptions');
const { createRequest } = require('./common/utils/createRequest');
const { encoder } = require('./common/utils/encoder');

const globalHttpAgent = new AgentKeepalive();
const globalHttpsAgent = new HttpsAgentKeepalive();
Expand Down Expand Up @@ -112,7 +113,7 @@ Client.STS = require('./sts');
proto.signature = function signature(stringToSign) {
debug('authorization stringToSign: %s', stringToSign);

return signUtils.computeSignature(this.options.accessKeySecret, stringToSign);
return signUtils.computeSignature(this.options.accessKeySecret, stringToSign, this.options.headerEncoding);
};

/**
Expand Down Expand Up @@ -142,7 +143,7 @@ proto.authorization = function authorization(method, resource, subres, headers)
parameters: subres
});

return signUtils.authorization(this.options.accessKeyId, this.options.accessKeySecret, stringToSign);
return signUtils.authorization(this.options.accessKeyId, this.options.accessKeySecret, stringToSign, this.options.headerEncoding);
};

/**
Expand Down Expand Up @@ -199,7 +200,7 @@ proto.request = async function request(params) {
proto._getResource = function _getResource(params) {
let resource = '/';
if (params.bucket) resource += `${params.bucket}/`;
if (params.object) resource += Buffer.from(params.object).toString('latin1');
if (params.object) resource += encoder(params.object, this.options.headerEncoding);

return resource;
};
Expand Down
3 changes: 2 additions & 1 deletion lib/common/client/initOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ module.exports = function (options) {
endpoint: null,
cname: false,
isRequestPay: false,
sldEnable: false
sldEnable: false,
headerEncoding: 'utf-8'
}, options);

opts.accessKeyId = opts.accessKeyId.trim();
Expand Down
12 changes: 6 additions & 6 deletions lib/common/signUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ exports.buildCanonicalString = function canonicalString(method, resourcePath, re
* @param {String} accessKeySecret
* @param {String} canonicalString
*/
exports.computeSignature = function computeSignature(accessKeySecret, canonicalString) {
exports.computeSignature = function computeSignature(accessKeySecret, canonicalString, headerEncoding = 'utf-8') {
const signature = crypto.createHmac('sha1', accessKeySecret);
return signature.update(Buffer.from(canonicalString, 'latin1')).digest('base64');
return signature.update(Buffer.from(canonicalString, headerEncoding)).digest('base64');
};

/**
* @param {String} accessKeyId
* @param {String} accessKeySecret
* @param {String} canonicalString
*/
exports.authorization = function authorization(accessKeyId, accessKeySecret, canonicalString) {
return `OSS ${accessKeyId}:${this.computeSignature(accessKeySecret, canonicalString)}`;
exports.authorization = function authorization(accessKeyId, accessKeySecret, canonicalString, headerEncoding) {
return `OSS ${accessKeyId}:${this.computeSignature(accessKeySecret, canonicalString, headerEncoding)}`;
};

/**
Expand All @@ -103,7 +103,7 @@ exports.authorization = function authorization(accessKeyId, accessKeySecret, can
* @param {String} resource
* @param {Number} expires
*/
exports._signatureForURL = function _signatureForURL(accessKeySecret, options = {}, resource, expires) {
exports._signatureForURL = function _signatureForURL(accessKeySecret, options = {}, resource, expires, headerEncoding) {
const headers = {};
const { subResource = {} } = options;

Expand Down Expand Up @@ -168,7 +168,7 @@ exports._signatureForURL = function _signatureForURL(accessKeySecret, options =
}, expires.toString());

return {
Signature: this.computeSignature(accessKeySecret, canonicalString),
Signature: this.computeSignature(accessKeySecret, canonicalString, headerEncoding),
subResource
};
};
5 changes: 3 additions & 2 deletions lib/common/utils/createRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const mime = require('mime');
const dateFormat = require('dateformat');
const copy = require('copy-to');
const path = require('path');
const { encoder } = require('./encoder');
function getHeader(headers, name) {
return headers[name] || headers[name.toLowerCase()];
}
Expand Down Expand Up @@ -56,11 +57,11 @@ function createRequest(params) {
const { hasOwnProperty } = Object.prototype;
for (const k in headers) {
if (headers[k] && hasOwnProperty.call(headers, k)) {
headers[k] = Buffer.from(String(headers[k])).toString('latin1');
headers[k] = encoder(String(headers[k]), this.options.headerEncoding);
}
}
const authResource = this._getResource(params);
headers.authorization = this.authorization(params.method, authResource, params.subres, headers);
headers.authorization = this.authorization(params.method, authResource, params.subres, headers, this.options.headerEncoding);
const url = this._getReqUrl(params);
debug('request %s %s, with headers %j, !!stream: %s', params.method, url, headers, !!params.stream);
const timeout = params.timeout || this.options.timeout;
Expand Down
5 changes: 3 additions & 2 deletions lib/common/utils/createRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const mime = require('mime');
const dateFormat = require('dateformat');
const copy = require('copy-to');
const path = require('path');
const { encoder } = require('./encoder')

interface Headers {
[propName: string]: any
Expand Down Expand Up @@ -73,12 +74,12 @@ export function createRequest(this: any, params) {
const { hasOwnProperty } = Object.prototype;
for (const k in headers) {
if (headers[k] && hasOwnProperty.call(headers, k)) {
headers[k] = Buffer.from(String(headers[k])).toString('latin1');
headers[k] = encoder(String(headers[k]), this.options.headerEncoding);
}
}

const authResource = this._getResource(params);
headers.authorization = this.authorization(params.method, authResource, params.subres, headers);
headers.authorization = this.authorization(params.method, authResource, params.subres, headers, this.options.headerEncoding);

const url = this._getReqUrl(params);
debug('request %s %s, with headers %j, !!stream: %s', params.method, url, headers, !!params.stream);
Expand Down
1 change: 1 addition & 0 deletions lib/common/utils/encoder.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function encoder(str: string, encoding?: string): string;
9 changes: 9 additions & 0 deletions lib/common/utils/encoder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encoder = void 0;
function encoder(str, encoding = 'utf-8') {
if (encoding === 'utf-8')
return str;
return Buffer.from(str).toString('latin1');
}
exports.encoder = encoder;
4 changes: 4 additions & 0 deletions lib/common/utils/encoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function encoder(str: string, encoding = 'utf-8') {
if (encoding === 'utf-8') return str;
return Buffer.from(str).toString('latin1');
}
2 changes: 1 addition & 1 deletion lib/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ proto.signatureUrl = function signatureUrl(name, options) {
options['security-token'] = this.options.stsToken;
}

const signRes = signHelper._signatureForURL(this.options.accessKeySecret, options, resource, expires);
const signRes = signHelper._signatureForURL(this.options.accessKeySecret, options, resource, expires, this.options.headerEncoding);

const url = urlutil.parse(this._getReqUrl(params));
url.query = {
Expand Down
82 changes: 56 additions & 26 deletions test/browser/browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,32 +751,6 @@ describe('browser', () => {
info = await store.head(originname);
assert.equal(info.res.headers['cache-control'], 'max-age=0, s-maxage=86400');
});

it('should 200 when set zh-cn meta', async () => {
const originname = `${prefix}ali-sdk/oss/copy-new-4.js`;
const result = await store.copy(originname, name, {
meta: {
a: '阿达的大多'
}
});
assert.equal(result.res.status, 200);
const info = await store.head(originname);
assert.equal(info.status, 200);
assert.equal(Buffer.from(info.meta.a, 'latin1').toString(), '阿达的大多');
});

it('should 200 when set zh-cn meta with zh-cn object name', async () => {
const originname = `${prefix}ali-sdk/oss/copy-new-4-中文.js`;
const result = await store.copy(originname, name, {
meta: {
a: '阿达的大多'
}
});
assert.equal(result.res.status, 200);
const info = await store.head(originname);
assert.equal(info.status, 200);
assert.equal(Buffer.from(info.meta.a, 'latin1').toString(), '阿达的大多');
});
});

describe('signatureUrl()', () => {
Expand Down Expand Up @@ -1634,4 +1608,60 @@ describe('browser', () => {
store.urllib.request.restore();
});
});

describe('options.headerEncoding', () => {
let store;
const utf8_content = '阿达的大多';
const latin1_content = Buffer.from(utf8_content).toString('latin1');
let name;
before(async () => {
store = oss(Object.assign({}, ossConfig, { headerEncoding: 'latin1' }));
name = `${prefix}ali-sdk/oss/put-new-latin1.js`;
const result = await store.put(name, Buffer.from('123'), {
meta: {
a: utf8_content
}
});
assert.equal(result.res.status, 200);
const info = await store.head(name);
assert.equal(info.status, 200);
assert.equal(info.meta.a, latin1_content);
});

it('copy() should return 200 when set zh-cn meta', async () => {
const originname = `${prefix}ali-sdk/oss/copy-new-latin1.js`;
const result = await store.copy(originname, name, {
meta: {
a: utf8_content
}
});
assert.equal(result.res.status, 200);
const info = await store.head(originname);
assert.equal(info.status, 200);
assert.equal(info.meta.a, latin1_content);
});

it('copy() should return 200 when set zh-cn meta with zh-cn object name', async () => {
const originname = `${prefix}ali-sdk/oss/copy-new-latin1-中文.js`;
const result = await store.copy(originname, name, {
meta: {
a: utf8_content
}
});
assert.equal(result.res.status, 200);
const info = await store.head(originname);
assert.equal(info.status, 200);
assert.equal(info.meta.a, latin1_content);
});

it('putMeta() should return 200', async () => {
const result = await store.putMeta(name, {
b: utf8_content
});
assert.equal(result.res.status, 200);
const info = await store.head(name);
assert.equal(info.status, 200);
assert.equal(info.meta.b, latin1_content);
});
});
});
Loading

0 comments on commit 4758a2a

Please sign in to comment.