Skip to content

Commit 8fc7da3

Browse files
committed
Only initiate the WebSocket closing handshake on the client
SignalR#3342
1 parent 866fe9f commit 8fc7da3

File tree

4 files changed

+45
-40
lines changed

4 files changed

+45
-40
lines changed

src/Microsoft.AspNet.SignalR.Client.JS/jquery.signalR.transports.webSockets.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
connection.transport,
8989
event);
9090

91-
connection.log("Unclean disconnect from websocket: " + event.reason || "[no reason given].");
91+
connection.log("Unclean disconnect from websocket: " + (event.reason || "[no reason given]."));
9292
} else {
9393
connection.log("Websocket closed.");
9494
}

src/Microsoft.AspNet.SignalR.Core/Owin/WebSockets/WebSocketHandler.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,18 @@ internal async Task ProcessWebSocketRequestAsync(WebSocket webSocket, Cancellati
211211

212212
try
213213
{
214+
#if CLIENT_NET45
214215
if (WebSocket.State == WebSocketState.Closed ||
215216
WebSocket.State == WebSocketState.Aborted)
216217
{
217218
// No-op if the socket is already closed or aborted
218219
}
219220
else
220221
{
221-
// Close the socket
222+
// Initiate the WebSocket closing handshake. Only the client should ever do this.
222223
await WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).PreserveCulture();
223224
}
225+
#endif
224226
}
225227
finally
226228
{

tests/Microsoft.AspNet.SignalR.Client.Tests/Client/Transports/WebSockets/ClientWebSocketHandlerFacts.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11

22
using System;
3+
using System.Net.WebSockets;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNet.SignalR.WebSockets;
37
using Moq;
48
using Xunit;
59

@@ -48,10 +52,46 @@ public void OnErrorCallsIntoWebSocketTransportOnErrorAndPassesException()
4852

4953
var webSocketHandler =
5054
new ClientWebSocketHandler(mockWebSocketTransport.Object) { Error = exception };
51-
55+
5256
webSocketHandler.OnError();
5357

5458
mockWebSocketTransport.Verify(p => p.OnError(exception), Times.Once());
5559
}
60+
61+
[Fact]
62+
public async Task WebSocketHandlerClosesIfWebSocketStateIsCloseSentAfterClosing()
63+
{
64+
var messageIndex = 0;
65+
var webSocketMessages = new[] { new WebSocketReceiveResult(0, WebSocketMessageType.Text, endOfMessage: false),
66+
new WebSocketReceiveResult(0, WebSocketMessageType.Text, endOfMessage: false),
67+
new WebSocketReceiveResult(0, WebSocketMessageType.Close, endOfMessage: true)};
68+
69+
var webSocket = new Mock<WebSocket>(MockBehavior.Strict);
70+
71+
webSocket.Setup(w => w.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None))
72+
.Returns(() => TaskAsyncHelper.FromResult(webSocketMessages[messageIndex++]));
73+
74+
WebSocketState state = WebSocketState.Open;
75+
webSocket.Setup(w => w.State).Returns(() => state);
76+
webSocket.Setup(w => w.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None))
77+
.Returns(() =>
78+
{
79+
state = WebSocketState.CloseSent;
80+
return TaskAsyncHelper.Empty;
81+
});
82+
83+
webSocket.Setup(w => w.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None))
84+
.Returns(() =>
85+
{
86+
state = WebSocketState.Closed;
87+
return TaskAsyncHelper.Empty;
88+
});
89+
webSocket.As<IDisposable>().Setup(w => w.Dispose());
90+
91+
var webSocketHandler = new Mock<WebSocketHandler>(64 * 1024) { CallBase = true };
92+
await webSocketHandler.Object.ProcessWebSocketRequestAsync(webSocket.Object, CancellationToken.None);
93+
94+
webSocket.VerifyAll();
95+
}
5696
}
5797
}

tests/Microsoft.AspNet.SignalR.Tests/Owin/WebSocketFacts.cs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ public void WebSocketHandlesClosedMessageGracefully()
5656
var webSocket = new Mock<WebSocket>(MockBehavior.Strict);
5757
var webSocketHandler = new Mock<WebSocketHandler>(MockBehavior.Strict, 64 * 1024);
5858

59-
webSocket.Setup(w => w.State).Returns(WebSocketState.Closed);
60-
6159
webSocket.Setup(w => w.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None))
6260
.Returns(() => TaskAsyncHelper.FromResult(webSocketMessages[messageIndex++]));
6361

@@ -71,41 +69,6 @@ public void WebSocketHandlesClosedMessageGracefully()
7169
webSocketHandler.VerifyAll();
7270
}
7371

74-
[Fact]
75-
public async Task WebSocketHandlerClosesIfWebSocketStateIsCloseSentAfterClosing()
76-
{
77-
var messageIndex = 0;
78-
var webSocketMessages = new[] { new WebSocketReceiveResult(0, WebSocketMessageType.Text, endOfMessage: false),
79-
new WebSocketReceiveResult(0, WebSocketMessageType.Text, endOfMessage: false),
80-
new WebSocketReceiveResult(0, WebSocketMessageType.Close, endOfMessage: true)};
81-
82-
var webSocket = new Mock<WebSocket>(MockBehavior.Strict);
83-
84-
webSocket.Setup(w => w.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None))
85-
.Returns(() => TaskAsyncHelper.FromResult(webSocketMessages[messageIndex++]));
86-
87-
WebSocketState state = WebSocketState.Open;
88-
webSocket.Setup(w => w.State).Returns(() => state);
89-
webSocket.Setup(w => w.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None))
90-
.Returns(() =>
91-
{
92-
state = WebSocketState.CloseSent;
93-
return TaskAsyncHelper.Empty;
94-
});
95-
96-
webSocket.Setup(w => w.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None))
97-
.Returns(() =>
98-
{
99-
state = WebSocketState.Closed;
100-
return TaskAsyncHelper.Empty;
101-
});
102-
103-
var webSocketHandler = new Mock<WebSocketHandler>(64 * 1024) {CallBase = true};
104-
await webSocketHandler.Object.ProcessWebSocketRequestAsync(webSocket.Object, CancellationToken.None);
105-
106-
webSocket.VerifyAll();
107-
}
108-
10972
[Theory]
11073
[InlineData(WebSocketState.Closed)]
11174
[InlineData(WebSocketState.CloseSent)]

0 commit comments

Comments
 (0)