From 195817832d9a38f7f19d4a67eb70c77f35a997af Mon Sep 17 00:00:00 2001 From: c43721 Date: Tue, 12 Nov 2024 21:30:28 -0600 Subject: [PATCH] feat: add timeout to requests --- .gitignore | 3 ++- cli.ts | 1 + deno.json | 4 ++-- deno.lock | 41 ----------------------------------------- src/rcon.ts | 24 ++++++++++++++---------- src/types.ts | 5 +++++ 6 files changed, 24 insertions(+), 54 deletions(-) delete mode 100644 deno.lock diff --git a/.gitignore b/.gitignore index 600d2d3..346a7da 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode \ No newline at end of file +.vscode +deno.lock \ No newline at end of file diff --git a/cli.ts b/cli.ts index 1b36c86..b6d7176 100644 --- a/cli.ts +++ b/cli.ts @@ -14,6 +14,7 @@ if (!args.password || !args.ip || !args.command) { using rcon = new Rcon({ host: args.ip, port, + timeout: 5_000, }); const didAuthenticate = await rcon.authenticate(args.password!); diff --git a/deno.json b/deno.json index f97c2c0..886797a 100644 --- a/deno.json +++ b/deno.json @@ -3,9 +3,9 @@ "imports": { "@std/bytes": "jsr:@std/bytes@^1.0.2", "@std/cli": "jsr:@std/cli@^1.0.6", - "@std/io": "jsr:@std/io@^0.225.0" + "@std/async": "jsr:@std/async@^1.0.8" }, - "version": "0.0.6", + "version": "0.0.7", "exports": "./mod.ts", "publish": { "include": ["README.md", "mod.ts", "src/"] diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 633548e..0000000 --- a/deno.lock +++ /dev/null @@ -1,41 +0,0 @@ -{ - "version": "4", - "specifiers": { - "jsr:@std/bytes@^1.0.2": "1.0.2", - "jsr:@std/cli@^1.0.6": "1.0.6", - "jsr:@std/io@0.225": "0.225.0", - "npm:@types/node@*": "18.16.19" - }, - "jsr": { - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" - }, - "@std/cli@1.0.6": { - "integrity": "d22d8b38c66c666d7ad1f2a66c5b122da1704f985d3c47f01129f05abb6c5d3d" - }, - "@std/io@0.225.0": { - "integrity": "c1db7c5e5a231629b32d64b9a53139445b2ca640d828c26bf23e1c55f8c079b3", - "dependencies": [ - "jsr:@std/bytes" - ] - } - }, - "npm": { - "@types/node@18.16.19": { - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" - } - }, - "remote": { - "https://deno.land/std@0.77.0/encoding/base64.ts": "b1d8f99b778981548457ec74bc6273ad785ffd6f61b2233bd5b30925345b565d", - "https://deno.land/std@0.77.0/encoding/hex.ts": "07a03ba41c96060a4ed4ba272e50b9e23f3c5b3839f4b069cdebc24d57434386", - "https://deno.land/std@0.77.0/node/_utils.ts": "3c3096695a3c6f926fb0d3e60f4bc534c5a28be3d6073e0eb6cd49654a675d68", - "https://deno.land/std@0.77.0/node/buffer.ts": "fa828a387ae0e044871a0337b66b044b47897dfb1bd64126b7204a0625fc8b30" - }, - "workspace": { - "dependencies": [ - "jsr:@std/bytes@^1.0.2", - "jsr:@std/cli@^1.0.6", - "jsr:@std/io@0.225" - ] - } -} diff --git a/src/rcon.ts b/src/rcon.ts index af03b0a..dd26043 100644 --- a/src/rcon.ts +++ b/src/rcon.ts @@ -2,6 +2,7 @@ import { protocol } from "./protocol.ts"; import { concat } from "@std/bytes"; import { createConnection, type Socket } from "node:net"; import { encode, decode } from "./packet.ts"; +import { abortable } from "@std/async"; import { NotAuthenticatedException, NotConnectedException, @@ -34,6 +35,8 @@ import type { RconOptions } from "./types.ts"; export class Rcon { #host: string; #port: number; + #timeout: number; + #connection?: Socket; #connected = false; #authenticated = false; @@ -44,10 +47,11 @@ export class Rcon { * @param {RconOptions} options The connection options */ constructor(options: RconOptions) { - const { host, port = 27015 } = options; + const { host, port = 27015, timeout = 30_000 } = options; this.#host = host; this.#port = port; + this.#timeout = timeout; } /** @@ -74,7 +78,7 @@ export class Rcon { /** * Authenticates the connection * @param password The RCON password - * + * * @returns {Promise} The result of the authentication */ public async authenticate(password: string): Promise { @@ -82,10 +86,9 @@ export class Rcon { this.#connect(); } - const response = await this.#send( - protocol.SERVERDATA_AUTH, - protocol.ID_AUTH, - password + const response = await abortable( + this.#send(protocol.SERVERDATA_AUTH, protocol.ID_AUTH, password), + AbortSignal.timeout(this.#timeout) ); if (response === "true") { @@ -100,7 +103,7 @@ export class Rcon { /** * Executes a command on the server * @param command The command to execute - * + * * @returns {Promise} The result of the execution */ public async execute(command: string): Promise { @@ -114,7 +117,10 @@ export class Rcon { const packetId = Math.floor(Math.random() * (256 - 1) + 1); - return await this.#send(protocol.SERVERDATA_EXECCOMMAND, packetId, command); + return await abortable( + this.#send(protocol.SERVERDATA_EXECCOMMAND, packetId, command), + AbortSignal.timeout(this.#timeout) + ); } /** @@ -185,7 +191,6 @@ export class Rcon { (decodedPacket.type === protocol.SERVERDATA_RESPONSE_VALUE || decodedPacket.id === protocol.ID_TERM) ) { - // concat the response- even if it's not a multipacket response if (decodedPacket.id != protocol.ID_TERM) { potentialMultiPacketResponse = concat([ potentialMultiPacketResponse, @@ -204,7 +209,6 @@ export class Rcon { this.#connection!.write(encodedTerminationPacket); } else if (decodedPacket.size <= 3700) { - // no need to check for ID_TERM here, since this packet will always be < 3700 return new TextDecoder().decode(potentialMultiPacketResponse); } } diff --git a/src/types.ts b/src/types.ts index 9a3bc80..a5b52d6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,4 +11,9 @@ export interface RconOptions { * (Optional- Default "27017") The port to connect to */ port?: number; + + /** + * (Optional- Default "30000") The timeout in milliseconds before a request is aborted + */ + timeout?: number; }