UNPKG

@dalongrong/nacos-config

Version:

nacos config client

259 lines 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpAgent = void 0; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const const_1 = require("./const"); const interface_1 = require("./interface"); const urllib = require("urllib"); const crypto = require("crypto"); const utils_1 = require("./utils"); const dns = require("dns"); class HttpAgent { constructor(options) { this.loggerDomain = 'Nacos'; this.debugPrefix = this.loggerDomain.toLowerCase(); this.debug = require('debug')(`${this.debugPrefix}:${process.pid}:http_agent`); this.options = options; this._accessToken = null; } get configuration() { return this.options.configuration; } get serverListMgr() { return this.configuration.get(interface_1.ClientOptionKeys.SERVER_MGR); } /** * HTTP 请求客户端 */ get httpclient() { return this.configuration.get(interface_1.ClientOptionKeys.HTTPCLIENT) || urllib; } get unit() { return this.configuration.get(interface_1.ClientOptionKeys.UNIT); } get secretKey() { return this.configuration.get(interface_1.ClientOptionKeys.SECRETKEY); } get requestTimeout() { return this.configuration.get(interface_1.ClientOptionKeys.REQUEST_TIMEOUT); } get accessKey() { return this.configuration.get(interface_1.ClientOptionKeys.ACCESSKEY); } get ssl() { return this.configuration.get(interface_1.ClientOptionKeys.SSL); } get serverPort() { return this.configuration.get(interface_1.ClientOptionKeys.SERVER_PORT); } get contextPath() { return this.configuration.get(interface_1.ClientOptionKeys.CONTEXTPATH) || 'nacos'; } get clusterName() { return this.configuration.get(interface_1.ClientOptionKeys.CLUSTER_NAME) || 'serverlist'; } get defaultEncoding() { return this.configuration.get(interface_1.ClientOptionKeys.DEFAULT_ENCODING) || 'utf8'; } get identityKey() { return this.configuration.get(interface_1.ClientOptionKeys.IDENTITY_KEY); } get identityValue() { return this.configuration.get(interface_1.ClientOptionKeys.IDENTITY_VALUE); } get endpointQueryParams() { return this.configuration.get(interface_1.ClientOptionKeys.ENDPOINT_QUERY_PARAMS); } get decodeRes() { return this.configuration.get(interface_1.ClientOptionKeys.DECODE_RES); } async login(username, password) { const unit = this.unit; const currentServer = await this.serverListMgr.getCurrentServerAddr(unit); let url = this.getRequestUrl(currentServer) + `/v1/auth/login`; try { const { encode = false, method = 'GET', data, timeout = this.requestTimeout, headers = {}, dataAsQueryString = false } = this.options; let requestData = { username: username, password: password, }; if (encode) { requestData = (0, utils_1.encodingParams)(data, this.defaultEncoding); } const res = await this.httpclient.request(url, { rejectUnauthorized: false, httpsAgent: false, method, data: requestData, dataType: 'text', headers, timeout, secureProtocol: 'TLSv1_2_method', dataAsQueryString, }); this.debug('%s %s, got %s, body: %j', method, url, res.status, res.data); switch (res.status) { case const_1.HTTP_OK: if (this.decodeRes) { return Object.assign(Object.assign({}, JSON.parse(this.decodeRes(res, method, this.defaultEncoding))), { genTime: Date.now() }); } return Object.assign(Object.assign({}, JSON.parse(this.decodeResData(res, method))), { genTime: Date.now() }); case const_1.HTTP_NOT_FOUND: return null; case const_1.HTTP_CONFLICT: await this.serverListMgr.updateCurrentServer(unit); break; default: await this.serverListMgr.updateCurrentServer(unit); break; } } catch (err) { if (err.code === dns.NOTFOUND) { throw err; } } } /** * 请求 * @param {String} path - 请求 path * @param {Object} [options] - 参数 * @return {String} value */ async request(path, options = {}) { var _a, _b, _c, _d, _e, _f; // 默认为当前单元 const unit = options.unit || this.unit; const ts = String(Date.now()); const { encode = false, method = 'GET', data, timeout = this.requestTimeout, headers = {}, dataAsQueryString = false } = options; const endTime = Date.now() + timeout; let lastErr; if (((_c = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.configuration) === null || _b === void 0 ? void 0 : _b.innerConfig) === null || _c === void 0 ? void 0 : _c.username) && ((_f = (_e = (_d = this.options) === null || _d === void 0 ? void 0 : _d.configuration) === null || _e === void 0 ? void 0 : _e.innerConfig) === null || _f === void 0 ? void 0 : _f.password)) { if (this._accessToken) { if (Date.now() - this._accessToken.genTime < this._accessToken.tokenTtl) { this._accessToken = await this.login(this.options.configuration.innerConfig.username, this.options.configuration.innerConfig.password); } } else { this._accessToken = await this.login(this.options.configuration.innerConfig.username, this.options.configuration.innerConfig.password); } data.accessToken = this._accessToken.accessToken; } let signStr = data.tenant; if (data.group && data.tenant) { signStr = data.tenant + '+' + data.group; } else if (data.group) { signStr = data.group; } const signature = crypto.createHmac('sha1', this.secretKey) .update(signStr + '+' + ts).digest() .toString('base64'); // 携带统一的头部信息 Object.assign(headers, Object.assign({ 'Client-Version': const_1.VERSION, 'Content-Type': 'application/x-www-form-urlencoded; charset=GBK', 'Spas-AccessKey': this.accessKey, timeStamp: ts, exConfigInfo: 'true', 'Spas-Signature': signature }, this.identityKey ? { [this.identityKey]: this.identityValue } : {})); let requestData = data; if (encode) { requestData = (0, utils_1.encodingParams)(data, this.defaultEncoding); } do { const currentServer = await this.serverListMgr.getCurrentServerAddr(unit); let url = this.getRequestUrl(currentServer) + `${path}`; this.debug('request unit: [%s] with url: %s', unit, url); try { const res = await this.httpclient.request(url, { rejectUnauthorized: false, httpsAgent: false, method, data: requestData, dataType: 'text', headers, timeout, secureProtocol: 'TLSv1_2_method', dataAsQueryString, }); this.debug('%s %s, got %s, body: %j', method, url, res.status, res.data); switch (res.status) { case const_1.HTTP_OK: if (this.decodeRes) { return this.decodeRes(res, method, this.defaultEncoding); } return this.decodeResData(res, method); case const_1.HTTP_NOT_FOUND: return null; case const_1.HTTP_CONFLICT: await this.serverListMgr.updateCurrentServer(unit); // JAVA 在外面业务类处理的这个逻辑,应该是需要重试的 lastErr = new Error(`[Client Worker] ${this.loggerDomain} server config being modified concurrently, data: ${JSON.stringify(data)}`); lastErr.name = `${this.loggerDomain}ServerConflictError`; break; default: await this.serverListMgr.updateCurrentServer(unit); // JAVA 还有一个针对 HTTP_FORBIDDEN 的处理,不过合并到 default 应该也没问题 lastErr = new Error(`${this.loggerDomain} Server Error Status: ${res.status}, url: ${url}, data: ${JSON.stringify(data)}`); lastErr.name = `${this.loggerDomain}ServerResponseError`; lastErr.body = res.data; break; } } catch (err) { if (err.code === dns.NOTFOUND) { throw err; } err.url = `${method} ${url}`; err.data = data; err.headers = headers; lastErr = err; } } while (Date.now() < endTime); throw lastErr; } // 获取请求 url getRequestUrl(currentServer) { let url; if (/:/.test(currentServer)) { url = `http://${currentServer}`; if (this.ssl) { url = `https://${currentServer}`; } } else { url = `http://${currentServer}:${this.serverPort}`; if (this.ssl) { url = `https://${currentServer}:${this.serverPort}`; } } return `${url}/${this.contextPath}`; } decodeResData(res, method = 'GET') { if (method === 'GET' && /charset=GBK/.test(res.headers['content-type']) && this.defaultEncoding === 'utf8') { try { return (0, utils_1.transformGBKToUTF8)(res.data); } catch (err) { console.error(`transform gbk data to utf8 error, msg=${err.messager}`); return res.data; } } else { return res.data; } } } exports.HttpAgent = HttpAgent; //# sourceMappingURL=http_agent.js.map