From 3fd4bdb59dc296ee87fa4a2e0c54c017b0420709 Mon Sep 17 00:00:00 2001
From: Marin Karamihalev <marin.karamihalev@iopsys.eu>
Date: Wed, 9 Mar 2022 12:34:04 +0100
Subject: [PATCH] made connectTimeout work

---
 README.md                    | 15 ++++++++++-----
 src/configurations/build.ts  | 22 ++++++++++++++++------
 src/configurations/iopsys.ts | 20 +++++++++-----------
 src/types.ts                 |  9 +++++++++
 4 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index 71dcadd..dcdc52c 100644
--- a/README.md
+++ b/README.md
@@ -23,10 +23,8 @@ const run = async () => {
     host: "my.ip.here",
     username: "username",
     password: "password",
-    port: 90001,
+    port: 9001,
     protocol: "ws",
-    fromId: "from::id",
-    toId: "to::id",
   });
 
   // Get property
@@ -45,13 +43,20 @@ run();
 
 ```javascript
 // options are based on https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
-// they additionaly require fromId and toId, more info: url.here
 const usp = await connect(options);
 ```
 
+> Additional connection options:
+
+> - fromId: Source ID
+> - toId: Destination ID
+> - closeOnDisconnect: Close connection if disconnected
+> - reconnectsBeforeClosing: Number of times to attempt reconnecting before closing connection
+> - connectTimeout: Timeout in milliseconds for connection
+
 - Get
 
-  - get object - all object end with a dot
+  - get object
 
   ```javascript
   await usp.get("Device.Time.");
diff --git a/src/configurations/build.ts b/src/configurations/build.ts
index de5b3f6..f225c76 100644
--- a/src/configurations/build.ts
+++ b/src/configurations/build.ts
@@ -21,7 +21,7 @@ const defaultSubscribeEndpoint = "/usp/controller";
 const defaultIdEndpoint = "obuspa/EndpointID";
 const defaultFromId = "proto::interop-usp-controller";
 const defaultConnectionTimeout = 5000;
-const idResolveTimeout = 5000;
+const defaultIdResolveTimeout = 5000;
 
 const fixId = (s: string) => s.split("+").join("%2B");
 
@@ -95,26 +95,36 @@ const buildConnect: BuildConnectionFn =
 
     const router = makeRouter();
     const callbackRouter = makeCallbackRouter();
+    
     const handleError = (err: any) =>
       events && events.onError && events.onError(err);
-    const handleOffline = () =>
+    const handleOffline = () => {
       events && events.onOffline && events.onOffline();
+      client.end();
+    };
     const handleReconnect = () =>
       events && events.onReconnect && events.onReconnect();
     const handleClose = () => events && events.onClose && events.onClose();
 
     callbackRouter.add("error", handleError);
 
-    const client = await connectClient(options);
+    let client: any = null;
+    const connectTimeout = options.connectTimeout || defaultConnectionTimeout;
+
+    try {
+      client = await timeoutWrapper(async () => connectClient(options), connectTimeout);
+    } catch(err) {
+      throw new Error(`connection timed out(${connectTimeout}ms)`)
+    }
 
     const handleInit = () =>
       new Promise<string>((resolve, reject) => {
         const id = setTimeout(
           () =>
             reject({
-              errMsg: `toId was not received within timeout(${idResolveTimeout})`,
+              errMsg: `toId was not received within timeout(${defaultIdResolveTimeout}ms)`,
             }),
-          idResolveTimeout
+          defaultIdResolveTimeout
         );
         client.on("message", (topic, data: any) => {
           clearTimeout(id);
@@ -179,7 +189,7 @@ const buildConnect: BuildConnectionFn =
       if (options.closeOnDisconnect === true) client.end();
       if (
         isTrackingReconnects &&
-        reconnectCount > (options.reconnectsBeforeClosing as number)
+        reconnectCount >= (options.reconnectsBeforeClosing as number)
       )
         client.end();
     });
diff --git a/src/configurations/iopsys.ts b/src/configurations/iopsys.ts
index e18aa13..2f08030 100644
--- a/src/configurations/iopsys.ts
+++ b/src/configurations/iopsys.ts
@@ -15,20 +15,18 @@ import rootMsgJson from "../specs/usp-msg-1-1";
 const isURL = (opts: ConnectionOptions): opts is URLConnectionOptions =>
   "url" in opts;
 
-const connectClient = (opts: ConnectionOptions): ConnectionClient => {
-  if (isURL(opts))
-    return mqttAsync.connectAsync(
-      opts.url,
-      opts as any
-    ) as unknown as ConnectionClient;
-  else
-    return opts.protocol?.startsWith("ws")
-      ? (mqttAsync.connectAsync(
+const connectClient = (opts: ConnectionOptions): ConnectionClient =>
+  isURL(opts)
+    ? (mqttAsync.connectAsync(
+        opts.url,
+        opts as any
+      ) as unknown as ConnectionClient)
+    : opts.protocol?.startsWith("ws")
+    ? (mqttAsync.connectAsync(
         `${opts.protocol}://${opts.host}:${opts.port}`,
         opts as any
       ) as unknown as ConnectionClient)
-      : (mqttAsync.connectAsync(opts) as unknown as ConnectionClient);
-};
+    : (mqttAsync.connectAsync(opts) as unknown as ConnectionClient);
 
 const loadProtobuf = async (): Promise<Proto> => {
   const rootRecord = protobuf.Root.fromJSON(rootRecordJson);
diff --git a/src/types.ts b/src/types.ts
index 3690adb..802396b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -468,8 +468,17 @@ export interface OtherConnectionOptions {
   ca?: CertType | Object[];
   key?: CertType;
   cert?: CertType;
+  /**
+   * Close connection if disconnected
+   */
   closeOnDisconnect?: boolean;
+  /**
+   * Number of times to attempt reconnecting before closing connection
+   */
   reconnectsBeforeClosing?: number;
+  /**
+   * Timeout in milliseconds for connection
+   */
   connectTimeout?: number;
 }
 
-- 
GitLab