Permalink
Browse files

Ensure Connection.Send doesn't silently fail due to a reconnecting WS

- .NET Client
- Previously WebSocketTransport.Send would noop if the WebSocket wasn't
  open, and this could occur while in the reconnecting state.

#2582
  • Loading branch information...
1 parent 16a95d1 commit ab36198ac0d06490efb6549fbeaa27d407608e1e @halter73 halter73 committed Dec 11, 2013
@@ -135,6 +135,9 @@
<data name="Error_DataCannotBeSentConnectionDisconnected" xml:space="preserve">
<value>Data cannot be sent because the connection is in the disconnected state. Call start before sending any data.</value>
</data>
+ <data name="Error_DataCannotBeSentDuringWebSocketReconnect" xml:space="preserve">
+ <value>Data cannot be sent because the WebSocket connection is reconnecting.</value>
+ </data>
<data name="Error_IncompatibleProtocolVersion" xml:space="preserve">
<value>You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.</value>
</data>

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -135,6 +135,9 @@
<data name="Error_DataCannotBeSentConnectionDisconnected" xml:space="preserve">
<value>Data cannot be sent because the connection is in the disconnected state. Call start before sending any data.</value>
</data>
+ <data name="Error_DataCannotBeSentDuringWebSocketReconnect" xml:space="preserve">
+ <value>Data cannot be sent because the WebSocket connection is reconnecting.</value>
+ </data>
<data name="Error_IncompatibleProtocolVersion" xml:space="preserve">
<value>You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.</value>
</data>
@@ -138,6 +138,20 @@ public void Abort(IConnection connection, TimeSpan timeout, string connectionDat
public Task Send(IConnection connection, string data, string connectionData)
{
+ if (connection == null)
+ {
+ throw new ArgumentNullException("connection");
+ }
+
+ // If we don't throw here when the WebSocket isn't open, WebSocketHander.SendAsync will noop.
+ if (WebSocket.State != WebSocketState.Open)
+ {
+ // Make this a faulted task and trigger the OnError even to maintain consistency with the HttpBasedTransports
+ var ex = new InvalidOperationException(Resources.Error_DataCannotBeSentDuringWebSocketReconnect);
+ connection.OnError(ex);
+ return TaskAsyncHelper.FromError(ex);
+ }
+
return SendAsync(data);
}
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Http;
using Microsoft.AspNet.SignalR.Client.Transports;
using Moq;
@@ -238,5 +238,33 @@ public void SendCatchesOnReceivedExceptions()
}
}
}
+
+ [Theory]
+ [InlineData(WebSocketState.Aborted)]
+ [InlineData(WebSocketState.Closed)]
+ [InlineData(WebSocketState.CloseReceived)]
+ [InlineData(WebSocketState.CloseSent)]
+ [InlineData(WebSocketState.Connecting)]
+ [InlineData(WebSocketState.Connecting)]
+ public void WebSocketSendReturnsAFaultedTaskWhenNotConnected(WebSocketState state)
+ {
+ var mockConnection = new Mock<Client.IConnection>(MockBehavior.Strict);
+ var mockWebSocket = new Mock<WebSocket>(MockBehavior.Strict);
+
+ mockWebSocket.SetupGet(ws => ws.State).Returns(state);
+ mockConnection.Setup(c => c.OnError(It.IsAny<InvalidOperationException>()));
+
+ var wsTransport = new WebSocketTransport();
+
+ wsTransport.WebSocket = mockWebSocket.Object;
+
+ var task = wsTransport.Send(mockConnection.Object, "", "");
+
+ Assert.True(task.IsFaulted);
+ Assert.IsType(typeof(InvalidOperationException), task.Exception.InnerException);
+
+ mockConnection.VerifyAll();
+ mockWebSocket.VerifyAll();
+ }
}
}
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Tests.Common;
using Microsoft.AspNet.SignalR.Tests.Common.Infrastructure;
@@ -58,5 +60,53 @@ public void ServerCannotReceiveMessagesOver64KBViaWebSockets(HostType hostType,
}
}
}
+
+ [Theory]
+ [InlineData(HostType.IISExpress, TransportType.Websockets)]
+ [InlineData(HostType.HttpListener, TransportType.Websockets)]
+ public async Task SendingDuringWebSocketReconnectFails(HostType hostType, TransportType transportType)
+ {
+ var wh1 = new ManualResetEventSlim();
+ var wh2 = new ManualResetEventSlim();
+
+ using (var host = CreateHost(hostType, transportType))
+ {
+ host.Initialize(keepAlive: null,
+ disconnectTimeout: 6,
+ connectionTimeout: 2,
+ enableAutoRejoiningGroups: true);
+
+ using (HubConnection connection = CreateHubConnection(host))
+ {
+ var proxy = connection.CreateHubProxy("demo");
+
+ connection.Reconnecting += async () =>
+ {
+ try
+ {
+ await connection.Send("test");
+ }
+ catch (InvalidOperationException)
+ {
+ wh1.Set();
+ }
+
+ try
+ {
+ await proxy.Invoke("GetValue");
+ }
+ catch (InvalidOperationException)
+ {
+ wh2.Set();
+ }
+ };
+
+ await connection.Start(host.Transport);
+
+ Assert.True(wh1.Wait(TimeSpan.FromSeconds(10)));
+ Assert.True(wh2.Wait(TimeSpan.FromSeconds(10)));
+ }
+ }
+ }
}
}

0 comments on commit ab36198

Please sign in to comment.