Skip to content

Commit fa85c57

Browse files
authored
feat(WebSocketModule): Adds binary message support to WebSocketModule (microsoft#1067)
The WebSocketModule was missing the `sendBinary` ReactMethod used to support binary messages. Fixes microsoft#1060
1 parent f87b06d commit fa85c57

File tree

3 files changed

+205
-9
lines changed

3 files changed

+205
-9
lines changed

ReactWindows/ReactNative.Net46/Modules/WebSocket/WebSocketModule.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ public void send(string message, int id)
109109
SendMessageInBackground(id, message);
110110
}
111111

112+
[ReactMethod]
113+
public void sendBinary(string message, int id)
114+
{
115+
SendMessageInBackground(id, Convert.FromBase64String(message));
116+
}
117+
112118
#endregion
113119

114120
#region Event Handlers
@@ -166,12 +172,13 @@ private void OnError(int id, WebSocketSharp.ErrorEventArgs args)
166172

167173
private void OnMessageReceived(int id, object sender, MessageEventArgs args)
168174
{
169-
var message = args.Data;
175+
var message = args.IsBinary ? Convert.ToBase64String(args.RawData) : args.Data;
170176
SendEvent("websocketMessage", new JObject
171-
{
172-
{ "id", id },
173-
{ "data", message },
174-
});
177+
{
178+
{ "id", id },
179+
{ "data", message },
180+
{ "type", args.IsBinary ? "binary" : "text" },
181+
});
175182
}
176183

177184
#endregion
@@ -188,6 +195,11 @@ private void SendMessageInBackground(int id, string message)
188195
_webSocketConnections[id].SendAsync(message, null);
189196
}
190197

198+
private void SendMessageInBackground(int id, byte[] message)
199+
{
200+
_webSocketConnections[id].SendAsync(message, null);
201+
}
202+
191203
private void SendEvent(string eventName, JObject parameters)
192204
{
193205
Context.GetJavaScriptModule<RCTDeviceEventEmitter>()

ReactWindows/ReactNative.Shared.Tests/Modules/WebSocket/WebSocketModuleTests.cs

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using ReactNative.Bridge;
44
using ReactNative.Modules.Core;
55
using ReactNative.Modules.WebSocket;
6+
using System;
7+
using System.Text;
68
using System.Threading;
79

810
namespace ReactNative.Tests.Modules.WebSocket
@@ -110,16 +112,104 @@ public void WebSocketModule_DataEvent()
110112
json = (JObject)args[1];
111113
waitHandle.Set();
112114
break;
113-
default:
115+
}
116+
}));
117+
118+
var module = new WebSocketModule(context);
119+
try
120+
{
121+
module.connect("ws://echo.websocket.org", null, null, 1);
122+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
123+
module.send("FooBarBaz", 1);
124+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
125+
}
126+
finally
127+
{
128+
module.close(1000, "None", 1);
129+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
130+
131+
waitHandle.Dispose();
132+
}
133+
134+
Assert.AreEqual(1, json["id"].Value<int>());
135+
Assert.AreEqual("text", json["type"].Value<string>());
136+
Assert.AreEqual("FooBarBaz", json["data"].Value<string>());
137+
}
138+
139+
[Test]
140+
[Category("Network")]
141+
public void WebSocketModule_DataEvent_Binary()
142+
{
143+
var waitHandle = new AutoResetEvent(false);
144+
var json = default(JObject);
145+
var context = CreateReactContext(new MockInvocationHandler((name, args) =>
146+
{
147+
var eventName = (string)args[0];
148+
switch (eventName)
149+
{
150+
case "websocketOpen":
151+
case "websocketClosed":
152+
waitHandle.Set();
153+
break;
154+
case "websocketMessage":
155+
json = (JObject)args[1];
156+
waitHandle.Set();
114157
break;
115158
}
116159
}));
117160

118161
var module = new WebSocketModule(context);
162+
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello World"));
119163
try
120164
{
121165
module.connect("ws://echo.websocket.org", null, null, 1);
122166
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
167+
module.sendBinary(encodedMessage, 1);
168+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
169+
}
170+
finally
171+
{
172+
module.close(1000, "None", 1);
173+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
174+
175+
waitHandle.Dispose();
176+
}
177+
178+
Assert.AreEqual(1, json["id"].Value<int>());
179+
Assert.AreEqual("binary", json["type"].Value<string>());
180+
Assert.AreEqual(encodedMessage, json["data"].Value<string>());
181+
}
182+
183+
[Test]
184+
[Category("Network")]
185+
public void WebSocketModule_DataEvent_Binary_ThenText()
186+
{
187+
var waitHandle = new AutoResetEvent(false);
188+
var json = default(JObject);
189+
var context = CreateReactContext(new MockInvocationHandler((name, args) =>
190+
{
191+
var eventName = (string)args[0];
192+
switch (eventName)
193+
{
194+
case "websocketOpen":
195+
case "websocketClosed":
196+
waitHandle.Set();
197+
break;
198+
case "websocketMessage":
199+
json = (JObject)args[1];
200+
waitHandle.Set();
201+
break;
202+
}
203+
}));
204+
205+
var module = new WebSocketModule(context);
206+
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello World"));
207+
try
208+
{
209+
module.connect("ws://echo.websocket.org", null, null, 1);
210+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
211+
module.sendBinary(encodedMessage, 1);
212+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
123213
module.send("FooBarBaz", 1);
124214
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
125215
}
@@ -132,9 +222,56 @@ public void WebSocketModule_DataEvent()
132222
}
133223

134224
Assert.AreEqual(1, json["id"].Value<int>());
225+
Assert.AreEqual("text", json["type"].Value<string>());
135226
Assert.AreEqual("FooBarBaz", json["data"].Value<string>());
136227
}
137228

229+
[Test]
230+
[Category("Network")]
231+
public void WebSocketModule_DataEvent_Text_ThenBinary()
232+
{
233+
var waitHandle = new AutoResetEvent(false);
234+
var json = default(JObject);
235+
var context = CreateReactContext(new MockInvocationHandler((name, args) =>
236+
{
237+
var eventName = (string)args[0];
238+
switch (eventName)
239+
{
240+
case "websocketOpen":
241+
case "websocketClosed":
242+
waitHandle.Set();
243+
break;
244+
case "websocketMessage":
245+
json = (JObject)args[1];
246+
waitHandle.Set();
247+
break;
248+
}
249+
}));
250+
251+
var module = new WebSocketModule(context);
252+
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello World"));
253+
try
254+
{
255+
module.connect("ws://echo.websocket.org", null, null, 1);
256+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
257+
module.send("FooBarBaz", 1);
258+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
259+
module.sendBinary(encodedMessage, 1);
260+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
261+
}
262+
finally
263+
{
264+
module.close(1000, "None", 1);
265+
Assert.IsTrue(waitHandle.WaitOne(WaitTimeoutInMs));
266+
267+
waitHandle.Dispose();
268+
}
269+
270+
Assert.AreEqual(1, json["id"].Value<int>());
271+
Assert.AreEqual("binary", json["type"].Value<string>());
272+
Assert.AreEqual(encodedMessage, json["data"].Value<string>());
273+
}
274+
138275
private ReactContext CreateReactContext(IInvocationHandler handler)
139276
{
140277
var eventEmitter = new RCTDeviceEventEmitter();

ReactWindows/ReactNative/Modules/WebSocket/WebSocketModule.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using ReactNative.Tracing;
77
using System;
88
using System.Collections.Generic;
9+
using System.Runtime.InteropServices.WindowsRuntime;
910
using Windows.Networking.Sockets;
1011
using Windows.Storage.Streams;
1112
using static System.FormattableString;
@@ -37,8 +38,6 @@ public void connect(string url, string[] protocols, JObject options, int id)
3738
{
3839
var webSocket = new MessageWebSocket();
3940

40-
webSocket.Control.MessageType = SocketMessageType.Utf8;
41-
4241
if (protocols != null)
4342
{
4443
foreach (var protocol in protocols)
@@ -98,6 +97,15 @@ public void close(ushort code, string reason, int id)
9897
[ReactMethod]
9998
public void send(string message, int id)
10099
{
100+
var webSocket = default(MessageWebSocket);
101+
if (!_webSocketConnections.TryGetValue(id, out webSocket))
102+
{
103+
throw new InvalidOperationException(
104+
Invariant($"Cannot send a message. Unknown WebSocket id '{id}'."));
105+
}
106+
107+
webSocket.Control.MessageType = SocketMessageType.Utf8;
108+
101109
var dataWriter = default(DataWriter);
102110
if (!_dataWriters.TryGetValue(id, out dataWriter))
103111
{
@@ -108,6 +116,28 @@ public void send(string message, int id)
108116
SendMessageInBackground(id, dataWriter, message);
109117
}
110118

119+
[ReactMethod]
120+
public void sendBinary(string message, int id)
121+
{
122+
var webSocket = default(MessageWebSocket);
123+
if (!_webSocketConnections.TryGetValue(id, out webSocket))
124+
{
125+
throw new InvalidOperationException(
126+
Invariant($"Cannot send a message. Unknown WebSocket id '{id}'."));
127+
}
128+
129+
webSocket.Control.MessageType = SocketMessageType.Binary;
130+
131+
var dataWriter = default(DataWriter);
132+
if (!_dataWriters.TryGetValue(id, out dataWriter))
133+
{
134+
throw new InvalidOperationException(
135+
Invariant($"Cannot send a message. Unknown WebSocket id '{id}'."));
136+
}
137+
138+
SendMessageInBackground(id, dataWriter, Convert.FromBase64String(message));
139+
}
140+
111141
private async void InitializeInBackground(int id, string url, MessageWebSocket webSocket)
112142
{
113143
try
@@ -143,6 +173,19 @@ private async void SendMessageInBackground(int id, DataWriter dataWriter, string
143173
}
144174
}
145175

176+
private async void SendMessageInBackground(int id, DataWriter dataWriter, byte[] message)
177+
{
178+
try
179+
{
180+
dataWriter.WriteBytes(message);
181+
await dataWriter.StoreAsync().AsTask().ConfigureAwait(false);
182+
}
183+
catch (Exception ex)
184+
{
185+
OnError(id, ex);
186+
}
187+
}
188+
146189
private void OnClosed(int id, IWebSocket webSocket, WebSocketClosedEventArgs args)
147190
{
148191
SendEvent("websocketClosed", new JObject
@@ -168,11 +211,15 @@ private void OnMessageReceived(int id, MessageWebSocket sender, MessageWebSocket
168211
{
169212
using (var reader = args.GetDataReader())
170213
{
171-
var message = reader.ReadString(reader.UnconsumedBufferLength);
214+
var message = args.MessageType == SocketMessageType.Binary
215+
? Convert.ToBase64String(reader.ReadBuffer(reader.UnconsumedBufferLength).ToArray())
216+
: reader.ReadString(reader.UnconsumedBufferLength);
217+
172218
SendEvent("websocketMessage", new JObject
173219
{
174220
{ "id", id },
175221
{ "data", message },
222+
{ "type", args.MessageType == SocketMessageType.Binary ? "binary" : "text" },
176223
});
177224
}
178225
}

0 commit comments

Comments
 (0)