diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/CloseMessage.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/CloseMessage.java new file mode 100644 index 0000000000..e63821c66a --- /dev/null +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/CloseMessage.java @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +package com.microsoft.aspnet.signalr; + +public class CloseMessage extends HubMessage { + String error; + + @Override + public HubMessageType getMessageType() { + return HubMessageType.CLOSE; + } + + public CloseMessage() { + } + + public CloseMessage(String error) { + this.error = error; + } + + public String getError() { + return this.error; + } +} diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/HubConnection.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/HubConnection.java index 7656f9e06b..f9ca982a37 100644 --- a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/HubConnection.java +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/HubConnection.java @@ -71,17 +71,21 @@ public class HubConnection { logger.log(LogLevel.Warning, "Failed to find handler for %s method", invocationMessage.target); } break; + case CLOSE: + logger.log(LogLevel.Information, "Close message received from server."); + CloseMessage closeMessage = (CloseMessage)message; + stop(closeMessage.getError()); + break; + case PING: + // We don't need to do anything in the case of a ping message. + break; case STREAM_INVOCATION: case STREAM_ITEM: - case CLOSE: case CANCEL_INVOCATION: case COMPLETION: logger.log(LogLevel.Error, "This client does not support %s messages", message.getMessageType()); throw new UnsupportedOperationException(String.format("The message type %s is not supported yet.", message.getMessageType())); - case PING: - // We don't need to do anything in the case of a ping message. - break; } } }; @@ -142,17 +146,29 @@ public class HubConnection { String handshake = HandshakeProtocol.createHandshakeRequestMessage(new HandshakeRequestMessage(protocol.getName(), protocol.getVersion())); transport.send(handshake); connectionState = HubConnectionState.CONNECTED; - logger.log(LogLevel.Information, "HubConnected started"); + logger.log(LogLevel.Information, "HubConnected started."); } /** * Stops a connection to the server. */ - public void stop(){ - logger.log(LogLevel.Debug, "Stopping HubConnection"); + private void stop(String errorMessage) { + if(errorMessage != null){ + logger.log(LogLevel.Error , "HubConnection disconnected with an error %s.", errorMessage); + } else { + logger.log(LogLevel.Debug, "Stopping HubConnection."); + } + transport.stop(); connectionState = HubConnectionState.DISCONNECTED; - logger.log(LogLevel.Information, "HubConnection stopped"); + logger.log(LogLevel.Information, "HubConnection stopped."); + } + + /** + * Stops a connection to the server. + */ + public void stop() { + stop(null); } /** diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java index 4bee9e089c..3703afb8d8 100644 --- a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/JsonHubProtocol.java @@ -62,7 +62,14 @@ public class JsonHubProtocol implements HubProtocol { hubMessages.add(new PingMessage()); break; case CLOSE: - //Don't care yet; + CloseMessage closeMessage; + if (jsonMessage.has("error")){ + String error = jsonMessage.get("error").getAsString(); + closeMessage = new CloseMessage(error); + } else { + closeMessage = new CloseMessage(); + } + hubMessages.add(closeMessage); break; } } diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/Transport.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/Transport.java index d8356ae949..e161953f0d 100644 --- a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/Transport.java +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/Transport.java @@ -4,7 +4,7 @@ package com.microsoft.aspnet.signalr; public interface Transport { - void start() throws InterruptedException; + void start() throws Exception; void send(String message) throws Exception; void setOnReceive(OnReceiveCallBack callback); void onReceive(String message) throws Exception; diff --git a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/WebSocketTransport.java b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/WebSocketTransport.java index 40ce5beb7e..4bc7f5556b 100644 --- a/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/WebSocketTransport.java +++ b/clients/java/signalr/src/main/java/com/microsoft/aspnet/signalr/WebSocketTransport.java @@ -39,10 +39,14 @@ public class WebSocketTransport implements Transport { } @Override - public void start() throws InterruptedException { - logger.log(LogLevel.Debug, "Starting Websocket connection"); + public void start() throws Exception { + logger.log(LogLevel.Debug, "Starting Websocket connection."); webSocketClient = createWebSocket(); - webSocketClient.connectBlocking(); + if (!webSocketClient.connectBlocking()) { + String errorMessage = "There was an error starting the Websockets transport."; + logger.log(LogLevel.Debug, errorMessage); + throw new Exception(errorMessage); + } logger.log(LogLevel.Information, "WebSocket transport connected to: %s", webSocketClient.getURI()); } diff --git a/clients/java/signalr/src/test/java/HubConnectionTest.java b/clients/java/signalr/src/test/java/HubConnectionTest.java index 617871d443..59985a8b05 100644 --- a/clients/java/signalr/src/test/java/HubConnectionTest.java +++ b/clients/java/signalr/src/test/java/HubConnectionTest.java @@ -22,6 +22,21 @@ public class HubConnectionTest { assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); } + @Test + public void HubConnectionClosesAfterCloseMessage() throws Exception { + MockTransport mockTransport = new MockTransport(); + HubConnection hubConnection = new HubConnection("http://example.com", mockTransport); + + hubConnection.start(); + mockTransport.receiveMessage("{}" + RECORD_SEPARATOR); + + assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); + + mockTransport.receiveMessage("{\"type\":7,\"error\": \"There was an error\"}" + RECORD_SEPARATOR); + + assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); + } + @Test public void RegisteringMultipleHandlersAndBothGetTriggered() throws Exception { AtomicReference value = new AtomicReference<>(0.0); diff --git a/clients/java/signalr/src/test/java/JsonHubProtocolTest.java b/clients/java/signalr/src/test/java/JsonHubProtocolTest.java index 31c349e8e5..faa866700d 100644 --- a/clients/java/signalr/src/test/java/JsonHubProtocolTest.java +++ b/clients/java/signalr/src/test/java/JsonHubProtocolTest.java @@ -45,6 +45,38 @@ public class JsonHubProtocolTest { assertEquals(HubMessageType.PING, messages[0].getMessageType()); } + @Test + public void ParseCloseMessage() { + String stringifiedMessage = "{\"type\":7}\u001E"; + HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); + + //We know it's only one message + assertEquals(1, messages.length); + + assertEquals(HubMessageType.CLOSE, messages[0].getMessageType()); + + //We can safely cast here because we know that it's a close message. + CloseMessage closeMessage = (CloseMessage) messages[0]; + + assertEquals(null, closeMessage.getError()); + } + + @Test + public void ParseCloseMessageWithError() { + String stringifiedMessage = "{\"type\":7,\"error\": \"There was an error\"}\u001E"; + HubMessage[] messages = jsonHubProtocol.parseMessages(stringifiedMessage); + + //We know it's only one message + assertEquals(1, messages.length); + + assertEquals(HubMessageType.CLOSE, messages[0].getMessageType()); + + //We can safely cast here because we know that it's a close message. + CloseMessage closeMessage = (CloseMessage) messages[0]; + + assertEquals("There was an error", closeMessage.getError()); + } + @Test public void ParseSingleMessage() { String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E"; diff --git a/clients/java/signalr/src/test/java/WebSocketTransportTest.java b/clients/java/signalr/src/test/java/WebSocketTransportTest.java index ba251c9a9f..beb07d9bdd 100644 --- a/clients/java/signalr/src/test/java/WebSocketTransportTest.java +++ b/clients/java/signalr/src/test/java/WebSocketTransportTest.java @@ -2,38 +2,22 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. import com.microsoft.aspnet.signalr.NullLogger; +import com.microsoft.aspnet.signalr.Transport; import com.microsoft.aspnet.signalr.WebSocketTransport; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.Collection; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.rules.ExpectedException; -import static org.junit.Assert.*; - -@RunWith(Parameterized.class) public class WebSocketTransportTest { - private String url; - private String expectedUrl; - public WebSocketTransportTest(String url, String expectedProtocol){ - this.url = url; - this.expectedUrl = expectedProtocol; - } - - @Parameterized.Parameters - public static Collection protocols(){ - return Arrays.asList(new String[][] { - {"http://example.com", "ws://example.com"}, - {"https://example.com", "wss://example.com"}, - {"ws://example.com", "ws://example.com"}, - {"wss://example.com", "wss://example.com"}}); - } + @Rule + public ExpectedException expectedEx = ExpectedException.none(); @Test - public void checkWebsocketUrlProtocol() throws URISyntaxException { - WebSocketTransport webSocketTransport = new WebSocketTransport(this.url, new NullLogger()); - assertEquals(this.expectedUrl, webSocketTransport.getUrl().toString()); + public void WebsocketThrowsIfItCantConnect() throws Exception { + expectedEx.expect(Exception.class); + expectedEx.expectMessage("There was an error starting the Websockets transport"); + Transport transport = new WebSocketTransport("www.notarealurl12345.fake", new NullLogger()); + transport.start(); } -} \ No newline at end of file +} diff --git a/clients/java/signalr/src/test/java/WebSocketTransportUrlFormatTest.java b/clients/java/signalr/src/test/java/WebSocketTransportUrlFormatTest.java new file mode 100644 index 0000000000..258c45584b --- /dev/null +++ b/clients/java/signalr/src/test/java/WebSocketTransportUrlFormatTest.java @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +import com.microsoft.aspnet.signalr.NullLogger; +import com.microsoft.aspnet.signalr.WebSocketTransport; + +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +public class WebSocketTransportUrlFormatTest { + private String url; + private String expectedUrl; + + public WebSocketTransportUrlFormatTest(String url, String expectedProtocol) { + this.url = url; + this.expectedUrl = expectedProtocol; + } + + @Parameterized.Parameters + public static Collection protocols() { + return Arrays.asList(new String[][]{ + {"http://example.com", "ws://example.com"}, + {"https://example.com", "wss://example.com"}, + {"ws://example.com", "ws://example.com"}, + {"wss://example.com", "wss://example.com"}}); + } + + @Test + public void checkWebsocketUrlProtocol() throws URISyntaxException { + WebSocketTransport webSocketTransport = new WebSocketTransport(this.url, new NullLogger()); + assertEquals(this.expectedUrl, webSocketTransport.getUrl().toString()); + } +} \ No newline at end of file