Skip to content

Commit

Permalink
feat(websocket): Re-enable native SSL (#965)
Browse files Browse the repository at this point in the history
* fix(websocket): Internal implementation fixes re-enabling SSL while retaining performance

* Catch normal exceptions if they occur

* Catch another WebSocket exception

* Make sure the stream still exists before we try to write to it

* fix(websocket): Only use the new sending technique if the stream is an SslStream

* fix(websocket): Use a better path generation strategy so a leading slash is not required
  • Loading branch information
Katori authored and miwarnec committed Jul 31, 2019
1 parent 2ca2c48 commit 7ed4a9a
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -61,6 +63,9 @@ internal class WebSocketImplementation : WebSocket

public event EventHandler<PongEventArgs> Pong;

Queue<ArraySegment<byte>> _messageQueue = new Queue<ArraySegment<byte>>();
SemaphoreSlim _sendSemaphore = new SemaphoreSlim(1, 1);

internal WebSocketImplementation(Guid guid, Func<MemoryStream> recycledStreamFactory, Stream stream, TimeSpan keepAliveInterval, string secWebSocketExtensions, bool includeExceptionInCloseResponse, bool isClient, string subProtocol)
{
_guid = guid;
Expand Down Expand Up @@ -134,6 +139,10 @@ public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byt
frame = await WebSocketFrameReader.ReadAsync(_stream, buffer, linkedCts.Token);
Events.Log.ReceivedFrame(_guid, frame.OpCode, frame.IsFinBitSet, frame.Count);
}
catch (SocketException ex)
{
// do nothing, the socket has been disconnected
}
catch (InternalBufferOverflowException ex)
{
await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.MessageTooBig, "Frame too large to fit in buffer. Use message fragmentation", ex);
Expand Down Expand Up @@ -520,7 +529,41 @@ ArraySegment<byte> GetBuffer(MemoryStream stream)
async Task WriteStreamToNetwork(MemoryStream stream, CancellationToken cancellationToken)
{
ArraySegment<byte> buffer = GetBuffer(stream);
await _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
if(_stream is SslStream)
{
_messageQueue.Enqueue(buffer);
await _sendSemaphore.WaitAsync();
try
{
while (_messageQueue.Count > 0)
{
var _buf = _messageQueue.Dequeue();
try
{
if (_stream != null && _stream.CanWrite)
{
await _stream.WriteAsync(_buf.Array, _buf.Offset, _buf.Count, cancellationToken).ConfigureAwait(false);
}
}
catch (IOException ex)
{
// do nothing, the socket is not connected
}
catch (SocketException ex)
{
// do nothing, the socket is not connected
}
}
}
finally
{
_sendSemaphore.Release();
}
}
else
{
await _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
}
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions Assets/Mirror/Runtime/Transport/Websocket/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ async void ProcessTcpClient(TcpClient tcpClient, CancellationToken token)
}

}
catch(IOException ex)
{
// do nothing. This will be thrown if the transport is closed
}
catch (ObjectDisposedException)
{
// do nothing. This will be thrown if the Listener has been stopped
Expand Down
27 changes: 26 additions & 1 deletion Assets/Mirror/Runtime/Transport/Websocket/WebsocketTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class WebsocketTransport : Transport

public int port = 7778;

public bool Secure;
public string CertificatePath;
public string CertificatePassword;

[Tooltip("Nagle Algorithm can be disabled by enabling NoDelay")]
public bool NoDelay = true;

Expand Down Expand Up @@ -51,7 +55,14 @@ public override bool Available()

public override void ClientConnect(string host)
{
client.Connect(new Uri($"ws://{host}:{port}"));
if (Secure)
{
client.Connect(new Uri($"wss://{host}:{port}"));
}
else
{
client.Connect(new Uri($"ws://{host}:{port}"));
}
}

public override bool ClientSend(int channelId, byte[] data) { client.Send(data); return true; }
Expand All @@ -63,6 +74,20 @@ public override void ClientConnect(string host)

public override void ServerStart()
{
server._secure = Secure;
if (Secure)
{
server._secure = Secure;
server._sslConfig = new Server.SslConfiguration
{
Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.IO.Path.Combine(Application.dataPath, CertificatePath),
CertificatePassword),
ClientCertificateRequired = false,
CheckCertificateRevocation = false,
EnabledSslProtocols = System.Security.Authentication.SslProtocols.Default
};
}
server.Listen(port);
}

Expand Down

0 comments on commit 7ed4a9a

Please sign in to comment.