artemis-http-client
Version:
artemis-http-client node.js SDK
193 lines (162 loc) • 5.26 kB
JavaScript
const {parse, format} = require('url');
const crypto = require('crypto');
const uuid = require('uuid');
const httpx = require('httpx');
const debug = require('debug')('artemis-http-client');
const ua = require('./ua');
const Base = require('./base');
const form = 'application/x-www-form-urlencoded';
const hasOwnProperty = function (obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
};
/**
* artemis-http-client
*/
class Client extends Base {
constructor(key, secret, stage = 'RELEASE') {
super();
this.appKey = key;
this.appSecret = Buffer.from(secret, 'utf8');
this.stage = stage;
}
buildStringToSign(method, headers, signedHeadersStr, url, data) {
// accept, contentMD5, contentType,
const lf = '\n';
var list = [method, lf];
var accept = headers['accept'];
if (accept) {
list.push(accept);
list.push(lf);
}
var contentMD5 = headers['content-md5'];
if (contentMD5) {
list.push(contentMD5);
list.push(lf);
}
var contentType = headers['content-type'] || '';
if (contentType) {
list.push(contentType);
list.push(lf);
}
var date = headers['date'];
if (date) {
list.push(date);
list.push(lf);
}
if (signedHeadersStr) {
list.push(signedHeadersStr);
list.push(lf);
}
if (contentType.startsWith(form)) {
list.push(this.buildUrl(url, data));
} else {
list.push(this.buildUrl(url));
}
return list.join('');
}
sign(stringToSign) {
return crypto.createHmac('sha256', this.appSecret)
.update(stringToSign, 'utf8').digest('base64');
}
md5(content) {
return crypto.createHash('md5')
.update(content, 'utf8')
.digest('base64');
}
getSignHeaderKeys(headers, signHeaders) {
var keys = Object.keys(headers).sort();
var signKeys = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// x-ca- 开头的header或者指定的header
if (key.startsWith('x-ca-') || hasOwnProperty(signHeaders, key)) {
signKeys.push(key);
}
}
// 按字典序排序
return signKeys.sort();
}
buildUrl(parsedUrl, data) {
var toStringify = Object.assign(parsedUrl.query, data);
var result = parsedUrl.pathname;
if (Object.keys(toStringify).length) {
var keys = Object.keys(toStringify).sort();
var list = new Array(keys.length);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!toStringify[key] && !('' + toStringify[key])) {
list[i] = `${key}=`;
} else {
list[i] = `${key}=${toStringify[key]}`;
}
}
result += '?' + list.join('&');
}
return result;
}
buildHeaders(headers = {}, signHeaders) {
return Object.assign({
'x-ca-timestamp': Date.now(),
'x-ca-key': this.appKey,
'accept': '*/*'
//,'content-type':'text/plain;charset=UTF-8'
}, headers, signHeaders);
}
getSignedHeadersString(signHeaders, headers) {
var list = [];
for (var i = 0; i < signHeaders.length; i++) {
var key = signHeaders[i];
list.push(key + ':' + headers[key]);
}
return list.join('\n');
}
async request(method, url, opts, originData) {
var signHeaders = opts.signHeaders;
// 小写化,合并之后的headers
var headers = this.buildHeaders(opts.headers, signHeaders);
var requestContentType = headers['content-type'] || '';
// if (method === 'POST' && !requestContentType.startsWith(form)) {
// headers['content-md5'] = this.md5(opts.data);
// }
var signHeaderKeys = this.getSignHeaderKeys(headers, signHeaders);
headers['x-ca-signature-headers'] = signHeaderKeys.join(',');
var signedHeadersStr = this.getSignedHeadersString(signHeaderKeys, headers);
var parsedUrl = parse(url, true);
var stringToSign = this.buildStringToSign(method, headers, signedHeadersStr, parsedUrl, originData);
headers['x-ca-signature'] = this.sign(stringToSign);
headers['user-agent'] = ua;
if (debug.enabled) {
debug('post body:');
debug('%s', opts.data);
}
var response = await httpx.request(parsedUrl, {
method: method,
headers: headers,
data: opts.data,
beforeRequest: function (opts) {
// FIXME: 证书有问题 https
opts.rejectUnauthorized = false;
return opts;
},
timeout: opts.timeout
});
var code = response.statusCode;
if (code !== 200) {
var message = await httpx.read(response, 'utf8') || '';
var err = new Error(`${message}`);
if (debug.enabled) {
debug('stringToSign:');
debug('client: %s', stringToSign.replace(/\n/g, '#'));
}
throw err;
}
var result = await httpx.read(response, 'utf8');
var contentType = response.headers['content-type'] || '';
if (contentType.startsWith('application/json')) {
result = JSON.parse(result);
}
return result;
}
}
module.exports = Client;
;