Initial WebSocket support development

This commit is contained in:
Stefano Merotta 2024-09-29 23:53:00 +02:00
parent aaac0c596c
commit 7362062556
5 changed files with 483 additions and 14 deletions

View file

@ -19,6 +19,7 @@ using Server.HuePickers;
using Server.Items;
using Server.Logging;
using Server.Menus;
using Server.Network.Sockets;
using System;
using System.Buffers;
using System.Collections.Concurrent;
@ -29,6 +30,9 @@ using System.Net.Sockets;
using System.Network;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Text;
using System.Security.Cryptography;
namespace Server.Network;
@ -41,7 +45,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(NetState));
private static readonly TimeSpan ConnectingSocketIdleLimit = TimeSpan.FromMilliseconds(5000); // 5 seconds
private static readonly TimeSpan ConnectingSocketIdleLimit = TimeSpan.FromMilliseconds(50000); // 5 seconds
private const int RecvPipeSize = 1024 * 64;
private const int SendPipeSize = 1024 * 256;
private const int HuePickerCap = 512;
@ -121,7 +125,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
public NetState(Socket connection)
{
Connection = connection;
Connection = new TcpSocket(connection);
Seeded = false;
HuePickers = [];
Menus = [];
@ -147,7 +151,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
try
{
_pollGroup.Add(connection, _handle);
_pollGroup.Add(Connection.Socket, _handle);
}
catch (Exception ex)
{
@ -216,7 +220,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
public bool Running => _running;
public Socket Connection { get; private set; }
public ISocket Connection { get; private set; }
public bool CompressionEnabled { get; set; }
@ -577,6 +581,11 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
_protocolState = ProtocolState.LoginServer_AwaitingLogin;
}
}
else if (length > 3 && packetId == 0x47)
{
_parserState = ParserState.ProcessingPacket;
_parserState = HandleWebSocket(buffer) ? ParserState.AwaitingNextPacket : ParserState.Error;
}
else if (length >= 4)
{
int newSeed = (packetId << 24) | (packetReader.ReadByte() << 16) | (packetReader.ReadByte() << 8) | packetReader.ReadByte();
@ -651,7 +660,12 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
case ProtocolState.GameServer_AwaitingGameServerLogin:
{
if (packetId != 0x91 && packetId != 0x80)
if (length > 3 && packetId == 0x47)
{
_parserState = ParserState.ProcessingPacket;
_parserState = HandleWebSocket(buffer) ? ParserState.AwaitingNextPacket : ParserState.Error;
}
else if (packetId != 0x91 && packetId != 0x80)
{
HandleError(packetId, packetLength);
return;
@ -832,7 +846,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
try
{
bytesWritten = Connection.Send(buffer, SocketFlags.None);
bytesWritten = Connection.Send(buffer);
}
catch (SocketException ex)
{
@ -875,10 +889,11 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
}
var bytesWritten = 0;
var forceClose = false;
try
{
bytesWritten = Connection.Receive(buffer, SocketFlags.None);
bytesWritten = Connection.Receive(buffer, out forceClose);
}
catch (SocketException ex)
{
@ -895,15 +910,18 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
TraceException(ex);
}
if (bytesWritten <= 0)
if (bytesWritten < 0 || forceClose)
{
Disconnect(string.Empty);
return;
}
DecodePacket(buffer, ref bytesWritten);
writer.Advance((uint)bytesWritten);
if (bytesWritten > 0)
{
DecodePacket(buffer, ref bytesWritten);
writer.Advance((uint)bytesWritten);
}
NextActivityCheck = Core.TickCount + 90000;
}
@ -1090,7 +1108,6 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
}
_running = false;
_disconnectReason = reason;
_disposed.Enqueue(this);
}
@ -1145,7 +1162,7 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
try
{
_pollGroup.Remove(Connection, _handle);
_pollGroup.Remove(Connection.Socket, _handle);
}
catch (Exception ex)
{
@ -1173,6 +1190,42 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
LogInfo(a != null ? $"Disconnected. [{count} Online] [{a}]" : $"Disconnected. [{count} Online]");
}
private bool HandleWebSocket(Span<byte> buffer)
{
try
{
string s = Encoding.UTF8.GetString(buffer);
if (!s.StartsWith("GET", StringComparison.InvariantCultureIgnoreCase))
{
Disconnect("BOH");
return false;
}
string swk = Regex.Match(s, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
string swka = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] swkaSha1 = SHA1.HashData(Encoding.UTF8.GetBytes(swka));
string swkaSha1Base64 = Convert.ToBase64String(swkaSha1);
byte[] response = Encoding.UTF8.GetBytes(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
"Sec-WebSocket-Accept: " + swkaSha1Base64 + "\r\n\r\n");
var old = Connection;
Connection = new WebSocket(old.Socket);
old.Send(response);
}
catch
{
Disconnect("BOH2");
return false;
}
return true;
}
private class NetStateConnectingComparer : IComparer<NetState>
{
public static readonly IComparer<NetState> Instance = new NetStateConnectingComparer();