Repository: Vantiq/vantiq-extension-sources (language: python)

Issue:
ExtJSdk: Websocket in inconsistent state is ignored
When web socket connection to vantiq is in an inconsistent state (remote closed, lost connection, lost intermediary, etc.), that fact is not reported by a send.  When the send is done, the exception is trapped and logged, but not reported the program.




Discussion / hints:
Will add code to call close() and throw an exception.  Caller's close handler will be called, and they can react accordingly (or react to the thrown exception, or both).
@msilva123

Fix patch:
diff --git a/extjsdk/src/main/java/io/vantiq/extjsdk/ExtensionWebSocketClient.java b/extjsdk/src/main/java/io/vantiq/extjsdk/ExtensionWebSocketClient.java
index 349a7680..f6039301 100644
--- a/extjsdk/src/main/java/io/vantiq/extjsdk/ExtensionWebSocketClient.java
+++ b/extjsdk/src/main/java/io/vantiq/extjsdk/ExtensionWebSocketClient.java
@@ -586,8 +586,12 @@ public void send(Object obj) {
                     this.webSocket.send(ByteString.of(bytes));
                 }
             }
-        }
-        catch (Exception e) {
+        } catch (IllegalStateException ise) {
+            log.warn("Error sending to WebSocket", ise);
+            sourceHasDisconnected();
+            close();
+            throw new RuntimeException("Lost connection to Vantiq source", ise);
+        } catch (Exception e) {
             log.warn("Error sending to WebSocket", e);
         }
     }


Test patch:
diff --git a/extjsdk/src/test/java/io/vantiq/extjsdk/FalseWebSocket.java b/extjsdk/src/test/java/io/vantiq/extjsdk/FalseWebSocket.java
index 4ef24083..5ee2289f 100644
--- a/extjsdk/src/test/java/io/vantiq/extjsdk/FalseWebSocket.java
+++ b/extjsdk/src/test/java/io/vantiq/extjsdk/FalseWebSocket.java
@@ -32,13 +32,20 @@
  */
 public class FalseWebSocket implements WebSocket {
     FalseBufferedSink s = new FalseBufferedSink();
+    boolean closedByRemote = false;
     
     public byte[] getMessage() {
         return s.retrieveSentBytes();
     }
     
+    public void setClosedByRemote(boolean newState) {
+        closedByRemote = newState;
+    }
     @Override
     public boolean send(@NotNull ByteString bytes) {
+        if (closedByRemote) {
+            throw new IllegalStateException("Emulating remote closure or other unexpected state");
+        }
         s.write(bytes);
         return true;
     }
diff --git a/extjsdk/src/test/java/io/vantiq/extjsdk/TestExtensionWebSocketClient.java b/extjsdk/src/test/java/io/vantiq/extjsdk/TestExtensionWebSocketClient.java
index 7977a87e..b2f9154e 100644
--- a/extjsdk/src/test/java/io/vantiq/extjsdk/TestExtensionWebSocketClient.java
+++ b/extjsdk/src/test/java/io/vantiq/extjsdk/TestExtensionWebSocketClient.java
@@ -180,6 +180,41 @@ public void testQueryResponseSingleMap() {
         assert socket.compareData("status", 200);
     }
     
+    boolean sawCloseHandler = false;
+    public Handler<ExtensionWebSocketClient> errorCloseHandler =  new Handler<ExtensionWebSocketClient>() {
+        @Override
+        public void handleMessage(ExtensionWebSocketClient message) {
+            sawCloseHandler = true;
+        }
+    };
+    
+    @Test
+    public void testLostConnection() {
+        Map<String, Object> queryData = new LinkedHashMap<>();
+        queryData.put("msg", "val");
+        queryData.put("val", "msg");
+        
+        markWsConnected(true);
+        markAuthSuccess(true);
+        markSourceConnected(true);
+        client.sendQueryResponse(200, queryAddress, queryData);
+        
+        assert socket.compareData("body", queryData);
+        assert socket.compareData("headers." + ExtensionServiceMessage.RESPONSE_ADDRESS_HEADER, queryAddress);
+        assert socket.compareData("status", 200);
+        
+        client.setCloseHandler(errorCloseHandler);
+        socket.setClosedByRemote(true);
+        sawCloseHandler = false;
+        try {
+            client.sendQueryResponse(200, queryAddress, queryData);
+        } catch (RuntimeException re) {
+            assert re.getCause() instanceof IllegalStateException;
+            assert !client.isOpen();
+            assert sawCloseHandler;
+        }
+    }
+    
     @Test
     public void testQueryResponseMapArray() {
         Map<String, Object>[] queryData = new Map[2];
@@ -517,9 +552,17 @@ private class FalseWebSocket implements WebSocket {
         
         Map<String,Object> lastData = null;
         boolean messageReceived = false;
-
+    
+        private boolean closedByRemote = false;
+    
+        public void setClosedByRemote(boolean newState) {
+            closedByRemote = newState;
+        }
         @Override
         public boolean send(ByteString bytes) {
+            if (closedByRemote) {
+                throw new IllegalStateException("Asked to emulate unexpected closure of websocket");
+            }
             try {
                 lastData = mapper.readValue(bytes.toByteArray(), Map.class);
             } catch (IOException e) {