This commit is contained in:
EonaCat 2024-04-24 20:07:45 +02:00
parent 8d7e806d14
commit 681f8c725f
167 changed files with 15977 additions and 16329 deletions

View File

@ -1,15 +1,15 @@
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
using EonaCat.Network;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using EonaCat.WebSockets;
internal class Program internal class Program
{ {
private static WSClient _client; private static AsyncWebSocketClient _client;
private static WSServer _server; private static AsyncWebSocketServer _server;
private static async Task Main(string[] args) private static async Task Main(string[] args)
{ {
@ -30,7 +30,7 @@ internal class Program
break; break;
case "1": case "1":
await CreateServerAndClientAsync(); await CreateServerAndClientAsync().ConfigureAwait(false);
break; break;
case "2": case "2":
@ -38,12 +38,13 @@ internal class Program
{ {
Console.Write("Enter message: "); Console.Write("Enter message: ");
var message = Console.ReadLine(); var message = Console.ReadLine();
_client.Send(message); await _client.SendTextAsync(message).ConfigureAwait(false);
} }
break; break;
case "3": case "3":
_client?.Close(); _client?.CloseAsync().ConfigureAwait(false);
return; return;
default: default:
@ -66,64 +67,101 @@ internal class Program
StartServer(serverUri, certificatePath, certificatePassword, requiredPassword); StartServer(serverUri, certificatePath, certificatePassword, requiredPassword);
// Start the client in the main thread // Start the client in the main thread
_client = new WSClient(clientUri, new X509Certificate(certificatePath, certificatePassword)); var configuration = new AsyncWebSocketClientConfiguration();
_client.OnConnect += (sender, e) => Console.WriteLine($"Connected to server"); _client = new AsyncWebSocketClient(new Uri(clientUri), OnServerTextReceived, OnServerBinaryReceived, OnServerConnected, OnServerDisconnected, OnServerFragmentationStreamOpened, OnServerFragmentationStreamOpened, OnServerFragmentationStreamClosed, configuration);
_client.OnMessageReceived += (sender, e) => Console.WriteLine($"Received message from server: {e.Data}"); await _client.ConnectAsync().ConfigureAwait(false);
_client.OnDisconnect += (sender, e) => Console.WriteLine($"Disconnected from server: {e.Code} : {e.Reason}"); //(sender, e) => Console.WriteLine($"Error: {sender}\n{e}");
_client.OnError += (sender, e) => Console.WriteLine($"Error: {sender}\n{e}");
_client.ConnectAsync();
Console.WriteLine("Connected to the server."); Console.WriteLine("Connected to the server.");
} }
private static Task OnServerFragmentationStreamClosed(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4)
{
// Do nothing
return Task.CompletedTask;
}
private static Task OnServerFragmentationStreamOpened(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4)
{
// Do nothing
return Task.CompletedTask;
}
private static Task OnServerDisconnected(AsyncWebSocketClient arg)
{
Console.WriteLine("Disconnected from server.");
return Task.CompletedTask;
}
private static Task OnServerConnected(AsyncWebSocketClient arg)
{
Console.WriteLine("Connected to server.");
return Task.CompletedTask;
}
private static Task OnServerBinaryReceived(AsyncWebSocketClient arg1, byte[] bytes, int arg3, int arg4)
{
Console.WriteLine($"Received binary {bytes} {arg3} {arg4}");
return Task.CompletedTask;
}
private static Task OnServerTextReceived(AsyncWebSocketClient arg1, string data)
{
Console.WriteLine($"Received message from server: {data}");
return Task.CompletedTask;
}
private static void CreateCertificate() private static void CreateCertificate()
{ {
Console.Write("Enter hostname: (default: localhost) "); Console.Write("Enter hostname: (default: localhost) ");
string hostname = Console.ReadLine(); var hostname = Console.ReadLine();
if (string.IsNullOrWhiteSpace(hostname)) if (string.IsNullOrWhiteSpace(hostname))
{ {
hostname = "localhost"; hostname = "localhost";
} }
int days = 30; var days = 30;
Console.Write("Enter days until expiration: (default: 30 days) "); Console.Write("Enter days until expiration: (default: 30 days) ");
if (int.TryParse(Console.ReadLine(), out int givenDays)) if (int.TryParse(Console.ReadLine(), out var givenDays))
{ {
days = givenDays; days = givenDays;
} }
Console.Write("Enter password, enter to skip: "); Console.Write("Enter password, enter to skip: ");
string password = Console.ReadLine(); var password = Console.ReadLine();
RSA rsa = RSA.Create(); var rsa = RSA.Create();
// Create a certificate request with the specified subject and key pair // Create a certificate request with the specified subject and key pair
CertificateRequest request = new CertificateRequest( var request = new CertificateRequest(
$"CN={hostname}", $"CN={hostname}",
rsa, rsa,
HashAlgorithmName.SHA256, HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1); RSASignaturePadding.Pkcs1);
// Create a self-signed certificate from the certificate request // Create a self-signed certificate from the certificate request
X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(days)); var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(days));
// Export the certificate to a file with password // Export the certificate to a file with password
byte[] certBytes = string.IsNullOrEmpty(password) var certBytes = string.IsNullOrEmpty(password)
? certificate.Export(X509ContentType.Pfx) ? certificate.Export(X509ContentType.Pfx)
: certificate.Export(X509ContentType.Pfx, password); : certificate.Export(X509ContentType.Pfx, password);
File.WriteAllBytes($"{hostname}.pfx", certBytes); File.WriteAllBytes($"{hostname}.pfx", certBytes);
Console.WriteLine($"Certificate for {hostname} created successfully and will expire on {certificate.NotAfter}."); Console.WriteLine(
$"Certificate for {hostname} created successfully and will expire on {certificate.NotAfter}.");
Console.WriteLine($"Path: {Path.Combine(AppContext.BaseDirectory, hostname)}.pfx"); Console.WriteLine($"Path: {Path.Combine(AppContext.BaseDirectory, hostname)}.pfx");
} }
private static void StartServer(string serverUri, string certificatePath, string certificatePassword, string requiredPassword) private static void StartServer(string serverUri, string certificatePath, string certificatePassword,
string requiredPassword)
{ {
_server = new WSServer(serverUri); var test = new AsyncWebSocketServerModuleCatalog();
_server.SSL.Certificate = new X509Certificate2(certificatePath, certificatePassword); //var module = new AsyncWebSocketServerModule
_server.AddEndpoint<WelcomeEndpoint>("/Welcome"); //test.RegisterModule();
_server.Start(); //_server = new AsyncWebSocketServer(serverUri);
//_server.SSL.Certificate = new X509Certificate2(certificatePath, certificatePassword);
//_server.AddEndpoint<WelcomeEndpoint>("/Welcome");
//_server.Start();
} }
} }

View File

@ -1,7 +1,6 @@
namespace EonaCat.Network namespace EonaCat.Network;
{
internal class Constants internal class Constants
{ {
public static string Version { get; set; } = "1.1.4"; public static string Version { get; set; } = "1.1.4";
} }
}

View File

@ -13,9 +13,9 @@
<PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags> <PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat Networking library with Quic, TCP, UDP, WebSockets and a Webserver</Description> <Description>EonaCat Networking library with Quic, TCP, UDP, WebSockets and a Webserver</Description>
<Version>1.1.5</Version> <Version>1.1.6</Version>
<AssemblyVersion>1.1.5</AssemblyVersion> <AssemblyVersion>1.1.6</AssemblyVersion>
<FileVersion>1.1.5</FileVersion> <FileVersion>1.1.6</FileVersion>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>

View File

@ -1,23 +1,29 @@
using EonaCat.LogSystem; using System;
using System.Net.Sockets;
using System.Text;
using EonaCat.LogSystem;
using EonaCat.Quic; using EonaCat.Quic;
using EonaCat.Quic.Connections; using EonaCat.Quic.Connections;
using EonaCat.Quic.Events; using EonaCat.Quic.Events;
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
using EonaCat.Quic.Streams; using EonaCat.Quic.Streams;
using System;
using System.Net.Sockets;
using System.Text;
namespace EonaCat.Network namespace EonaCat.Network;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public static class NetworkHelper public static class NetworkHelper
{ {
internal static Logging Logger = new Logging(); internal static Logging Logger = new();
private static QuicServer _quicServer; private static QuicServer _quicServer;
//TODO: add udp and tcp example methods
/// <summary>
/// Character bit encoding type web
/// </summary>
public static Encoding GlobalEncoding = Encoding.UTF8;
/// <summary> /// <summary>
/// OnQuicClientConnected event /// OnQuicClientConnected event
/// </summary> /// </summary>
@ -76,12 +82,13 @@ namespace EonaCat.Network
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="streamType"></param> /// <param name="streamType"></param>
/// <returns></returns> /// <returns></returns>
public static QuicStream QuicStartClient(string ip, int port = 11000, StreamType streamType = StreamType.ClientBidirectional) public static QuicStream QuicStartClient(string ip, int port = 11000,
StreamType streamType = StreamType.ClientBidirectional)
{ {
QuicClient client = new QuicClient(); var client = new QuicClient();
// Connect to peer (Server) // Connect to peer (Server)
QuicConnection connection = client.Connect(ip, port); var connection = client.Connect(ip, port);
// Create a data stream // Create a data stream
return connection.CreateStream(streamType); return connection.CreateStream(streamType);
@ -96,31 +103,26 @@ namespace EonaCat.Network
if (_quicServer != null) if (_quicServer != null)
{ {
_quicServer.Close(); _quicServer.Close();
Logger.Info($"The Quic server has been successfully stopped"); Logger.Info("The Quic server has been successfully stopped");
} }
return true; return true;
} }
//TODO: add udp and tcp example methods
/// <summary>
/// Character bit encoding type web
/// </summary>
public static Encoding GlobalEncoding = Encoding.UTF8;
private static void Main(string[] args) private static void Main(string[] args)
{ {
var client = new UdpClient(); var client = new UdpClient();
for (int i = 0; i < 5000; i++) for (var i = 0; i < 5000; i++)
{ {
// TCP TEST // TCP TEST
} }
for (int i = 0; i < 5000; i++) for (var i = 0; i < 5000; i++)
{ {
// UDP TEST // UDP TEST
} }
Console.ReadLine(); Console.ReadLine();
} }
} }
@ -133,4 +135,3 @@ namespace EonaCat.Network
IPv4, IPv4,
IPv6 IPv6
} }
}

View File

@ -1,10 +1,9 @@
using EonaCat.Quic.Infrastructure; using System.Collections.Generic;
using EonaCat.Quic.Infrastructure;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using EonaCat.Quic.InternalInfrastructure; using EonaCat.Quic.InternalInfrastructure;
using System.Collections.Generic;
namespace EonaCat.Quic.Connections namespace EonaCat.Quic.Connections;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,11 +17,11 @@ namespace EonaCat.Quic.Connections
/// Starting point for connection identifiers. /// Starting point for connection identifiers.
/// ConnectionId's are incremented sequentially by 1. /// ConnectionId's are incremented sequentially by 1.
/// </summary> /// </summary>
private static readonly NumberSpace _ns = new NumberSpace(QuicSettings.MaximumConnectionIds); private static readonly NumberSpace _ns = new(QuicSettings.MaximumConnectionIds);
private static readonly Dictionary<ulong, QuicConnection> _pool = new Dictionary<ulong, QuicConnection>(); private static readonly Dictionary<ulong, QuicConnection> _pool = new();
private static readonly List<QuicConnection> _draining = new List<QuicConnection>(); private static readonly List<QuicConnection> _draining = new();
/// <summary> /// <summary>
/// Adds a connection to the connection pool. /// Adds a connection to the connection pool.
@ -71,4 +70,3 @@ namespace EonaCat.Quic.Connections
return _pool[id]; return _pool[id];
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Connections namespace EonaCat.Quic.Connections;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,4 +9,3 @@
Closed, Closed,
Draining Draining
} }
}

View File

@ -1,4 +1,5 @@
using EonaCat.Quic.Constants; using System.Collections.Generic;
using EonaCat.Quic.Constants;
using EonaCat.Quic.Events; using EonaCat.Quic.Events;
using EonaCat.Quic.Exceptions; using EonaCat.Quic.Exceptions;
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
@ -9,27 +10,41 @@ using EonaCat.Quic.Infrastructure.Packets;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using EonaCat.Quic.InternalInfrastructure; using EonaCat.Quic.InternalInfrastructure;
using EonaCat.Quic.Streams; using EonaCat.Quic.Streams;
using System.Collections.Generic;
namespace EonaCat.Quic.Connections namespace EonaCat.Quic.Connections;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class QuicConnection public class QuicConnection
{ {
private readonly NumberSpace _numberSpace = new NumberSpace(); private readonly NumberSpace _numberSpace = new();
private readonly PacketWireTransfer _pwt; private readonly PacketWireTransfer _pwt;
private ulong _currentTransferRate;
private ConnectionState _state;
private string _lastError;
private readonly Dictionary<ulong, QuicStream> _streams; private readonly Dictionary<ulong, QuicStream> _streams;
public IntegerParts ConnectionId { get; private set; } private ulong _currentTransferRate;
public IntegerParts PeerConnectionId { get; private set; } private string _lastError;
private ConnectionState _state;
public PacketCreator PacketCreator { get; private set; } internal QuicConnection(ConnectionData connection)
{
_currentTransferRate = 0;
_state = ConnectionState.Open;
_lastError = string.Empty;
_streams = new Dictionary<ulong, QuicStream>();
_pwt = connection.PWT;
ConnectionId = connection.ConnectionId;
PeerConnectionId = connection.PeerConnectionId;
// Also creates a new number space
PacketCreator = new PacketCreator(ConnectionId, PeerConnectionId);
MaxData = QuicSettings.MaxData;
MaxStreams = QuicSettings.MaximumStreamId;
}
public IntegerParts ConnectionId { get; }
public IntegerParts PeerConnectionId { get; }
public PacketCreator PacketCreator { get; }
public ulong MaxData { get; private set; } public ulong MaxData { get; private set; }
public ulong MaxStreams { get; private set; } public ulong MaxStreams { get; private set; }
@ -43,13 +58,13 @@ namespace EonaCat.Quic.Connections
/// <returns>A new stream instance or Null if the connection is terminated.</returns> /// <returns>A new stream instance or Null if the connection is terminated.</returns>
public QuicStream CreateStream(StreamType type) public QuicStream CreateStream(StreamType type)
{ {
uint streamId = _numberSpace.Get(); var streamId = _numberSpace.Get();
if (_state != ConnectionState.Open) if (_state != ConnectionState.Open)
{ {
return null; return null;
} }
QuicStream stream = new QuicStream(this, new StreamId(streamId, type)); var stream = new QuicStream(this, new StreamId(streamId, type));
_streams.Add(streamId, stream); _streams.Add(streamId, stream);
return stream; return stream;
@ -59,7 +74,7 @@ namespace EonaCat.Quic.Connections
{ {
QuicStream stream = null; QuicStream stream = null;
foreach (Frame frame in frames) foreach (var frame in frames)
{ {
if (frame.Type == 0x01) if (frame.Type == 0x01)
{ {
@ -122,7 +137,7 @@ namespace EonaCat.Quic.Connections
private void OnConnectionCloseFrame(Frame frame) private void OnConnectionCloseFrame(Frame frame)
{ {
ConnectionCloseFrame ccf = (ConnectionCloseFrame)frame; var ccf = (ConnectionCloseFrame)frame;
_state = ConnectionState.Draining; _state = ConnectionState.Draining;
_lastError = ccf.ReasonPhrase; _lastError = ccf.ReasonPhrase;
@ -131,11 +146,11 @@ namespace EonaCat.Quic.Connections
private void OnRstStreamFrame(Frame frame) private void OnRstStreamFrame(Frame frame)
{ {
ResetStreamFrame rsf = (ResetStreamFrame)frame; var rsf = (ResetStreamFrame)frame;
if (_streams.ContainsKey(rsf.StreamId)) if (_streams.ContainsKey(rsf.StreamId))
{ {
// Find and reset the stream // Find and reset the stream
QuicStream stream = _streams[rsf.StreamId]; var stream = _streams[rsf.StreamId];
stream.ResetStream(rsf); stream.ResetStream(rsf);
// Remove the stream from the connection // Remove the stream from the connection
@ -147,7 +162,7 @@ namespace EonaCat.Quic.Connections
{ {
QuicStream stream; QuicStream stream;
StreamFrame sf = (StreamFrame)frame; var sf = (StreamFrame)frame;
StreamId streamId = sf.StreamId; StreamId streamId = sf.StreamId;
if (_streams.ContainsKey(streamId.Id) == false) if (_streams.ContainsKey(streamId.Id) == false)
@ -177,7 +192,7 @@ namespace EonaCat.Quic.Connections
private void OnMaxDataFrame(Frame frame) private void OnMaxDataFrame(Frame frame)
{ {
MaxDataFrame sf = (MaxDataFrame)frame; var sf = (MaxDataFrame)frame;
if (sf.MaximumData.Value > MaxData) if (sf.MaximumData.Value > MaxData)
{ {
MaxData = sf.MaximumData.Value; MaxData = sf.MaximumData.Value;
@ -186,19 +201,19 @@ namespace EonaCat.Quic.Connections
private void OnMaxStreamDataFrame(Frame frame) private void OnMaxStreamDataFrame(Frame frame)
{ {
MaxStreamDataFrame msdf = (MaxStreamDataFrame)frame; var msdf = (MaxStreamDataFrame)frame;
StreamId streamId = msdf.StreamId; StreamId streamId = msdf.StreamId;
if (_streams.ContainsKey(streamId.Id)) if (_streams.ContainsKey(streamId.Id))
{ {
// Find and set the new maximum stream data on the stream // Find and set the new maximum stream data on the stream
QuicStream stream = _streams[streamId.Id]; var stream = _streams[streamId.Id];
stream.SetMaximumStreamData(msdf.MaximumStreamData.Value); stream.SetMaximumStreamData(msdf.MaximumStreamData.Value);
} }
} }
private void OnMaxStreamFrame(Frame frame) private void OnMaxStreamFrame(Frame frame)
{ {
MaxStreamsFrame msf = (MaxStreamsFrame)frame; var msf = (MaxStreamsFrame)frame;
if (msf.MaximumStreams > MaxStreams) if (msf.MaximumStreams > MaxStreams)
{ {
MaxStreams = msf.MaximumStreams.Value; MaxStreams = msf.MaximumStreams.Value;
@ -210,29 +225,13 @@ namespace EonaCat.Quic.Connections
TerminateConnection(); TerminateConnection();
} }
internal QuicConnection(ConnectionData connection)
{
_currentTransferRate = 0;
_state = ConnectionState.Open;
_lastError = string.Empty;
_streams = new Dictionary<ulong, QuicStream>();
_pwt = connection.PWT;
ConnectionId = connection.ConnectionId;
PeerConnectionId = connection.PeerConnectionId;
// Also creates a new number space
PacketCreator = new PacketCreator(ConnectionId, PeerConnectionId);
MaxData = QuicSettings.MaxData;
MaxStreams = QuicSettings.MaximumStreamId;
}
public QuicStream OpenStream() public QuicStream OpenStream()
{ {
QuicStream stream = null; QuicStream stream = null;
while (stream == null) while (stream == null)
{ {
Packet packet = _pwt.ReadPacket(); var packet = _pwt.ReadPacket();
if (packet is ShortHeaderPacket shp) if (packet is ShortHeaderPacket shp)
{ {
stream = ProcessFrames(shp.GetFrames()); stream = ProcessFrames(shp.GetFrames());
@ -248,7 +247,7 @@ namespace EonaCat.Quic.Connections
/// <returns></returns> /// <returns></returns>
internal void ReceivePacket() internal void ReceivePacket()
{ {
Packet packet = _pwt.ReadPacket(); var packet = _pwt.ReadPacket();
if (packet is ShortHeaderPacket shp) if (packet is ShortHeaderPacket shp)
{ {
@ -279,12 +278,14 @@ namespace EonaCat.Quic.Connections
_state = ConnectionState.Draining; _state = ConnectionState.Draining;
_streams.Clear(); _streams.Clear();
ConnectionPool.RemoveConnection(this.ConnectionId); ConnectionPool.RemoveConnection(ConnectionId);
} }
internal void SendMaximumStreamReachedError() internal void SendMaximumStreamReachedError()
{ {
ShortHeaderPacket packet = PacketCreator.CreateConnectionClosePacket(Infrastructure.ErrorCode.STREAM_LIMIT_ERROR, 0x00, ErrorConstants.MaxNumberOfStreams); var packet =
PacketCreator.CreateConnectionClosePacket(ErrorCode.STREAM_LIMIT_ERROR, 0x00,
ErrorConstants.MaxNumberOfStreams);
Send(packet); Send(packet);
} }
@ -296,7 +297,7 @@ namespace EonaCat.Quic.Connections
internal bool Send(Packet packet) internal bool Send(Packet packet)
{ {
// Encode the packet // Encode the packet
byte[] data = packet.Encode(); var data = packet.Encode();
// Increment the connection transfer rate // Increment the connection transfer rate
IncrementRate(data.Length); IncrementRate(data.Length);
@ -304,7 +305,8 @@ namespace EonaCat.Quic.Connections
// If the maximum transfer rate is reached, send FLOW_CONTROL_ERROR // If the maximum transfer rate is reached, send FLOW_CONTROL_ERROR
if (MaximumReached()) if (MaximumReached())
{ {
packet = PacketCreator.CreateConnectionClosePacket(Infrastructure.ErrorCode.FLOW_CONTROL_ERROR, 0x00, ErrorConstants.MaxDataTransfer); packet = PacketCreator.CreateConnectionClosePacket(ErrorCode.FLOW_CONTROL_ERROR, 0x00,
ErrorConstants.MaxDataTransfer);
TerminateConnection(); TerminateConnection();
} }
@ -315,9 +317,8 @@ namespace EonaCat.Quic.Connections
return true; return true;
} }
bool result = _pwt.SendPacket(packet); var result = _pwt.SendPacket(packet);
return result; return result;
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Constants namespace EonaCat.Quic.Constants;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,4 +9,3 @@
public const string MaxNumberOfStreams = "Maximum number of streams reached."; public const string MaxNumberOfStreams = "Maximum number of streams reached.";
public const string PMTUNotReached = "PMTU have not been reached."; public const string PMTUNotReached = "PMTU have not been reached.";
} }
}

View File

@ -1,7 +1,6 @@
using EonaCat.Quic.Streams; using EonaCat.Quic.Streams;
namespace EonaCat.Quic.Context namespace EonaCat.Quic.Context;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,6 +9,15 @@ namespace EonaCat.Quic.Context
/// </summary> /// </summary>
public class QuicStreamContext public class QuicStreamContext
{ {
/// <summary>
/// Internal constructor to prevent creating the context outside the scope of Quic.
/// </summary>
/// <param name="stream"></param>
internal QuicStreamContext(QuicStream stream)
{
Stream = stream;
StreamId = stream.StreamId;
}
///// <summary> ///// <summary>
///// The connection's context. ///// The connection's context.
///// </summary> ///// </summary>
@ -25,6 +33,8 @@ namespace EonaCat.Quic.Context
/// </summary> /// </summary>
public ulong StreamId { get; private set; } public ulong StreamId { get; private set; }
internal QuicStream Stream { get; set; }
/// <summary> /// <summary>
/// Send data to the client. /// Send data to the client.
/// </summary> /// </summary>
@ -57,21 +67,8 @@ namespace EonaCat.Quic.Context
// TODO: Close out the stream by sending appropriate packets to the peer // TODO: Close out the stream by sending appropriate packets to the peer
} }
internal QuicStream Stream { get; set; }
/// <summary>
/// Internal constructor to prevent creating the context outside the scope of Quic.
/// </summary>
/// <param name="stream"></param>
internal QuicStreamContext(QuicStream stream)
{
Stream = stream;
StreamId = stream.StreamId;
}
internal void SetData(byte[] data) internal void SetData(byte[] data)
{ {
Data = data; Data = data;
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Connections; using EonaCat.Quic.Connections;
using EonaCat.Quic.Streams; using EonaCat.Quic.Streams;
namespace EonaCat.Quic.Events namespace EonaCat.Quic.Events;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -13,4 +12,3 @@ namespace EonaCat.Quic.Events
public delegate void StreamDataReceivedEvent(QuicStream stream, byte[] data); public delegate void StreamDataReceivedEvent(QuicStream stream, byte[] data);
public delegate void ConnectionClosedEvent(QuicConnection connection); public delegate void ConnectionClosedEvent(QuicConnection connection);
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Connections; using EonaCat.Quic.Connections;
using EonaCat.Quic.Streams; using EonaCat.Quic.Streams;
namespace EonaCat.Quic.Events namespace EonaCat.Quic.Events;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -16,4 +15,3 @@ namespace EonaCat.Quic.Events
{ {
public QuicConnection Connection { get; set; } public QuicConnection Connection { get; set; }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Exceptions namespace EonaCat.Quic.Exceptions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -11,4 +10,3 @@ namespace EonaCat.Quic.Exceptions
{ {
} }
} }
}

View File

@ -1,17 +1,16 @@
using System; using System;
namespace EonaCat.Quic.Exceptions namespace EonaCat.Quic.Exceptions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class ServerNotStartedException : Exception public class ServerNotStartedException : Exception
{ {
public ServerNotStartedException() public ServerNotStartedException()
{ } {
}
public ServerNotStartedException(string message) : base($"EonaCat Network: {message}") public ServerNotStartedException(string message) : base($"EonaCat Network: {message}")
{ {
} }
} }
}

View File

@ -1,17 +1,16 @@
using System; using System;
namespace EonaCat.Quic.Exceptions namespace EonaCat.Quic.Exceptions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class StreamException : Exception public class StreamException : Exception
{ {
public StreamException() public StreamException()
{ } {
}
public StreamException(string message) : base($"EonaCat Network: {message}") public StreamException(string message) : base($"EonaCat Network: {message}")
{ {
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Helpers namespace EonaCat.Quic.Helpers;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -21,19 +20,19 @@ namespace EonaCat.Quic.Helpers
public byte ReadByte() public byte ReadByte()
{ {
byte result = _array[_offset++]; var result = _array[_offset++];
return result; return result;
} }
public byte PeekByte() public byte PeekByte()
{ {
byte result = _array[_offset]; var result = _array[_offset];
return result; return result;
} }
public byte[] ReadBytes(int count) public byte[] ReadBytes(int count)
{ {
byte[] bytes = new byte[count]; var bytes = new byte[count];
Buffer.BlockCopy(_array, _offset, bytes, 0, count); Buffer.BlockCopy(_array, _offset, bytes, 0, count);
_offset += count; _offset += count;
@ -48,16 +47,16 @@ namespace EonaCat.Quic.Helpers
public ushort ReadUInt16() public ushort ReadUInt16()
{ {
byte[] bytes = ReadBytes(2); var bytes = ReadBytes(2);
ushort result = ByteHelpers.ToUInt16(bytes); var result = ByteHelpers.ToUInt16(bytes);
return result; return result;
} }
public uint ReadUInt32() public uint ReadUInt32()
{ {
byte[] bytes = ReadBytes(4); var bytes = ReadBytes(4);
uint result = ByteHelpers.ToUInt32(bytes); var result = ByteHelpers.ToUInt32(bytes);
return result; return result;
} }
@ -65,10 +64,10 @@ namespace EonaCat.Quic.Helpers
public IntegerVar ReadIntegerVar() public IntegerVar ReadIntegerVar()
{ {
// Set Token Length and Token // Set Token Length and Token
byte initial = PeekByte(); var initial = PeekByte();
int size = IntegerVar.Size(initial); var size = IntegerVar.Size(initial);
byte[] bytes = new byte[size]; var bytes = new byte[size];
Buffer.BlockCopy(_array, _offset, bytes, 0, size); Buffer.BlockCopy(_array, _offset, bytes, 0, size);
_offset += size; _offset += size;
@ -77,7 +76,7 @@ namespace EonaCat.Quic.Helpers
public IntegerParts ReadGranularInteger(int size) public IntegerParts ReadGranularInteger(int size)
{ {
byte[] data = ReadBytes(size); var data = ReadBytes(size);
IntegerParts result = data; IntegerParts result = data;
return result; return result;
@ -85,7 +84,7 @@ namespace EonaCat.Quic.Helpers
public StreamId ReadStreamId() public StreamId ReadStreamId()
{ {
byte[] streamId = ReadBytes(8); var streamId = ReadBytes(8);
StreamId result = streamId; StreamId result = streamId;
return result; return result;
@ -96,4 +95,3 @@ namespace EonaCat.Quic.Helpers
return _offset < _length; return _offset < _length;
} }
} }
}

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Text; using System.Text;
namespace EonaCat.Quic.Helpers namespace EonaCat.Quic.Helpers;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,7 +9,7 @@ namespace EonaCat.Quic.Helpers
{ {
public static byte[] GetBytes(ulong integer) public static byte[] GetBytes(ulong integer)
{ {
byte[] result = BitConverter.GetBytes(integer); var result = BitConverter.GetBytes(integer);
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
Array.Reverse(result); Array.Reverse(result);
@ -21,7 +20,7 @@ namespace EonaCat.Quic.Helpers
public static byte[] GetBytes(uint integer) public static byte[] GetBytes(uint integer)
{ {
byte[] result = BitConverter.GetBytes(integer); var result = BitConverter.GetBytes(integer);
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
Array.Reverse(result); Array.Reverse(result);
@ -32,7 +31,7 @@ namespace EonaCat.Quic.Helpers
public static byte[] GetBytes(ushort integer) public static byte[] GetBytes(ushort integer)
{ {
byte[] result = BitConverter.GetBytes(integer); var result = BitConverter.GetBytes(integer);
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
Array.Reverse(result); Array.Reverse(result);
@ -43,7 +42,7 @@ namespace EonaCat.Quic.Helpers
public static byte[] GetBytes(string str) public static byte[] GetBytes(string str)
{ {
byte[] result = Encoding.UTF8.GetBytes(str); var result = Encoding.UTF8.GetBytes(str);
return result; return result;
} }
@ -55,7 +54,7 @@ namespace EonaCat.Quic.Helpers
Array.Reverse(data); Array.Reverse(data);
} }
ulong result = BitConverter.ToUInt64(data, 0); var result = BitConverter.ToUInt64(data, 0);
return result; return result;
} }
@ -67,7 +66,7 @@ namespace EonaCat.Quic.Helpers
Array.Reverse(data); Array.Reverse(data);
} }
uint result = BitConverter.ToUInt32(data, 0); var result = BitConverter.ToUInt32(data, 0);
return result; return result;
} }
@ -79,16 +78,15 @@ namespace EonaCat.Quic.Helpers
Array.Reverse(data); Array.Reverse(data);
} }
ushort result = BitConverter.ToUInt16(data, 0); var result = BitConverter.ToUInt16(data, 0);
return result; return result;
} }
public static string GetString(byte[] str) public static string GetString(byte[] str)
{ {
string result = Encoding.UTF8.GetString(str); var result = Encoding.UTF8.GetString(str);
return result; return result;
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Helpers namespace EonaCat.Quic.Helpers;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -9,18 +8,18 @@ namespace EonaCat.Quic.Helpers
{ {
public const ulong MaxValue = 18446744073709551615; public const ulong MaxValue = 18446744073709551615;
public ulong Value { get; }
public byte Size => RequiredBytes(Value);
public IntegerParts(ulong integer) public IntegerParts(ulong integer)
{ {
Value = integer; Value = integer;
} }
public ulong Value { get; }
public byte Size => RequiredBytes(Value);
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
return Encode(this.Value); return Encode(Value);
} }
public static implicit operator byte[](IntegerParts integer) public static implicit operator byte[](IntegerParts integer)
@ -45,12 +44,12 @@ namespace EonaCat.Quic.Helpers
public static byte[] Encode(ulong integer) public static byte[] Encode(ulong integer)
{ {
byte requiredBytes = RequiredBytes(integer); var requiredBytes = RequiredBytes(integer);
int offset = 8 - requiredBytes; var offset = 8 - requiredBytes;
byte[] uInt64Bytes = ByteHelpers.GetBytes(integer); var uInt64Bytes = ByteHelpers.GetBytes(integer);
byte[] result = new byte[requiredBytes]; var result = new byte[requiredBytes];
Buffer.BlockCopy(uInt64Bytes, offset, result, 0, requiredBytes); Buffer.BlockCopy(uInt64Bytes, offset, result, 0, requiredBytes);
return result; return result;
@ -58,12 +57,12 @@ namespace EonaCat.Quic.Helpers
public static ulong Decode(byte[] bytes) public static ulong Decode(byte[] bytes)
{ {
int i = 8 - bytes.Length; var i = 8 - bytes.Length;
byte[] buffer = new byte[8]; var buffer = new byte[8];
Buffer.BlockCopy(bytes, 0, buffer, i, bytes.Length); Buffer.BlockCopy(bytes, 0, buffer, i, bytes.Length);
ulong res = ByteHelpers.ToUInt64(buffer); var res = ByteHelpers.ToUInt64(buffer);
return res; return res;
} }
@ -96,4 +95,3 @@ namespace EonaCat.Quic.Helpers
return result; return result;
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Helpers namespace EonaCat.Quic.Helpers;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -13,17 +12,17 @@
public class StreamId public class StreamId
{ {
public ulong Id { get; }
public ulong IntegerValue { get; }
public StreamType Type { get; private set; }
public StreamId(ulong id, StreamType type) public StreamId(ulong id, StreamType type)
{ {
Id = id; Id = id;
Type = type; Type = type;
IntegerValue = id << 2 | (ulong)type; IntegerValue = (id << 2) | (ulong)type;
} }
public ulong Id { get; }
public ulong IntegerValue { get; }
public StreamType Type { get; }
public static implicit operator byte[](StreamId id) public static implicit operator byte[](StreamId id)
{ {
return Encode(id.Id, id.Type); return Encode(id.Id, id.Type);
@ -46,9 +45,9 @@
public static byte[] Encode(ulong id, StreamType type) public static byte[] Encode(ulong id, StreamType type)
{ {
ulong identifier = id << 2 | (ulong)type; var identifier = (id << 2) | (ulong)type;
byte[] result = ByteHelpers.GetBytes(identifier); var result = ByteHelpers.GetBytes(identifier);
return result; return result;
} }
@ -56,14 +55,13 @@
public static StreamId Decode(byte[] data) public static StreamId Decode(byte[] data)
{ {
StreamId result; StreamId result;
ulong id = ByteHelpers.ToUInt64(data); var id = ByteHelpers.ToUInt64(data);
ulong identifier = id >> 2; var identifier = id >> 2;
ulong type = 0x03 & id; var type = 0x03 & id;
StreamType streamType = (StreamType)type; var streamType = (StreamType)type;
result = new StreamId(identifier, streamType); result = new StreamId(identifier, streamType);
return result; return result;
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Helpers namespace EonaCat.Quic.Helpers;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -9,13 +8,13 @@ namespace EonaCat.Quic.Helpers
{ {
public const ulong MaxValue = 4611686018427387903; public const ulong MaxValue = 4611686018427387903;
public ulong Value { get; }
public IntegerVar(ulong integer) public IntegerVar(ulong integer)
{ {
Value = integer; Value = integer;
} }
public ulong Value { get; }
public static implicit operator byte[](IntegerVar integer) public static implicit operator byte[](IntegerVar integer)
{ {
return Encode(integer.Value); return Encode(integer.Value);
@ -43,19 +42,19 @@ namespace EonaCat.Quic.Helpers
public static int Size(byte firstByte) public static int Size(byte firstByte)
{ {
int result = (int)Math.Pow(2, (firstByte >> 6)); var result = (int)Math.Pow(2, firstByte >> 6);
return result; return result;
} }
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
return Encode(this.Value); return Encode(Value);
} }
public static byte[] Encode(ulong integer) public static byte[] Encode(ulong integer)
{ {
int requiredBytes = 0; var requiredBytes = 0;
if (integer <= byte.MaxValue >> 2) /* 63 */ if (integer <= byte.MaxValue >> 2) /* 63 */
{ {
requiredBytes = 1; requiredBytes = 1;
@ -77,14 +76,14 @@ namespace EonaCat.Quic.Helpers
throw new ArgumentOutOfRangeException("Value is larger than IntegerVar.MaxValue."); throw new ArgumentOutOfRangeException("Value is larger than IntegerVar.MaxValue.");
} }
int offset = 8 - requiredBytes; var offset = 8 - requiredBytes;
byte[] uInt64Bytes = ByteHelpers.GetBytes(integer); var uInt64Bytes = ByteHelpers.GetBytes(integer);
byte first = uInt64Bytes[offset]; var first = uInt64Bytes[offset];
first = (byte)(first | (requiredBytes / 2) << 6); first = (byte)(first | ((requiredBytes / 2) << 6));
uInt64Bytes[offset] = first; uInt64Bytes[offset] = first;
byte[] result = new byte[requiredBytes]; var result = new byte[requiredBytes];
Buffer.BlockCopy(uInt64Bytes, offset, result, 0, requiredBytes); Buffer.BlockCopy(uInt64Bytes, offset, result, 0, requiredBytes);
return result; return result;
@ -92,15 +91,14 @@ namespace EonaCat.Quic.Helpers
public static ulong Decode(byte[] bytes) public static ulong Decode(byte[] bytes)
{ {
int i = 8 - bytes.Length; var i = 8 - bytes.Length;
byte[] buffer = new byte[8]; var buffer = new byte[8];
Buffer.BlockCopy(bytes, 0, buffer, i, bytes.Length); Buffer.BlockCopy(bytes, 0, buffer, i, bytes.Length);
buffer[i] = (byte)(buffer[i] & (255 >> 2)); buffer[i] = (byte)(buffer[i] & (255 >> 2));
ulong res = ByteHelpers.ToUInt64(buffer); var res = ByteHelpers.ToUInt64(buffer);
return res; return res;
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Infrastructure namespace EonaCat.Quic.Infrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -23,4 +22,3 @@
AEAD_LIMIT_REACHED = 0xF, AEAD_LIMIT_REACHED = 0xF,
CRYPTO_ERROR = 0x100 CRYPTO_ERROR = 0x100
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Infrastructure.Exceptions namespace EonaCat.Quic.Infrastructure.Exceptions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -15,4 +14,3 @@ namespace EonaCat.Quic.Infrastructure.Exceptions
{ {
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
using EonaCat.Quic.Infrastructure.Frames; using EonaCat.Quic.Infrastructure.Frames;
namespace EonaCat.Quic.Infrastructure namespace EonaCat.Quic.Infrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,7 +17,7 @@ namespace EonaCat.Quic.Infrastructure
public Frame GetFrame() public Frame GetFrame()
{ {
Frame result; Frame result;
byte frameType = _array.PeekByte(); var frameType = _array.PeekByte();
switch (frameType) switch (frameType)
{ {
case 0x00: case 0x00:
@ -151,4 +150,3 @@ namespace EonaCat.Quic.Infrastructure
return result; return result;
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,20 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class ConnectionCloseFrame : Frame public class ConnectionCloseFrame : Frame
{ {
public byte ActualType { get; set; }
public override byte Type => 0x1c;
public IntegerVar ErrorCode { get; set; }
public IntegerVar FrameType { get; set; }
public IntegerVar ReasonPhraseLength { get; set; }
public string ReasonPhrase { get; set; }
public ConnectionCloseFrame() public ConnectionCloseFrame()
{ {
ErrorCode = 0; ErrorCode = 0;
@ -42,6 +34,13 @@ namespace EonaCat.Quic.Infrastructure.Frames
ReasonPhrase = reason; ReasonPhrase = reason;
} }
public byte ActualType { get; set; }
public override byte Type => 0x1c;
public IntegerVar ErrorCode { get; set; }
public IntegerVar FrameType { get; set; }
public IntegerVar ReasonPhraseLength { get; set; }
public string ReasonPhrase { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
ActualType = array.ReadByte(); ActualType = array.ReadByte();
@ -53,13 +52,13 @@ namespace EonaCat.Quic.Infrastructure.Frames
ReasonPhraseLength = array.ReadIntegerVar(); ReasonPhraseLength = array.ReadIntegerVar();
byte[] rp = array.ReadBytes((int)ReasonPhraseLength.Value); var rp = array.ReadBytes((int)ReasonPhraseLength.Value);
ReasonPhrase = ByteHelpers.GetString(rp); ReasonPhrase = ByteHelpers.GetString(rp);
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
ActualType ActualType
}; };
@ -74,11 +73,10 @@ namespace EonaCat.Quic.Infrastructure.Frames
byte[] rpl = new IntegerVar((ulong)ReasonPhrase.Length); byte[] rpl = new IntegerVar((ulong)ReasonPhrase.Length);
result.AddRange(rpl); result.AddRange(rpl);
byte[] reasonPhrase = ByteHelpers.GetBytes(ReasonPhrase); var reasonPhrase = ByteHelpers.GetBytes(ReasonPhrase);
result.AddRange(reasonPhrase); result.AddRange(reasonPhrase);
} }
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,8 +1,8 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames;
namespace EonaCat.Quic.Infrastructure.Frames
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class CryptoFrame : Frame public class CryptoFrame : Frame
@ -19,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,15 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames;
namespace EonaCat.Quic.Infrastructure.Frames
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class DataBlockedFrame : Frame public class DataBlockedFrame : Frame
{ {
public override byte Type => 0x14;
public IntegerVar MaximumData { get; set; }
public DataBlockedFrame() public DataBlockedFrame()
{ {
} }
@ -19,15 +16,18 @@ namespace EonaCat.Quic.Infrastructure.Frames
MaximumData = dataLimit; MaximumData = dataLimit;
} }
public override byte Type => 0x14;
public IntegerVar MaximumData { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
MaximumData = array.ReadIntegerVar(); MaximumData = array.ReadIntegerVar();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -36,4 +36,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,7 +1,6 @@
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -16,4 +15,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
public abstract void Decode(ByteArray array); public abstract void Decode(ByteArray array);
} }
}

View File

@ -1,8 +1,8 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames;
namespace EonaCat.Quic.Infrastructure.Frames
{
public class MaxDataFrame : Frame public class MaxDataFrame : Frame
{ {
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
@ -20,7 +20,7 @@ namespace EonaCat.Quic.Infrastructure.Frames
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -29,4 +29,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,19 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class MaxStreamDataFrame : Frame public class MaxStreamDataFrame : Frame
{ {
public override byte Type => 0x11;
public IntegerVar StreamId { get; set; }
public IntegerVar MaximumStreamData { get; set; }
public StreamId ConvertedStreamId { get; set; }
public MaxStreamDataFrame() public MaxStreamDataFrame()
{ {
} }
@ -24,16 +17,22 @@ namespace EonaCat.Quic.Infrastructure.Frames
MaximumStreamData = maximumStreamData; MaximumStreamData = maximumStreamData;
} }
public override byte Type => 0x11;
public IntegerVar StreamId { get; set; }
public IntegerVar MaximumStreamData { get; set; }
public StreamId ConvertedStreamId { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
StreamId = array.ReadIntegerVar(); StreamId = array.ReadIntegerVar();
MaximumStreamData = array.ReadIntegerVar(); MaximumStreamData = array.ReadIntegerVar();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -43,4 +42,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,15 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames;
namespace EonaCat.Quic.Infrastructure.Frames
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class MaxStreamsFrame : Frame public class MaxStreamsFrame : Frame
{ {
public override byte Type => 0x12;
public IntegerVar MaximumStreams { get; set; }
public MaxStreamsFrame() public MaxStreamsFrame()
{ {
} }
@ -19,15 +16,18 @@ namespace EonaCat.Quic.Infrastructure.Frames
MaximumStreams = new IntegerVar(maximumStreamId); MaximumStreams = new IntegerVar(maximumStreamId);
} }
public override byte Type => 0x12;
public IntegerVar MaximumStreams { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
MaximumStreams = array.ReadIntegerVar(); MaximumStreams = array.ReadIntegerVar();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -36,4 +36,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -12,12 +11,12 @@ namespace EonaCat.Quic.Infrastructure.Frames
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> data = new List<byte> var data = new List<byte>
{ {
Type Type
}; };
@ -25,4 +24,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return data.ToArray(); return data.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -12,12 +11,12 @@ namespace EonaCat.Quic.Infrastructure.Frames
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> data = new List<byte> var data = new List<byte>
{ {
Type Type
}; };
@ -25,4 +24,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return data.ToArray(); return data.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -15,7 +14,7 @@ namespace EonaCat.Quic.Infrastructure.Frames
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
StreamId = array.ReadIntegerVar(); StreamId = array.ReadIntegerVar();
ApplicationProtocolErrorCode = array.ReadIntegerVar(); ApplicationProtocolErrorCode = array.ReadIntegerVar();
FinalSize = array.ReadIntegerVar(); FinalSize = array.ReadIntegerVar();
@ -23,7 +22,7 @@ namespace EonaCat.Quic.Infrastructure.Frames
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -34,4 +33,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,17 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class StreamDataBlockedFrame : Frame public class StreamDataBlockedFrame : Frame
{ {
public override byte Type => 0x15;
public IntegerVar StreamId { get; set; }
public IntegerVar MaximumStreamData { get; set; }
public StreamDataBlockedFrame() public StreamDataBlockedFrame()
{ {
} }
@ -22,16 +17,20 @@ namespace EonaCat.Quic.Infrastructure.Frames
MaximumStreamData = streamDataLimit; MaximumStreamData = streamDataLimit;
} }
public override byte Type => 0x15;
public IntegerVar StreamId { get; set; }
public IntegerVar MaximumStreamData { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
StreamId = array.ReadIntegerVar(); StreamId = array.ReadIntegerVar();
MaximumStreamData = array.ReadIntegerVar(); MaximumStreamData = array.ReadIntegerVar();
} }
public override byte[] Encode() public override byte[] Encode()
{ {
List<byte> result = new List<byte> var result = new List<byte>
{ {
Type Type
}; };
@ -41,4 +40,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,13 +9,6 @@ namespace EonaCat.Quic.Infrastructure.Frames
{ {
public byte ActualType = 0x08; public byte ActualType = 0x08;
public override byte Type => 0x08;
public IntegerVar StreamId { get; set; }
public IntegerVar Offset { get; set; }
public IntegerVar Length { get; set; }
public byte[] StreamData { get; set; }
public bool EndOfStream { get; set; }
public StreamFrame() public StreamFrame()
{ {
} }
@ -30,13 +22,20 @@ namespace EonaCat.Quic.Infrastructure.Frames
EndOfStream = eos; EndOfStream = eos;
} }
public override byte Type => 0x08;
public IntegerVar StreamId { get; set; }
public IntegerVar Offset { get; set; }
public IntegerVar Length { get; set; }
public byte[] StreamData { get; set; }
public bool EndOfStream { get; set; }
public override void Decode(ByteArray array) public override void Decode(ByteArray array)
{ {
byte type = array.ReadByte(); var type = array.ReadByte();
byte OFF_BIT = (byte)(type & 0x04); var OFF_BIT = (byte)(type & 0x04);
byte LEN_BIT = (byte)(type & 0x02); var LEN_BIT = (byte)(type & 0x02);
byte FIN_BIT = (byte)(type & 0x01); var FIN_BIT = (byte)(type & 0x01);
StreamId = array.ReadIntegerVar(); StreamId = array.ReadIntegerVar();
if (OFF_BIT > 0) if (OFF_BIT > 0)
@ -69,16 +68,16 @@ namespace EonaCat.Quic.Infrastructure.Frames
ActualType = (byte)(ActualType | 0x02); ActualType = (byte)(ActualType | 0x02);
} }
if (EndOfStream == true) if (EndOfStream)
{ {
ActualType = (byte)(ActualType | 0x01); ActualType = (byte)(ActualType | 0x01);
} }
byte OFF_BIT = (byte)(ActualType & 0x04); var OFF_BIT = (byte)(ActualType & 0x04);
byte LEN_BIT = (byte)(ActualType & 0x02); var LEN_BIT = (byte)(ActualType & 0x02);
byte FIN_BIT = (byte)(ActualType & 0x01); var FIN_BIT = (byte)(ActualType & 0x01);
List<byte> result = new List<byte> var result = new List<byte>
{ {
ActualType ActualType
}; };
@ -100,4 +99,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Helpers; using System;
using System; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Frames namespace EonaCat.Quic.Infrastructure.Frames;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,4 +19,3 @@ namespace EonaCat.Quic.Infrastructure.Frames
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,12 +1,11 @@
namespace EonaCat.Quic.Infrastructure namespace EonaCat.Quic.Infrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class NumberSpace public class NumberSpace
{ {
private readonly uint _max = uint.MaxValue; private readonly uint _max = uint.MaxValue;
private uint _n = 0; private uint _n;
public NumberSpace() public NumberSpace()
{ {
@ -33,4 +32,3 @@
return _n; return _n;
} }
} }
}

View File

@ -3,8 +3,7 @@ using EonaCat.Quic.Infrastructure.Frames;
using EonaCat.Quic.Infrastructure.Packets; using EonaCat.Quic.Infrastructure.Packets;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
namespace EonaCat.Quic.Infrastructure.PacketProcessing namespace EonaCat.Quic.Infrastructure.PacketProcessing;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -12,19 +11,16 @@ namespace EonaCat.Quic.Infrastructure.PacketProcessing
{ {
public InitialPacket CreateInitialPacket(IntegerParts sourceConnectionId, IntegerParts destinationConnectionId) public InitialPacket CreateInitialPacket(IntegerParts sourceConnectionId, IntegerParts destinationConnectionId)
{ {
InitialPacket packet = new InitialPacket(destinationConnectionId, sourceConnectionId); var packet = new InitialPacket(destinationConnectionId, sourceConnectionId);
packet.PacketNumber = 0; packet.PacketNumber = 0;
packet.SourceConnectionId = sourceConnectionId; packet.SourceConnectionId = sourceConnectionId;
packet.DestinationConnectionId = destinationConnectionId; packet.DestinationConnectionId = destinationConnectionId;
packet.Version = QuicVersion.CurrentVersion; packet.Version = QuicVersion.CurrentVersion;
int length = packet.Encode().Length; var length = packet.Encode().Length;
int padding = QuicSettings.PMTU - length; var padding = QuicSettings.PMTU - length;
for (int i = 0; i < padding; i++) for (var i = 0; i < padding; i++) packet.AttachFrame(new PaddingFrame());
{
packet.AttachFrame(new PaddingFrame());
}
return packet; return packet;
} }
@ -34,4 +30,3 @@ namespace EonaCat.Quic.Infrastructure.PacketProcessing
return new VersionNegotiationPacket(); return new VersionNegotiationPacket();
} }
} }
}

View File

@ -2,15 +2,14 @@
using EonaCat.Quic.Infrastructure.Frames; using EonaCat.Quic.Infrastructure.Frames;
using EonaCat.Quic.Infrastructure.Packets; using EonaCat.Quic.Infrastructure.Packets;
namespace EonaCat.Quic.Infrastructure.PacketProcessing namespace EonaCat.Quic.Infrastructure.PacketProcessing;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class PacketCreator public class PacketCreator
{ {
private readonly NumberSpace _ns;
private readonly IntegerParts _connectionId; private readonly IntegerParts _connectionId;
private readonly NumberSpace _ns;
private readonly IntegerParts _peerConnectionId; private readonly IntegerParts _peerConnectionId;
public PacketCreator(IntegerParts connectionId, IntegerParts peerConnectionId) public PacketCreator(IntegerParts connectionId, IntegerParts peerConnectionId)
@ -23,7 +22,7 @@ namespace EonaCat.Quic.Infrastructure.PacketProcessing
public ShortHeaderPacket CreateConnectionClosePacket(ErrorCode code, byte frameType, string reason) public ShortHeaderPacket CreateConnectionClosePacket(ErrorCode code, byte frameType, string reason)
{ {
ShortHeaderPacket packet = new ShortHeaderPacket(_peerConnectionId.Size); var packet = new ShortHeaderPacket(_peerConnectionId.Size);
packet.PacketNumber = _ns.Get(); packet.PacketNumber = _ns.Get();
packet.DestinationConnectionId = (byte)_peerConnectionId; packet.DestinationConnectionId = (byte)_peerConnectionId;
packet.AttachFrame(new ConnectionCloseFrame(code, frameType, reason)); packet.AttachFrame(new ConnectionCloseFrame(code, frameType, reason));
@ -33,7 +32,7 @@ namespace EonaCat.Quic.Infrastructure.PacketProcessing
public ShortHeaderPacket CreateDataPacket(ulong streamId, byte[] data, ulong offset, bool eos) public ShortHeaderPacket CreateDataPacket(ulong streamId, byte[] data, ulong offset, bool eos)
{ {
ShortHeaderPacket packet = new ShortHeaderPacket(_peerConnectionId.Size); var packet = new ShortHeaderPacket(_peerConnectionId.Size);
packet.PacketNumber = _ns.Get(); packet.PacketNumber = _ns.Get();
packet.DestinationConnectionId = (byte)_peerConnectionId; packet.DestinationConnectionId = (byte)_peerConnectionId;
packet.AttachFrame(new StreamFrame(streamId, data, offset, eos)); packet.AttachFrame(new StreamFrame(streamId, data, offset, eos));
@ -51,4 +50,3 @@ namespace EonaCat.Quic.Infrastructure.PacketProcessing
return new ShortHeaderPacket(0); return new ShortHeaderPacket(0);
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Infrastructure namespace EonaCat.Quic.Infrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,4 +9,3 @@
Handshake = 0x2, Handshake = 0x2,
RetryPacket = 0x3 RetryPacket = 0x3
} }
}

View File

@ -1,24 +1,12 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class InitialPacket : Packet public class InitialPacket : Packet
{ {
public override byte Type => 0b1100_1100; //0xDC; // 1101 1100
public byte DestinationConnectionIdLength { get; set; }
public IntegerParts DestinationConnectionId { get; set; }
public byte SourceConnectionIdLength { get; set; }
public IntegerParts SourceConnectionId { get; set; }
public IntegerVar TokenLength { get; set; }
public byte[] Token { get; set; }
public IntegerVar Length { get; set; }
public IntegerParts PacketNumber { get; set; }
public InitialPacket() public InitialPacket()
{ {
} }
@ -32,12 +20,23 @@ namespace EonaCat.Quic.Infrastructure.Packets
SourceConnectionId = sourceConnectionId; SourceConnectionId = sourceConnectionId;
} }
public override byte Type => 0b1100_1100; //0xDC; // 1101 1100
public byte DestinationConnectionIdLength { get; set; }
public IntegerParts DestinationConnectionId { get; set; }
public byte SourceConnectionIdLength { get; set; }
public IntegerParts SourceConnectionId { get; set; }
public IntegerVar TokenLength { get; set; }
public byte[] Token { get; set; }
public IntegerVar Length { get; set; }
public IntegerParts PacketNumber { get; set; }
public override void Decode(byte[] packet) public override void Decode(byte[] packet)
{ {
ByteArray array = new ByteArray(packet); var array = new ByteArray(packet);
byte type = array.ReadByte(); var type = array.ReadByte();
// Size of the packet PacketNumber is determined by the last 2 bits of the Type. // Size of the packet PacketNumber is determined by the last 2 bits of the Type.
int pnSize = (type & 0x03) + 1; var pnSize = (type & 0x03) + 1;
Version = array.ReadUInt32(); Version = array.ReadUInt32();
@ -64,14 +63,14 @@ namespace EonaCat.Quic.Infrastructure.Packets
Length = Length - PacketNumber.Size; Length = Length - PacketNumber.Size;
this.DecodeFrames(array); DecodeFrames(array);
} }
public override byte[] Encode() public override byte[] Encode()
{ {
byte[] frames = EncodeFrames(); var frames = EncodeFrames();
List<byte> result = new List<byte> var result = new List<byte>
{ {
(byte)(Type | (PacketNumber.Size - 1)) (byte)(Type | (PacketNumber.Size - 1))
}; };
@ -100,4 +99,3 @@ namespace EonaCat.Quic.Infrastructure.Packets
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,27 +1,18 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class LongHeaderPacket : Packet public class LongHeaderPacket : Packet
{ {
public override byte Type => 0b1100_0000; // 1100 0000
public byte DestinationConnectionIdLength { get; set; }
public IntegerParts DestinationConnectionId { get; set; }
public byte SourceConnectionIdLength { get; set; }
public IntegerParts SourceConnectionId { get; set; }
public PacketType PacketType { get; set; }
public LongHeaderPacket() public LongHeaderPacket()
{ {
} }
public LongHeaderPacket(PacketType packetType, IntegerParts destinationConnectionId, IntegerParts sourceConnectionId) public LongHeaderPacket(PacketType packetType, IntegerParts destinationConnectionId,
IntegerParts sourceConnectionId)
{ {
PacketType = packetType; PacketType = packetType;
DestinationConnectionIdLength = destinationConnectionId.Size; DestinationConnectionIdLength = destinationConnectionId.Size;
@ -31,11 +22,20 @@ namespace EonaCat.Quic.Infrastructure.Packets
SourceConnectionId = sourceConnectionId; SourceConnectionId = sourceConnectionId;
} }
public override byte Type => 0b1100_0000; // 1100 0000
public byte DestinationConnectionIdLength { get; set; }
public IntegerParts DestinationConnectionId { get; set; }
public byte SourceConnectionIdLength { get; set; }
public IntegerParts SourceConnectionId { get; set; }
public PacketType PacketType { get; set; }
public override void Decode(byte[] packet) public override void Decode(byte[] packet)
{ {
ByteArray array = new ByteArray(packet); var array = new ByteArray(packet);
byte type = array.ReadByte(); var type = array.ReadByte();
PacketType = DecodeTypeFiled(type); PacketType = DecodeTypeFiled(type);
Version = array.ReadUInt32(); Version = array.ReadUInt32();
@ -52,14 +52,14 @@ namespace EonaCat.Quic.Infrastructure.Packets
SourceConnectionId = array.ReadGranularInteger(SourceConnectionIdLength); SourceConnectionId = array.ReadGranularInteger(SourceConnectionIdLength);
} }
this.DecodeFrames(array); DecodeFrames(array);
} }
public override byte[] Encode() public override byte[] Encode()
{ {
byte[] frames = EncodeFrames(); var frames = EncodeFrames();
List<byte> result = new List<byte> var result = new List<byte>
{ {
EncodeTypeField() EncodeTypeField()
}; };
@ -84,16 +84,15 @@ namespace EonaCat.Quic.Infrastructure.Packets
private byte EncodeTypeField() private byte EncodeTypeField()
{ {
byte type = (byte)(Type | ((byte)PacketType << 4) & 0b0011_0000); var type = (byte)(Type | (((byte)PacketType << 4) & 0b0011_0000));
return type; return type;
} }
private PacketType DecodeTypeFiled(byte type) private PacketType DecodeTypeFiled(byte type)
{ {
PacketType result = (PacketType)((type & 0b0011_0000) >> 4); var result = (PacketType)((type & 0b0011_0000) >> 4);
return result; return result;
} }
} }
}

View File

@ -1,11 +1,10 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using EonaCat.Quic.Helpers;
using EonaCat.Quic.Infrastructure.Exceptions; using EonaCat.Quic.Infrastructure.Exceptions;
using EonaCat.Quic.Infrastructure.Frames; using EonaCat.Quic.Infrastructure.Frames;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using System.Collections.Generic;
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,7 +13,7 @@ namespace EonaCat.Quic.Infrastructure.Packets
/// </summary> /// </summary>
public abstract class Packet public abstract class Packet
{ {
protected List<Frame> _frames = new List<Frame>(); protected List<Frame> _frames = new();
public abstract byte Type { get; } public abstract byte Type { get; }
public uint Version { get; set; } public uint Version { get; set; }
@ -35,9 +34,9 @@ namespace EonaCat.Quic.Infrastructure.Packets
public virtual void DecodeFrames(ByteArray array) public virtual void DecodeFrames(ByteArray array)
{ {
FrameParser factory = new FrameParser(array); var factory = new FrameParser(array);
Frame result; Frame result;
int frames = 0; var frames = 0;
while (array.HasData() && frames <= QuicSettings.PMTU) while (array.HasData() && frames <= QuicSettings.PMTU)
{ {
result = factory.GetFrame(); result = factory.GetFrame();
@ -57,8 +56,8 @@ namespace EonaCat.Quic.Infrastructure.Packets
public virtual byte[] EncodeFrames() public virtual byte[] EncodeFrames()
{ {
List<byte> result = new List<byte>(); var result = new List<byte>();
foreach (Frame frame in _frames) foreach (var frame in _frames)
{ {
result.AddRange(frame.Encode()); result.AddRange(frame.Encode());
} }
@ -66,4 +65,3 @@ namespace EonaCat.Quic.Infrastructure.Packets
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,14 +1,19 @@
using EonaCat.Quic.Helpers; using System.Collections.Generic;
using System.Collections.Generic; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class ShortHeaderPacket : Packet public class ShortHeaderPacket : Packet
{ {
public byte ActualType = 0b0100_0000; public byte ActualType = 0b0100_0000;
public ShortHeaderPacket(byte destinationConnectionIdLength)
{
DestinationConnectionIdLength = destinationConnectionIdLength;
}
public override byte Type => 0b0100_0000; public override byte Type => 0b0100_0000;
public IntegerParts DestinationConnectionId { get; set; } public IntegerParts DestinationConnectionId { get; set; }
@ -17,18 +22,13 @@ namespace EonaCat.Quic.Infrastructure.Packets
// Field not transferred! Only the connection knows about the length of the ConnectionId // Field not transferred! Only the connection knows about the length of the ConnectionId
public byte DestinationConnectionIdLength { get; set; } public byte DestinationConnectionIdLength { get; set; }
public ShortHeaderPacket(byte destinationConnectionIdLength)
{
DestinationConnectionIdLength = destinationConnectionIdLength;
}
public override void Decode(byte[] packet) public override void Decode(byte[] packet)
{ {
ByteArray array = new ByteArray(packet); var array = new ByteArray(packet);
byte type = array.ReadByte(); var type = array.ReadByte();
DestinationConnectionId = array.ReadGranularInteger(DestinationConnectionIdLength); DestinationConnectionId = array.ReadGranularInteger(DestinationConnectionIdLength);
int pnSize = (type & 0x03) + 1; var pnSize = (type & 0x03) + 1;
PacketNumber = array.ReadBytes(pnSize); PacketNumber = array.ReadBytes(pnSize);
DecodeFrames(array); DecodeFrames(array);
@ -36,9 +36,9 @@ namespace EonaCat.Quic.Infrastructure.Packets
public override byte[] Encode() public override byte[] Encode()
{ {
byte[] frames = EncodeFrames(); var frames = EncodeFrames();
List<byte> result = new List<byte> var result = new List<byte>
{ {
(byte)(Type | (PacketNumber.Size - 1)) (byte)(Type | (PacketNumber.Size - 1))
}; };
@ -51,4 +51,3 @@ namespace EonaCat.Quic.Infrastructure.Packets
return result.ToArray(); return result.ToArray();
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -9,7 +8,7 @@
{ {
Packet result = null; Packet result = null;
QuicPacketType type = GetPacketType(data); var type = GetPacketType(data);
switch (type) switch (type)
{ {
case QuicPacketType.Initial: case QuicPacketType.Initial:
@ -39,7 +38,7 @@
return QuicPacketType.Broken; return QuicPacketType.Broken;
} }
byte type = data[0]; var type = data[0];
if ((type & 0xC0) == 0xC0) if ((type & 0xC0) == 0xC0)
{ {
@ -64,4 +63,3 @@
return QuicPacketType.Broken; return QuicPacketType.Broken;
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.Quic.Infrastructure.Packets namespace EonaCat.Quic.Infrastructure.Packets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -19,4 +18,3 @@ namespace EonaCat.Quic.Infrastructure.Packets
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Infrastructure namespace EonaCat.Quic.Infrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -11,4 +10,3 @@
VersionNegotiation, VersionNegotiation,
Broken Broken
} }
}

View File

@ -1,12 +1,12 @@
namespace EonaCat.Quic.Infrastructure.Settings namespace EonaCat.Quic.Infrastructure.Settings;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class QuicSettings public class QuicSettings
{ {
/// <summary> /// <summary>
/// Path Maximum Transmission Unit. Indicates the mandatory initial packet capacity, and the maximum UDP packet capacity. /// Path Maximum Transmission Unit. Indicates the mandatory initial packet capacity, and the maximum UDP packet
/// capacity.
/// </summary> /// </summary>
public const int PMTU = 1200; public const int PMTU = 1200;
@ -26,7 +26,8 @@
public const int MaximumStreamId = 128; public const int MaximumStreamId = 128;
/// <summary> /// <summary>
/// Maximum packets that can be transferred before any data transfer (loss of packets, packet resent, infinite ack loop) /// Maximum packets that can be transferred before any data transfer (loss of packets, packet resent, infinite ack
/// loop)
/// </summary> /// </summary>
public const int MaximumInitialPacketNumber = 100; public const int MaximumInitialPacketNumber = 100;
@ -52,4 +53,3 @@
/// </summary> /// </summary>
public const int MaxStreamData = 78125; public const int MaxStreamData = 78125;
} }
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace EonaCat.Quic.Infrastructure.Settings namespace EonaCat.Quic.Infrastructure.Settings;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -9,6 +8,5 @@ namespace EonaCat.Quic.Infrastructure.Settings
{ {
public const int CurrentVersion = 16; public const int CurrentVersion = 16;
public static readonly List<uint> SupportedVersions = new List<uint>() { 15, 16 }; public static readonly List<uint> SupportedVersions = new() { 15, 16 };
}
} }

View File

@ -1,21 +1,19 @@
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
namespace EonaCat.Quic.InternalInfrastructure namespace EonaCat.Quic.InternalInfrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
internal class ConnectionData internal class ConnectionData
{ {
public PacketWireTransfer PWT { get; set; }
public IntegerParts ConnectionId { get; set; }
public IntegerParts PeerConnectionId { get; set; }
public ConnectionData(PacketWireTransfer pwt, IntegerParts connectionId, IntegerParts peerConnnectionId) public ConnectionData(PacketWireTransfer pwt, IntegerParts connectionId, IntegerParts peerConnnectionId)
{ {
PWT = pwt; PWT = pwt;
ConnectionId = connectionId; ConnectionId = connectionId;
PeerConnectionId = peerConnnectionId; PeerConnectionId = peerConnnectionId;
} }
}
public PacketWireTransfer PWT { get; set; }
public IntegerParts ConnectionId { get; set; }
public IntegerParts PeerConnectionId { get; set; }
} }

View File

@ -1,19 +1,18 @@
using EonaCat.Quic.Exceptions; using System.Net;
using EonaCat.Quic.Infrastructure.Packets;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using EonaCat.Quic.Exceptions;
using EonaCat.Quic.Infrastructure.Packets;
namespace EonaCat.Quic.InternalInfrastructure namespace EonaCat.Quic.InternalInfrastructure;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
internal class PacketWireTransfer internal class PacketWireTransfer
{ {
private readonly UdpClient _client; private readonly UdpClient _client;
private IPEndPoint _peerEndpoint;
private readonly Unpacker _unpacker; private readonly Unpacker _unpacker;
private IPEndPoint _peerEndpoint;
public PacketWireTransfer(UdpClient client, IPEndPoint peerEndpoint) public PacketWireTransfer(UdpClient client, IPEndPoint peerEndpoint)
{ {
@ -26,22 +25,22 @@ namespace EonaCat.Quic.InternalInfrastructure
public Packet ReadPacket() public Packet ReadPacket()
{ {
// Await response for sucessfull connection creation by the server // Await response for sucessfull connection creation by the server
byte[] peerData = _client.Receive(ref _peerEndpoint); var peerData = _client.Receive(ref _peerEndpoint);
if (peerData == null) if (peerData == null)
{ {
throw new ConnectionException("Server did not respond properly."); throw new ConnectionException("Server did not respond properly.");
} }
Packet packet = _unpacker.Unpack(peerData); var packet = _unpacker.Unpack(peerData);
return packet; return packet;
} }
public bool SendPacket(Packet packet) public bool SendPacket(Packet packet)
{ {
byte[] data = packet.Encode(); var data = packet.Encode();
int sent = _client.Send(data, data.Length, _peerEndpoint); var sent = _client.Send(data, data.Length, _peerEndpoint);
return sent > 0; return sent > 0;
} }
@ -51,4 +50,3 @@ namespace EonaCat.Quic.InternalInfrastructure
return _peerEndpoint; return _peerEndpoint;
} }
} }
}

View File

@ -1,4 +1,8 @@
using EonaCat.Quic.Connections; using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using EonaCat.Quic.Connections;
using EonaCat.Quic.Exceptions; using EonaCat.Quic.Exceptions;
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
using EonaCat.Quic.Infrastructure.Frames; using EonaCat.Quic.Infrastructure.Frames;
@ -6,14 +10,8 @@ using EonaCat.Quic.Infrastructure.PacketProcessing;
using EonaCat.Quic.Infrastructure.Packets; using EonaCat.Quic.Infrastructure.Packets;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using EonaCat.Quic.InternalInfrastructure; using EonaCat.Quic.InternalInfrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
namespace EonaCat.Quic namespace EonaCat.Quic;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -22,13 +20,13 @@ namespace EonaCat.Quic
/// </summary> /// </summary>
public class QuicClient : QuicTransport public class QuicClient : QuicTransport
{ {
private IPEndPoint _peerIp;
private readonly UdpClient _client; private readonly UdpClient _client;
private QuicConnection _connection;
private readonly InitialPacketCreator _packetCreator; private readonly InitialPacketCreator _packetCreator;
private QuicConnection _connection;
private ulong _maximumStreams = QuicSettings.MaximumStreamId; private ulong _maximumStreams = QuicSettings.MaximumStreamId;
private IPEndPoint _peerIp;
private PacketWireTransfer _pwt; private PacketWireTransfer _pwt;
public QuicClient() public QuicClient()
@ -56,19 +54,20 @@ namespace EonaCat.Quic
{ {
ipAddress = IPAddress.Parse(ip); ipAddress = IPAddress.Parse(ip);
} }
_peerIp = new IPEndPoint(ipAddress, port); _peerIp = new IPEndPoint(ipAddress, port);
// Initialize packet reader // Initialize packet reader
_pwt = new PacketWireTransfer(_client, _peerIp); _pwt = new PacketWireTransfer(_client, _peerIp);
// Start initial protocol process // Start initial protocol process
InitialPacket connectionPacket = _packetCreator.CreateInitialPacket(0, 0); var connectionPacket = _packetCreator.CreateInitialPacket(0, 0);
// Send the initial packet // Send the initial packet
_pwt.SendPacket(connectionPacket); _pwt.SendPacket(connectionPacket);
// Await response for sucessfull connection creation by the server // Await response for sucessfull connection creation by the server
InitialPacket packet = (InitialPacket)_pwt.ReadPacket(); var packet = (InitialPacket)_pwt.ReadPacket();
HandleInitialFrames(packet); HandleInitialFrames(packet);
EstablishConnection(packet.SourceConnectionId, packet.SourceConnectionId); EstablishConnection(packet.SourceConnectionId, packet.SourceConnectionId);
@ -82,10 +81,10 @@ namespace EonaCat.Quic
/// <param name="packet"></param> /// <param name="packet"></param>
private void HandleInitialFrames(Packet packet) private void HandleInitialFrames(Packet packet)
{ {
List<Frame> frames = packet.GetFrames(); var frames = packet.GetFrames();
for (int i = frames.Count - 1; i > 0; i--) for (var i = frames.Count - 1; i > 0; i--)
{ {
Frame frame = frames[i]; var frame = frames[i];
if (frame is ConnectionCloseFrame ccf) if (frame is ConnectionCloseFrame ccf)
{ {
throw new ConnectionException(ccf.ReasonPhrase); throw new ConnectionException(ccf.ReasonPhrase);
@ -111,8 +110,7 @@ namespace EonaCat.Quic
/// <param name="peerConnectionId"></param> /// <param name="peerConnectionId"></param>
private void EstablishConnection(IntegerParts connectionId, IntegerParts peerConnectionId) private void EstablishConnection(IntegerParts connectionId, IntegerParts peerConnectionId)
{ {
ConnectionData connection = new ConnectionData(_pwt, connectionId, peerConnectionId); var connection = new ConnectionData(_pwt, connectionId, peerConnectionId);
_connection = new QuicConnection(connection); _connection = new QuicConnection(connection);
} }
} }
}

View File

@ -1,4 +1,8 @@
using EonaCat.Quic.Connections; using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using EonaCat.Quic.Connections;
using EonaCat.Quic.Constants; using EonaCat.Quic.Constants;
using EonaCat.Quic.Events; using EonaCat.Quic.Events;
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
@ -8,13 +12,8 @@ using EonaCat.Quic.Infrastructure.PacketProcessing;
using EonaCat.Quic.Infrastructure.Packets; using EonaCat.Quic.Infrastructure.Packets;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using EonaCat.Quic.InternalInfrastructure; using EonaCat.Quic.InternalInfrastructure;
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
namespace EonaCat.Quic namespace EonaCat.Quic;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -23,19 +22,17 @@ namespace EonaCat.Quic
/// </summary> /// </summary>
public class QuicServer : QuicTransport public class QuicServer : QuicTransport
{ {
private readonly Unpacker _unpacker; private readonly string _hostname;
private readonly InitialPacketCreator _packetCreator; private readonly InitialPacketCreator _packetCreator;
private PacketWireTransfer _pwt; private readonly int _port;
private readonly Unpacker _unpacker;
private UdpClient _client; private UdpClient _client;
private readonly int _port; private PacketWireTransfer _pwt;
private readonly string _hostname;
private bool _started; private bool _started;
public event ClientConnectedEvent OnClientConnected;
/// <summary> /// <summary>
/// Create a new instance of QuicListener. /// Create a new instance of QuicListener.
/// </summary> /// </summary>
@ -50,6 +47,8 @@ namespace EonaCat.Quic
_packetCreator = new InitialPacketCreator(); _packetCreator = new InitialPacketCreator();
} }
public event ClientConnectedEvent OnClientConnected;
/// <summary> /// <summary>
/// Starts the listener. /// Starts the listener.
/// </summary> /// </summary>
@ -72,10 +71,10 @@ namespace EonaCat.Quic
while (true) while (true)
{ {
Packet packet = _pwt.ReadPacket(); var packet = _pwt.ReadPacket();
if (packet is InitialPacket) if (packet is InitialPacket)
{ {
QuicConnection connection = ProcessInitialPacket(packet, _pwt.LastTransferEndpoint()); var connection = ProcessInitialPacket(packet, _pwt.LastTransferEndpoint());
OnClientConnected?.Invoke(connection); OnClientConnected?.Invoke(connection);
} }
@ -111,22 +110,24 @@ namespace EonaCat.Quic
// Unsupported version. Version negotiation packet is sent only on initial connection. All other packets are dropped. (5.2.2 / 16th draft) // Unsupported version. Version negotiation packet is sent only on initial connection. All other packets are dropped. (5.2.2 / 16th draft)
if (packet.Version != QuicVersion.CurrentVersion || !QuicVersion.SupportedVersions.Contains(packet.Version)) if (packet.Version != QuicVersion.CurrentVersion || !QuicVersion.SupportedVersions.Contains(packet.Version))
{ {
VersionNegotiationPacket vnp = _packetCreator.CreateVersionNegotiationPacket(); var vnp = _packetCreator.CreateVersionNegotiationPacket();
data = vnp.Encode(); data = vnp.Encode();
_client.Send(data, data.Length, endPoint); _client.Send(data, data.Length, endPoint);
return null; return null;
} }
InitialPacket cast = packet as InitialPacket; var cast = packet as InitialPacket;
InitialPacket ip = _packetCreator.CreateInitialPacket(0, cast.SourceConnectionId); var ip = _packetCreator.CreateInitialPacket(0, cast.SourceConnectionId);
// Protocol violation if the initial packet is smaller than the PMTU. (pt. 14 / 16th draft) // Protocol violation if the initial packet is smaller than the PMTU. (pt. 14 / 16th draft)
if (cast.Encode().Length < QuicSettings.PMTU) if (cast.Encode().Length < QuicSettings.PMTU)
{ {
ip.AttachFrame(new ConnectionCloseFrame(ErrorCode.PROTOCOL_VIOLATION, 0x00, ErrorConstants.PMTUNotReached)); ip.AttachFrame(new ConnectionCloseFrame(ErrorCode.PROTOCOL_VIOLATION, 0x00, ErrorConstants.PMTUNotReached));
} }
else if (ConnectionPool.AddConnection(new ConnectionData(new PacketWireTransfer(_client, endPoint), cast.SourceConnectionId, 0), out ulong availableConnectionId) == true) else if (ConnectionPool.AddConnection(
new ConnectionData(new PacketWireTransfer(_client, endPoint), cast.SourceConnectionId, 0),
out var availableConnectionId))
{ {
// Tell the peer the available connection id // Tell the peer the available connection id
ip.SourceConnectionId = (byte)availableConnectionId; ip.SourceConnectionId = (byte)availableConnectionId;
@ -145,7 +146,7 @@ namespace EonaCat.Quic
} }
data = ip.Encode(); data = ip.Encode();
int dataSent = _client.Send(data, data.Length, endPoint); var dataSent = _client.Send(data, data.Length, endPoint);
if (dataSent > 0) if (dataSent > 0)
{ {
return result; return result;
@ -154,4 +155,3 @@ namespace EonaCat.Quic
return null; return null;
} }
} }
}

View File

@ -1,8 +1,7 @@
using EonaCat.Quic.Connections; using EonaCat.Quic.Connections;
using EonaCat.Quic.Infrastructure.Packets; using EonaCat.Quic.Infrastructure.Packets;
namespace EonaCat.Quic namespace EonaCat.Quic;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,9 +13,9 @@ namespace EonaCat.Quic
/// <param name="packet"></param> /// <param name="packet"></param>
protected void ProcessShortHeaderPacket(Packet packet) protected void ProcessShortHeaderPacket(Packet packet)
{ {
ShortHeaderPacket shp = (ShortHeaderPacket)packet; var shp = (ShortHeaderPacket)packet;
QuicConnection connection = ConnectionPool.Find(shp.DestinationConnectionId); var connection = ConnectionPool.Find(shp.DestinationConnectionId);
// No suitable connection found. Discard the packet. // No suitable connection found. Discard the packet.
if (connection == null) if (connection == null)
@ -27,4 +26,3 @@ namespace EonaCat.Quic
connection.ProcessFrames(shp.GetFrames()); connection.ProcessFrames(shp.GetFrames());
} }
} }
}

View File

@ -1,17 +1,16 @@
using EonaCat.Quic.Connections; using System;
using System.Collections.Generic;
using System.Linq;
using EonaCat.Quic.Connections;
using EonaCat.Quic.Constants; using EonaCat.Quic.Constants;
using EonaCat.Quic.Events; using EonaCat.Quic.Events;
using EonaCat.Quic.Exceptions; using EonaCat.Quic.Exceptions;
using EonaCat.Quic.Helpers; using EonaCat.Quic.Helpers;
using EonaCat.Quic.Infrastructure;
using EonaCat.Quic.Infrastructure.Frames; using EonaCat.Quic.Infrastructure.Frames;
using EonaCat.Quic.Infrastructure.Packets;
using EonaCat.Quic.Infrastructure.Settings; using EonaCat.Quic.Infrastructure.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
namespace EonaCat.Quic.Streams namespace EonaCat.Quic.Streams;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -20,20 +19,12 @@ namespace EonaCat.Quic.Streams
/// </summary> /// </summary>
public class QuicStream public class QuicStream
{ {
private readonly SortedList<ulong, byte[]> _data = new SortedList<ulong, byte[]>();
private readonly QuicConnection _connection; private readonly QuicConnection _connection;
private ulong _maximumStreamData; private readonly SortedList<ulong, byte[]> _data = new();
private ulong _currentTransferRate; private ulong _currentTransferRate;
private ulong _maximumStreamData;
private ulong _sendOffset; private ulong _sendOffset;
public StreamState State { get; set; }
public StreamType Type { get; set; }
public StreamId StreamId { get; }
public StreamDataReceivedEvent OnStreamDataReceived { get; set; }
public byte[] Data => _data.SelectMany(v => v.Value).ToArray();
public QuicStream(QuicConnection connection, StreamId streamId) public QuicStream(QuicConnection connection, StreamId streamId)
{ {
StreamId = streamId; StreamId = streamId;
@ -46,6 +37,14 @@ namespace EonaCat.Quic.Streams
_connection = connection; _connection = connection;
} }
public StreamState State { get; set; }
public StreamType Type { get; set; }
public StreamId StreamId { get; }
public StreamDataReceivedEvent OnStreamDataReceived { get; set; }
public byte[] Data => _data.SelectMany(v => v.Value).ToArray();
public bool Send(byte[] data) public bool Send(byte[] data)
{ {
if (Type == StreamType.ServerUnidirectional) if (Type == StreamType.ServerUnidirectional)
@ -55,26 +54,26 @@ namespace EonaCat.Quic.Streams
_connection.IncrementRate(data.Length); _connection.IncrementRate(data.Length);
int numberOfPackets = (data.Length / QuicSettings.PMTU) + 1; var numberOfPackets = data.Length / QuicSettings.PMTU + 1;
int leftoverCarry = data.Length % QuicSettings.PMTU; var leftoverCarry = data.Length % QuicSettings.PMTU;
for (int i = 0; i < numberOfPackets; i++) for (var i = 0; i < numberOfPackets; i++)
{ {
bool eos = false; var eos = false;
int dataSize = QuicSettings.PMTU; var dataSize = QuicSettings.PMTU;
if (i == numberOfPackets - 1) if (i == numberOfPackets - 1)
{ {
eos = true; eos = true;
dataSize = leftoverCarry; dataSize = leftoverCarry;
} }
byte[] buffer = new byte[dataSize]; var buffer = new byte[dataSize];
Buffer.BlockCopy(data, (int)_sendOffset, buffer, 0, dataSize); Buffer.BlockCopy(data, (int)_sendOffset, buffer, 0, dataSize);
ShortHeaderPacket packet = _connection.PacketCreator.CreateDataPacket(this.StreamId.IntegerValue, buffer, _sendOffset, eos); var packet = _connection.PacketCreator.CreateDataPacket(StreamId.IntegerValue, buffer, _sendOffset, eos);
if (i == 0 && data.Length >= QuicSettings.MaxStreamData) if (i == 0 && data.Length >= QuicSettings.MaxStreamData)
{ {
packet.AttachFrame(new MaxStreamDataFrame(this.StreamId.IntegerValue, (ulong)(data.Length + 1))); packet.AttachFrame(new MaxStreamDataFrame(StreamId.IntegerValue, (ulong)(data.Length + 1)));
} }
if (_connection.MaximumReached()) if (_connection.MaximumReached())
@ -101,10 +100,7 @@ namespace EonaCat.Quic.Streams
throw new StreamException("Cannot receive data on unidirectional stream."); throw new StreamException("Cannot receive data on unidirectional stream.");
} }
while (!IsStreamFull() || State == StreamState.Receive) while (!IsStreamFull() || State == StreamState.Receive) _connection.ReceivePacket();
{
_connection.ReceivePacket();
}
return Data; return Data;
} }
@ -155,7 +151,7 @@ namespace EonaCat.Quic.Streams
return; return;
} }
byte[] data = frame.StreamData; var data = frame.StreamData;
if (frame.Offset != null) if (frame.Offset != null)
{ {
_data.Add(frame.Offset.Value, frame.StreamData); _data.Add(frame.Offset.Value, frame.StreamData);
@ -177,7 +173,8 @@ namespace EonaCat.Quic.Streams
// Terminate connection if maximum stream data is reached // Terminate connection if maximum stream data is reached
if (_currentTransferRate >= _maximumStreamData) if (_currentTransferRate >= _maximumStreamData)
{ {
ShortHeaderPacket errorPacket = _connection.PacketCreator.CreateConnectionClosePacket(Infrastructure.ErrorCode.FLOW_CONTROL_ERROR, frame.ActualType, ErrorConstants.MaxDataTransfer); var errorPacket = _connection.PacketCreator.CreateConnectionClosePacket(ErrorCode.FLOW_CONTROL_ERROR,
frame.ActualType, ErrorConstants.MaxDataTransfer);
_connection.SendData(errorPacket); _connection.SendData(errorPacket);
_connection.TerminateConnection(); _connection.TerminateConnection();
@ -214,4 +211,3 @@ namespace EonaCat.Quic.Streams
return true; return true;
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.Quic.Streams namespace EonaCat.Quic.Streams;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -11,4 +10,3 @@
DataRead, DataRead,
ResetReceived ResetReceived
} }
}

View File

@ -1,8 +1,7 @@
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
namespace EonaCat.Network namespace EonaCat.Network;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -15,9 +14,8 @@ namespace EonaCat.Network
public IPAddress Address => HasEndpoint ? ((IPEndPoint)EndPoint).Address : null; public IPAddress Address => HasEndpoint ? ((IPEndPoint)EndPoint).Address : null;
public int Port => HasEndpoint ? ((IPEndPoint)EndPoint).Port : 0; public int Port => HasEndpoint ? ((IPEndPoint)EndPoint).Port : 0;
public Socket Socket { get; set; } public Socket Socket { get; set; }
public bool IsIpv6 { get; set; } public bool IsIPv6 { get; set; }
public bool IsWebSocket { get; internal set; } public bool IsWebSocket { get; internal set; }
public string ClientId { get; internal set; } public string ClientId { get; internal set; }
public string ClientName { get; internal set; } public string ClientName { get; internal set; }
} }
}

View File

@ -1,6 +1,10 @@
using System; using System;
using System.IO;
using System.Net; using System.Net;
using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EonaCat.Network namespace EonaCat.Network
@ -9,55 +13,54 @@ namespace EonaCat.Network
{ {
private const int BUFFER_SIZE = 4096; private const int BUFFER_SIZE = 4096;
/// <summary> private Socket _socket;
/// OnConnect event private bool _isIPv6;
/// </summary>
public bool IsIPv6 => _isIPv6;
public event Action<RemoteInfo> OnConnect; public event Action<RemoteInfo> OnConnect;
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive; public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect; public event Action<RemoteInfo> OnDisconnect;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError; public event Action<Exception, string> OnError;
private Socket socket; public async Task ConnectAsync(string ipAddress, int port, bool useSsl = false, SslOptions sslOptions = null)
/// <summary>
/// Create TCP client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public Task ConnectAsync(string ipAddress, int port)
{ {
if (!IPAddress.TryParse(ipAddress, out IPAddress ip)) if (!IPAddress.TryParse(ipAddress, out var ip))
{ {
throw new Exception("EonaCat Network: Invalid ipAddress given"); throw new ArgumentException("Invalid ipAddress given");
} }
return CreateSocketTcpClientAsync(ip, port); await CreateSocketTcpClientAsync(ip, port, useSsl, sslOptions);
} }
private async Task CreateSocketTcpClientAsync(IPAddress ipAddress, int port) private async Task CreateSocketTcpClientAsync(IPAddress ipAddress, int port, bool useSsl, SslOptions sslOptions)
{ {
IsIp6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6; _isIPv6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
socket = new Socket(IsIp6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket = new Socket(_isIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try try
{ {
await socket.ConnectAsync(ipAddress, port).ConfigureAwait(false); await _socket.ConnectAsync(ipAddress, port).ConfigureAwait(false);
OnConnect?.Invoke(new RemoteInfo { IsTcp = true, IsIpv6 = IsIp6 }); OnConnect?.Invoke(new RemoteInfo { IsTcp = true, IsIPv6 = _isIPv6 });
if (useSsl)
{
var sslStream = new SslStream(new NetworkStream(_socket), false, ValidateRemoteCertificate, null);
try
{
await sslStream.AuthenticateAsClientAsync(ipAddress.ToString(), sslOptions.ClientCertificates, sslOptions.SslProtocol, sslOptions.CheckCertificateRevocation);
_ = StartReceivingSslAsync(sslStream);
}
catch (AuthenticationException ex)
{
OnError?.Invoke(ex, $"AuthenticationException: {ex.Message}");
Disconnect();
}
}
else
{
_ = StartReceivingAsync(); _ = StartReceivingAsync();
} }
}
catch (SocketException ex) catch (SocketException ex)
{ {
OnError?.Invoke(ex, $"SocketException: {ex.Message}"); OnError?.Invoke(ex, $"SocketException: {ex.Message}");
@ -65,26 +68,24 @@ namespace EonaCat.Network
} }
} }
public bool IsIp6 { get; set; }
private async Task StartReceivingAsync() private async Task StartReceivingAsync()
{ {
byte[] buffer = new byte[BUFFER_SIZE]; // Increased buffer size for better performance var buffer = new byte[BUFFER_SIZE];
while (socket.Connected) while (_socket.Connected)
{ {
try try
{ {
int received = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false); var received = await _socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false);
if (received > 0) if (received > 0)
{ {
byte[] data = new byte[received]; var data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, received); Buffer.BlockCopy(buffer, 0, data, 0, received);
OnReceive?.Invoke(new RemoteInfo OnReceive?.Invoke(new RemoteInfo
{ {
IsTcp = true, IsTcp = true,
Data = data, Data = data,
EndPoint = socket.RemoteEndPoint, EndPoint = _socket.RemoteEndPoint,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6 IsIPv6 = _isIPv6
}); });
} }
else else
@ -98,25 +99,73 @@ namespace EonaCat.Network
break; break;
} }
} }
OnDisconnect?.Invoke(new RemoteInfo { IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6 });
}
/// <summary> OnDisconnect?.Invoke(new RemoteInfo
/// Send data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public Task SendAsync(byte[] data)
{ {
return socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None); IsTcp = true,
EndPoint = _socket.RemoteEndPoint,
IsIPv6 = _isIPv6
});
}
private async Task StartReceivingSslAsync(SslStream sslStream)
{
var buffer = new byte[BUFFER_SIZE];
while (_socket.Connected)
{
try
{
var received = await sslStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
if (received > 0)
{
var data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, received);
OnReceive?.Invoke(new RemoteInfo
{
IsTcp = true,
Data = data,
EndPoint = _socket.RemoteEndPoint,
IsIPv6 = _isIPv6
});
}
else
{
break;
}
}
catch (IOException ex)
{
OnError?.Invoke(ex, $"IOException: {ex.Message}");
break;
}
catch (AuthenticationException ex)
{
OnError?.Invoke(ex, $"AuthenticationException: {ex.Message}");
break;
}
}
OnDisconnect?.Invoke(new RemoteInfo
{
IsTcp = true,
EndPoint = _socket.RemoteEndPoint,
IsIPv6 = _isIPv6
});
}
public async Task SendAsync(byte[] data)
{
await _socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false);
} }
/// <summary>
/// Disconnect
/// </summary>
public void Disconnect() public void Disconnect()
{ {
socket.Close(); _socket.Close();
}
private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return sslPolicyErrors == SslPolicyErrors.None;
} }
} }
} }

View File

@ -1,6 +1,10 @@
using System; using System;
using System.IO;
using System.Net; using System.Net;
using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -10,61 +14,33 @@ namespace EonaCat.Network
{ {
private const int BUFFER_SIZE = 4096; private const int BUFFER_SIZE = 4096;
/// <summary>
/// OnConnect event
/// </summary>
public event Action<RemoteInfo> OnConnect;
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnSend event
/// </summary>
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
private readonly TcpListener _listener; private readonly TcpListener _listener;
private CancellationTokenSource _cancellationTokenSource; private readonly X509Certificate2 _certificate;
private readonly SslOptions _sslOptions;
private Task _acceptTask; private Task _acceptTask;
private CancellationTokenSource _cancellationTokenSource;
/// <summary> public SocketTcpServer(IPAddress ipAddress, int port, X509Certificate2 certificate = null, SslOptions sslOptions = null)
/// Create TCP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public SocketTcpServer(IPAddress ipAddress, int port)
{ {
_listener = new TcpListener(ipAddress, port); _listener = new TcpListener(ipAddress, port);
_certificate = certificate;
_sslOptions = sslOptions;
} }
/// <summary> public SocketTcpServer(string ipAddress, int port, X509Certificate2 certificate = null, SslOptions sslOptions = null)
/// Create TCP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public SocketTcpServer(string ipAddress, int port)
{ {
IPAddress address = IPAddress.Parse(ipAddress); var address = IPAddress.Parse(ipAddress);
_listener = new TcpListener(address, port); _listener = new TcpListener(address, port);
_certificate = certificate;
_sslOptions = sslOptions;
} }
/// <summary> public event Action<RemoteInfo> OnConnect;
/// Start TCP server public event Action<RemoteInfo> OnReceive;
/// </summary> public event Action<RemoteInfo> OnSend;
/// <param name="cancellationToken"></param> public event Action<RemoteInfo> OnDisconnect;
/// <returns></returns> public event Action<Exception, string> OnError;
public Task StartAsync(CancellationToken cancellationToken = default) public Task StartAsync(CancellationToken cancellationToken = default)
{ {
_listener.Start(); _listener.Start();
@ -79,8 +55,8 @@ namespace EonaCat.Network
{ {
try try
{ {
var socket = await _listener.AcceptSocketAsync().ConfigureAwait(false); var tcpClient = await _listener.AcceptTcpClientAsync().ConfigureAwait(false);
_ = HandleConnectionAsync(socket, cancellationToken); _ = HandleConnectionAsync(tcpClient, cancellationToken);
} }
catch (SocketException ex) catch (SocketException ex)
{ {
@ -89,32 +65,51 @@ namespace EonaCat.Network
} }
} }
private async Task HandleConnectionAsync(Socket socket, CancellationToken cancellationToken) private async Task HandleConnectionAsync(TcpClient tcpClient, CancellationToken cancellationToken)
{
var remoteEndpoint = tcpClient.Client.RemoteEndPoint;
using (tcpClient)
{ {
OnConnect?.Invoke(new RemoteInfo OnConnect?.Invoke(new RemoteInfo
{ {
Socket = socket, Socket = tcpClient.Client,
IsTcp = true, IsTcp = true,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6, IsIPv6 = remoteEndpoint.AddressFamily == AddressFamily.InterNetworkV6,
EndPoint = socket.RemoteEndPoint EndPoint = remoteEndpoint
}); });
byte[] buffer = new byte[BUFFER_SIZE]; Stream stream = tcpClient.GetStream();
if (_certificate != null)
{
var sslStream = new SslStream(stream, false, ValidateRemoteCertificate, null);
try
{
await sslStream.AuthenticateAsServerAsync(_certificate, _sslOptions.ClientCertificateRequired, _sslOptions.SslProtocol, _sslOptions.CheckCertificateRevocation);
stream = sslStream;
}
catch (AuthenticationException ex)
{
OnError?.Invoke(ex, $"AuthenticationException: {ex.Message}");
return;
}
}
var buffer = new byte[BUFFER_SIZE];
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
try try
{ {
int received = await ReceiveAsync(socket, buffer, cancellationToken).ConfigureAwait(false); var received = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
if (received > 0) if (received > 0)
{ {
byte[] data = new byte[received]; var data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, received); Buffer.BlockCopy(buffer, 0, data, 0, received);
OnReceive?.Invoke(new RemoteInfo OnReceive?.Invoke(new RemoteInfo
{ {
Socket = socket, Socket = tcpClient.Client,
IsTcp = true, IsTcp = true,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6, IsIPv6 = remoteEndpoint.AddressFamily == AddressFamily.InterNetworkV6,
EndPoint = socket.RemoteEndPoint, EndPoint = remoteEndpoint,
Data = data Data = data
}); });
} }
@ -123,35 +118,23 @@ namespace EonaCat.Network
break; break;
} }
} }
catch (SocketException ex) catch (IOException ex)
{ {
OnError?.Invoke(ex, $"SocketException: {ex.Message}"); OnError?.Invoke(ex, $"IOException: {ex.Message}");
break; break;
} }
} }
}
OnDisconnect?.Invoke(new RemoteInfo OnDisconnect?.Invoke(new RemoteInfo
{ {
Socket = socket, Socket = tcpClient.Client,
IsTcp = true, IsTcp = true,
EndPoint = socket.RemoteEndPoint, EndPoint = remoteEndpoint,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6 IsIPv6 = remoteEndpoint.AddressFamily == AddressFamily.InterNetworkV6
}); });
} }
private Task<int> ReceiveAsync(Socket socket, byte[] buffer, CancellationToken cancellationToken)
{
return Task<int>.Factory.FromAsync(socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndReceive);
}
/// <summary>
/// Send data to socket
/// </summary>
/// <param name="socket"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendToAsync(Socket socket, byte[] data) public async Task SendToAsync(Socket socket, byte[] data)
{ {
await socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false); await socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false);
@ -160,15 +143,11 @@ namespace EonaCat.Network
Socket = socket, Socket = socket,
IsTcp = true, IsTcp = true,
EndPoint = socket.RemoteEndPoint, EndPoint = socket.RemoteEndPoint,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6, IsIPv6 = socket.AddressFamily == AddressFamily.InterNetworkV6,
Data = data Data = data
}); });
} }
/// <summary>
/// Stop TCP server
/// </summary>
/// <returns></returns>
public async Task StopAsync() public async Task StopAsync()
{ {
_cancellationTokenSource.Cancel(); _cancellationTokenSource.Cancel();
@ -179,8 +158,19 @@ namespace EonaCat.Network
{ {
await _acceptTask.ConfigureAwait(false); await _acceptTask.ConfigureAwait(false);
} }
catch (AggregateException) { } catch (AggregateException)
{
} }
} }
} }
private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (_sslOptions != null && _sslOptions.CertificateValidationCallback != null)
{
return _sslOptions.CertificateValidationCallback(sender, certificate, chain, sslPolicyErrors);
}
return sslPolicyErrors == SslPolicyErrors.None;
}
}
} }

View File

@ -0,0 +1,14 @@
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace EonaCat.Network;
public class SslOptions
{
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
public bool CheckCertificateRevocation { get; set; } = true;
public X509CertificateCollection ClientCertificates { get; set; }
public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; }
public bool ClientCertificateRequired { get; set; }
}

View File

@ -4,10 +4,27 @@ using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EonaCat.Network namespace EonaCat.Network;
{
public class SocketUdpClient public class SocketUdpClient
{ {
private UdpClient _udpClient;
/// <summary>
/// Create UDP client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="cancellationToken"></param>
public SocketUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default)
{
CreateUdpClient(ipAddress, port, cancellationToken);
}
public bool IsMulticastGroupEnabled { get; set; }
public bool IsIp6 { get; private set; }
/// <summary> /// <summary>
/// OnConnect event /// OnConnect event
/// </summary> /// </summary>
@ -33,19 +50,6 @@ namespace EonaCat.Network
/// </summary> /// </summary>
public event Action<Exception, string> OnError; public event Action<Exception, string> OnError;
private UdpClient _udpClient;
/// <summary>
/// Create UDP client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="cancellationToken"></param>
public SocketUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default)
{
CreateUdpClient(ipAddress, port, cancellationToken);
}
private void CreateUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default) private void CreateUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default)
{ {
IsIp6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6; IsIp6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
@ -67,14 +71,9 @@ namespace EonaCat.Network
_ = StartReceivingAsync(cancellationToken); _ = StartReceivingAsync(cancellationToken);
} }
public bool IsMulticastGroupEnabled { get; set; }
public bool IsIp6 { get; private set; }
private async Task StartReceivingAsync(CancellationToken cancellationToken) private async Task StartReceivingAsync(CancellationToken cancellationToken)
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{
try try
{ {
var result = await _udpClient.ReceiveAsync().ConfigureAwait(false); var result = await _udpClient.ReceiveAsync().ConfigureAwait(false);
@ -85,7 +84,7 @@ namespace EonaCat.Network
OnError?.Invoke(ex, $"SocketException: {ex.Message}"); OnError?.Invoke(ex, $"SocketException: {ex.Message}");
break; break;
} }
}
OnDisconnect?.Invoke(_udpClient.Client.RemoteEndPoint); OnDisconnect?.Invoke(_udpClient.Client.RemoteEndPoint);
} }
@ -112,4 +111,3 @@ namespace EonaCat.Network
_udpClient.Close(); _udpClient.Close();
} }
} }
}

View File

@ -6,37 +6,12 @@ using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EonaCat.Network namespace EonaCat.Network;
{
public class SocketUdpServer public class SocketUdpServer
{ {
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnSend event
/// </summary>
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
private UdpClient udpClient; private UdpClient udpClient;
/// <summary>
/// Determines if the UDP server is IpV6
/// </summary>
public bool IsIp6 { get; private set; }
/// <summary> /// <summary>
/// Create UDP server /// Create UDP server
/// </summary> /// </summary>
@ -55,7 +30,7 @@ namespace EonaCat.Network
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
public SocketUdpServer(string ipAddress, int port) public SocketUdpServer(string ipAddress, int port)
{ {
if (!IPAddress.TryParse(ipAddress, out IPAddress ip)) if (!IPAddress.TryParse(ipAddress, out var ip))
{ {
throw new Exception("EonaCat Network: Invalid ipAddress given"); throw new Exception("EonaCat Network: Invalid ipAddress given");
} }
@ -63,6 +38,31 @@ namespace EonaCat.Network
CreateUdpServer(ip, port); CreateUdpServer(ip, port);
} }
/// <summary>
/// Determines if the UDP server is IpV6
/// </summary>
public bool IsIp6 { get; private set; }
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnSend event
/// </summary>
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
private static void SetConnectionReset(Socket socket) private static void SetConnectionReset(Socket socket)
{ {
if (socket.ProtocolType != ProtocolType.Udp && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (socket.ProtocolType != ProtocolType.Udp && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -99,14 +99,13 @@ namespace EonaCat.Network
public async Task StartAsync(CancellationToken cancellationToken = default) public async Task StartAsync(CancellationToken cancellationToken = default)
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{
try try
{ {
var result = await udpClient.ReceiveAsync().ConfigureAwait(false); var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
OnReceive?.Invoke(new RemoteInfo() OnReceive?.Invoke(new RemoteInfo
{ {
EndPoint = result.RemoteEndPoint, EndPoint = result.RemoteEndPoint,
IsIpv6 = result.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, IsIPv6 = result.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6,
Data = result.Buffer Data = result.Buffer
}); });
} }
@ -115,7 +114,6 @@ namespace EonaCat.Network
OnError?.Invoke(ex, $"SocketException: {ex.Message}"); OnError?.Invoke(ex, $"SocketException: {ex.Message}");
} }
} }
}
/// <summary> /// <summary>
/// Send data to endPoint /// Send data to endPoint
@ -128,7 +126,8 @@ namespace EonaCat.Network
if (endPoint is IPEndPoint ipEndPoint) if (endPoint is IPEndPoint ipEndPoint)
{ {
await udpClient.SendAsync(data, data.Length, ipEndPoint).ConfigureAwait(false); await udpClient.SendAsync(data, data.Length, ipEndPoint).ConfigureAwait(false);
OnSend?.Invoke(new RemoteInfo() { EndPoint = endPoint, Data = data, IsIpv6 = endPoint.AddressFamily == AddressFamily.InterNetworkV6 }); OnSend?.Invoke(new RemoteInfo
{ EndPoint = endPoint, Data = data, IsIPv6 = endPoint.AddressFamily == AddressFamily.InterNetworkV6 });
} }
} }
@ -148,4 +147,3 @@ namespace EonaCat.Network
} }
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,17 +13,23 @@ namespace EonaCat.WebSockets.Buffer
{ {
if (buffer == null) if (buffer == null)
{ {
throw new ArgumentNullException(!string.IsNullOrEmpty(bufferParameterName) ? bufferParameterName : "buffer"); throw new ArgumentNullException(!string.IsNullOrEmpty(bufferParameterName)
? bufferParameterName
: "buffer");
} }
if (offset < 0 || offset > buffer.Length) if (offset < 0 || offset > buffer.Length)
{ {
throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(offsetParameterName) ? offsetParameterName : "offset"); throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(offsetParameterName)
? offsetParameterName
: "offset");
} }
if (count < 0 || count > (buffer.Length - offset)) if (count < 0 || count > buffer.Length - offset)
{ {
throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(countParameterName) ? countParameterName : "count"); throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(countParameterName)
? countParameterName
: "count");
} }
} }
@ -32,18 +37,23 @@ namespace EonaCat.WebSockets.Buffer
{ {
if (arraySegment.Array == null) if (arraySegment.Array == null)
{ {
throw new ArgumentNullException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Array"); throw new ArgumentNullException((!string.IsNullOrEmpty(arraySegmentParameterName)
? arraySegmentParameterName
: "arraySegment") + ".Array");
} }
if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length) if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length)
{ {
throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Offset"); throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName)
? arraySegmentParameterName
: "arraySegment") + ".Offset");
} }
if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset)) if (arraySegment.Count < 0 || arraySegment.Count > arraySegment.Array.Length - arraySegment.Offset)
{ {
throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Count"); throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName)
} ? arraySegmentParameterName
: "arraySegment") + ".Count");
} }
} }
} }

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,4 +13,3 @@ namespace EonaCat.WebSockets.Buffer
void ReturnBuffers(IEnumerable<ArraySegment<byte>> buffers); void ReturnBuffers(IEnumerable<ArraySegment<byte>> buffers);
void ReturnBuffers(params ArraySegment<byte>[] buffers); void ReturnBuffers(params ArraySegment<byte>[] buffers);
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,23 +13,25 @@ namespace EonaCat.WebSockets.Buffer
ref ArraySegment<byte> sessionBuffer, ref ArraySegment<byte> sessionBuffer,
ref int sessionBufferCount) ref int sessionBufferCount)
{ {
if (sessionBuffer.Count < (sessionBufferCount + receiveCount)) if (sessionBuffer.Count < sessionBufferCount + receiveCount)
{ {
ArraySegment<byte> autoExpandedBuffer = bufferManager.BorrowBuffer(); var autoExpandedBuffer = bufferManager.BorrowBuffer();
if (autoExpandedBuffer.Count < (sessionBufferCount + receiveCount) * 2) if (autoExpandedBuffer.Count < (sessionBufferCount + receiveCount) * 2)
{ {
bufferManager.ReturnBuffer(autoExpandedBuffer); bufferManager.ReturnBuffer(autoExpandedBuffer);
autoExpandedBuffer = new ArraySegment<byte>(new byte[(sessionBufferCount + receiveCount) * 2]); autoExpandedBuffer = new ArraySegment<byte>(new byte[(sessionBufferCount + receiveCount) * 2]);
} }
Array.Copy(sessionBuffer.Array, sessionBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, sessionBufferCount); Array.Copy(sessionBuffer.Array, sessionBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset,
sessionBufferCount);
var discardBuffer = sessionBuffer; var discardBuffer = sessionBuffer;
sessionBuffer = autoExpandedBuffer; sessionBuffer = autoExpandedBuffer;
bufferManager.ReturnBuffer(discardBuffer); bufferManager.ReturnBuffer(discardBuffer);
} }
Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset + sessionBufferCount, receiveCount); Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, sessionBuffer.Array,
sessionBuffer.Offset + sessionBufferCount, receiveCount);
sessionBufferCount = sessionBufferCount + receiveCount; sessionBufferCount = sessionBufferCount + receiveCount;
} }
@ -40,22 +41,25 @@ namespace EonaCat.WebSockets.Buffer
ref ArraySegment<byte> sessionBuffer, ref ArraySegment<byte> sessionBuffer,
ref int sessionBufferCount) ref int sessionBufferCount)
{ {
if ((sessionBufferCount - shiftStart) < shiftStart) if (sessionBufferCount - shiftStart < shiftStart)
{ {
Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, sessionBuffer.Array,
sessionBuffer.Offset, sessionBufferCount - shiftStart);
sessionBufferCount = sessionBufferCount - shiftStart; sessionBufferCount = sessionBufferCount - shiftStart;
} }
else else
{ {
ArraySegment<byte> copyBuffer = bufferManager.BorrowBuffer(); var copyBuffer = bufferManager.BorrowBuffer();
if (copyBuffer.Count < (sessionBufferCount - shiftStart)) if (copyBuffer.Count < sessionBufferCount - shiftStart)
{ {
bufferManager.ReturnBuffer(copyBuffer); bufferManager.ReturnBuffer(copyBuffer);
copyBuffer = new ArraySegment<byte>(new byte[sessionBufferCount - shiftStart]); copyBuffer = new ArraySegment<byte>(new byte[sessionBufferCount - shiftStart]);
} }
Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, copyBuffer.Array, copyBuffer.Offset, sessionBufferCount - shiftStart); Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, copyBuffer.Array, copyBuffer.Offset,
Array.Copy(copyBuffer.Array, copyBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); sessionBufferCount - shiftStart);
Array.Copy(copyBuffer.Array, copyBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset,
sessionBufferCount - shiftStart);
sessionBufferCount = sessionBufferCount - shiftStart; sessionBufferCount = sessionBufferCount - shiftStart;
bufferManager.ReturnBuffer(copyBuffer); bufferManager.ReturnBuffer(copyBuffer);
@ -68,20 +72,21 @@ namespace EonaCat.WebSockets.Buffer
ref int receiveBufferOffset, ref int receiveBufferOffset,
int receiveCount) int receiveCount)
{ {
if ((receiveBufferOffset + receiveCount) < receiveBuffer.Count) if (receiveBufferOffset + receiveCount < receiveBuffer.Count)
{ {
receiveBufferOffset = receiveBufferOffset + receiveCount; receiveBufferOffset = receiveBufferOffset + receiveCount;
} }
else else
{ {
ArraySegment<byte> autoExpandedBuffer = bufferManager.BorrowBuffer(); var autoExpandedBuffer = bufferManager.BorrowBuffer();
if (autoExpandedBuffer.Count < (receiveBufferOffset + receiveCount) * 2) if (autoExpandedBuffer.Count < (receiveBufferOffset + receiveCount) * 2)
{ {
bufferManager.ReturnBuffer(autoExpandedBuffer); bufferManager.ReturnBuffer(autoExpandedBuffer);
autoExpandedBuffer = new ArraySegment<byte>(new byte[(receiveBufferOffset + receiveCount) * 2]); autoExpandedBuffer = new ArraySegment<byte>(new byte[(receiveBufferOffset + receiveCount) * 2]);
} }
Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, receiveBufferOffset + receiveCount); Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset,
receiveBufferOffset + receiveCount);
receiveBufferOffset = receiveBufferOffset + receiveCount; receiveBufferOffset = receiveBufferOffset + receiveCount;
var discardBuffer = receiveBuffer; var discardBuffer = receiveBuffer;
@ -90,4 +95,3 @@ namespace EonaCat.WebSockets.Buffer
} }
} }
} }
}

View File

@ -3,8 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -28,71 +27,23 @@ namespace EonaCat.WebSockets.Buffer
private const int TrialsCount = 100; private const int TrialsCount = 100;
private static SegmentBufferManager _defaultBufferManager; private static SegmentBufferManager _defaultBufferManager;
private readonly int _segmentChunks;
private readonly int _chunkSize;
private readonly int _segmentSize;
private readonly bool _allowedToCreateMemory; private readonly bool _allowedToCreateMemory;
private readonly ConcurrentStack<ArraySegment<byte>> _buffers = new ConcurrentStack<ArraySegment<byte>>(); private readonly ConcurrentStack<ArraySegment<byte>> _buffers = new();
private readonly object _creatingNewSegmentLock = new();
private readonly List<byte[]> _segments; private readonly List<byte[]> _segments;
private readonly object _creatingNewSegmentLock = new object(); private readonly int _segmentSize;
public static SegmentBufferManager Default
{
get
{
// default to 1024 1kb buffers if people don't want to manage it on their own;
if (_defaultBufferManager == null)
{
_defaultBufferManager = new SegmentBufferManager(1024, 1024, 1);
}
return _defaultBufferManager;
}
}
public static void SetDefaultBufferManager(SegmentBufferManager manager)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
_defaultBufferManager = manager;
}
public int ChunkSize
{
get { return _chunkSize; }
}
public int SegmentsCount
{
get { return _segments.Count; }
}
public int SegmentChunksCount
{
get { return _segmentChunks; }
}
public int AvailableBuffers
{
get { return _buffers.Count; }
}
public int TotalBufferSize
{
get { return _segments.Count * _segmentSize; }
}
public SegmentBufferManager(int segmentChunks, int chunkSize) public SegmentBufferManager(int segmentChunks, int chunkSize)
: this(segmentChunks, chunkSize, 1) { } : this(segmentChunks, chunkSize, 1)
{
}
public SegmentBufferManager(int segmentChunks, int chunkSize, int initialSegments) public SegmentBufferManager(int segmentChunks, int chunkSize, int initialSegments)
: this(segmentChunks, chunkSize, initialSegments, true) { } : this(segmentChunks, chunkSize, initialSegments, true)
{
}
/// <summary> /// <summary>
/// Constructs a new <see cref="SegmentBufferManager"></see> object /// Constructs a new <see cref="SegmentBufferManager"></see> object
@ -118,47 +69,44 @@ namespace EonaCat.WebSockets.Buffer
throw new ArgumentException("initialSegments"); throw new ArgumentException("initialSegments");
} }
_segmentChunks = segmentChunks; SegmentChunksCount = segmentChunks;
_chunkSize = chunkSize; ChunkSize = chunkSize;
_segmentSize = _segmentChunks * _chunkSize; _segmentSize = SegmentChunksCount * ChunkSize;
_segments = new List<byte[]>(); _segments = new List<byte[]>();
_allowedToCreateMemory = true; _allowedToCreateMemory = true;
for (int i = 0; i < initialSegments; i++) for (var i = 0; i < initialSegments; i++) CreateNewSegment(true);
{
CreateNewSegment(true);
}
_allowedToCreateMemory = allowedToCreateMemory; _allowedToCreateMemory = allowedToCreateMemory;
} }
private void CreateNewSegment(bool forceCreation) public static SegmentBufferManager Default
{ {
if (!_allowedToCreateMemory) get
{ {
throw new UnableToCreateMemoryException(); // default to 1024 1kb buffers if people don't want to manage it on their own;
if (_defaultBufferManager == null)
{
_defaultBufferManager = new SegmentBufferManager(1024, 1024, 1);
} }
lock (_creatingNewSegmentLock) return _defaultBufferManager;
{ }
if (!forceCreation && _buffers.Count > _segmentChunks / 2)
{
return;
} }
var bytes = new byte[_segmentSize]; public int ChunkSize { get; }
_segments.Add(bytes);
for (int i = 0; i < _segmentChunks; i++) public int SegmentsCount => _segments.Count;
{
var chunk = new ArraySegment<byte>(bytes, i * _chunkSize, _chunkSize); public int SegmentChunksCount { get; }
_buffers.Push(chunk);
} public int AvailableBuffers => _buffers.Count;
}
} public int TotalBufferSize => _segments.Count * _segmentSize;
public ArraySegment<byte> BorrowBuffer() public ArraySegment<byte> BorrowBuffer()
{ {
int trial = 0; var trial = 0;
while (trial < TrialsCount) while (trial < TrialsCount)
{ {
ArraySegment<byte> result; ArraySegment<byte> result;
@ -170,6 +118,7 @@ namespace EonaCat.WebSockets.Buffer
CreateNewSegment(false); CreateNewSegment(false);
trial++; trial++;
} }
throw new UnableToAllocateBufferException(); throw new UnableToAllocateBufferException();
} }
@ -194,6 +143,7 @@ namespace EonaCat.WebSockets.Buffer
result[totalReceived] = piece; result[totalReceived] = piece;
++totalReceived; ++totalReceived;
} }
if (totalReceived == count) if (totalReceived == count)
{ {
return result; return result;
@ -202,6 +152,7 @@ namespace EonaCat.WebSockets.Buffer
CreateNewSegment(false); CreateNewSegment(false);
trial++; trial++;
} }
throw new UnableToAllocateBufferException(); throw new UnableToAllocateBufferException();
} }
catch catch
@ -255,6 +206,40 @@ namespace EonaCat.WebSockets.Buffer
} }
} }
public static void SetDefaultBufferManager(SegmentBufferManager manager)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
_defaultBufferManager = manager;
}
private void CreateNewSegment(bool forceCreation)
{
if (!_allowedToCreateMemory)
{
throw new UnableToCreateMemoryException();
}
lock (_creatingNewSegmentLock)
{
if (!forceCreation && _buffers.Count > SegmentChunksCount / 2)
{
return;
}
var bytes = new byte[_segmentSize];
_segments.Add(bytes);
for (var i = 0; i < SegmentChunksCount; i++)
{
var chunk = new ArraySegment<byte>(bytes, i * ChunkSize, ChunkSize);
_buffers.Push(chunk);
}
}
}
private bool ValidateBuffer(ArraySegment<byte> buffer) private bool ValidateBuffer(ArraySegment<byte> buffer)
{ {
if (buffer.Array == null || buffer.Count == 0 || buffer.Array.Length < buffer.Offset + buffer.Count) if (buffer.Array == null || buffer.Count == 0 || buffer.Array.Length < buffer.Offset + buffer.Count)
@ -262,7 +247,7 @@ namespace EonaCat.WebSockets.Buffer
return false; return false;
} }
if (buffer.Count != _chunkSize) if (buffer.Count != ChunkSize)
{ {
return false; return false;
} }
@ -270,4 +255,3 @@ namespace EonaCat.WebSockets.Buffer
return true; return true;
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -13,4 +12,3 @@ namespace EonaCat.WebSockets.Buffer
{ {
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Buffer namespace EonaCat.WebSockets.Buffer;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -13,4 +12,3 @@ namespace EonaCat.WebSockets.Buffer
{ {
} }
} }
}

View File

@ -8,8 +8,7 @@ using EonaCat.WebSockets.Buffer;
using EonaCat.WebSockets.Extensions; using EonaCat.WebSockets.Extensions;
using EonaCat.WebSockets.SubProtocols; using EonaCat.WebSockets.SubProtocols;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -38,15 +37,15 @@ namespace EonaCat.WebSockets
KeepAliveTimeout = TimeSpan.FromSeconds(5); KeepAliveTimeout = TimeSpan.FromSeconds(5);
ReasonableFragmentSize = 4096; ReasonableFragmentSize = 4096;
EnabledExtensions = new Dictionary<string, IWebSocketExtensionNegotiator>() EnabledExtensions = new Dictionary<string, IWebSocketExtensionNegotiator>
{ {
{ PerMessageCompressionExtension.RegisteredToken, new PerMessageCompressionExtensionNegotiator() }, { PerMessageCompressionExtension.RegisteredToken, new PerMessageCompressionExtensionNegotiator() }
}; };
EnabledSubProtocols = new Dictionary<string, IWebSocketSubProtocolNegotiator>(); EnabledSubProtocols = new Dictionary<string, IWebSocketSubProtocolNegotiator>();
OfferedExtensions = new List<WebSocketExtensionOfferDescription>() OfferedExtensions = new List<WebSocketExtensionOfferDescription>
{ {
new WebSocketExtensionOfferDescription(PerMessageCompressionExtension.RegisteredToken), new(PerMessageCompressionExtension.RegisteredToken)
}; };
RequestedSubProtocols = new List<WebSocketSubProtocolRequestDescription>(); RequestedSubProtocols = new List<WebSocketSubProtocolRequestDescription>();
} }
@ -78,4 +77,3 @@ namespace EonaCat.WebSockets
public List<WebSocketExtensionOfferDescription> OfferedExtensions { get; set; } public List<WebSocketExtensionOfferDescription> OfferedExtensions { get; set; }
public List<WebSocketSubProtocolRequestDescription> RequestedSubProtocols { get; set; } public List<WebSocketSubProtocolRequestDescription> RequestedSubProtocols { get; set; }
} }
}

View File

@ -1,7 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -16,4 +15,3 @@ namespace EonaCat.WebSockets
Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count); Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count);
Task OnServerFragmentationStreamClosed(AsyncWebSocketClient client, byte[] data, int offset, int count); Task OnServerFragmentationStreamClosed(AsyncWebSocketClient client, byte[] data, int offset, int count);
} }
}

View File

@ -1,21 +1,20 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
internal class InternalAsyncWebSocketClientMessageDispatcherImplementation : IAsyncWebSocketClientMessageDispatcher internal class InternalAsyncWebSocketClientMessageDispatcherImplementation : IAsyncWebSocketClientMessageDispatcher
{ {
private Func<AsyncWebSocketClient, string, Task> _onServerTextReceived; private readonly Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerBinaryReceived;
private Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerBinaryReceived; private readonly Func<AsyncWebSocketClient, Task> _onServerConnected;
private Func<AsyncWebSocketClient, Task> _onServerConnected; private readonly Func<AsyncWebSocketClient, Task> _onServerDisconnected;
private Func<AsyncWebSocketClient, Task> _onServerDisconnected; private readonly Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamClosed;
private readonly Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamContinued;
private Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamOpened; private readonly Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamOpened;
private Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamContinued; private readonly Func<AsyncWebSocketClient, string, Task> _onServerTextReceived;
private Func<AsyncWebSocketClient, byte[], int, int, Task> _onServerFragmentationStreamClosed;
public InternalAsyncWebSocketClientMessageDispatcherImplementation() public InternalAsyncWebSocketClientMessageDispatcherImplementation()
{ {
@ -94,7 +93,8 @@ namespace EonaCat.WebSockets
} }
} }
public async Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count) public async Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset,
int count)
{ {
if (_onServerFragmentationStreamContinued != null) if (_onServerFragmentationStreamContinued != null)
{ {
@ -110,4 +110,3 @@ namespace EonaCat.WebSockets
} }
} }
} }
}

View File

@ -4,19 +4,17 @@ using System.Linq;
using System.Net; using System.Net;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using EonaCat.WebSockets.Buffer;
using EonaCat.Logger.Extensions; using EonaCat.Logger.Extensions;
using EonaCat.Logger.Managers;
using EonaCat.Network; using EonaCat.Network;
using EonaCat.WebSockets.Buffer;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
internal sealed class WebSocketClientHandshaker internal sealed class WebSocketClientHandshaker
{ {
private static readonly char[] _headerLineSplitter = new char[] { '\r', '\n' }; private static readonly char[] _headerLineSplitter = { '\r', '\n' };
internal static byte[] CreateOpenningHandshakeRequest(AsyncWebSocketClient client, out string secWebSocketKey) internal static byte[] CreateOpenningHandshakeRequest(AsyncWebSocketClient client, out string secWebSocketKey)
{ {
@ -37,11 +35,13 @@ namespace EonaCat.WebSockets
// The request MUST contain an |Upgrade| header field whose value // The request MUST contain an |Upgrade| header field whose value
// MUST include the "websocket" keyword. // MUST include the "websocket" keyword.
sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Upgrade, Consts.WebSocketUpgradeToken); sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Upgrade,
Consts.WebSocketUpgradeToken);
// The request MUST contain a |Connection| header field whose value // The request MUST contain a |Connection| header field whose value
// MUST include the "Upgrade" token. // MUST include the "Upgrade" token.
sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Connection, Consts.WebSocketConnectionToken); sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Connection,
Consts.WebSocketConnectionToken);
// The request MUST include a header field with the name // The request MUST include a header field with the name
// |Sec-WebSocket-Key|. The value of this header field MUST be a // |Sec-WebSocket-Key|. The value of this header field MUST be a
@ -53,7 +53,8 @@ namespace EonaCat.WebSockets
// The request MUST include a header field with the name // The request MUST include a header field with the name
// |Sec-WebSocket-Version|. The value of this header field MUST be 13. // |Sec-WebSocket-Version|. The value of this header field MUST be 13.
sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketVersion, Consts.WebSocketVersion); sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketVersion,
Consts.WebSocketVersion);
// The request MAY include a header field with the name // The request MAY include a header field with the name
// |Sec-WebSocket-Extensions|. If present, this value indicates // |Sec-WebSocket-Extensions|. If present, this value indicates
@ -63,7 +64,8 @@ namespace EonaCat.WebSockets
{ {
foreach (var extension in client.OfferedExtensions) foreach (var extension in client.OfferedExtensions)
{ {
sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketExtensions, extension.ExtensionNegotiationOffer); sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketExtensions,
extension.ExtensionNegotiationOffer);
} }
} }
@ -80,7 +82,8 @@ namespace EonaCat.WebSockets
{ {
foreach (var description in client.RequestedSubProtocols) foreach (var description in client.RequestedSubProtocols)
{ {
sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketProtocol, description.RequestedSubProtocol); sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketProtocol,
description.RequestedSubProtocol);
} }
} }
@ -116,7 +119,8 @@ namespace EonaCat.WebSockets
return Encoding.UTF8.GetBytes(request); return Encoding.UTF8.GetBytes(request);
} }
internal static bool VerifyOpenningHandshakeResponse(AsyncWebSocketClient client, byte[] buffer, int offset, int count, string secWebSocketKey) internal static bool VerifyOpenningHandshakeResponse(AsyncWebSocketClient client, byte[] buffer, int offset,
int count, string secWebSocketKey)
{ {
BufferValidator.ValidateBuffer(buffer, offset, count, "buffer"); BufferValidator.ValidateBuffer(buffer, offset, count, "buffer");
if (string.IsNullOrEmpty(secWebSocketKey)) if (string.IsNullOrEmpty(secWebSocketKey))
@ -141,8 +145,8 @@ namespace EonaCat.WebSockets
ParseOpenningHandshakeResponseHeaders(response, out headers, out extensions, out protocols); ParseOpenningHandshakeResponseHeaders(response, out headers, out extensions, out protocols);
if (headers == null) if (headers == null)
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to invalid headers.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to invalid headers.");
} }
// If the status code received from the server is not 101, the // If the status code received from the server is not 101, the
@ -152,14 +156,14 @@ namespace EonaCat.WebSockets
// using a 3xx status code (but clients are not required to follow them), etc. // using a 3xx status code (but clients are not required to follow them), etc.
if (!headers.ContainsKey(Consts.HttpStatusCodeName)) if (!headers.ContainsKey(Consts.HttpStatusCodeName))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to lack of status code.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to lack of status code.");
} }
if (!headers.ContainsKey(Consts.HttpStatusCodeDescription)) if (!headers.ContainsKey(Consts.HttpStatusCodeDescription))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to lack of status description.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to lack of status description.");
} }
if (headers[Consts.HttpStatusCodeName] == ((int)HttpStatusCode.BadRequest).ToString()) if (headers[Consts.HttpStatusCodeName] == ((int)HttpStatusCode.BadRequest).ToString())
@ -182,11 +186,12 @@ namespace EonaCat.WebSockets
// _Fail the WebSocket Connection_. // _Fail the WebSocket Connection_.
if (!headers.ContainsKey(HttpKnownHeaderNames.Connection)) if (!headers.ContainsKey(HttpKnownHeaderNames.Connection))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to lack of connection header item.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to lack of connection header item.");
} }
if (headers[HttpKnownHeaderNames.Connection].ToLowerInvariant() != Consts.WebSocketConnectionToken.ToLowerInvariant()) if (headers[HttpKnownHeaderNames.Connection].ToLowerInvariant() !=
Consts.WebSocketConnectionToken.ToLowerInvariant())
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(string.Format(
"Handshake with remote [{0}] failed due to invalid connection header item value [{1}].", "Handshake with remote [{0}] failed due to invalid connection header item value [{1}].",
@ -199,11 +204,12 @@ namespace EonaCat.WebSockets
// MUST _Fail the WebSocket Connection_. // MUST _Fail the WebSocket Connection_.
if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade)) if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to lack of upgrade header item.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to lack of upgrade header item.");
} }
if (headers[HttpKnownHeaderNames.Upgrade].ToLowerInvariant() != Consts.WebSocketUpgradeToken.ToLowerInvariant()) if (headers[HttpKnownHeaderNames.Upgrade].ToLowerInvariant() !=
Consts.WebSocketUpgradeToken.ToLowerInvariant())
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(string.Format(
"Handshake with remote [{0}] failed due to invalid upgrade header item value [{1}].", "Handshake with remote [{0}] failed due to invalid upgrade header item value [{1}].",
@ -218,11 +224,11 @@ namespace EonaCat.WebSockets
// trailing whitespace, the client MUST _Fail the WebSocket Connection_. // trailing whitespace, the client MUST _Fail the WebSocket Connection_.
if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketAccept)) if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketAccept))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Accept header item.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to lack of Sec-WebSocket-Accept header item.");
} }
string challenge = GetSecWebSocketAcceptString(secWebSocketKey); var challenge = GetSecWebSocketAcceptString(secWebSocketKey);
if (!headers[HttpKnownHeaderNames.SecWebSocketAccept].Equals(challenge, StringComparison.OrdinalIgnoreCase)) if (!headers[HttpKnownHeaderNames.SecWebSocketAccept].Equals(challenge, StringComparison.OrdinalIgnoreCase))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(string.Format(
@ -238,13 +244,13 @@ namespace EonaCat.WebSockets
if (extensions != null) if (extensions != null)
{ {
foreach (var extension in extensions) foreach (var extension in extensions)
{
// The empty string is not the same as the null value for these // The empty string is not the same as the null value for these
// purposes and is not a legal value for this field. // purposes and is not a legal value for this field.
{
if (string.IsNullOrWhiteSpace(extension)) if (string.IsNullOrWhiteSpace(extension))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to empty extension.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to empty extension.");
} }
} }
@ -260,24 +266,24 @@ namespace EonaCat.WebSockets
{ {
if (!protocols.Any()) if (!protocols.Any())
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to empty sub-protocol.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to empty sub-protocol.");
} }
if (protocols.Count > 1) if (protocols.Count > 1)
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to suggest to use multiple sub-protocols.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to suggest to use multiple sub-protocols.");
} }
foreach (var protocol in protocols) foreach (var protocol in protocols)
{
// The empty string is not the same as the null value for these // The empty string is not the same as the null value for these
// purposes and is not a legal value for this field. // purposes and is not a legal value for this field.
{
if (string.IsNullOrWhiteSpace(protocol)) if (string.IsNullOrWhiteSpace(protocol))
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to empty sub-protocol.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to empty sub-protocol.");
} }
} }
@ -286,14 +292,14 @@ namespace EonaCat.WebSockets
if (!suggestedProtocols.Any()) if (!suggestedProtocols.Any())
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to invalid sub-protocol.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to invalid sub-protocol.");
} }
if (suggestedProtocols.Count() > 1) if (suggestedProtocols.Count() > 1)
{ {
throw new WebSocketHandshakeException(string.Format( throw new WebSocketHandshakeException(
"Handshake with remote [{0}] failed due to suggest to use multiple sub-protocols.", client.RemoteEndPoint)); $"Handshake with remote [{client.RemoteEndPoint}] failed due to suggest to use multiple sub-protocols.");
} }
// The value chosen MUST be derived // The value chosen MUST be derived
@ -334,9 +340,9 @@ namespace EonaCat.WebSockets
var lines = response.Split(_headerLineSplitter).Where(l => l.Length > 0); var lines = response.Split(_headerLineSplitter).Where(l => l.Length > 0);
foreach (var line in lines) foreach (var line in lines)
{
// HTTP/1.1 101 Switching Protocols // HTTP/1.1 101 Switching Protocols
// HTTP/1.1 400 Bad Request // HTTP/1.1 400 Bad Request
{
if (line.StartsWith(@"HTTP/")) if (line.StartsWith(@"HTTP/"))
{ {
var segements = line.Split(' '); var segements = line.Split(' ');
@ -401,14 +407,13 @@ namespace EonaCat.WebSockets
{ {
string retVal; string retVal;
using (SHA1 sha1 = SHA1.Create()) using (var sha1 = SHA1.Create())
{ {
string acceptString = string.Concat(secWebSocketKey, Consts.SecWebSocketKeyGuid); var acceptString = string.Concat(secWebSocketKey, Consts.SecWebSocketKeyGuid);
byte[] toHash = Encoding.UTF8.GetBytes(acceptString); var toHash = Encoding.UTF8.GetBytes(acceptString);
retVal = Convert.ToBase64String(sha1.ComputeHash(toHash)); retVal = Convert.ToBase64String(sha1.ComputeHash(toHash));
} }
return retVal; return retVal;
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,4 +17,3 @@ namespace EonaCat.WebSockets
{ {
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,4 +17,3 @@ namespace EonaCat.WebSockets
{ {
} }
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,4 +17,3 @@
byte[] ProcessIncomingMessagePayload(byte[] payload, int offset, int count); byte[] ProcessIncomingMessagePayload(byte[] payload, int offset, int count);
byte[] ProcessOutgoingMessagePayload(byte[] payload, int offset, int count); byte[] ProcessOutgoingMessagePayload(byte[] payload, int offset, int count);
} }
}

View File

@ -1,5 +1,4 @@
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -8,4 +7,3 @@
bool NegotiateAsServer(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension); bool NegotiateAsServer(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension);
bool NegotiateAsClient(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension); bool NegotiateAsClient(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension);
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -15,20 +14,14 @@ namespace EonaCat.WebSockets.Extensions
throw new ArgumentNullException("valueValidator"); throw new ArgumentNullException("valueValidator");
} }
this.ValueValidator = valueValidator; ValueValidator = valueValidator;
this.DefaultValue = defaultValue; DefaultValue = defaultValue;
} }
public override ExtensionParameterType ParameterType public override ExtensionParameterType ParameterType =>
{ ExtensionParameterType.Single | ExtensionParameterType.Valuable;
get
{
return ExtensionParameterType.Single | ExtensionParameterType.Valuable;
}
}
public Func<string, bool> ValueValidator { get; private set; } public Func<string, bool> ValueValidator { get; private set; }
public T DefaultValue { get; private set; } public T DefaultValue { get; private set; }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,15 +13,14 @@ namespace EonaCat.WebSockets.Extensions
throw new ArgumentNullException("name"); throw new ArgumentNullException("name");
} }
this.Name = name; Name = name;
} }
public string Name { get; private set; } public string Name { get; }
public abstract ExtensionParameterType ParameterType { get; } public abstract ExtensionParameterType ParameterType { get; }
public override string ToString() public override string ToString()
{ {
return string.Format("{0}", this.Name); return $"{Name}";
}
} }
} }

View File

@ -1,5 +1,4 @@
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,12 +9,5 @@
{ {
} }
public override ExtensionParameterType ParameterType public override ExtensionParameterType ParameterType => ExtensionParameterType.Single;
{
get
{
return ExtensionParameterType.Single;
}
}
}
} }

View File

@ -1,29 +1,21 @@
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class AgreedValuableParameter<T> : AgreedExtensionParameter public class AgreedValuableParameter<T> : AgreedExtensionParameter
{ {
public AgreedValuableParameter(string name, T @value) public AgreedValuableParameter(string name, T value)
: base(name) : base(name)
{ {
this.Value = @value; Value = value;
} }
public override ExtensionParameterType ParameterType public override ExtensionParameterType ParameterType => ExtensionParameterType.Valuable;
{
get
{
return ExtensionParameterType.Valuable;
}
}
public T Value { get; private set; } public T Value { get; }
public override string ToString() public override string ToString()
{ {
return string.Format("{0}={1}", this.Name, this.Value); return $"{Name}={Value}";
}
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,15 +13,14 @@ namespace EonaCat.WebSockets.Extensions
throw new ArgumentNullException("name"); throw new ArgumentNullException("name");
} }
this.Name = name; Name = name;
} }
public string Name { get; private set; } public string Name { get; }
public abstract ExtensionParameterType ParameterType { get; } public abstract ExtensionParameterType ParameterType { get; }
public override string ToString() public override string ToString()
{ {
return string.Format("{0}", this.Name); return $"{Name}";
}
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -9,6 +8,5 @@ namespace EonaCat.WebSockets.Extensions
public enum ExtensionParameterType : byte public enum ExtensionParameterType : byte
{ {
Single = 0x1, Single = 0x1,
Valuable = 0x2, Valuable = 0x2
}
} }

View File

@ -1,5 +1,4 @@
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -10,12 +9,5 @@
{ {
} }
public override ExtensionParameterType ParameterType public override ExtensionParameterType ParameterType => ExtensionParameterType.Single;
{
get
{
return ExtensionParameterType.Single;
}
}
}
} }

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -15,17 +14,10 @@ namespace EonaCat.WebSockets.Extensions
throw new ArgumentNullException("valueValidator"); throw new ArgumentNullException("valueValidator");
} }
this.ValueValidator = valueValidator; ValueValidator = valueValidator;
} }
public override ExtensionParameterType ParameterType public override ExtensionParameterType ParameterType => ExtensionParameterType.Valuable;
{
get
{
return ExtensionParameterType.Valuable;
}
}
public Func<string, bool> ValueValidator { get; private set; } public Func<string, bool> ValueValidator { get; private set; }
} }
}

View File

@ -4,8 +4,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using EonaCat.WebSockets.Buffer; using EonaCat.WebSockets.Buffer;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -33,7 +32,7 @@ namespace EonaCat.WebSockets.Extensions
{ {
using (var memory = new MemoryStream()) using (var memory = new MemoryStream())
{ {
using (var deflate = new DeflateStream(memory, CompressionMode.Compress, leaveOpen: true)) using (var deflate = new DeflateStream(memory, CompressionMode.Compress, true))
{ {
deflate.Write(raw, offset, count); deflate.Write(raw, offset, count);
} }
@ -55,10 +54,10 @@ namespace EonaCat.WebSockets.Extensions
try try
{ {
using (var input = new MemoryStream(raw, offset, count)) using (var input = new MemoryStream(raw, offset, count))
using (var deflate = new DeflateStream(input, CompressionMode.Decompress, leaveOpen: true)) using (var deflate = new DeflateStream(input, CompressionMode.Decompress, true))
using (var memory = new MemoryStream()) using (var memory = new MemoryStream())
{ {
int readCount = 0; var readCount = 0;
do do
{ {
readCount = deflate.Read(buffer.Array, buffer.Offset, buffer.Count); readCount = deflate.Read(buffer.Array, buffer.Offset, buffer.Count);
@ -66,8 +65,7 @@ namespace EonaCat.WebSockets.Extensions
{ {
memory.Write(buffer.Array, buffer.Offset, readCount); memory.Write(buffer.Array, buffer.Offset, readCount);
} }
} } while (readCount > 0);
while (readCount > 0);
return memory.ToArray(); return memory.ToArray();
} }
@ -78,4 +76,3 @@ namespace EonaCat.WebSockets.Extensions
} }
} }
} }
}

View File

@ -3,8 +3,7 @@ using System.Linq;
using System.Text; using System.Text;
using EonaCat.WebSockets.Buffer; using EonaCat.WebSockets.Buffer;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -16,9 +15,9 @@ namespace EonaCat.WebSockets.Extensions
// to use any advertised extensions and MUST NOT use them unless the // to use any advertised extensions and MUST NOT use them unless the
// server indicates that it wishes to use the extension. // server indicates that it wishes to use the extension.
public static readonly string RegisteredToken = @"permessage-deflate"; public static readonly string RegisteredToken = @"permessage-deflate";
private readonly SortedList<int, AgreedExtensionParameter> _agreedParameters;
private readonly DeflateCompression _deflater; private readonly DeflateCompression _deflater;
private SortedList<int, AgreedExtensionParameter> _agreedParameters;
public PerMessageCompressionExtension() public PerMessageCompressionExtension()
{ {
@ -32,27 +31,27 @@ namespace EonaCat.WebSockets.Extensions
_agreedParameters = agreedParameters; _agreedParameters = agreedParameters;
} }
public string Name { get { return RegisteredToken; } } public string Name => RegisteredToken;
// PMCEs use the RSV1 bit of the WebSocket frame header to indicate whether a // PMCEs use the RSV1 bit of the WebSocket frame header to indicate whether a
// message is compressed or not so that an endpoint can choose not to // message is compressed or not so that an endpoint can choose not to
// compress messages with incompressible contents. // compress messages with incompressible contents.
public bool Rsv1BitOccupied { get { return true; } } public bool Rsv1BitOccupied => true;
public bool Rsv2BitOccupied { get { return false; } } public bool Rsv2BitOccupied => false;
public bool Rsv3BitOccupied { get { return false; } } public bool Rsv3BitOccupied => false;
public string GetAgreedOffer() public string GetAgreedOffer()
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append(this.Name); sb.Append(Name);
if (_agreedParameters != null && _agreedParameters.Any()) if (_agreedParameters != null && _agreedParameters.Any())
{ {
foreach (var parameter in _agreedParameters.Values) foreach (var parameter in _agreedParameters.Values)
{ {
sb.Append("; "); sb.Append("; ");
sb.Append(parameter.ToString()); sb.Append(parameter);
} }
} }
@ -94,4 +93,3 @@ namespace EonaCat.WebSockets.Extensions
return _deflater.Compress(payload, offset, count); return _deflater.Compress(payload, offset, count);
} }
} }
}

View File

@ -1,26 +1,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public sealed class PerMessageCompressionExtensionNegotiator : IWebSocketExtensionNegotiator public sealed class PerMessageCompressionExtensionNegotiator : IWebSocketExtensionNegotiator
{ {
private static readonly char[] TrimableChars = new char[] { ' ', ';', '\r', '\n' }; private static readonly char[] TrimableChars = { ' ', ';', '\r', '\n' };
public bool NegotiateAsServer(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension) public bool NegotiateAsServer(string offer, out string invalidParameter,
out IWebSocketExtension negotiatedExtension)
{ {
return Negotiate(offer, AgreeAsServer, out invalidParameter, out negotiatedExtension); return Negotiate(offer, AgreeAsServer, out invalidParameter, out negotiatedExtension);
} }
public bool NegotiateAsClient(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension) public bool NegotiateAsClient(string offer, out string invalidParameter,
out IWebSocketExtension negotiatedExtension)
{ {
return Negotiate(offer, AgreeAsClient, out invalidParameter, out negotiatedExtension); return Negotiate(offer, AgreeAsClient, out invalidParameter, out negotiatedExtension);
} }
private bool Negotiate(string offer, Func<AgreedExtensionParameter, bool> agree, out string invalidParameter, out IWebSocketExtension negotiatedExtension) private bool Negotiate(string offer, Func<AgreedExtensionParameter, bool> agree, out string invalidParameter,
out IWebSocketExtension negotiatedExtension)
{ {
invalidParameter = null; invalidParameter = null;
negotiatedExtension = null; negotiatedExtension = null;
@ -31,7 +33,8 @@ namespace EonaCat.WebSockets.Extensions
return false; return false;
} }
var segements = offer.Replace('\r', ' ').Replace('\n', ' ').TrimStart(TrimableChars).TrimEnd(TrimableChars).Split(';'); var segements = offer.Replace('\r', ' ').Replace('\n', ' ').TrimStart(TrimableChars).TrimEnd(TrimableChars)
.Split(';');
var offeredExtensionName = segements[0].TrimStart(TrimableChars).TrimEnd(TrimableChars); var offeredExtensionName = segements[0].TrimStart(TrimableChars).TrimEnd(TrimableChars);
if (string.IsNullOrEmpty(offeredExtensionName)) if (string.IsNullOrEmpty(offeredExtensionName))
@ -40,7 +43,8 @@ namespace EonaCat.WebSockets.Extensions
return false; return false;
} }
if (string.Compare(offeredExtensionName, PerMessageCompressionExtension.RegisteredToken, StringComparison.OrdinalIgnoreCase) != 0) if (string.Compare(offeredExtensionName, PerMessageCompressionExtension.RegisteredToken,
StringComparison.OrdinalIgnoreCase) != 0)
{ {
invalidParameter = offeredExtensionName; invalidParameter = offeredExtensionName;
return false; return false;
@ -55,7 +59,7 @@ namespace EonaCat.WebSockets.Extensions
// This set of elements MAY include multiple PMCEs with the same extension // This set of elements MAY include multiple PMCEs with the same extension
// name to offer the possibility to use the same algorithm with // name to offer the possibility to use the same algorithm with
// different configuration parameters. // different configuration parameters.
for (int i = 1; i < segements.Length; i++) for (var i = 1; i < segements.Length; i++)
{ {
var offeredParameter = segements[i]; var offeredParameter = segements[i];
if (!PerMessageCompressionExtensionParameters.ValidateParameter(offeredParameter)) if (!PerMessageCompressionExtensionParameters.ValidateParameter(offeredParameter))
@ -70,7 +74,7 @@ namespace EonaCat.WebSockets.Extensions
// that a server accepts PMCEs with higher preference if the server supports them. // that a server accepts PMCEs with higher preference if the server supports them.
var agreedSet = new SortedList<int, AgreedExtensionParameter>(); var agreedSet = new SortedList<int, AgreedExtensionParameter>();
for (int i = 1; i < segements.Length; i++) for (var i = 1; i < segements.Length; i++)
{ {
var offeredParameter = segements[i]; var offeredParameter = segements[i];
var agreeingParameter = PerMessageCompressionExtensionParameters.ResolveParameter(offeredParameter); var agreeingParameter = PerMessageCompressionExtensionParameters.ResolveParameter(offeredParameter);
@ -132,4 +136,3 @@ namespace EonaCat.WebSockets.Extensions
} }
} }
} }
}

View File

@ -2,8 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,22 +13,26 @@ namespace EonaCat.WebSockets.Extensions
public const string ServerMaxWindowBitsParameterName = @"server_max_window_bits"; public const string ServerMaxWindowBitsParameterName = @"server_max_window_bits";
public const string ClientMaxWindowBitsParameterName = @"client_max_window_bits"; public const string ClientMaxWindowBitsParameterName = @"client_max_window_bits";
public static readonly SingleParameter ServerNoContextTakeOver = new SingleParameter(ServerNoContextTakeOverParameterName); public static readonly SingleParameter ServerNoContextTakeOver = new(ServerNoContextTakeOverParameterName);
public static readonly SingleParameter ClientNoContextTakeOver = new SingleParameter(ClientNoContextTakeOverParameterName); public static readonly SingleParameter ClientNoContextTakeOver = new(ClientNoContextTakeOverParameterName);
public static readonly AbsentableValueParameter<byte> ServerMaxWindowBits = new AbsentableValueParameter<byte>(ServerMaxWindowBitsParameterName, ValidateServerMaxWindowBitsParameterValue, 15);
public static readonly AbsentableValueParameter<byte> ClientMaxWindowBits = new AbsentableValueParameter<byte>(ClientMaxWindowBitsParameterName, ValidateClientMaxWindowBitsParameterValue, 15);
public static readonly IEnumerable<ExtensionParameter> AllAvailableParameters = new List<ExtensionParameter>() public static readonly AbsentableValueParameter<byte> ServerMaxWindowBits =
new(ServerMaxWindowBitsParameterName, ValidateServerMaxWindowBitsParameterValue, 15);
public static readonly AbsentableValueParameter<byte> ClientMaxWindowBits =
new(ClientMaxWindowBitsParameterName, ValidateClientMaxWindowBitsParameterValue, 15);
public static readonly IEnumerable<ExtensionParameter> AllAvailableParameters = new List<ExtensionParameter>
{ {
ServerNoContextTakeOver, ServerNoContextTakeOver,
ClientNoContextTakeOver, ClientNoContextTakeOver,
ServerMaxWindowBits, ServerMaxWindowBits,
ClientMaxWindowBits, ClientMaxWindowBits
}; };
public static readonly IEnumerable<string> AllAvailableParameterNames = AllAvailableParameters.Select(p => p.Name); public static readonly IEnumerable<string> AllAvailableParameterNames = AllAvailableParameters.Select(p => p.Name);
private static bool ValidateServerMaxWindowBitsParameterValue(string @value) private static bool ValidateServerMaxWindowBitsParameterValue(string value)
{ {
// A client MAY include the "server_max_window_bits" extension parameter // A client MAY include the "server_max_window_bits" extension parameter
// in an extension negotiation offer. This parameter has a decimal // in an extension negotiation offer. This parameter has a decimal
@ -38,13 +41,13 @@ namespace EonaCat.WebSockets.Extensions
// MUST conform to the ABNF below. // MUST conform to the ABNF below.
// server-max-window-bits = 1*DIGIT // server-max-window-bits = 1*DIGIT
if (string.IsNullOrWhiteSpace(@value)) if (string.IsNullOrWhiteSpace(value))
{ {
return false; return false;
} }
int paramValue = -1; var paramValue = -1;
if (int.TryParse(@value, out paramValue)) if (int.TryParse(value, out paramValue))
{ {
if (8 <= paramValue && paramValue <= 15) if (8 <= paramValue && paramValue <= 15)
{ {
@ -55,7 +58,7 @@ namespace EonaCat.WebSockets.Extensions
return false; return false;
} }
private static bool ValidateClientMaxWindowBitsParameterValue(string @value) private static bool ValidateClientMaxWindowBitsParameterValue(string value)
{ {
// A client MAY include the "client_max_window_bits" extension parameter // A client MAY include the "client_max_window_bits" extension parameter
// in an extension negotiation offer. This parameter has no value or a // in an extension negotiation offer. This parameter has no value or a
@ -65,13 +68,13 @@ namespace EonaCat.WebSockets.Extensions
// conform to the ABNF below. // conform to the ABNF below.
// client-max-window-bits = 1*DIGIT // client-max-window-bits = 1*DIGIT
if (string.IsNullOrWhiteSpace(@value)) if (string.IsNullOrWhiteSpace(value))
{ {
return false; return false;
} }
int paramValue = -1; var paramValue = -1;
if (int.TryParse(@value, out paramValue)) if (int.TryParse(value, out paramValue))
{ {
if (8 <= paramValue && paramValue <= 15) if (8 <= paramValue && paramValue <= 15)
{ {
@ -93,11 +96,11 @@ namespace EonaCat.WebSockets.Extensions
var inputParameterName = keyValuePair[0].TrimStart().TrimEnd(); var inputParameterName = keyValuePair[0].TrimStart().TrimEnd();
ExtensionParameter matchedParameter = null; ExtensionParameter matchedParameter = null;
foreach (var @param in AllAvailableParameters) foreach (var param in AllAvailableParameters)
{ {
if (string.Compare(inputParameterName, @param.Name, StringComparison.OrdinalIgnoreCase) == 0) if (string.Compare(inputParameterName, param.Name, StringComparison.OrdinalIgnoreCase) == 0)
{ {
matchedParameter = @param; matchedParameter = param;
break; break;
} }
} }
@ -168,11 +171,11 @@ namespace EonaCat.WebSockets.Extensions
var inputParameterName = keyValuePair[0].TrimStart().TrimEnd(); var inputParameterName = keyValuePair[0].TrimStart().TrimEnd();
ExtensionParameter matchedParameter = null; ExtensionParameter matchedParameter = null;
foreach (var @param in AllAvailableParameters) foreach (var param in AllAvailableParameters)
{ {
if (string.Compare(inputParameterName, @param.Name, StringComparison.OrdinalIgnoreCase) == 0) if (string.Compare(inputParameterName, param.Name, StringComparison.OrdinalIgnoreCase) == 0)
{ {
matchedParameter = @param; matchedParameter = param;
break; break;
} }
} }
@ -189,7 +192,8 @@ namespace EonaCat.WebSockets.Extensions
{ {
if (keyValuePair.Length == 1) if (keyValuePair.Length == 1)
{ {
return new AgreedValuableParameter<byte>(matchedParameter.Name, ((AbsentableValueParameter<byte>)matchedParameter).DefaultValue); return new AgreedValuableParameter<byte>(matchedParameter.Name,
((AbsentableValueParameter<byte>)matchedParameter).DefaultValue);
} }
var inputParameterValue = keyValuePair[1].TrimStart().TrimEnd(); var inputParameterValue = keyValuePair[1].TrimStart().TrimEnd();
@ -200,4 +204,3 @@ namespace EonaCat.WebSockets.Extensions
} }
} }
} }
}

View File

@ -1,7 +1,6 @@
using System; using System;
namespace EonaCat.WebSockets.Extensions namespace EonaCat.WebSockets.Extensions;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -14,9 +13,8 @@ namespace EonaCat.WebSockets.Extensions
throw new ArgumentNullException("offer"); throw new ArgumentNullException("offer");
} }
this.ExtensionNegotiationOffer = offer; ExtensionNegotiationOffer = offer;
} }
public string ExtensionNegotiationOffer { get; private set; } public string ExtensionNegotiationOffer { get; private set; }
} }
}

View File

@ -1,25 +1,23 @@
using System; using System;
using EonaCat.WebSockets.Buffer; using EonaCat.WebSockets.Buffer;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public sealed class BinaryFragmentationFrame : Frame public sealed class BinaryFragmentationFrame : Frame
{ {
private OpCode _opCode; public BinaryFragmentationFrame(OpCode opCode, byte[] data, int offset, int count, bool isFin = false,
bool isMasked = true)
public BinaryFragmentationFrame(OpCode opCode, byte[] data, int offset, int count, bool isFin = false, bool isMasked = true)
{ {
BufferValidator.ValidateBuffer(data, offset, count, "data"); BufferValidator.ValidateBuffer(data, offset, count, "data");
_opCode = opCode; OpCode = opCode;
this.Data = data; Data = data;
this.Offset = offset; Offset = offset;
this.Count = count; Count = count;
this.IsFin = isFin; IsFin = isFin;
this.IsMasked = isMasked; IsMasked = isMasked;
} }
public byte[] Data { get; private set; } public byte[] Data { get; private set; }
@ -28,10 +26,7 @@ namespace EonaCat.WebSockets
public bool IsFin { get; private set; } public bool IsFin { get; private set; }
public bool IsMasked { get; private set; } public bool IsMasked { get; private set; }
public override OpCode OpCode public override OpCode OpCode { get; }
{
get { return _opCode; }
}
public byte[] ToArray(IFrameBuilder builder) public byte[] ToArray(IFrameBuilder builder)
{ {
@ -43,4 +38,3 @@ namespace EonaCat.WebSockets
return builder.EncodeFrame(this); return builder.EncodeFrame(this);
} }
} }
}

View File

@ -1,8 +1,7 @@
using System; using System;
using EonaCat.WebSockets.Buffer; using EonaCat.WebSockets.Buffer;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -12,20 +11,20 @@ namespace EonaCat.WebSockets
{ {
BufferValidator.ValidateArraySegment(segment, "segment"); BufferValidator.ValidateArraySegment(segment, "segment");
this.Data = segment.Array; Data = segment.Array;
this.Offset = segment.Offset; Offset = segment.Offset;
this.Count = segment.Count; Count = segment.Count;
this.IsMasked = isMasked; IsMasked = isMasked;
} }
public BinaryFrame(byte[] data, int offset, int count, bool isMasked = true) public BinaryFrame(byte[] data, int offset, int count, bool isMasked = true)
{ {
BufferValidator.ValidateBuffer(data, offset, count, "data"); BufferValidator.ValidateBuffer(data, offset, count, "data");
this.Data = data; Data = data;
this.Offset = offset; Offset = offset;
this.Count = count; Count = count;
this.IsMasked = isMasked; IsMasked = isMasked;
} }
public byte[] Data { get; private set; } public byte[] Data { get; private set; }
@ -33,10 +32,7 @@ namespace EonaCat.WebSockets
public int Count { get; private set; } public int Count { get; private set; }
public bool IsMasked { get; private set; } public bool IsMasked { get; private set; }
public override OpCode OpCode public override OpCode OpCode => OpCode.Binary;
{
get { return OpCode.Binary; }
}
public byte[] ToArray(IFrameBuilder builder) public byte[] ToArray(IFrameBuilder builder)
{ {
@ -48,4 +44,3 @@ namespace EonaCat.WebSockets
return builder.EncodeFrame(this); return builder.EncodeFrame(this);
} }
} }
}

View File

@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using EonaCat.WebSockets.Extensions; using EonaCat.WebSockets.Extensions;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -18,6 +17,7 @@ namespace EonaCat.WebSockets
byte[] EncodeFrame(BinaryFragmentationFrame frame); byte[] EncodeFrame(BinaryFragmentationFrame frame);
bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader); bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader);
void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset, out int payloadCount);
} void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset,
out int payloadCount);
} }

View File

@ -4,8 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using EonaCat.WebSockets.Extensions; using EonaCat.WebSockets.Extensions;
namespace EonaCat.WebSockets namespace EonaCat.WebSockets;
{
// This file is part of the EonaCat project(s) which is released under the Apache License. // This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
@ -40,13 +39,9 @@ namespace EonaCat.WebSockets
public class WebSocketFrameBuilder : IFrameBuilder public class WebSocketFrameBuilder : IFrameBuilder
{ {
private static readonly byte[] EmptyArray = new byte[0]; private static readonly byte[] EmptyArray = new byte[0];
private static readonly Random _rng = new Random(DateTime.UtcNow.Millisecond); private static readonly Random _rng = new(DateTime.UtcNow.Millisecond);
private static readonly int MaskingKeyLength = 4; private static readonly int MaskingKeyLength = 4;
public WebSocketFrameBuilder()
{
}
public SortedList<int, IWebSocketExtension> NegotiatedExtensions { get; set; } public SortedList<int, IWebSocketExtension> NegotiatedExtensions { get; set; }
public byte[] EncodeFrame(PingFrame frame) public byte[] EncodeFrame(PingFrame frame)
@ -59,12 +54,10 @@ namespace EonaCat.WebSockets
throw new WebSocketException("All control frames must have a payload length of 125 bytes or less."); throw new WebSocketException("All control frames must have a payload length of 125 bytes or less.");
} }
return Encode(frame.OpCode, data, 0, data.Length, isMasked: frame.IsMasked); return Encode(frame.OpCode, data, 0, data.Length, frame.IsMasked);
}
else
{
return Encode(frame.OpCode, EmptyArray, 0, 0, isMasked: frame.IsMasked);
} }
return Encode(frame.OpCode, EmptyArray, 0, 0, frame.IsMasked);
} }
public byte[] EncodeFrame(PongFrame frame) public byte[] EncodeFrame(PongFrame frame)
@ -77,12 +70,10 @@ namespace EonaCat.WebSockets
throw new WebSocketException("All control frames must have a payload length of 125 bytes or less."); throw new WebSocketException("All control frames must have a payload length of 125 bytes or less.");
} }
return Encode(frame.OpCode, data, 0, data.Length, isMasked: frame.IsMasked); return Encode(frame.OpCode, data, 0, data.Length, frame.IsMasked);
}
else
{
return Encode(frame.OpCode, EmptyArray, 0, 0, isMasked: frame.IsMasked);
} }
return Encode(frame.OpCode, EmptyArray, 0, 0, frame.IsMasked);
} }
public byte[] EncodeFrame(CloseFrame frame) public byte[] EncodeFrame(CloseFrame frame)
@ -100,29 +91,29 @@ namespace EonaCat.WebSockets
// may be useful for debugging or passing information relevant to the // may be useful for debugging or passing information relevant to the
// script that opened the connection. As the data is not guaranteed to // script that opened the connection. As the data is not guaranteed to
// be human readable, clients MUST NOT show it to end users. // be human readable, clients MUST NOT show it to end users.
int payloadLength = (string.IsNullOrEmpty(frame.CloseReason) ? 0 : Encoding.UTF8.GetMaxByteCount(frame.CloseReason.Length)) + 2; var payloadLength = (string.IsNullOrEmpty(frame.CloseReason)
? 0
: Encoding.UTF8.GetMaxByteCount(frame.CloseReason.Length)) + 2;
if (payloadLength > 125) if (payloadLength > 125)
{ {
throw new WebSocketException("All control frames must have a payload length of 125 bytes or less."); throw new WebSocketException("All control frames must have a payload length of 125 bytes or less.");
} }
byte[] payload = new byte[payloadLength]; var payload = new byte[payloadLength];
int higherByte = (int)frame.CloseCode / 256; var higherByte = (int)frame.CloseCode / 256;
int lowerByte = (int)frame.CloseCode % 256; var lowerByte = (int)frame.CloseCode % 256;
payload[0] = (byte)higherByte; payload[0] = (byte)higherByte;
payload[1] = (byte)lowerByte; payload[1] = (byte)lowerByte;
if (!string.IsNullOrEmpty(frame.CloseReason)) if (!string.IsNullOrEmpty(frame.CloseReason))
{ {
int count = Encoding.UTF8.GetBytes(frame.CloseReason, 0, frame.CloseReason.Length, payload, 2); var count = Encoding.UTF8.GetBytes(frame.CloseReason, 0, frame.CloseReason.Length, payload, 2);
return Encode(frame.OpCode, payload, 0, 2 + count, isMasked: frame.IsMasked); return Encode(frame.OpCode, payload, 0, 2 + count, frame.IsMasked);
}
else
{
return Encode(frame.OpCode, payload, 0, payload.Length, isMasked: frame.IsMasked);
} }
return Encode(frame.OpCode, payload, 0, payload.Length, frame.IsMasked);
} }
public byte[] EncodeFrame(TextFrame frame) public byte[] EncodeFrame(TextFrame frame)
@ -130,22 +121,74 @@ namespace EonaCat.WebSockets
if (!string.IsNullOrEmpty(frame.Text)) if (!string.IsNullOrEmpty(frame.Text))
{ {
var data = Encoding.UTF8.GetBytes(frame.Text); var data = Encoding.UTF8.GetBytes(frame.Text);
return Encode(frame.OpCode, data, 0, data.Length, isMasked: frame.IsMasked); return Encode(frame.OpCode, data, 0, data.Length, frame.IsMasked);
}
else
{
return Encode(frame.OpCode, EmptyArray, 0, 0, isMasked: frame.IsMasked);
} }
return Encode(frame.OpCode, EmptyArray, 0, 0, frame.IsMasked);
} }
public byte[] EncodeFrame(BinaryFrame frame) public byte[] EncodeFrame(BinaryFrame frame)
{ {
return Encode(frame.OpCode, frame.Data, frame.Offset, frame.Count, isMasked: frame.IsMasked); return Encode(frame.OpCode, frame.Data, frame.Offset, frame.Count, frame.IsMasked);
} }
public byte[] EncodeFrame(BinaryFragmentationFrame frame) public byte[] EncodeFrame(BinaryFragmentationFrame frame)
{ {
return Encode(frame.OpCode, frame.Data, frame.Offset, frame.Count, isMasked: frame.IsMasked, isFin: frame.IsFin); return Encode(frame.OpCode, frame.Data, frame.Offset, frame.Count, frame.IsMasked, frame.IsFin);
}
public bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader)
{
frameHeader = DecodeFrameHeader(buffer, offset, count);
return frameHeader != null;
}
public void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset,
out int payloadCount)
{
payload = buffer;
payloadOffset = offset + frameHeader.Length;
payloadCount = frameHeader.PayloadLength;
if (frameHeader.IsMasked)
{
payload = new byte[payloadCount];
for (var i = 0; i < payloadCount; i++)
payload[i] = (byte)(buffer[payloadOffset + i] ^
buffer[offset + frameHeader.MaskingKeyOffset + i % MaskingKeyLength]);
payloadOffset = 0;
payloadCount = payload.Length;
}
// Payload data: (x+y) bytes
// Extension data: x bytes
// Application data: y bytes
// The "Extension data" is 0 bytes unless an extension has been
// negotiated. Any extension MUST specify the length of the
// "Extension data", or how that length may be calculated, and how
// the extension use MUST be negotiated during the opening handshake.
// If present, the "Extension data" is included in the total payload length.
if (NegotiatedExtensions != null)
{
byte[] bakedBuffer = null;
foreach (var extension in NegotiatedExtensions.Reverse().Select(e => e.Value))
{
if (bakedBuffer == null)
{
bakedBuffer = extension.ProcessIncomingMessagePayload(payload, payloadOffset, payloadCount);
}
else
{
bakedBuffer = extension.ProcessIncomingMessagePayload(bakedBuffer, 0, bakedBuffer.Length);
}
}
payload = bakedBuffer;
payloadOffset = 0;
payloadCount = payload.Length;
}
} }
private byte[] Encode(OpCode opCode, byte[] payload, int offset, int count, bool isMasked = true, bool isFin = true) private byte[] Encode(OpCode opCode, byte[] payload, int offset, int count, bool isMasked = true, bool isFin = true)
@ -158,10 +201,10 @@ namespace EonaCat.WebSockets
// "Extension data", or how that length may be calculated, and how // "Extension data", or how that length may be calculated, and how
// the extension use MUST be negotiated during the opening handshake. // the extension use MUST be negotiated during the opening handshake.
// If present, the "Extension data" is included in the total payload length. // If present, the "Extension data" is included in the total payload length.
if (this.NegotiatedExtensions != null) if (NegotiatedExtensions != null)
{ {
byte[] bakedBuffer = null; byte[] bakedBuffer = null;
foreach (var extension in this.NegotiatedExtensions.Values) foreach (var extension in NegotiatedExtensions.Values)
{ {
if (bakedBuffer == null) if (bakedBuffer == null)
{ {
@ -194,17 +237,17 @@ namespace EonaCat.WebSockets
else if (count < 65536) else if (count < 65536)
{ {
fragment = new byte[2 + 2 + (isMasked ? MaskingKeyLength : 0) + count]; fragment = new byte[2 + 2 + (isMasked ? MaskingKeyLength : 0) + count];
fragment[1] = (byte)126; fragment[1] = 126;
fragment[2] = (byte)(count / 256); fragment[2] = (byte)(count / 256);
fragment[3] = (byte)(count % 256); fragment[3] = (byte)(count % 256);
} }
else else
{ {
fragment = new byte[2 + 8 + (isMasked ? MaskingKeyLength : 0) + count]; fragment = new byte[2 + 8 + (isMasked ? MaskingKeyLength : 0) + count];
fragment[1] = (byte)127; fragment[1] = 127;
int left = count; var left = count;
for (int i = 9; i > 1; i--) for (var i = 9; i > 1; i--)
{ {
fragment[i] = (byte)(left % 256); fragment[i] = (byte)(left % 256);
left = left / 256; left = left / 256;
@ -230,9 +273,9 @@ namespace EonaCat.WebSockets
// the negotiated extensions defines the meaning of such a nonzero // the negotiated extensions defines the meaning of such a nonzero
// value, the receiving endpoint MUST _Fail the WebSocket // value, the receiving endpoint MUST _Fail the WebSocket
// Connection_. // Connection_.
if (this.NegotiatedExtensions != null) if (NegotiatedExtensions != null)
{ {
foreach (var extension in this.NegotiatedExtensions.Values) foreach (var extension in NegotiatedExtensions.Values)
{ {
if (extension.Rsv1BitOccupied) if (extension.Rsv1BitOccupied)
{ {
@ -282,26 +325,23 @@ namespace EonaCat.WebSockets
// entails a suitable source of entropy for security-sensitive applications. // entails a suitable source of entropy for security-sensitive applications.
if (isMasked) if (isMasked)
{ {
int maskingKeyIndex = fragment.Length - (MaskingKeyLength + count); var maskingKeyIndex = fragment.Length - (MaskingKeyLength + count);
for (var i = maskingKeyIndex; i < maskingKeyIndex + MaskingKeyLength; i++) for (var i = maskingKeyIndex; i < maskingKeyIndex + MaskingKeyLength; i++)
{
fragment[i] = (byte)_rng.Next(0, 255); fragment[i] = (byte)_rng.Next(0, 255);
}
if (count > 0) if (count > 0)
{ {
int payloadIndex = fragment.Length - count; var payloadIndex = fragment.Length - count;
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ fragment[payloadIndex + i] =
fragment[payloadIndex + i] = (byte)(payload[offset + i] ^ fragment[maskingKeyIndex + i % MaskingKeyLength]); (byte)(payload[offset + i] ^ fragment[maskingKeyIndex + i % MaskingKeyLength]);
}
} }
} }
else else
{ {
if (count > 0) if (count > 0)
{ {
int payloadIndex = fragment.Length - count; var payloadIndex = fragment.Length - count;
Array.Copy(payload, offset, fragment, payloadIndex, count); Array.Copy(payload, offset, fragment, payloadIndex, count);
} }
} }
@ -309,12 +349,6 @@ namespace EonaCat.WebSockets
return fragment; return fragment;
} }
public bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader)
{
frameHeader = DecodeFrameHeader(buffer, offset, count);
return frameHeader != null;
}
private Header DecodeFrameHeader(byte[] buffer, int offset, int count) private Header DecodeFrameHeader(byte[] buffer, int offset, int count)
{ {
if (count < 2) if (count < 2)
@ -323,16 +357,16 @@ namespace EonaCat.WebSockets
} }
// parse fixed header // parse fixed header
var header = new Header() var header = new Header
{ {
IsFIN = ((buffer[offset + 0] & 0x80) == 0x80), IsFIN = (buffer[offset + 0] & 0x80) == 0x80,
IsRSV1 = ((buffer[offset + 0] & 0x40) == 0x40), IsRSV1 = (buffer[offset + 0] & 0x40) == 0x40,
IsRSV2 = ((buffer[offset + 0] & 0x20) == 0x20), IsRSV2 = (buffer[offset + 0] & 0x20) == 0x20,
IsRSV3 = ((buffer[offset + 0] & 0x10) == 0x10), IsRSV3 = (buffer[offset + 0] & 0x10) == 0x10,
OpCode = (OpCode)(buffer[offset + 0] & 0x0f), OpCode = (OpCode)(buffer[offset + 0] & 0x0f),
IsMasked = ((buffer[offset + 1] & 0x80) == 0x80), IsMasked = (buffer[offset + 1] & 0x80) == 0x80,
PayloadLength = (buffer[offset + 1] & 0x7f), PayloadLength = buffer[offset + 1] & 0x7f,
Length = 2, Length = 2
}; };
// parse extended payload length // parse extended payload length
@ -358,10 +392,10 @@ namespace EonaCat.WebSockets
} }
else else
{ {
int totalLength = 0; var totalLength = 0;
int level = 1; var level = 1;
for (int i = 7; i >= 0; i--) for (var i = 7; i >= 0; i--)
{ {
totalLength += buffer[offset + i + 2] * level; totalLength += buffer[offset + i + 2] * level;
level *= 256; level *= 256;
@ -385,53 +419,4 @@ namespace EonaCat.WebSockets
return header; return header;
} }
public void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset, out int payloadCount)
{
payload = buffer;
payloadOffset = offset + frameHeader.Length;
payloadCount = frameHeader.PayloadLength;
if (frameHeader.IsMasked)
{
payload = new byte[payloadCount];
for (var i = 0; i < payloadCount; i++)
{
payload[i] = (byte)(buffer[payloadOffset + i] ^ buffer[offset + frameHeader.MaskingKeyOffset + i % MaskingKeyLength]);
}
payloadOffset = 0;
payloadCount = payload.Length;
}
// Payload data: (x+y) bytes
// Extension data: x bytes
// Application data: y bytes
// The "Extension data" is 0 bytes unless an extension has been
// negotiated. Any extension MUST specify the length of the
// "Extension data", or how that length may be calculated, and how
// the extension use MUST be negotiated during the opening handshake.
// If present, the "Extension data" is included in the total payload length.
if (this.NegotiatedExtensions != null)
{
byte[] bakedBuffer = null;
foreach (var extension in this.NegotiatedExtensions.Reverse().Select(e => e.Value))
{
if (bakedBuffer == null)
{
bakedBuffer = extension.ProcessIncomingMessagePayload(payload, payloadOffset, payloadCount);
}
else
{
bakedBuffer = extension.ProcessIncomingMessagePayload(bakedBuffer, 0, bakedBuffer.Length);
}
}
payload = bakedBuffer;
payloadOffset = 0;
payloadCount = payload.Length;
}
}
}
} }

Some files were not shown because too many files have changed in this diff Show More