UNPKG

artemis-http-client

Version:
193 lines (162 loc) 5.26 kB
'use strict'; 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;