UNPKG

@litert/televoke

Version:
197 lines 7.69 kB
"use strict"; /** * Copyright 2025 Angus.Fenying <fenying@litert.org> * * Licensed 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 * * https://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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createLegacyHttpClient = createLegacyHttpClient; const $Http = require("http"); const $Https = require("https"); const Shared = require("../../shared"); const v1 = require("../../shared/Encodings/v1"); const node_events_1 = require("node:events"); const HTTP_HEADER_CONTENT_LENGTH = 'content-length'; const HTTP_HEADER_TV_VER = 'x-tv-ver'; const DEFAULT_MAX_CONNECTIONS = 100; const DEFAULT_TIMEOUT = 30000; const disabledStreamManager = Shared.createDisabledStreamManagerFactory()(null); class TvLegacyHttpClient extends node_events_1.EventEmitter { constructor(_request, _opts, _agent, _retryOnConnReset = true, _apiNameWrapper) { var _a; super(); this._request = _request; this._opts = _opts; this._agent = _agent; this._retryOnConnReset = _retryOnConnReset; this._apiNameWrapper = _apiNameWrapper; this.writable = true; this.ended = false; this.finished = false; this.transporter = null; (_a = this._opts).headers ?? (_a.headers = {}); } get timeout() { return this._opts.timeout ?? DEFAULT_TIMEOUT; } get streams() { return disabledStreamManager; } setHeaders(newHeaders, append = true) { if (append) { Object.assign(this._opts.headers, newHeaders); } else { this._opts.headers = newHeaders; } } sendBinaryChunk() { return Promise.reject(new Shared.errors.cmd_not_impl()); } sendMessage() { return Promise.reject(new Shared.errors.cmd_not_impl()); } ping() { return Promise.reject(new Shared.errors.cmd_not_impl()); } async invoke(api, ...args) { try { return await this._invoke(api, ...args); } catch (e) { if (this._retryOnConnReset && e?.code === 'ECONNRESET') { return this._invoke(api, ...args); } else { throw e; } } } _invoke(api, ...args) { const CST = Date.now(); const content = JSON.stringify({ ttl: this._opts.timeout ?? DEFAULT_TIMEOUT, rid: 0, cst: CST, args, api: this._apiNameWrapper?.(api) ?? api }); return new Promise((resolve, reject) => { const length = Buffer.byteLength(content); if (length > v1.MAX_PACKET_SIZE) { reject(new Shared.errors.invalid_packet({ reason: 'packet_too_large' })); return; } const req = this._request({ ...this._opts, agent: this._agent, method: 'POST', headers: { ...this._opts.headers, [HTTP_HEADER_CONTENT_LENGTH]: Buffer.byteLength(content), [HTTP_HEADER_TV_VER]: 1 }, }, (resp) => { if (resp.statusCode !== 200) { resp.socket.destroy(); reject(new Shared.errors.invalid_response({ reason: 'invalid_status_code', statusCode: resp.statusCode })); return; } if (!resp.headers[HTTP_HEADER_CONTENT_LENGTH]) { resp.socket.destroy(); reject(new Shared.errors.invalid_response({ reason: 'missing_content_length' })); return; } const length = parseInt(resp.headers[HTTP_HEADER_CONTENT_LENGTH]); if (!Number.isSafeInteger(length) || length > v1.MAX_PACKET_SIZE) { // Maximum request packet is 64MB resp.socket.destroy(); reject(new Shared.errors.invalid_packet({ reason: 'packet_too_large' })); return; } const buf = Buffer.allocUnsafe(length); let offset = 0; resp.on('data', (chunk) => { const index = offset; offset += chunk.byteLength; if (offset > length) { resp.removeAllListeners('end'); resp.removeAllListeners('data'); resp.destroy(); reject(new Shared.errors.invalid_packet({ reason: 'packet_length_mismatched' })); return; } chunk.copy(buf, index); }).on('end', () => { let data; try { data = JSON.parse(buf); } catch (e) { reject(new Shared.errors.invalid_response({ reason: 'invalid_json', raw: buf }, e)); return; } switch (data.code) { case v1.EResponseCode.OK: resolve(data.body); break; case v1.EResponseCode.FAILURE: reject(new Shared.TvErrorResponse(data.body)); break; case v1.EResponseCode.API_NOT_FOUND: reject(new Shared.errors.api_not_found({ api })); break; case v1.EResponseCode.MALFORMED_ARGUMENTS: case v1.EResponseCode.SYSTEM_ERROR: default: reject(new Shared.errors.server_internal_error({ api, ...data })); break; } }); }); req.once('timeout', () => { reject(new Shared.errors.timeout({ metadata: { api, time: Date.now(), details: null } })); }); req.once('error', reject); req.end(content); }); } connect() { return Promise.resolve(); } close() { this._agent.destroy(); return Promise.resolve(); } } function createLegacyHttpClient(opts) { return new TvLegacyHttpClient(opts.https ? $Https.request : $Http.request, opts, opts.https ? new $Https.Agent({ 'maxSockets': opts.maxConnections ?? DEFAULT_MAX_CONNECTIONS, 'keepAlive': opts.keepAlive ?? true, 'keepAliveMsecs': opts.keepAliveTimeout ?? DEFAULT_TIMEOUT }) : new $Http.Agent({ 'maxSockets': opts.maxConnections ?? DEFAULT_MAX_CONNECTIONS, 'keepAlive': opts.keepAlive ?? true, 'keepAliveMsecs': opts.keepAliveTimeout ?? DEFAULT_TIMEOUT }), opts.retryOnConnReset, opts.apiNameWrapper); } //# sourceMappingURL=LegacyHttp.Client.Native.js.map