Updated
This commit is contained in:
parent
08a2be3221
commit
2dca7a7f4f
|
@ -66,8 +66,7 @@ internal class Program
|
|||
StartServer(serverUri, certificatePath, certificatePassword, requiredPassword);
|
||||
|
||||
// Start the client in the main thread
|
||||
_client = new WSClient(clientUri);
|
||||
_client.SSL.Certificates.Add(new X509Certificate(certificatePath, certificatePassword));
|
||||
_client = new WSClient(clientUri, new X509Certificate(certificatePath, certificatePassword));
|
||||
_client.OnConnect += (sender, e) => Console.WriteLine($"Connected to server");
|
||||
_client.OnMessageReceived += (sender, e) => Console.WriteLine($"Received message from server: {e.Data}");
|
||||
_client.OnDisconnect += (sender, e) => Console.WriteLine($"Disconnected from server: {e.Code} : {e.Reason}");
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Network
|
||||
namespace EonaCat.Network
|
||||
{
|
||||
internal class Constants
|
||||
{
|
||||
public static string Version { get; set; } = "1.1.2";
|
||||
public static string Version { get; set; } = "1.1.4";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,9 @@
|
|||
<PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
|
||||
<PackageReleaseNotes></PackageReleaseNotes>
|
||||
<Description>EonaCat Networking library with Quic, TCP, UDP, WebSockets and a Webserver</Description>
|
||||
<Version>1.1.3</Version>
|
||||
<AssemblyVersion>1.1.3</AssemblyVersion>
|
||||
<FileVersion>1.1.3</FileVersion>
|
||||
<Version>1.1.4</Version>
|
||||
<AssemblyVersion>1.1.4</AssemblyVersion>
|
||||
<FileVersion>1.1.4</FileVersion>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using EonaCat.Quic.Infrastructure;
|
||||
using EonaCat.Quic.Infrastructure.Settings;
|
||||
using EonaCat.Quic.InternalInfrastructure;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Connections
|
||||
|
|
|
@ -9,7 +9,6 @@ using EonaCat.Quic.Infrastructure.Packets;
|
|||
using EonaCat.Quic.Infrastructure.Settings;
|
||||
using EonaCat.Quic.InternalInfrastructure;
|
||||
using EonaCat.Quic.Streams;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Connections
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Streams;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Quic.Context
|
||||
{
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
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.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
{
|
||||
// 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.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Frames
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
{
|
||||
// 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.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using EonaCat.Quic.Infrastructure.Frames;
|
||||
using EonaCat.Quic.Infrastructure.Packets;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.PacketProcessing
|
||||
{
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
namespace EonaCat.Quic.Infrastructure
|
||||
{
|
||||
// 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.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using EonaCat.Quic.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Packets
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using EonaCat.Quic.Infrastructure.Exceptions;
|
||||
using EonaCat.Quic.Infrastructure.Frames;
|
||||
using EonaCat.Quic.Infrastructure.Settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Packets
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EonaCat.Quic.Infrastructure.Settings
|
||||
{
|
||||
|
|
|
@ -5,6 +5,51 @@ namespace EonaCat.Network
|
|||
{
|
||||
public enum CloseStatusCode : ushort
|
||||
{
|
||||
//1000: Normal Closure
|
||||
//Indicates a normal closure, meaning that the purpose for which the connection was established has been fulfilled.
|
||||
|
||||
//1001: Going Away
|
||||
//The endpoint is going away, such as a server going down or a browser tab is closing.
|
||||
|
||||
//1002: Protocol Error
|
||||
//Indicates that the endpoint is terminating the connection due to a protocol error.
|
||||
|
||||
//1003: Unsupported Data
|
||||
//Indicates that the endpoint is terminating the connection because it received data that it cannot process.
|
||||
|
||||
//1005: No Status Received
|
||||
//Reserved. It indicates that no status code was received.
|
||||
|
||||
//1006: Abnormal Closure
|
||||
//Reserved. It is a reserved value and should not be set as a status code in a Close control frame by an endpoint.
|
||||
|
||||
//1007: Invalid frame payload data
|
||||
//Indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message.
|
||||
|
||||
//1008: Policy Violation
|
||||
//Indicates that an endpoint is terminating the connection because it has received a message that violates its policy.
|
||||
|
||||
//1009: Message Too Big
|
||||
//Indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process.
|
||||
|
||||
//1010: Missing Extension
|
||||
//Indicates that an endpoint is terminating the connection because it has received a message that requires negotiation of an extension, and the client did not offer that extension.
|
||||
|
||||
//1011: Internal Error
|
||||
//Indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.
|
||||
|
||||
//1012: Service Restart
|
||||
//Reserved. It indicates that the service is restarting.
|
||||
|
||||
//1013: Try Again Later
|
||||
//Reserved. It indicates that the server is temporarily unable to process the request.
|
||||
|
||||
//1014: Bad Gateway
|
||||
//Reserved. It indicates that an intermediate server failed to fulfill a request.
|
||||
|
||||
//1015: TLS Handshake Failure
|
||||
//Reserved. It indicates that the connection was closed due to a failure to perform a TLS handshake.
|
||||
|
||||
Normal = 1000,
|
||||
|
||||
Away = 1001,
|
||||
|
@ -25,10 +70,16 @@ namespace EonaCat.Network
|
|||
|
||||
TooBig = 1009,
|
||||
|
||||
MandatoryExtension = 1010,
|
||||
MissingExtension = 1010,
|
||||
|
||||
ServerError = 1011,
|
||||
|
||||
ServiceRestart = 1012,
|
||||
|
||||
TryAgainLater = 1013,
|
||||
|
||||
BadGateway = 1014,
|
||||
|
||||
TlsHandshakeFailure = 1015
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ namespace EonaCat.Network
|
|||
: base(scheme, parameters)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the domain for Digest authentication.
|
||||
/// </summary>
|
||||
|
@ -104,49 +105,32 @@ namespace EonaCat.Network
|
|||
return string.Format($"Basic realm=\"{{0}}\"", Parameters["realm"]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Digest authentication string representation of the authentication challenge.
|
||||
/// </summary>
|
||||
/// <returns>A string representing the Digest authentication challenge.</returns>
|
||||
internal override string ToDigestString()
|
||||
{
|
||||
var output = new StringBuilder(DIGEST_SIZE);
|
||||
var realm = Parameters["realm"];
|
||||
var nonce = Parameters["nonce"];
|
||||
|
||||
var domain = Parameters["domain"];
|
||||
if (domain != null)
|
||||
if (realm != null)
|
||||
{
|
||||
output.AppendFormat($"Digest realm=\"{Parameters["realm"]}\", domain=\"{domain}\", nonce=\"{Parameters["nonce"]}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
output.AppendFormat($"Digest realm=\"{Parameters["realm"]}\", nonce=\"{Parameters["nonce"]}\"");
|
||||
}
|
||||
output.AppendFormat("Digest realm=\"{0}\", nonce=\"{1}\"", realm, nonce);
|
||||
|
||||
var opaque = Parameters["opaque"];
|
||||
if (opaque != null)
|
||||
{
|
||||
output.AppendFormat($", opaque=\"{opaque}\"");
|
||||
}
|
||||
|
||||
var stale = Parameters["stale"];
|
||||
if (stale != null)
|
||||
{
|
||||
output.AppendFormat($", stale={stale}");
|
||||
}
|
||||
|
||||
var algorithm = Parameters["algorithm"];
|
||||
if (algorithm != null)
|
||||
{
|
||||
output.AppendFormat($", algorithm={algorithm}");
|
||||
}
|
||||
|
||||
var qop = Parameters["qop"];
|
||||
if (qop != null)
|
||||
{
|
||||
output.AppendFormat($", qop=\"{qop}\"");
|
||||
AppendIfNotNull(output, "domain", Parameters["domain"]);
|
||||
AppendIfNotNull(output, "opaque", Parameters["opaque"]);
|
||||
AppendIfNotNull(output, "stale", Parameters["stale"]);
|
||||
AppendIfNotNull(output, "algorithm", Parameters["algorithm"]);
|
||||
AppendIfNotNull(output, "qop", Parameters["qop"]);
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
private static void AppendIfNotNull(StringBuilder output, string key, string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
output.AppendFormat(", {0}=\"{1}\"", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ namespace EonaCat.Network
|
|||
: base(scheme, parameters)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cnonce value for Digest authentication.
|
||||
/// </summary>
|
||||
|
@ -108,6 +109,7 @@ namespace EonaCat.Network
|
|||
internal uint NonceCount => _nonceCount < uint.MaxValue
|
||||
? _nonceCount
|
||||
: 0;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the authentication response to an identity.
|
||||
/// </summary>
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace EonaCat.Network
|
|||
/// Gets the web headers associated with the chunk stream.
|
||||
/// </summary>
|
||||
internal WebHeaderCollection Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads a specified amount of data from the chunk stream.
|
||||
/// </summary>
|
||||
|
@ -159,6 +160,7 @@ namespace EonaCat.Network
|
|||
|
||||
return nread;
|
||||
}
|
||||
|
||||
private InputChunkState SeekCrLf(byte[] buffer, ref int offset, int length)
|
||||
{
|
||||
if (!_gotChunck)
|
||||
|
@ -316,6 +318,7 @@ namespace EonaCat.Network
|
|||
|
||||
return InputChunkState.End;
|
||||
}
|
||||
|
||||
private void Write(byte[] buffer, ref int offset, int length)
|
||||
{
|
||||
if (_state == InputChunkState.End)
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace EonaCat.Network
|
|||
[ComVisible(true)]
|
||||
public class WebHeaderCollection : NameValueCollection, ISerializable
|
||||
{
|
||||
private const int BUFFER_SIZE = 65535;
|
||||
private static readonly Dictionary<string, HttpHeaderInfo> _headers;
|
||||
private readonly bool _internallyUsed;
|
||||
private HttpHeaderType _state;
|
||||
|
@ -136,10 +137,12 @@ namespace EonaCat.Network
|
|||
throw new ArgumentException(ex.Message, nameof(serializationInfo), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override string[] AllKeys => base.AllKeys;
|
||||
public override int Count => base.Count;
|
||||
public override KeysCollection Keys => base.Keys;
|
||||
internal HttpHeaderType State => _state;
|
||||
|
||||
public string this[HttpRequestHeader header]
|
||||
{
|
||||
get
|
||||
|
@ -165,6 +168,7 @@ namespace EonaCat.Network
|
|||
Add(header, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRestricted(string headerName)
|
||||
{
|
||||
return isRestricted(CheckName(headerName), false);
|
||||
|
@ -462,7 +466,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
value = value.Trim();
|
||||
if (value.Length > 65535)
|
||||
if (value.Length > BUFFER_SIZE)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "Greater than 65,535 characters.");
|
||||
}
|
||||
|
@ -517,6 +521,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
base.Add(name, CheckValue(value));
|
||||
}
|
||||
|
||||
private void CheckRestricted(string name)
|
||||
{
|
||||
if (!_internallyUsed && isRestricted(name, true))
|
||||
|
@ -544,6 +549,7 @@ namespace EonaCat.Network
|
|||
"This collection has already been used to store the response headers.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DoWithCheckingState(
|
||||
Action<string, string> action, string name, string value, bool setState)
|
||||
{
|
||||
|
@ -578,6 +584,7 @@ namespace EonaCat.Network
|
|||
CheckRestricted(name);
|
||||
action(name, CheckValue(value));
|
||||
}
|
||||
|
||||
private void removeWithoutCheckingName(string name, string unuse)
|
||||
{
|
||||
CheckRestricted(name);
|
||||
|
|
|
@ -84,6 +84,7 @@ namespace EonaCat.Network
|
|||
/// Gets the stream of the underlying TCP connection.
|
||||
/// </summary>
|
||||
internal Stream Stream => _context.Connection.Stream;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace EonaCat.Network
|
|||
private NameValueCollection _queryString;
|
||||
private WebRequest _request;
|
||||
private IPrincipal _user;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TcpListenerWSContext"/> class.
|
||||
/// </summary>
|
||||
|
@ -130,6 +131,7 @@ namespace EonaCat.Network
|
|||
/// Gets the stream of the underlying TCP connection.
|
||||
/// </summary>
|
||||
internal Stream Stream { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -332,6 +332,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
internal int[] Ports => _ports;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object comparand)
|
||||
{
|
||||
|
@ -414,7 +415,7 @@ namespace EonaCat.Network
|
|||
internal string ToResponseString()
|
||||
{
|
||||
return _name.Length > 0
|
||||
? (_version == 0 ? toResponseStringVersion0() : toResponseStringVersion1())
|
||||
? (_version == 0 ? ToResponseStringVersion0() : ToResponseStringVersion1())
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
|
@ -493,7 +494,7 @@ namespace EonaCat.Network
|
|||
return true;
|
||||
}
|
||||
|
||||
private string toResponseStringVersion0()
|
||||
private string ToResponseStringVersion0()
|
||||
{
|
||||
var output = new StringBuilder(64);
|
||||
output.AppendFormat("{0}={1}", _name, _value);
|
||||
|
@ -530,7 +531,7 @@ namespace EonaCat.Network
|
|||
return output.ToString();
|
||||
}
|
||||
|
||||
private string toResponseStringVersion1()
|
||||
private string ToResponseStringVersion1()
|
||||
{
|
||||
var output = new StringBuilder(64);
|
||||
output.AppendFormat("{0}={1}; Version={2}", _name, _value, _version);
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace EonaCat.Network
|
|||
public class CookieCollection : ICollection, IEnumerable
|
||||
{
|
||||
private readonly List<Cookie> _list;
|
||||
private object _sync;
|
||||
private object _locker;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CookieCollection"/> class.
|
||||
|
@ -26,7 +26,6 @@ namespace EonaCat.Network
|
|||
_list = new List<Cookie>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of cookies in the collection.
|
||||
/// </summary>
|
||||
|
@ -45,7 +44,7 @@ namespace EonaCat.Network
|
|||
/// <summary>
|
||||
/// Gets an object that can be used to synchronize access to the collection.
|
||||
/// </summary>
|
||||
public object SyncRoot => _sync ??= ((ICollection)_list).SyncRoot;
|
||||
public object SyncRoot => _locker ??= ((ICollection)_list).SyncRoot;
|
||||
|
||||
internal IList<Cookie> List => _list;
|
||||
|
||||
|
@ -62,6 +61,7 @@ namespace EonaCat.Network
|
|||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cookie at the specified index.
|
||||
/// </summary>
|
||||
|
@ -105,6 +105,7 @@ namespace EonaCat.Network
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified cookie to the collection, updating it if it already exists.
|
||||
/// </summary>
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace EonaCat.Network
|
|||
: base(serializationInfo, streamingContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates a <see cref="SerializationInfo"/> with the data needed to serialize the exception.
|
||||
/// </summary>
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace EonaCat.Network
|
|||
private List<HttpListenerPrefix> _all; // host == '+'
|
||||
private Dictionary<HttpListenerPrefix, HttpListener> _prefixes;
|
||||
private List<HttpListenerPrefix> _unhandled; // host == '*'
|
||||
|
||||
static EndPointListener()
|
||||
{
|
||||
_defaultCertFolderPath =
|
||||
|
|
|
@ -618,6 +618,7 @@ namespace EonaCat.Network
|
|||
_position = 0;
|
||||
_requestBuffer = new MemoryStream();
|
||||
}
|
||||
|
||||
private void UnregisterContext()
|
||||
{
|
||||
if (!_contextRegistered)
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace EonaCat.Network
|
|||
/// Gets a value indicating whether the header is multi-value in a response.
|
||||
/// </summary>
|
||||
internal bool IsMultiValueInResponse => (Type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the header is multi-value.
|
||||
/// </summary>
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace EonaCat.Network
|
|||
private string _realm;
|
||||
private SSLConfigServer _sslConfig;
|
||||
private Func<IIdentity, NetworkCredential> _userCredFinder;
|
||||
|
||||
static HttpListener()
|
||||
{
|
||||
_defaultRealm = "SECRET AREA";
|
||||
|
@ -60,6 +61,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public static bool IsSupported => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server accepts every
|
||||
/// handshake request without checking the request URI.
|
||||
|
@ -147,6 +149,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public bool IsListening => _listening;
|
||||
|
||||
public HttpListenerPrefixCollection Prefixes
|
||||
{
|
||||
get
|
||||
|
@ -217,6 +220,7 @@ namespace EonaCat.Network
|
|||
internal bool IsDisposed { get; private set; }
|
||||
|
||||
internal bool ReuseAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Aborts the listener and releases all resources associated with it.
|
||||
/// </summary>
|
||||
|
@ -400,14 +404,14 @@ namespace EonaCat.Network
|
|||
throw new HttpListenerException(995);
|
||||
}
|
||||
|
||||
var ctx = GetContextFromQueue();
|
||||
if (ctx == null)
|
||||
var context = GetContextFromQueue();
|
||||
if (context == null)
|
||||
{
|
||||
_waitQueue.Add(asyncResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
asyncResult.Complete(ctx, true);
|
||||
asyncResult.Complete(context, true);
|
||||
}
|
||||
|
||||
return asyncResult;
|
||||
|
@ -630,10 +634,10 @@ namespace EonaCat.Network
|
|||
return null;
|
||||
}
|
||||
|
||||
var ctx = contextQueue[0];
|
||||
var context = contextQueue[0];
|
||||
contextQueue.RemoveAt(0);
|
||||
|
||||
return ctx;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace EonaCat.Network
|
|||
internal class HttpListenerAsyncResult : IAsyncResult
|
||||
{
|
||||
private readonly AsyncCallback _callback;
|
||||
private readonly object _sync;
|
||||
private readonly object _locker;
|
||||
private bool _completed;
|
||||
private HttpListenerContext _context;
|
||||
private Exception _exception;
|
||||
|
@ -27,7 +27,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
_callback = callback;
|
||||
AsyncState = state;
|
||||
_sync = new object();
|
||||
_locker = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,7 +42,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _waitHandle ??= new ManualResetEvent(_completed);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _completed;
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ namespace EonaCat.Network
|
|||
/// Gets or sets a value indicating whether the asynchronous operation is in progress.
|
||||
/// </summary>
|
||||
internal bool InGet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completes the asynchronous operation with the specified exception.
|
||||
/// </summary>
|
||||
|
@ -129,7 +130,7 @@ namespace EonaCat.Network
|
|||
// Private method to complete the asynchronous operation
|
||||
private static void complete(HttpListenerAsyncResult asyncResult)
|
||||
{
|
||||
lock (asyncResult._sync)
|
||||
lock (asyncResult._locker)
|
||||
{
|
||||
asyncResult._completed = true;
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace EonaCat.Network
|
|||
/// Gets or sets the <see cref="HttpListener"/> associated with the context.
|
||||
/// </summary>
|
||||
internal HttpListener Listener { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Accepts a WebSocket connection with the specified protocol.
|
||||
/// </summary>
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace EonaCat.Network
|
|||
: base(serializationInfo, streamingContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Win32 error code associated with this exception.
|
||||
/// </summary>
|
||||
|
|
|
@ -294,7 +294,6 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
internal bool CloseConnection { get; set; }
|
||||
|
||||
internal bool HeadersSent { get; set; }
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace EonaCat.Network
|
|||
internal class HttpStreamAsyncResult : IAsyncResult
|
||||
{
|
||||
private readonly AsyncCallback _callback;
|
||||
private readonly object _sync;
|
||||
private readonly object _locker;
|
||||
private bool _isCompleted;
|
||||
private ManualResetEvent _waitHandle;
|
||||
|
||||
|
@ -17,15 +17,16 @@ namespace EonaCat.Network
|
|||
{
|
||||
_callback = callback;
|
||||
AsyncState = state;
|
||||
_sync = new object();
|
||||
_locker = new object();
|
||||
}
|
||||
|
||||
public object AsyncState { get; }
|
||||
|
||||
public WaitHandle AsyncWaitHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _waitHandle ??= new ManualResetEvent(_isCompleted);
|
||||
}
|
||||
|
@ -33,11 +34,12 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public bool CompletedSynchronously => SyncRead == Count;
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _isCompleted;
|
||||
}
|
||||
|
@ -55,9 +57,10 @@ namespace EonaCat.Network
|
|||
internal int Offset { get; set; }
|
||||
|
||||
internal int SyncRead { get; set; }
|
||||
|
||||
internal void Complete()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_isCompleted)
|
||||
{
|
||||
|
|
|
@ -15,8 +15,9 @@ namespace EonaCat.Network
|
|||
internal sealed class HttpUtility
|
||||
{
|
||||
private static readonly char[] _hexChars = "0123456789abcdef".ToCharArray();
|
||||
private static readonly object _sync = new object();
|
||||
private static readonly object _locker = new object();
|
||||
private static Dictionary<string, char> _entities;
|
||||
|
||||
public static string HtmlAttributeEncode(string s)
|
||||
{
|
||||
if (s == null || s.Length == 0 || !s.Contains('&', '"', '<', '>'))
|
||||
|
@ -1212,7 +1213,7 @@ namespace EonaCat.Network
|
|||
|
||||
private static Dictionary<string, char> getEntities()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_entities == null)
|
||||
{
|
||||
|
@ -1234,6 +1235,7 @@ namespace EonaCat.Network
|
|||
? c - 'A' + 10
|
||||
: -1;
|
||||
}
|
||||
|
||||
private static bool notEncoded(char c)
|
||||
{
|
||||
return c == '!' ||
|
||||
|
|
|
@ -5,7 +5,6 @@ using EonaCat.Logger.SplunkServer;
|
|||
using EonaCat.Logger.Syslog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace EonaCat.Network
|
||||
{
|
||||
|
@ -26,10 +25,11 @@ namespace EonaCat.Network
|
|||
internal class Logger
|
||||
{
|
||||
private static readonly LogManager _logManager;
|
||||
|
||||
static Logger()
|
||||
{
|
||||
_logManager = new LogManager(new LoggerSettings { RemoveMessagePrefix = true });
|
||||
_logManager.OnException += _logManager_OnException;
|
||||
_logManager = new LogManager(new LoggerSettings());
|
||||
_logManager.OnException += LogManager_OnException;
|
||||
}
|
||||
|
||||
internal static bool DisableConsole { get; set; }
|
||||
|
@ -37,6 +37,7 @@ namespace EonaCat.Network
|
|||
internal static bool IsLoggingEnabled { get; set; }
|
||||
internal static string LoggingDirectory { get; private set; }
|
||||
private static bool HasBeenSetup { get; set; }
|
||||
|
||||
internal static void AddGrayLogServer(string hostname, int port)
|
||||
{
|
||||
_logManager.Settings.GrayLogServers.Add(new GrayLogServer(hostname, port));
|
||||
|
@ -69,6 +70,16 @@ namespace EonaCat.Network
|
|||
|
||||
internal static void Error(Exception exception, string message = null, bool isCriticalException = false, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null)
|
||||
{
|
||||
if (!IsLoggingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisableConsole)
|
||||
{
|
||||
writeToConsole = false;
|
||||
}
|
||||
|
||||
if (!HasBeenSetup)
|
||||
{
|
||||
Setup();
|
||||
|
@ -86,6 +97,16 @@ namespace EonaCat.Network
|
|||
|
||||
internal static void Error(string message = null, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1", bool isCriticalException = false)
|
||||
{
|
||||
if (!IsLoggingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisableConsole)
|
||||
{
|
||||
writeToConsole = false;
|
||||
}
|
||||
|
||||
if (!HasBeenSetup)
|
||||
{
|
||||
Setup();
|
||||
|
@ -169,7 +190,7 @@ namespace EonaCat.Network
|
|||
Write(message, ELogType.WARNING, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings);
|
||||
}
|
||||
|
||||
private static void _logManager_OnException(object? sender, ErrorMessage e)
|
||||
private static void LogManager_OnException(object? sender, ErrorMessage e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
if (e.Exception != null)
|
||||
|
@ -177,11 +198,12 @@ namespace EonaCat.Network
|
|||
Console.WriteLine(e.Exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Write(string message, ELogType logType, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null)
|
||||
{
|
||||
if (!HasBeenSetup)
|
||||
if (!IsLoggingEnabled)
|
||||
{
|
||||
Setup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisableConsole)
|
||||
|
@ -189,6 +211,11 @@ namespace EonaCat.Network
|
|||
writeToConsole = false;
|
||||
}
|
||||
|
||||
if (!HasBeenSetup)
|
||||
{
|
||||
Setup();
|
||||
}
|
||||
|
||||
if (grayLogSettings != null)
|
||||
{
|
||||
_logManager.Write(message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings.Facility, grayLogSettings.Source, grayLogSettings.Version);
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public bool CheckForCertificateRevocation { get; set; }
|
||||
|
||||
public LocalCertificateSelectionCallback ClientCertificateSelectionCallback
|
||||
{
|
||||
get
|
||||
|
|
|
@ -57,6 +57,7 @@ namespace EonaCat.Network
|
|||
|
||||
public bool IsClientCertificateRequired { get; set; }
|
||||
public SslProtocols SslProtocols { get; set; }
|
||||
|
||||
private static bool ValidateClientCertificate(
|
||||
object sender,
|
||||
X509Certificate certificate,
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace EonaCat.Network
|
|||
private int _count;
|
||||
private bool _disposed;
|
||||
private int _offset;
|
||||
|
||||
internal RequestStream(Stream stream, byte[] buffer, int offset, int count)
|
||||
: this(stream, buffer, offset, count, -1)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace EonaCat.Network
|
|||
private bool _sendChunked;
|
||||
private Stream _stream;
|
||||
private Action<byte[], int, int> _writeBody;
|
||||
|
||||
internal ResponseStream(
|
||||
Stream stream, HttpListenerResponse response, bool ignoreWriteExceptions)
|
||||
{
|
||||
|
@ -251,6 +252,7 @@ namespace EonaCat.Network
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeChunked(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var size = getChunkSizeBytes(count, false);
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
internal CloseEventArgs()
|
||||
{
|
||||
PayloadData = PayloadData.Empty;
|
||||
Payload = Payload.Empty;
|
||||
}
|
||||
|
||||
internal CloseEventArgs(ushort code)
|
||||
|
@ -22,14 +22,14 @@ namespace EonaCat.Network
|
|||
{
|
||||
}
|
||||
|
||||
internal CloseEventArgs(PayloadData payloadData)
|
||||
internal CloseEventArgs(Payload payload)
|
||||
{
|
||||
PayloadData = payloadData;
|
||||
Payload = payload;
|
||||
}
|
||||
|
||||
internal CloseEventArgs(ushort code, string reason)
|
||||
{
|
||||
PayloadData = new PayloadData(code, reason);
|
||||
Payload = new Payload(code, reason);
|
||||
}
|
||||
|
||||
internal CloseEventArgs(CloseStatusCode code, string reason)
|
||||
|
@ -37,9 +37,24 @@ namespace EonaCat.Network
|
|||
{
|
||||
}
|
||||
|
||||
public ushort Code => PayloadData.Code;
|
||||
public string Reason => PayloadData.Reason ?? string.Empty;
|
||||
/// <summary>
|
||||
/// Code
|
||||
/// </summary>
|
||||
public ushort Code => Payload.Code;
|
||||
|
||||
/// <summary>
|
||||
/// Reason
|
||||
/// </summary>
|
||||
public string Reason => Payload.Reason ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Determnines if both the client and server requests where handled
|
||||
/// </summary>
|
||||
public bool WasClean { get; internal set; }
|
||||
internal PayloadData PayloadData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Payload
|
||||
/// </summary>
|
||||
internal Payload Payload { get; }
|
||||
}
|
||||
}
|
|
@ -10,15 +10,16 @@ namespace EonaCat.Network
|
|||
private readonly byte[] _rawData;
|
||||
private string _data;
|
||||
private bool _dataSet;
|
||||
|
||||
internal MessageEventArgs(WSFrame frame)
|
||||
{
|
||||
Opcode = frame.Opcode;
|
||||
_rawData = frame.PayloadData.ApplicationData;
|
||||
_rawData = frame.Payload.ApplicationData;
|
||||
}
|
||||
|
||||
internal MessageEventArgs(OperationCode opcode, byte[] rawData)
|
||||
{
|
||||
if ((ulong)rawData.LongLength > PayloadData.MaxLength)
|
||||
if ((ulong)rawData.LongLength > Payload.MaxLength)
|
||||
{
|
||||
throw new WSException(CloseStatusCode.TooBig);
|
||||
}
|
||||
|
@ -39,6 +40,7 @@ namespace EonaCat.Network
|
|||
public bool IsBinary => Opcode == OperationCode.Binary;
|
||||
public bool IsPing => Opcode == OperationCode.Ping;
|
||||
public bool IsText => Opcode == OperationCode.Text;
|
||||
|
||||
public byte[] RawData
|
||||
{
|
||||
get
|
||||
|
@ -49,6 +51,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
internal OperationCode Opcode { get; }
|
||||
|
||||
private void setData()
|
||||
{
|
||||
if (_dataSet)
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace EonaCat.Network
|
|||
private const int BUFFER_SIZE = 1024;
|
||||
private static readonly byte[] _last = new byte[] { 0x00 };
|
||||
private static readonly int _retry = 5;
|
||||
|
||||
public static bool Contains(this string value, params char[] chars)
|
||||
{
|
||||
return chars == null || chars.Length == 0
|
||||
|
@ -55,20 +56,14 @@ namespace EonaCat.Network
|
|||
|
||||
public static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
|
||||
{
|
||||
if (eventHandler != null)
|
||||
{
|
||||
eventHandler(sender, e);
|
||||
}
|
||||
eventHandler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
public static void Emit<TEventArgs>(
|
||||
this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
|
||||
where TEventArgs : EventArgs
|
||||
{
|
||||
if (eventHandler != null)
|
||||
{
|
||||
eventHandler(sender, e);
|
||||
}
|
||||
eventHandler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
public static CookieCollection GetCookies(this NameValueCollection headers, bool response)
|
||||
|
@ -161,7 +156,7 @@ namespace EonaCat.Network
|
|||
|
||||
/// <summary>
|
||||
/// An extension method to determine if an IP address is internal
|
||||
/// Class A Private IP Range: 10.0.0.0 ? 10.255.255.255
|
||||
/// Class A Private IP Range: 10.0.0.0 ? 10.255.255.255
|
||||
/// Class B Private IP Range: 172.16.0.0 ? 172.31.255.255
|
||||
/// Class C Private IP Range: 192.168.0.0 ? 192.168.255.25
|
||||
/// </summary>
|
||||
|
@ -174,10 +169,13 @@ namespace EonaCat.Network
|
|||
{
|
||||
case 10:
|
||||
return true;
|
||||
|
||||
case 172:
|
||||
return bytes[1] < 32 && bytes[1] >= 16;
|
||||
|
||||
case 192:
|
||||
return bytes[1] == 168;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -741,10 +739,7 @@ namespace EonaCat.Network
|
|||
var nread = source.EndRead(ar);
|
||||
if (nread <= 0)
|
||||
{
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -754,10 +749,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -767,10 +759,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -839,7 +828,7 @@ namespace EonaCat.Network
|
|||
? "A policy violation has occurred."
|
||||
: code == CloseStatusCode.TooBig
|
||||
? "A too big message has been received."
|
||||
: code == CloseStatusCode.MandatoryExtension
|
||||
: code == CloseStatusCode.MissingExtension
|
||||
? "WebSocket client didn't receive expected extension(s)."
|
||||
: code == CloseStatusCode.ServerError
|
||||
? "WebSocket server got an internal error."
|
||||
|
@ -1101,10 +1090,7 @@ namespace EonaCat.Network
|
|||
|
||||
if (nread == length)
|
||||
{
|
||||
if (completed != null)
|
||||
{
|
||||
completed(buff.SubArray(0, offset + nread));
|
||||
}
|
||||
completed?.Invoke(buff.SubArray(0, offset + nread));
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1118,10 +1104,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1131,10 +1114,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1199,10 +1179,7 @@ namespace EonaCat.Network
|
|||
catch (Exception ex)
|
||||
{
|
||||
dest.Dispose();
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
},
|
||||
null
|
||||
|
@ -1216,10 +1193,7 @@ namespace EonaCat.Network
|
|||
catch (Exception ex)
|
||||
{
|
||||
dest.Dispose();
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1558,20 +1532,14 @@ namespace EonaCat.Network
|
|||
bufferLength,
|
||||
() =>
|
||||
{
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
|
||||
input.Dispose();
|
||||
},
|
||||
ex =>
|
||||
{
|
||||
input.Dispose();
|
||||
if (error != null)
|
||||
{
|
||||
error(ex);
|
||||
}
|
||||
error?.Invoke(ex);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace EonaCat.Network
|
||||
{
|
||||
internal class PayloadData : IEnumerable<byte>
|
||||
internal class Payload : IEnumerable<byte>
|
||||
{
|
||||
private ushort _code;
|
||||
private bool _codeSet;
|
||||
|
@ -16,17 +16,17 @@ namespace EonaCat.Network
|
|||
private string _reason;
|
||||
private bool _reasonSet;
|
||||
|
||||
public static readonly PayloadData Empty;
|
||||
public static readonly Payload Empty;
|
||||
|
||||
public static readonly ulong MaxLength;
|
||||
|
||||
static PayloadData()
|
||||
static Payload()
|
||||
{
|
||||
Empty = new PayloadData();
|
||||
Empty = new Payload();
|
||||
MaxLength = long.MaxValue;
|
||||
}
|
||||
|
||||
internal PayloadData()
|
||||
internal Payload()
|
||||
{
|
||||
_code = (ushort)CloseStatusCode.NoStatus;
|
||||
_reason = string.Empty;
|
||||
|
@ -37,7 +37,7 @@ namespace EonaCat.Network
|
|||
_reasonSet = true;
|
||||
}
|
||||
|
||||
internal PayloadData(PayloadData original)
|
||||
internal Payload(Payload original)
|
||||
{
|
||||
_code = original._code;
|
||||
_codeSet = original._codeSet;
|
||||
|
@ -48,22 +48,20 @@ namespace EonaCat.Network
|
|||
|
||||
_data = new byte[_length];
|
||||
original._data.CopyTo(_data, 0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal PayloadData(byte[] data)
|
||||
internal Payload(byte[] data)
|
||||
: this(data, data.LongLength)
|
||||
{
|
||||
}
|
||||
|
||||
internal PayloadData(byte[] data, long length)
|
||||
internal Payload(byte[] data, long length)
|
||||
{
|
||||
_data = data;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
internal PayloadData(ushort code, string reason)
|
||||
internal Payload(ushort code, string reason)
|
||||
{
|
||||
_code = code;
|
||||
_reason = reason ?? string.Empty;
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace EonaCat.Network
|
||||
{
|
||||
internal enum Rsv : byte
|
||||
internal enum ReservedBits : byte
|
||||
{
|
||||
Off = 0x0,
|
||||
|
|
@ -44,7 +44,7 @@ namespace EonaCat.Network
|
|||
throw new ArgumentException("It contains '..'.", nameof(path));
|
||||
}
|
||||
|
||||
tryReadFile(createFilePath(path), out byte[] contents);
|
||||
tryReadFile(CreateFilePath(path), out byte[] contents);
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ namespace EonaCat.Network
|
|||
throw new ArgumentException("It contains '..'.", nameof(path));
|
||||
}
|
||||
|
||||
return tryReadFile(createFilePath(path), out contents);
|
||||
return tryReadFile(CreateFilePath(path), out contents);
|
||||
}
|
||||
|
||||
private static bool tryReadFile(string path, out byte[] contents)
|
||||
|
@ -90,7 +90,7 @@ namespace EonaCat.Network
|
|||
return true;
|
||||
}
|
||||
|
||||
private string createFilePath(string childPath)
|
||||
private string CreateFilePath(string childPath)
|
||||
{
|
||||
childPath = childPath.TrimStart('/', '\\');
|
||||
return new StringBuilder(_docRootPath, 32)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace EonaCat.Network
|
||||
|
@ -17,7 +16,8 @@ namespace EonaCat.Network
|
|||
private HttpListener _listener;
|
||||
private Thread _receiveThread;
|
||||
private volatile ServerState _state;
|
||||
private object _sync;
|
||||
private object _locker;
|
||||
|
||||
public HttpServer()
|
||||
{
|
||||
init("*", System.Net.IPAddress.Any, 80, false);
|
||||
|
@ -158,7 +158,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -234,7 +234,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -256,15 +256,17 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
return WebSocketServices.AutoCleanSessions;
|
||||
return WSEndpoints.AutoCleanSessions;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
WebSocketServices.AutoCleanSessions = value;
|
||||
WSEndpoints.AutoCleanSessions = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Port { get; private set; }
|
||||
|
||||
public string Realm
|
||||
{
|
||||
get
|
||||
|
@ -280,7 +282,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -308,7 +310,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -350,7 +352,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -363,24 +365,25 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
public TimeSpan WaitTime
|
||||
public TimeSpan ResponseWaitingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return WebSocketServices.WaitTime;
|
||||
return WSEndpoints.ResponseWaitingTime;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
WebSocketServices.WaitTime = value;
|
||||
WSEndpoints.ResponseWaitingTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
public WSEndpointManager WebSocketServices { get; private set; }
|
||||
public WSEndpointManager WSEndpoints { get; private set; }
|
||||
|
||||
public void AddWebSocketService<TEndpoint>(string path)
|
||||
where TEndpoint : WSEndpoint, new()
|
||||
{
|
||||
WebSocketServices.AddService<TEndpoint>(path, null);
|
||||
WSEndpoints.AddEndpoint<TEndpoint>(path, null);
|
||||
}
|
||||
|
||||
public void AddWebSocketService<TEndpoint>(
|
||||
|
@ -388,12 +391,12 @@ namespace EonaCat.Network
|
|||
)
|
||||
where TEndpoint : WSEndpoint, new()
|
||||
{
|
||||
WebSocketServices.AddService(path, initializer);
|
||||
WSEndpoints.AddEndpoint(path, initializer);
|
||||
}
|
||||
|
||||
public bool RemoveWebSocketService(string path)
|
||||
{
|
||||
return WebSocketServices.RemoveService(path);
|
||||
return WSEndpoints.RemoveEndpoint(path);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
|
@ -422,9 +425,9 @@ namespace EonaCat.Network
|
|||
throw new ArgumentOutOfRangeException(nameof(code), message);
|
||||
}
|
||||
|
||||
if (code == (ushort)CloseStatusCode.MandatoryExtension)
|
||||
if (code == (ushort)CloseStatusCode.MissingExtension)
|
||||
{
|
||||
var message = $"{(ushort)CloseStatusCode.MandatoryExtension} cannot be used.";
|
||||
var message = $"{(ushort)CloseStatusCode.MissingExtension} cannot be used.";
|
||||
throw new ArgumentException(message, nameof(code));
|
||||
}
|
||||
|
||||
|
@ -454,7 +457,7 @@ namespace EonaCat.Network
|
|||
|
||||
public void Stop(CloseStatusCode code, string reason)
|
||||
{
|
||||
if (code == CloseStatusCode.MandatoryExtension)
|
||||
if (code == CloseStatusCode.MissingExtension)
|
||||
{
|
||||
var message = "MandatoryExtension cannot be used.";
|
||||
throw new ArgumentException(message, nameof(code));
|
||||
|
@ -548,7 +551,7 @@ namespace EonaCat.Network
|
|||
|
||||
private void abort()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -562,7 +565,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
try
|
||||
{
|
||||
WebSocketServices.Stop((ushort)CloseStatusCode.Abnormal, string.Empty);
|
||||
WSEndpoints.Stop((ushort)CloseStatusCode.Abnormal, string.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -620,6 +623,7 @@ namespace EonaCat.Network
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void init(
|
||||
string hostname, System.Net.IPAddress address, int port, bool secure
|
||||
)
|
||||
|
@ -631,8 +635,8 @@ namespace EonaCat.Network
|
|||
|
||||
_docRootPath = "./Public";
|
||||
_listener = createListener(_hostname, Port, IsSecure);
|
||||
WebSocketServices = new WSEndpointManager();
|
||||
_sync = new object();
|
||||
WSEndpoints = new WSEndpointManager();
|
||||
_locker = new object();
|
||||
}
|
||||
|
||||
private void processRequest(HttpListenerContext context)
|
||||
|
@ -674,7 +678,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
var path = context.RequestUri.AbsolutePath;
|
||||
|
||||
if (!WebSocketServices.InternalTryGetServiceHost(path, out WSEndpointHost host))
|
||||
if (!WSEndpoints.InternalTryGetEndpointHost(path, out WSEndpointHost host))
|
||||
{
|
||||
context.Close(HttpStatusCode.NotImplemented);
|
||||
return;
|
||||
|
@ -687,29 +691,29 @@ namespace EonaCat.Network
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
HttpListenerContext ctx = null;
|
||||
HttpListenerContext context = null;
|
||||
try
|
||||
{
|
||||
ctx = _listener.GetContext();
|
||||
context = _listener.GetContext();
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
state =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ctx.Request.IsUpgradeTo("websocket"))
|
||||
if (context.Request.IsUpgradeTo("websocket"))
|
||||
{
|
||||
processRequest(ctx.AcceptWebSocket(null));
|
||||
processRequest(context.AcceptWebSocket(null));
|
||||
return;
|
||||
}
|
||||
|
||||
processRequest(ctx);
|
||||
processRequest(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex.Message);
|
||||
Logger.Debug(ex.ToString());
|
||||
|
||||
ctx.Connection.Close(true);
|
||||
context.Connection.Close(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -729,7 +733,7 @@ namespace EonaCat.Network
|
|||
Logger.Error(ex.Message);
|
||||
Logger.Debug(ex.ToString());
|
||||
|
||||
ctx?.Connection.Close(true);
|
||||
context?.Connection.Close(true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -755,7 +759,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state == ServerState.Start)
|
||||
{
|
||||
|
@ -769,7 +773,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
WebSocketServices.Start();
|
||||
WSEndpoints.Start();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -777,7 +781,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
catch
|
||||
{
|
||||
WebSocketServices.Stop((ushort)CloseStatusCode.ServerError, string.Empty);
|
||||
WSEndpoints.Stop((ushort)CloseStatusCode.ServerError, string.Empty);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -822,7 +826,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state == ServerState.ShuttingDown)
|
||||
{
|
||||
|
@ -844,7 +848,7 @@ namespace EonaCat.Network
|
|||
var threw = false;
|
||||
try
|
||||
{
|
||||
WebSocketServices.Stop(code, reason);
|
||||
WSEndpoints.Stop(code, reason);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace EonaCat.Network
|
|||
|
||||
public WSContext Context { get; private set; }
|
||||
public Func<CookieCollection, CookieCollection, bool> CookiesValidator { get; set; }
|
||||
|
||||
public bool EmitOnPing
|
||||
{
|
||||
get
|
||||
|
@ -41,6 +42,7 @@ namespace EonaCat.Network
|
|||
public string ID { get; private set; }
|
||||
public bool IgnoreExtensions { get; set; }
|
||||
public Func<string, bool> OriginValidator { get; set; }
|
||||
|
||||
public string Protocol
|
||||
{
|
||||
get
|
||||
|
@ -67,6 +69,7 @@ namespace EonaCat.Network
|
|||
public DateTime StartTime { get; private set; }
|
||||
public WSState State => _websocket != null ? _websocket.ReadyState : WSState.Connecting;
|
||||
protected WSSessionManager Sessions { get; private set; }
|
||||
|
||||
internal void Start(WSContext context, WSSessionManager sessions)
|
||||
{
|
||||
if (_websocket != null)
|
||||
|
@ -86,10 +89,10 @@ namespace EonaCat.Network
|
|||
_websocket.IgnoreExtensions = IgnoreExtensions;
|
||||
_websocket.Protocol = _protocol;
|
||||
|
||||
var waitTime = sessions.WaitTime;
|
||||
if (waitTime != _websocket.WaitTime)
|
||||
var responseWaitingTime = sessions.ResponseWaitingTime;
|
||||
if (responseWaitingTime != _websocket.ResponseWaitingTime)
|
||||
{
|
||||
_websocket.WaitTime = waitTime;
|
||||
_websocket.ResponseWaitingTime = responseWaitingTime;
|
||||
}
|
||||
|
||||
_websocket.OnConnect += onOpen;
|
||||
|
|
|
@ -14,7 +14,8 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public abstract Type EndpointType { get; }
|
||||
public bool KeepClean
|
||||
|
||||
public bool AutoCleanSessions
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -29,20 +30,22 @@ namespace EonaCat.Network
|
|||
|
||||
public string Path { get; }
|
||||
public WSSessionManager Sessions { get; }
|
||||
public TimeSpan WaitTime
|
||||
|
||||
public TimeSpan ResponseWaitingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return Sessions.WaitTime;
|
||||
return Sessions.ResponseWaitingTime;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Sessions.WaitTime = value;
|
||||
Sessions.ResponseWaitingTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal ServerState State => Sessions.State;
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
Sessions.Start();
|
||||
|
|
|
@ -5,25 +5,24 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace EonaCat.Network
|
||||
{
|
||||
public class WSEndpointManager
|
||||
{
|
||||
private readonly Dictionary<string, WSEndpointHost> _hosts;
|
||||
private readonly object _sync;
|
||||
private readonly object _locker;
|
||||
private volatile bool _clean;
|
||||
private volatile ServerState _state;
|
||||
private TimeSpan _waitTime;
|
||||
private TimeSpan _responseWaitingTime;
|
||||
|
||||
internal WSEndpointManager()
|
||||
{
|
||||
_clean = true;
|
||||
_hosts = new Dictionary<string, WSEndpointHost>();
|
||||
_state = ServerState.Started;
|
||||
_sync = ((ICollection)_hosts).SyncRoot;
|
||||
_waitTime = TimeSpan.FromSeconds(1);
|
||||
_locker = ((ICollection)_hosts).SyncRoot;
|
||||
_responseWaitingTime = TimeSpan.FromSeconds(1);
|
||||
}
|
||||
|
||||
public bool AutoCleanSessions
|
||||
|
@ -41,7 +40,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -51,7 +50,7 @@ namespace EonaCat.Network
|
|||
|
||||
foreach (var host in _hosts.Values)
|
||||
{
|
||||
host.KeepClean = value;
|
||||
host.AutoCleanSessions = value;
|
||||
}
|
||||
|
||||
_clean = value;
|
||||
|
@ -63,7 +62,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _hosts.Count;
|
||||
}
|
||||
|
@ -74,7 +73,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _hosts.Values.ToList();
|
||||
}
|
||||
|
@ -85,18 +84,18 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _hosts.Keys.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan WaitTime
|
||||
public TimeSpan ResponseWaitingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _waitTime;
|
||||
return _responseWaitingTime;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -112,7 +111,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -122,10 +121,10 @@ namespace EonaCat.Network
|
|||
|
||||
foreach (var host in _hosts.Values)
|
||||
{
|
||||
host.WaitTime = value;
|
||||
host.ResponseWaitingTime = value;
|
||||
}
|
||||
|
||||
_waitTime = value;
|
||||
_responseWaitingTime = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,12 +154,13 @@ namespace EonaCat.Network
|
|||
throw new ArgumentException(message, nameof(path));
|
||||
}
|
||||
|
||||
InternalTryGetServiceHost(path, out WSEndpointHost host);
|
||||
InternalTryGetEndpointHost(path, out WSEndpointHost host);
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
public void AddService<TEndpoint>(
|
||||
|
||||
public void AddEndpoint<TEndpoint>(
|
||||
string path, Action<TEndpoint> initializer
|
||||
)
|
||||
where TEndpoint : WSEndpoint, new()
|
||||
|
@ -188,7 +188,7 @@ namespace EonaCat.Network
|
|||
|
||||
path = HttpUtility.UrlDecode(path).TrimSlashFromEnd();
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_hosts.TryGetValue(path, out WSEndpointHost host))
|
||||
{
|
||||
|
@ -201,12 +201,12 @@ namespace EonaCat.Network
|
|||
|
||||
if (!_clean)
|
||||
{
|
||||
host.KeepClean = false;
|
||||
host.AutoCleanSessions = false;
|
||||
}
|
||||
|
||||
if (_waitTime != host.WaitTime)
|
||||
if (_responseWaitingTime != host.ResponseWaitingTime)
|
||||
{
|
||||
host.WaitTime = _waitTime;
|
||||
host.ResponseWaitingTime = _responseWaitingTime;
|
||||
}
|
||||
|
||||
if (_state == ServerState.Start)
|
||||
|
@ -222,7 +222,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
List<WSEndpointHost> hosts = null;
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
hosts = _hosts.Values.ToList();
|
||||
_hosts.Clear();
|
||||
|
@ -237,7 +237,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
public bool RemoveService(string path)
|
||||
public bool RemoveEndpoint(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
|
@ -263,7 +263,7 @@ namespace EonaCat.Network
|
|||
path = HttpUtility.UrlDecode(path).TrimSlashFromEnd();
|
||||
|
||||
WSEndpointHost host;
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!_hosts.TryGetValue(path, out host))
|
||||
{
|
||||
|
@ -281,7 +281,7 @@ namespace EonaCat.Network
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetServiceHost(string path, out WSEndpointHost host)
|
||||
public bool TryGetEndpointHost(string path, out WSEndpointHost host)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ namespace EonaCat.Network
|
|||
throw new ArgumentException(message, nameof(path));
|
||||
}
|
||||
|
||||
return InternalTryGetServiceHost(path, out host);
|
||||
return InternalTryGetEndpointHost(path, out host);
|
||||
}
|
||||
|
||||
internal void Add<TEndpoint>(string path, Func<TEndpoint> creator)
|
||||
|
@ -312,7 +312,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
path = HttpUtility.UrlDecode(path).TrimSlashFromEnd();
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_hosts.TryGetValue(path, out WSEndpointHost host))
|
||||
{
|
||||
|
@ -325,12 +325,12 @@ namespace EonaCat.Network
|
|||
|
||||
if (!_clean)
|
||||
{
|
||||
host.KeepClean = false;
|
||||
host.AutoCleanSessions = false;
|
||||
}
|
||||
|
||||
if (_waitTime != host.WaitTime)
|
||||
if (_responseWaitingTime != host.ResponseWaitingTime)
|
||||
{
|
||||
host.WaitTime = _waitTime;
|
||||
host.ResponseWaitingTime = _responseWaitingTime;
|
||||
}
|
||||
|
||||
if (_state == ServerState.Start)
|
||||
|
@ -342,13 +342,13 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
internal bool InternalTryGetServiceHost(
|
||||
internal bool InternalTryGetEndpointHost(
|
||||
string path, out WSEndpointHost host
|
||||
)
|
||||
{
|
||||
path = HttpUtility.UrlDecode(path).TrimSlashFromEnd();
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _hosts.TryGetValue(path, out host);
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ namespace EonaCat.Network
|
|||
|
||||
internal void Start()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
foreach (var host in _hosts.Values)
|
||||
{
|
||||
|
@ -369,7 +369,7 @@ namespace EonaCat.Network
|
|||
|
||||
internal void Stop(ushort code, string reason)
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
_state = ServerState.ShuttingDown;
|
||||
|
||||
|
@ -399,10 +399,7 @@ namespace EonaCat.Network
|
|||
host.Sessions.Broadcast(opcode, data, cache);
|
||||
}
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -431,10 +428,7 @@ namespace EonaCat.Network
|
|||
host.Sessions.Broadcast(opcode, stream, cache);
|
||||
}
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
public class WSServer
|
||||
{
|
||||
private static readonly string _defaultRealm;
|
||||
private static readonly string _defaultRealm = "EonaCat Network - Area51";
|
||||
private bool _allowForwardedRequest;
|
||||
private AuthenticationSchemes _authSchemes;
|
||||
private bool _dnsStyle;
|
||||
|
@ -23,12 +23,8 @@ namespace EonaCat.Network
|
|||
private SSLConfigServer _sslConfig;
|
||||
private SSLConfigServer _sslConfigInUse;
|
||||
private volatile ServerState _state;
|
||||
private object _sync;
|
||||
private object _locker;
|
||||
private Func<IIdentity, NetworkCredential> _userCredentialsFinder;
|
||||
static WSServer()
|
||||
{
|
||||
_defaultRealm = "SECRET AREA";
|
||||
}
|
||||
|
||||
public WSServer()
|
||||
{
|
||||
|
@ -115,6 +111,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public System.Net.IPAddress Address { get; private set; }
|
||||
|
||||
public bool AllowForwardedRequest
|
||||
{
|
||||
get
|
||||
|
@ -130,7 +127,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!CanSet(out message))
|
||||
{
|
||||
|
@ -158,7 +155,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!CanSet(out message))
|
||||
{
|
||||
|
@ -188,6 +185,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
public WSEndpointManager Endpoints { get; private set; }
|
||||
|
||||
public Func<IIdentity, NetworkCredential> FindCredentials
|
||||
{
|
||||
get
|
||||
|
@ -203,7 +201,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!CanSet(out message))
|
||||
{
|
||||
|
@ -237,7 +235,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!CanSet(out message))
|
||||
{
|
||||
|
@ -265,7 +263,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!CanSet(out message))
|
||||
{
|
||||
|
@ -291,31 +289,33 @@ namespace EonaCat.Network
|
|||
return GetSSLConfig();
|
||||
}
|
||||
}
|
||||
public TimeSpan WaitTime
|
||||
|
||||
public TimeSpan ResponseWaitingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return Endpoints.WaitTime;
|
||||
return Endpoints.ResponseWaitingTime;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Endpoints.WaitTime = value;
|
||||
Endpoints.ResponseWaitingTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEndpoint<TEndpoint>(string path) where TEndpoint : WSEndpoint, new()
|
||||
{
|
||||
Endpoints.AddService<TEndpoint>(path, null);
|
||||
Endpoints.AddEndpoint<TEndpoint>(path, null);
|
||||
}
|
||||
|
||||
public void AddEndpoint<TEndpoint>(string path, Action<TEndpoint> initializer) where TEndpoint : WSEndpoint, new()
|
||||
{
|
||||
Endpoints.AddService(path, initializer);
|
||||
Endpoints.AddEndpoint(path, initializer);
|
||||
}
|
||||
|
||||
public bool RemoveEndpoint(string path)
|
||||
{
|
||||
return Endpoints.RemoveService(path);
|
||||
return Endpoints.RemoveEndpoint(path);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
|
@ -348,9 +348,9 @@ namespace EonaCat.Network
|
|||
throw new ArgumentOutOfRangeException(nameof(code), message);
|
||||
}
|
||||
|
||||
if (code == (ushort)CloseStatusCode.MandatoryExtension)
|
||||
if (code == (ushort)CloseStatusCode.MissingExtension)
|
||||
{
|
||||
var message = $"{(ushort)CloseStatusCode.MandatoryExtension} cannot be used.";
|
||||
var message = $"{(ushort)CloseStatusCode.MissingExtension} cannot be used.";
|
||||
throw new ArgumentException(message, nameof(code));
|
||||
}
|
||||
|
||||
|
@ -380,7 +380,7 @@ namespace EonaCat.Network
|
|||
|
||||
public void Stop(CloseStatusCode code, string reason)
|
||||
{
|
||||
if (code == CloseStatusCode.MandatoryExtension)
|
||||
if (code == CloseStatusCode.MissingExtension)
|
||||
{
|
||||
var message = "MandatoryExtension cannot be used.";
|
||||
throw new ArgumentException(message, nameof(code));
|
||||
|
@ -446,7 +446,7 @@ namespace EonaCat.Network
|
|||
|
||||
private void abort()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -499,6 +499,7 @@ namespace EonaCat.Network
|
|||
|| Uri.CheckHostName(name) != UriHostNameType.Dns
|
||||
|| name == _hostname;
|
||||
}
|
||||
|
||||
private string GetRealm()
|
||||
{
|
||||
var realm = _realm;
|
||||
|
@ -525,7 +526,7 @@ namespace EonaCat.Network
|
|||
_dnsStyle = Uri.CheckHostName(hostname) == UriHostNameType.Dns;
|
||||
_listener = new TcpListener(address, port);
|
||||
Endpoints = new WSEndpointManager();
|
||||
_sync = new object();
|
||||
_locker = new object();
|
||||
}
|
||||
|
||||
private void processRequest(TcpListenerWSContext context)
|
||||
|
@ -552,7 +553,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
if (!Endpoints.InternalTryGetServiceHost(uri.AbsolutePath, out WSEndpointHost host))
|
||||
if (!Endpoints.InternalTryGetEndpointHost(uri.AbsolutePath, out WSEndpointHost host))
|
||||
{
|
||||
context.Close(HttpStatusCode.NotImplemented);
|
||||
return;
|
||||
|
@ -574,15 +575,15 @@ namespace EonaCat.Network
|
|||
{
|
||||
try
|
||||
{
|
||||
var ctx = new TcpListenerWSContext(
|
||||
var context = new TcpListenerWSContext(
|
||||
cl, null, IsSecure, _sslConfigInUse);
|
||||
|
||||
if (!ctx.Authenticate(_authSchemes, _realmInUse, _userCredentialsFinder))
|
||||
if (!context.Authenticate(_authSchemes, _realmInUse, _userCredentialsFinder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
processRequest(ctx);
|
||||
processRequest(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -641,7 +642,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state == ServerState.Start)
|
||||
{
|
||||
|
@ -717,7 +718,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state == ServerState.ShuttingDown)
|
||||
{
|
||||
|
|
|
@ -12,25 +12,26 @@ namespace EonaCat.Network
|
|||
{
|
||||
public class WSSessionManager
|
||||
{
|
||||
private readonly object _forSweep;
|
||||
private const int _cleanupIntervalInMiliSeconds = 60000;
|
||||
private readonly object _cleanLocker;
|
||||
private readonly Dictionary<string, IWSSession> _sessions;
|
||||
private readonly object _sync;
|
||||
private readonly object _locker;
|
||||
private volatile bool _clean;
|
||||
private volatile ServerState _state;
|
||||
private volatile bool _sweeping;
|
||||
private System.Timers.Timer _sweepTimer;
|
||||
private TimeSpan _waitTime;
|
||||
private TimeSpan _responseWaitingTime;
|
||||
|
||||
internal WSSessionManager()
|
||||
{
|
||||
_clean = true;
|
||||
_forSweep = new object();
|
||||
_cleanLocker = new object();
|
||||
_sessions = new Dictionary<string, IWSSession>();
|
||||
_state = ServerState.Started;
|
||||
_sync = ((ICollection)_sessions).SyncRoot;
|
||||
_waitTime = TimeSpan.FromSeconds(1);
|
||||
_locker = ((ICollection)_sessions).SyncRoot;
|
||||
_responseWaitingTime = TimeSpan.FromSeconds(1);
|
||||
|
||||
setSweepTimer(60000);
|
||||
setCleanupTimer(_cleanupIntervalInMiliSeconds);
|
||||
}
|
||||
|
||||
public IEnumerable<string> ActiveIDs
|
||||
|
@ -51,7 +52,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _sessions.Count;
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ namespace EonaCat.Network
|
|||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -108,7 +109,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -130,7 +131,7 @@ namespace EonaCat.Network
|
|||
return Enumerable.Empty<IWSSession>();
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -142,11 +143,11 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
public TimeSpan WaitTime
|
||||
internal TimeSpan ResponseWaitingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _waitTime;
|
||||
return _responseWaitingTime;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -162,7 +163,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (!canSet(out message))
|
||||
{
|
||||
|
@ -170,12 +171,13 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
_waitTime = value;
|
||||
_responseWaitingTime = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ServerState State => _state;
|
||||
|
||||
public IWSSession this[string id]
|
||||
{
|
||||
get
|
||||
|
@ -195,6 +197,7 @@ namespace EonaCat.Network
|
|||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
public void Broadcast(byte[] data)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
|
@ -538,7 +541,7 @@ namespace EonaCat.Network
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_forSweep)
|
||||
lock (_cleanLocker)
|
||||
{
|
||||
if (_sweeping)
|
||||
{
|
||||
|
@ -556,7 +559,7 @@ namespace EonaCat.Network
|
|||
break;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -602,7 +605,7 @@ namespace EonaCat.Network
|
|||
|
||||
internal string Add(IWSSession session)
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
@ -624,7 +627,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -640,7 +643,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -658,7 +661,7 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -671,7 +674,7 @@ namespace EonaCat.Network
|
|||
|
||||
internal bool Remove(string id)
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
return _sessions.Remove(id);
|
||||
}
|
||||
|
@ -679,7 +682,7 @@ namespace EonaCat.Network
|
|||
|
||||
internal void Start()
|
||||
{
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
_sweepTimer.Enabled = _clean;
|
||||
_state = ServerState.Start;
|
||||
|
@ -690,11 +693,11 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (code == (ushort)CloseStatusCode.NoStatus)
|
||||
{ // == no status
|
||||
stop(PayloadData.Empty, true);
|
||||
stop(Payload.Empty, true);
|
||||
return;
|
||||
}
|
||||
|
||||
stop(new PayloadData(code, reason), !code.IsReserved());
|
||||
stop(new Payload(code, reason), !code.IsReserved());
|
||||
}
|
||||
|
||||
private static string createID()
|
||||
|
@ -712,17 +715,14 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
session.Context.WebSocket.Send(opcode, data, cache);
|
||||
}
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -745,17 +745,14 @@ namespace EonaCat.Network
|
|||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
session.Context.WebSocket.Send(opcode, stream, cache);
|
||||
}
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
completed();
|
||||
}
|
||||
completed?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -789,21 +786,21 @@ namespace EonaCat.Network
|
|||
|
||||
private Dictionary<string, bool> broadping(byte[] frameAsBytes)
|
||||
{
|
||||
var ret = new Dictionary<string, bool>();
|
||||
var result = new Dictionary<string, bool>();
|
||||
|
||||
foreach (var session in Sessions)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
Logger.Error("The service is shutting down.");
|
||||
Logger.Error("The endpoint is shutting down.");
|
||||
break;
|
||||
}
|
||||
|
||||
var res = session.Context.WebSocket.Ping(frameAsBytes, _waitTime);
|
||||
ret.Add(session.ID, res);
|
||||
var pingResult = session.Context.WebSocket.Ping(frameAsBytes, _responseWaitingTime);
|
||||
result.Add(session.ID, pingResult);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool canSet(out string message)
|
||||
|
@ -812,38 +809,39 @@ namespace EonaCat.Network
|
|||
|
||||
if (_state == ServerState.Start)
|
||||
{
|
||||
message = "The service has already started.";
|
||||
message = "The endpoint has already started.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_state == ServerState.ShuttingDown)
|
||||
{
|
||||
message = "The service is shutting down.";
|
||||
message = "The endpoint is shutting down.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private void setSweepTimer(double interval)
|
||||
|
||||
private void setCleanupTimer(double interval)
|
||||
{
|
||||
_sweepTimer = new System.Timers.Timer(interval);
|
||||
_sweepTimer.Elapsed += (sender, e) => Sweep();
|
||||
}
|
||||
|
||||
private void stop(PayloadData payloadData, bool send)
|
||||
private void stop(Payload payload, bool send)
|
||||
{
|
||||
var bytes = send
|
||||
? WSFrame.CreateCloseFrame(payloadData, false).ToArray()
|
||||
? WSFrame.CreateCloseFrame(payload, false).ToArray()
|
||||
: null;
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
_state = ServerState.ShuttingDown;
|
||||
|
||||
_sweepTimer.Enabled = false;
|
||||
foreach (var session in _sessions.Values.ToList())
|
||||
{
|
||||
session.Context.WebSocket.Close(payloadData, bytes);
|
||||
session.Context.WebSocket.Close(payload, bytes);
|
||||
}
|
||||
|
||||
_state = ServerState.Stop;
|
||||
|
@ -859,7 +857,7 @@ namespace EonaCat.Network
|
|||
return false;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
lock (_locker)
|
||||
{
|
||||
if (_state != ServerState.Start)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,32 +13,33 @@ namespace EonaCat.Network
|
|||
{
|
||||
internal static readonly byte[] EmptyPingBytes;
|
||||
private const int BUFFER_SIZE = 1024;
|
||||
|
||||
static WSFrame()
|
||||
{
|
||||
EmptyPingBytes = CreatePingFrame(false).ToArray();
|
||||
}
|
||||
|
||||
internal WSFrame(OperationCode opcode, PayloadData payloadData, bool mask)
|
||||
: this(FinalFrame.Final, opcode, payloadData, false, mask)
|
||||
internal WSFrame(OperationCode opcode, Payload payload, bool mask)
|
||||
: this(FinalFrame.Final, opcode, payload, false, mask)
|
||||
{
|
||||
}
|
||||
|
||||
internal WSFrame(FinalFrame finalFrame, OperationCode opcode, byte[] data, bool compressed, bool mask)
|
||||
: this(finalFrame, opcode, new PayloadData(data), compressed, mask)
|
||||
: this(finalFrame, opcode, new Payload(data), compressed, mask)
|
||||
{
|
||||
}
|
||||
|
||||
internal WSFrame(
|
||||
FinalFrame fin, OperationCode opcode, PayloadData payloadData, bool compressed, bool mask)
|
||||
FinalFrame fin, OperationCode opcode, Payload payload, bool compressed, bool mask)
|
||||
{
|
||||
Fin = fin;
|
||||
Rsv1 = opcode.IsData() && compressed ? Rsv.On : Rsv.Off;
|
||||
Rsv2 = Rsv.Off;
|
||||
Rsv3 = Rsv.Off;
|
||||
Rsv1 = opcode.IsData() && compressed ? ReservedBits.On : ReservedBits.Off;
|
||||
Rsv2 = ReservedBits.Off;
|
||||
Rsv3 = ReservedBits.Off;
|
||||
Opcode = opcode;
|
||||
PayloadData = new PayloadData(payloadData);
|
||||
Payload = new Payload(payload);
|
||||
|
||||
var len = PayloadData.Length;
|
||||
var len = Payload.Length;
|
||||
if (len < 126)
|
||||
{
|
||||
PayloadLength = (byte)len;
|
||||
|
@ -58,8 +59,8 @@ namespace EonaCat.Network
|
|||
if (mask)
|
||||
{
|
||||
Mask = Mask.On;
|
||||
MaskingKey = createMaskingKey();
|
||||
PayloadData.Mask(MaskingKey);
|
||||
MaskingKey = CreateMaskingKey();
|
||||
Payload.Mask(MaskingKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -71,11 +72,12 @@ namespace EonaCat.Network
|
|||
private WSFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public byte[] ExtendedPayloadLength { get; private set; }
|
||||
public FinalFrame Fin { get; private set; }
|
||||
public bool IsBinary => Opcode == OperationCode.Binary;
|
||||
public bool IsClose => Opcode == OperationCode.Close;
|
||||
public bool IsCompressed => Rsv1 == Rsv.On;
|
||||
public bool IsCompressed => Rsv1 == ReservedBits.On;
|
||||
public bool IsContinuation => Opcode == OperationCode.Continue;
|
||||
public bool IsControl => Opcode >= OperationCode.Close;
|
||||
public bool IsData => Opcode == OperationCode.Text || Opcode == OperationCode.Binary;
|
||||
|
@ -85,15 +87,15 @@ namespace EonaCat.Network
|
|||
public bool IsPing => Opcode == OperationCode.Ping;
|
||||
public bool IsPong => Opcode == OperationCode.Pong;
|
||||
public bool IsText => Opcode == OperationCode.Text;
|
||||
public ulong Length => 2 + (ulong)(ExtendedPayloadLength.Length + MaskingKey.Length) + PayloadData.Length;
|
||||
public ulong Length => 2 + (ulong)(ExtendedPayloadLength.Length + MaskingKey.Length) + Payload.Length;
|
||||
public Mask Mask { get; private set; }
|
||||
public byte[] MaskingKey { get; private set; }
|
||||
public OperationCode Opcode { get; private set; }
|
||||
public PayloadData PayloadData { get; private set; }
|
||||
public Payload Payload { get; private set; }
|
||||
public byte PayloadLength { get; private set; }
|
||||
public Rsv Rsv1 { get; private set; }
|
||||
public Rsv Rsv2 { get; private set; }
|
||||
public Rsv Rsv3 { get; private set; }
|
||||
public ReservedBits Rsv1 { get; private set; }
|
||||
public ReservedBits Rsv2 { get; private set; }
|
||||
public ReservedBits Rsv3 { get; private set; }
|
||||
internal int ExtendedPayloadLengthCount => PayloadLength < 126 ? 0 : (PayloadLength == 126 ? 2 : 8);
|
||||
|
||||
internal ulong FullPayloadLength => PayloadLength < 126
|
||||
|
@ -101,6 +103,7 @@ namespace EonaCat.Network
|
|||
: PayloadLength == 126
|
||||
? ExtendedPayloadLength.ToUInt16(ByteOrder.Big)
|
||||
: ExtendedPayloadLength.ToUInt64(ByteOrder.Big);
|
||||
|
||||
public IEnumerator<byte> GetEnumerator()
|
||||
{
|
||||
foreach (var b in ToArray())
|
||||
|
@ -116,12 +119,12 @@ namespace EonaCat.Network
|
|||
|
||||
public void Print(bool dumped)
|
||||
{
|
||||
Console.WriteLine(dumped ? dump(this) : print(this));
|
||||
Console.WriteLine(dumped ? Dump(this) : Print(this));
|
||||
}
|
||||
|
||||
public string PrintToString(bool dumped)
|
||||
{
|
||||
return dumped ? dump(this) : print(this);
|
||||
return dumped ? Dump(this) : Print(this);
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
|
@ -149,7 +152,7 @@ namespace EonaCat.Network
|
|||
|
||||
if (PayloadLength > 0)
|
||||
{
|
||||
var bytes = PayloadData.ToArray();
|
||||
var bytes = Payload.ToArray();
|
||||
if (PayloadLength < 127)
|
||||
{
|
||||
buff.Write(bytes, 0, bytes.Length);
|
||||
|
@ -171,34 +174,34 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
internal static WSFrame CreateCloseFrame(
|
||||
PayloadData payloadData, bool mask
|
||||
Payload payload, bool mask
|
||||
)
|
||||
{
|
||||
return new WSFrame(
|
||||
FinalFrame.Final, OperationCode.Close, payloadData, false, mask
|
||||
FinalFrame.Final, OperationCode.Close, payload, false, mask
|
||||
);
|
||||
}
|
||||
|
||||
internal static WSFrame CreatePingFrame(bool mask)
|
||||
{
|
||||
return new WSFrame(
|
||||
FinalFrame.Final, OperationCode.Ping, PayloadData.Empty, false, mask
|
||||
FinalFrame.Final, OperationCode.Ping, Payload.Empty, false, mask
|
||||
);
|
||||
}
|
||||
|
||||
internal static WSFrame CreatePingFrame(byte[] data, bool mask)
|
||||
{
|
||||
return new WSFrame(
|
||||
FinalFrame.Final, OperationCode.Ping, new PayloadData(data), false, mask
|
||||
FinalFrame.Final, OperationCode.Ping, new Payload(data), false, mask
|
||||
);
|
||||
}
|
||||
|
||||
internal static WSFrame CreatePongFrame(
|
||||
PayloadData payloadData, bool mask
|
||||
Payload payload, bool mask
|
||||
)
|
||||
{
|
||||
return new WSFrame(
|
||||
FinalFrame.Final, OperationCode.Pong, payloadData, false, mask
|
||||
FinalFrame.Final, OperationCode.Pong, payload, false, mask
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -207,7 +210,7 @@ namespace EonaCat.Network
|
|||
var frame = readHeader(stream);
|
||||
readExtendedPayloadLength(stream, frame);
|
||||
readMaskingKey(stream, frame);
|
||||
readPayloadData(stream, frame);
|
||||
readPayload(stream, frame);
|
||||
|
||||
if (unmask)
|
||||
{
|
||||
|
@ -235,7 +238,7 @@ namespace EonaCat.Network
|
|||
stream,
|
||||
frame1,
|
||||
frame2 =>
|
||||
readPayloadDataAsync(
|
||||
readPayloadAsync(
|
||||
stream,
|
||||
frame2,
|
||||
frame3 =>
|
||||
|
@ -265,11 +268,11 @@ namespace EonaCat.Network
|
|||
}
|
||||
|
||||
Mask = Mask.Off;
|
||||
PayloadData.Mask(MaskingKey);
|
||||
Payload.Mask(MaskingKey);
|
||||
MaskingKey = WSClient.EmptyBytes;
|
||||
}
|
||||
|
||||
private static byte[] createMaskingKey()
|
||||
private static byte[] CreateMaskingKey()
|
||||
{
|
||||
var key = new byte[4];
|
||||
WSClient.RandomNumber.GetBytes(key);
|
||||
|
@ -277,40 +280,40 @@ namespace EonaCat.Network
|
|||
return key;
|
||||
}
|
||||
|
||||
private static string dump(WSFrame frame)
|
||||
private static string Dump(WSFrame frame)
|
||||
{
|
||||
var len = frame.Length;
|
||||
var cnt = (long)(len / 4);
|
||||
var rem = (int)(len % 4);
|
||||
var amount = (long)(len / 4);
|
||||
var remainder = (int)(len % 4);
|
||||
|
||||
int cntDigit;
|
||||
string cntFmt;
|
||||
if (cnt < 10000)
|
||||
int digitAmount;
|
||||
string frameCount;
|
||||
if (amount < 10000)
|
||||
{
|
||||
cntDigit = 4;
|
||||
cntFmt = "{0,4}";
|
||||
digitAmount = 4;
|
||||
frameCount = "{0,4}";
|
||||
}
|
||||
else if (cnt < 0x010000)
|
||||
else if (amount < 0x010000)
|
||||
{
|
||||
cntDigit = 4;
|
||||
cntFmt = "{0,4:X}";
|
||||
digitAmount = 4;
|
||||
frameCount = "{0,4:X}";
|
||||
}
|
||||
else if (cnt < 0x0100000000)
|
||||
else if (amount < 0x0100000000)
|
||||
{
|
||||
cntDigit = 8;
|
||||
cntFmt = "{0,8:X}";
|
||||
digitAmount = 8;
|
||||
frameCount = "{0,8:X}";
|
||||
}
|
||||
else
|
||||
{
|
||||
cntDigit = 16;
|
||||
cntFmt = "{0,16:X}";
|
||||
digitAmount = 16;
|
||||
frameCount = "{0,16:X}";
|
||||
}
|
||||
|
||||
var spFmt = string.Format("{{0,{0}}}", cntDigit);
|
||||
var headerFmt = string.Format(@"
|
||||
var spFmt = string.Format("{{0,{0}}}", digitAmount);
|
||||
var headerFormat = string.Format(@"
|
||||
{0} 01234567 89ABCDEF 01234567 89ABCDEF
|
||||
{0}+--------+--------+--------+--------+\n", spFmt);
|
||||
var lineFmt = string.Format("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", cntFmt);
|
||||
var lineFmt = string.Format("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", frameCount);
|
||||
var footerFmt = string.Format("{0}+--------+--------+--------+--------+", spFmt);
|
||||
|
||||
var output = new StringBuilder(64);
|
||||
|
@ -322,13 +325,13 @@ namespace EonaCat.Network
|
|||
};
|
||||
var printLine = linePrinter();
|
||||
|
||||
output.AppendFormat(headerFmt, string.Empty);
|
||||
output.AppendFormat(headerFormat, string.Empty);
|
||||
|
||||
var bytes = frame.ToArray();
|
||||
for (long i = 0; i <= cnt; i++)
|
||||
for (long i = 0; i <= amount; i++)
|
||||
{
|
||||
var j = i * 4;
|
||||
if (i < cnt)
|
||||
if (i < amount)
|
||||
{
|
||||
printLine(
|
||||
Convert.ToString(bytes[j], 2).PadLeft(8, '0'),
|
||||
|
@ -339,12 +342,12 @@ namespace EonaCat.Network
|
|||
continue;
|
||||
}
|
||||
|
||||
if (rem > 0)
|
||||
if (remainder > 0)
|
||||
{
|
||||
printLine(
|
||||
Convert.ToString(bytes[j], 2).PadLeft(8, '0'),
|
||||
rem >= 2 ? Convert.ToString(bytes[j + 1], 2).PadLeft(8, '0') : string.Empty,
|
||||
rem == 3 ? Convert.ToString(bytes[j + 2], 2).PadLeft(8, '0') : string.Empty,
|
||||
remainder >= 2 ? Convert.ToString(bytes[j + 1], 2).PadLeft(8, '0') : string.Empty,
|
||||
remainder == 3 ? Convert.ToString(bytes[j + 2], 2).PadLeft(8, '0') : string.Empty,
|
||||
string.Empty);
|
||||
}
|
||||
}
|
||||
|
@ -353,7 +356,7 @@ namespace EonaCat.Network
|
|||
return output.ToString();
|
||||
}
|
||||
|
||||
private static string print(WSFrame frame)
|
||||
private static string Print(WSFrame frame)
|
||||
{
|
||||
// Payload Length
|
||||
var payloadLen = frame.PayloadLength;
|
||||
|
@ -370,10 +373,10 @@ namespace EonaCat.Network
|
|||
: payloadLen > 125
|
||||
? "---"
|
||||
: frame.IsText && !(frame.IsFragment || frame.IsMasked || frame.IsCompressed)
|
||||
? frame.PayloadData.ApplicationData.UTF8Decode()
|
||||
: frame.PayloadData.ToString();
|
||||
? frame.Payload.ApplicationData.UTF8Decode()
|
||||
: frame.Payload.ToString();
|
||||
|
||||
var fmt = @"
|
||||
var format = @"
|
||||
FIN: {0}
|
||||
RSV1: {1}
|
||||
RSV2: {2}
|
||||
|
@ -386,7 +389,7 @@ Extended Payload Length: {7}
|
|||
Payload Data: {9}";
|
||||
|
||||
return string.Format(
|
||||
fmt,
|
||||
format,
|
||||
frame.Fin,
|
||||
frame.Rsv1,
|
||||
frame.Rsv2,
|
||||
|
@ -399,7 +402,7 @@ Extended Payload Length: {7}
|
|||
payload);
|
||||
}
|
||||
|
||||
private static WSFrame processHeader(byte[] header)
|
||||
private static WSFrame ProcessHeader(byte[] header)
|
||||
{
|
||||
if (header.Length != 2)
|
||||
{
|
||||
|
@ -410,13 +413,13 @@ Extended Payload Length: {7}
|
|||
var fin = (header[0] & 0x80) == 0x80 ? FinalFrame.Final : FinalFrame.More;
|
||||
|
||||
// RSV1
|
||||
var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
|
||||
var rsv1 = (header[0] & 0x40) == 0x40 ? ReservedBits.On : ReservedBits.Off;
|
||||
|
||||
// RSV2
|
||||
var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
|
||||
var rsv2 = (header[0] & 0x20) == 0x20 ? ReservedBits.On : ReservedBits.Off;
|
||||
|
||||
// RSV3
|
||||
var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
|
||||
var rsv3 = (header[0] & 0x10) == 0x10 ? ReservedBits.On : ReservedBits.Off;
|
||||
|
||||
// Opcode
|
||||
var opcode = (byte)(header[0] & 0x0f);
|
||||
|
@ -429,7 +432,7 @@ Extended Payload Length: {7}
|
|||
|
||||
var err = !opcode.IsSupported()
|
||||
? "An unsupported opcode."
|
||||
: !opcode.IsData() && rsv1 == Rsv.On
|
||||
: !opcode.IsData() && rsv1 == ReservedBits.On
|
||||
? "A non data frame is compressed."
|
||||
: opcode.IsControl() && fin == FinalFrame.More
|
||||
? "A control frame is fragmented."
|
||||
|
@ -507,13 +510,13 @@ Extended Payload Length: {7}
|
|||
|
||||
private static WSFrame readHeader(Stream stream)
|
||||
{
|
||||
return processHeader(stream.ReadBytes(2));
|
||||
return ProcessHeader(stream.ReadBytes(2));
|
||||
}
|
||||
|
||||
private static void readHeaderAsync(
|
||||
Stream stream, Action<WSFrame> completed, Action<Exception> error)
|
||||
{
|
||||
stream.ReadBytesAsync(2, bytes => completed(processHeader(bytes)), error);
|
||||
stream.ReadBytesAsync(2, bytes => completed(ProcessHeader(bytes)), error);
|
||||
}
|
||||
|
||||
private static WSFrame readMaskingKey(Stream stream, WSFrame frame)
|
||||
|
@ -566,16 +569,16 @@ Extended Payload Length: {7}
|
|||
error);
|
||||
}
|
||||
|
||||
private static WSFrame readPayloadData(Stream stream, WSFrame frame)
|
||||
private static WSFrame readPayload(Stream stream, WSFrame frame)
|
||||
{
|
||||
var len = frame.FullPayloadLength;
|
||||
if (len == 0)
|
||||
{
|
||||
frame.PayloadData = PayloadData.Empty;
|
||||
frame.Payload = Payload.Empty;
|
||||
return frame;
|
||||
}
|
||||
|
||||
if (len > PayloadData.MaxLength)
|
||||
if (len > Payload.MaxLength)
|
||||
{
|
||||
throw new WSException(CloseStatusCode.TooBig, "A frame has a long payload length.");
|
||||
}
|
||||
|
@ -591,11 +594,11 @@ Extended Payload Length: {7}
|
|||
"The payload data of a frame cannot be read from the stream.");
|
||||
}
|
||||
|
||||
frame.PayloadData = new PayloadData(bytes, llen);
|
||||
frame.Payload = new Payload(bytes, llen);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static void readPayloadDataAsync(
|
||||
private static void readPayloadAsync(
|
||||
Stream stream,
|
||||
WSFrame frame,
|
||||
Action<WSFrame> completed,
|
||||
|
@ -604,13 +607,13 @@ Extended Payload Length: {7}
|
|||
var len = frame.FullPayloadLength;
|
||||
if (len == 0)
|
||||
{
|
||||
frame.PayloadData = PayloadData.Empty;
|
||||
frame.Payload = Payload.Empty;
|
||||
completed(frame);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > PayloadData.MaxLength)
|
||||
if (len > Payload.MaxLength)
|
||||
{
|
||||
throw new WSException(CloseStatusCode.TooBig, "A frame has a long payload length.");
|
||||
}
|
||||
|
@ -624,7 +627,7 @@ Extended Payload Length: {7}
|
|||
"The payload data of a frame cannot be read from the stream.");
|
||||
}
|
||||
|
||||
frame.PayloadData = new PayloadData(bytes, llen);
|
||||
frame.Payload = new Payload(bytes, llen);
|
||||
completed(frame);
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace EonaCat.Network
|
|||
internal byte[] EntityBodyData;
|
||||
protected const string CrLf = "\r\n";
|
||||
private const int _headersMaxLength = 8192;
|
||||
|
||||
protected WebBase(Version version, NameValueCollection headers)
|
||||
{
|
||||
ProtocolVersion = version;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Network
|
||||
|
@ -26,6 +25,7 @@ namespace EonaCat.Network
|
|||
HttpMethod = method;
|
||||
RequestUri = uri;
|
||||
}
|
||||
|
||||
public AuthenticationResponse AuthenticationResponse
|
||||
{
|
||||
get
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Network
|
||||
|
@ -28,6 +27,7 @@ namespace EonaCat.Network
|
|||
StatusCode = code;
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public CookieCollection Cookies => Headers.GetCookies(true);
|
||||
|
||||
public bool HasConnectionClose => Headers.Contains("Connection", "close");
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
|
|
|
@ -40,32 +40,32 @@ namespace EonaCat.Network
|
|||
_Routes = routes;
|
||||
}
|
||||
|
||||
internal async Task Process(HttpContext ctx, CancellationToken token)
|
||||
internal async Task Process(HttpContext context, CancellationToken token)
|
||||
{
|
||||
if (ctx == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (ctx.Request == null)
|
||||
if (context.Request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx.Request));
|
||||
throw new ArgumentNullException(nameof(context.Request));
|
||||
}
|
||||
|
||||
if (ctx.Response == null)
|
||||
if (context.Response == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx.Response));
|
||||
throw new ArgumentNullException(nameof(context.Response));
|
||||
}
|
||||
|
||||
if (ctx.Request.Method != HttpMethod.GET
|
||||
&& ctx.Request.Method != HttpMethod.HEAD)
|
||||
if (context.Request.Method != HttpMethod.GET
|
||||
&& context.Request.Method != HttpMethod.HEAD)
|
||||
{
|
||||
Set500Response(ctx);
|
||||
await ctx.Response.Send(token).ConfigureAwait(false);
|
||||
Set500Response(context);
|
||||
await context.Response.Send(token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
string filePath = ctx.Request.Url.RawWithoutQuery;
|
||||
string filePath = context.Request.Url.RawWithoutQuery;
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
while (filePath.StartsWith("/"))
|
||||
|
@ -88,35 +88,35 @@ namespace EonaCat.Network
|
|||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Set404Response(ctx);
|
||||
await ctx.Response.Send(token).ConfigureAwait(false);
|
||||
Set404Response(context);
|
||||
await context.Response.Send(token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
FileInfo fi = new FileInfo(filePath);
|
||||
long contentLength = fi.Length;
|
||||
|
||||
if (ctx.Request.Method == HttpMethod.GET)
|
||||
if (context.Request.Method == HttpMethod.GET)
|
||||
{
|
||||
FileStream fs = new FileStream(filePath, ContentFileMode, ContentFileAccess, ContentFileShare);
|
||||
ctx.Response.StatusCode = 200;
|
||||
ctx.Response.ContentLength = contentLength;
|
||||
ctx.Response.ContentType = GetContentType(filePath);
|
||||
await ctx.Response.Send(contentLength, fs, token).ConfigureAwait(false);
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.ContentLength = contentLength;
|
||||
context.Response.ContentType = GetContentType(filePath);
|
||||
await context.Response.Send(contentLength, fs, token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (ctx.Request.Method == HttpMethod.HEAD)
|
||||
else if (context.Request.Method == HttpMethod.HEAD)
|
||||
{
|
||||
ctx.Response.StatusCode = 200;
|
||||
ctx.Response.ContentLength = contentLength;
|
||||
ctx.Response.ContentType = GetContentType(filePath);
|
||||
await ctx.Response.Send(contentLength, token).ConfigureAwait(false);
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.ContentLength = contentLength;
|
||||
context.Response.ContentType = GetContentType(filePath);
|
||||
await context.Response.Send(contentLength, token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Set500Response(ctx);
|
||||
await ctx.Response.Send(token).ConfigureAwait(false);
|
||||
Set500Response(context);
|
||||
await context.Response.Send(token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -137,16 +137,16 @@ namespace EonaCat.Network
|
|||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
private void Set404Response(HttpContext ctx)
|
||||
private void Set404Response(HttpContext context)
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
ctx.Response.ContentLength = 0;
|
||||
context.Response.StatusCode = 404;
|
||||
context.Response.ContentLength = 0;
|
||||
}
|
||||
|
||||
private void Set500Response(HttpContext ctx)
|
||||
private void Set500Response(HttpContext context)
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
ctx.Response.ContentLength = 0;
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.ContentLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -129,7 +129,6 @@ namespace EonaCat.Network
|
|||
throw new ArgumentNullException(nameof(rawUrl));
|
||||
}
|
||||
|
||||
|
||||
if (Matcher.Match(
|
||||
BuildConsolidatedRegex(method, rawUrl),
|
||||
out object val))
|
||||
|
|
|
@ -374,9 +374,9 @@ namespace EonaCat.Network
|
|||
continue;
|
||||
}
|
||||
|
||||
System.Net.HttpListenerContext listenerCtx = await _HttpListener.GetContextAsync().ConfigureAwait(false);
|
||||
System.Net.HttpListenerContext listenercontext = await _HttpListener.GetContextAsync().ConfigureAwait(false);
|
||||
Interlocked.Increment(ref _RequestCount);
|
||||
HttpContext ctx = null;
|
||||
HttpContext context = null;
|
||||
|
||||
Task unawaited = Task.Run(async () =>
|
||||
{
|
||||
|
@ -385,49 +385,49 @@ namespace EonaCat.Network
|
|||
try
|
||||
{
|
||||
Events.HandleConnectionReceived(this, new ConnectionEventArgs(
|
||||
listenerCtx.Request.RemoteEndPoint.Address.ToString(),
|
||||
listenerCtx.Request.RemoteEndPoint.Port));
|
||||
listenercontext.Request.RemoteEndPoint.Address.ToString(),
|
||||
listenercontext.Request.RemoteEndPoint.Port));
|
||||
|
||||
ctx = new HttpContext(listenerCtx, _Settings, Events);
|
||||
context = new HttpContext(listenercontext, _Settings, Events);
|
||||
|
||||
Events.HandleRequestReceived(this, new RequestEventArgs(ctx));
|
||||
Events.HandleRequestReceived(this, new RequestEventArgs(context));
|
||||
|
||||
if (_Settings.Debug.Requests)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
Statistics.IncrementRequestCounter(ctx.Request.Method);
|
||||
Statistics.IncrementReceivedPayloadBytes(ctx.Request.ContentLength);
|
||||
Statistics.IncrementRequestCounter(context.Request.Method);
|
||||
Statistics.IncrementReceivedPayloadBytes(context.Request.ContentLength);
|
||||
|
||||
if (!_Settings.AccessControl.Permit(ctx.Request.Source.IpAddress))
|
||||
if (!_Settings.AccessControl.Permit(context.Request.Source.IpAddress))
|
||||
{
|
||||
Events.HandleRequestDenied(this, new RequestEventArgs(ctx));
|
||||
Events.HandleRequestDenied(this, new RequestEventArgs(context));
|
||||
|
||||
if (_Settings.Debug.AccessControl)
|
||||
{
|
||||
Events.Logger?.Invoke(_Header + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " denied due to access control");
|
||||
Events.Logger?.Invoke(_Header + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " denied due to access control");
|
||||
}
|
||||
|
||||
listenerCtx.Response.StatusCode = 403;
|
||||
listenerCtx.Response.Close();
|
||||
listenercontext.Response.StatusCode = 403;
|
||||
listenercontext.Response.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.Request.Method == HttpMethod.OPTIONS)
|
||||
if (context.Request.Method == HttpMethod.OPTIONS)
|
||||
{
|
||||
if (_Routes.Preflight != null)
|
||||
{
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "preflight route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "preflight route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
await _Routes.Preflight(ctx).ConfigureAwait(false);
|
||||
await _Routes.Preflight(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -435,85 +435,85 @@ namespace EonaCat.Network
|
|||
bool terminate = false;
|
||||
if (_Routes.PreRouting != null)
|
||||
{
|
||||
terminate = await _Routes.PreRouting(ctx).ConfigureAwait(false);
|
||||
terminate = await _Routes.PreRouting(context).ConfigureAwait(false);
|
||||
if (terminate)
|
||||
{
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "prerouting terminated connection for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "prerouting terminated connection for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.Request.Method == HttpMethod.GET || ctx.Request.Method == HttpMethod.HEAD)
|
||||
if (context.Request.Method == HttpMethod.GET || context.Request.Method == HttpMethod.HEAD)
|
||||
{
|
||||
if (_Routes.Content.Match(ctx.Request.Url.RawWithoutQuery, out ContentRoute cr))
|
||||
if (_Routes.Content.Match(context.Request.Url.RawWithoutQuery, out ContentRoute cr))
|
||||
{
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "content route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "content route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.RouteType = RouteTypeEnum.Content;
|
||||
ctx.Route = cr;
|
||||
await _Routes.ContentHandler.Process(ctx, token).ConfigureAwait(false);
|
||||
context.RouteType = RouteTypeEnum.Content;
|
||||
context.Route = cr;
|
||||
await _Routes.ContentHandler.Process(context, token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Func<HttpContext, Task> handler = _Routes.Static.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out StaticRoute sr);
|
||||
Func<HttpContext, Task> handler = _Routes.Static.Match(context.Request.Method, context.Request.Url.RawWithoutQuery, out StaticRoute sr);
|
||||
if (handler != null)
|
||||
{
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "static route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "static route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.RouteType = RouteTypeEnum.Static;
|
||||
ctx.Route = sr;
|
||||
await handler(ctx).ConfigureAwait(false);
|
||||
context.RouteType = RouteTypeEnum.Static;
|
||||
context.Route = sr;
|
||||
await handler(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
handler = _Routes.Parameter.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out Dictionary<string, string> parameters, out ParameterRoute pr);
|
||||
handler = _Routes.Parameter.Match(context.Request.Method, context.Request.Url.RawWithoutQuery, out Dictionary<string, string> parameters, out ParameterRoute pr);
|
||||
if (handler != null)
|
||||
{
|
||||
ctx.Request.Url.Parameters = new Dictionary<string, string>(parameters);
|
||||
context.Request.Url.Parameters = new Dictionary<string, string>(parameters);
|
||||
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "parameter route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "parameter route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.RouteType = RouteTypeEnum.Parameter;
|
||||
ctx.Route = pr;
|
||||
await handler(ctx).ConfigureAwait(false);
|
||||
context.RouteType = RouteTypeEnum.Parameter;
|
||||
context.Route = pr;
|
||||
await handler(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
handler = _Routes.Dynamic.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out DynamicRoute dr);
|
||||
handler = _Routes.Dynamic.Match(context.Request.Method, context.Request.Url.RawWithoutQuery, out DynamicRoute dr);
|
||||
if (handler != null)
|
||||
{
|
||||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "dynamic route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "dynamic route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.RouteType = RouteTypeEnum.Dynamic;
|
||||
ctx.Route = dr;
|
||||
await handler(ctx).ConfigureAwait(false);
|
||||
context.RouteType = RouteTypeEnum.Dynamic;
|
||||
context.Route = dr;
|
||||
await handler(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -522,12 +522,12 @@ namespace EonaCat.Network
|
|||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "default route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "default route for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.RouteType = RouteTypeEnum.Default;
|
||||
await _Routes.Default(ctx).ConfigureAwait(false);
|
||||
context.RouteType = RouteTypeEnum.Default;
|
||||
await _Routes.Default(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -535,31 +535,31 @@ namespace EonaCat.Network
|
|||
if (_Settings.Debug.Routing)
|
||||
{
|
||||
Events.Logger?.Invoke(
|
||||
_Header + "default route not found for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
|
||||
ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
|
||||
_Header + "default route not found for " + context.Request.Source.IpAddress + ":" + context.Request.Source.Port + " " +
|
||||
context.Request.Method.ToString() + " " + context.Request.Url.RawWithoutQuery);
|
||||
}
|
||||
|
||||
ctx.Response.StatusCode = 404;
|
||||
ctx.Response.ContentType = Pages.Default404Page.ContentType;
|
||||
await ctx.Response.Send(Pages.Default404Page.Content).ConfigureAwait(false);
|
||||
context.Response.StatusCode = 404;
|
||||
context.Response.ContentType = Pages.Default404Page.ContentType;
|
||||
await context.Response.Send(Pages.Default404Page.Content).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception eInner)
|
||||
{
|
||||
ctx.Response.StatusCode = 500;
|
||||
ctx.Response.ContentType = Pages.Default500Page.ContentType;
|
||||
await ctx.Response.Send(Pages.Default500Page.Content).ConfigureAwait(false);
|
||||
Events.HandleExceptionEncountered(this, new ExceptionEventArgs(ctx, eInner));
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.ContentType = Pages.Default500Page.ContentType;
|
||||
await context.Response.Send(Pages.Default500Page.Content).ConfigureAwait(false);
|
||||
Events.HandleExceptionEncountered(this, new ExceptionEventArgs(context, eInner));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref _RequestCount);
|
||||
|
||||
if (ctx != null && ctx.Response != null && ctx.Response.ResponseSent)
|
||||
if (context != null && context.Response != null && context.Response.ResponseSent)
|
||||
{
|
||||
Events.HandleResponseSent(this, new ResponseEventArgs(ctx, TotalMsFrom(startTime)));
|
||||
Statistics.IncrementSentPayloadBytes(ctx.Response.ContentLength);
|
||||
Events.HandleResponseSent(this, new ResponseEventArgs(context, TotalMsFrom(startTime)));
|
||||
Statistics.IncrementSentPayloadBytes(context.Response.ContentLength);
|
||||
}
|
||||
}
|
||||
}, token);
|
||||
|
|
|
@ -184,14 +184,14 @@ namespace EonaCat.Network
|
|||
_ContentHandler = new ContentRouteHandler(_Content);
|
||||
}
|
||||
|
||||
private async Task PreflightInternal(HttpContext ctx)
|
||||
private async Task PreflightInternal(HttpContext context)
|
||||
{
|
||||
ctx.Response.StatusCode = 200;
|
||||
context.Response.StatusCode = 200;
|
||||
|
||||
string[] requestedHeaders = null;
|
||||
if (ctx.Request.Headers != null)
|
||||
if (context.Request.Headers != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> curr in ctx.Request.Headers)
|
||||
foreach (KeyValuePair<string, string> curr in context.Request.Headers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(curr.Key))
|
||||
{
|
||||
|
@ -235,11 +235,11 @@ namespace EonaCat.Network
|
|||
|
||||
foreach (KeyValuePair<string, string> header in _Settings.Headers)
|
||||
{
|
||||
ctx.Response.Headers.Add(header.Key, header.Value);
|
||||
context.Response.Headers.Add(header.Key, header.Value);
|
||||
}
|
||||
|
||||
ctx.Response.ContentLength = 0;
|
||||
await ctx.Response.Send().ConfigureAwait(false);
|
||||
context.Response.ContentLength = 0;
|
||||
await context.Response.Send().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,19 +82,19 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
internal ExceptionEventArgs(HttpContext ctx, Exception e)
|
||||
internal ExceptionEventArgs(HttpContext context, Exception e)
|
||||
{
|
||||
if (ctx != null)
|
||||
if (context != null)
|
||||
{
|
||||
Ip = ctx.Request.Source.IpAddress;
|
||||
Port = ctx.Request.Source.Port;
|
||||
Method = ctx.Request.Method;
|
||||
Url = ctx.Request.Url.Full;
|
||||
Query = ctx.Request.Query.Elements;
|
||||
RequestHeaders = ctx.Request.Headers;
|
||||
RequestContentLength = ctx.Request.ContentLength;
|
||||
StatusCode = ctx.Response.StatusCode;
|
||||
ResponseContentLength = ctx.Response.ContentLength;
|
||||
Ip = context.Request.Source.IpAddress;
|
||||
Port = context.Request.Source.Port;
|
||||
Method = context.Request.Method;
|
||||
Url = context.Request.Url.Full;
|
||||
Query = context.Request.Query.Elements;
|
||||
RequestHeaders = context.Request.Headers;
|
||||
RequestContentLength = context.Request.ContentLength;
|
||||
StatusCode = context.Response.StatusCode;
|
||||
ResponseContentLength = context.Response.ContentLength;
|
||||
}
|
||||
|
||||
Exception = e;
|
||||
|
|
|
@ -46,15 +46,15 @@ namespace EonaCat.Network
|
|||
/// </summary>
|
||||
public long ContentLength { get; private set; } = 0;
|
||||
|
||||
internal RequestEventArgs(HttpContext ctx)
|
||||
internal RequestEventArgs(HttpContext context)
|
||||
{
|
||||
Ip = ctx.Request.Source.IpAddress;
|
||||
Port = ctx.Request.Source.Port;
|
||||
Method = ctx.Request.Method;
|
||||
Url = ctx.Request.Url.Full;
|
||||
Query = ctx.Request.Query.Elements;
|
||||
Headers = ctx.Request.Headers;
|
||||
ContentLength = ctx.Request.ContentLength;
|
||||
Ip = context.Request.Source.IpAddress;
|
||||
Port = context.Request.Source.Port;
|
||||
Method = context.Request.Method;
|
||||
Url = context.Request.Url.Full;
|
||||
Query = context.Request.Query.Elements;
|
||||
Headers = context.Request.Headers;
|
||||
ContentLength = context.Request.ContentLength;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,17 +66,17 @@ namespace EonaCat.Network
|
|||
/// </summary>
|
||||
public double TotalMs { get; private set; } = 0;
|
||||
|
||||
internal ResponseEventArgs(HttpContext ctx, double totalMs)
|
||||
internal ResponseEventArgs(HttpContext context, double totalMs)
|
||||
{
|
||||
Ip = ctx.Request.Source.IpAddress;
|
||||
Port = ctx.Request.Source.Port;
|
||||
Method = ctx.Request.Method;
|
||||
Url = ctx.Request.Url.Full;
|
||||
Query = ctx.Request.Query.Elements;
|
||||
RequestHeaders = ctx.Request.Headers;
|
||||
RequestContentLength = ctx.Request.ContentLength;
|
||||
StatusCode = ctx.Response.StatusCode;
|
||||
ResponseContentLength = ctx.Response.ContentLength;
|
||||
Ip = context.Request.Source.IpAddress;
|
||||
Port = context.Request.Source.Port;
|
||||
Method = context.Request.Method;
|
||||
Url = context.Request.Url.Full;
|
||||
Query = context.Request.Query.Elements;
|
||||
RequestHeaders = context.Request.Headers;
|
||||
RequestContentLength = context.Request.ContentLength;
|
||||
StatusCode = context.Response.StatusCode;
|
||||
ResponseContentLength = context.Response.ContentLength;
|
||||
TotalMs = totalMs;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,11 +50,11 @@ namespace EonaCat.Network
|
|||
{
|
||||
}
|
||||
|
||||
internal HttpContext(System.Net.HttpListenerContext ctx, EonaCatWebserverSettings settings, EonaCatWebserverEvents events)
|
||||
internal HttpContext(System.Net.HttpListenerContext context, EonaCatWebserverSettings settings, EonaCatWebserverEvents events)
|
||||
{
|
||||
if (ctx == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (events == null)
|
||||
|
@ -62,9 +62,9 @@ namespace EonaCat.Network
|
|||
throw new ArgumentNullException(nameof(events));
|
||||
}
|
||||
|
||||
_Context = ctx;
|
||||
Request = new HttpRequest(ctx);
|
||||
Response = new HttpResponse(Request, ctx, settings, events);
|
||||
_Context = context;
|
||||
Request = new HttpRequest(context);
|
||||
Response = new HttpResponse(Request, context, settings, events);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -182,41 +182,41 @@ namespace EonaCat.Network
|
|||
/// HTTP request.
|
||||
/// Instantiate the object using an HttpListenerContext.
|
||||
/// </summary>
|
||||
/// <param name="ctx">HttpListenerContext.</param>
|
||||
public HttpRequest(System.Net.HttpListenerContext ctx)
|
||||
/// <param name="context">HttpListenerContext.</param>
|
||||
public HttpRequest(System.Net.HttpListenerContext context)
|
||||
{
|
||||
if (ctx == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (ctx.Request == null)
|
||||
if (context.Request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx.Request));
|
||||
throw new ArgumentNullException(nameof(context.Request));
|
||||
}
|
||||
|
||||
ListenerContext = ctx;
|
||||
Keepalive = ctx.Request.KeepAlive;
|
||||
ContentLength = ctx.Request.ContentLength64;
|
||||
Useragent = ctx.Request.UserAgent;
|
||||
ContentType = ctx.Request.ContentType;
|
||||
ListenerContext = context;
|
||||
Keepalive = context.Request.KeepAlive;
|
||||
ContentLength = context.Request.ContentLength64;
|
||||
Useragent = context.Request.UserAgent;
|
||||
ContentType = context.Request.ContentType;
|
||||
|
||||
_Uri = new Uri(ctx.Request.Url.ToString().Trim());
|
||||
_Uri = new Uri(context.Request.Url.ToString().Trim());
|
||||
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
TimestampUtc = DateTime.Now.ToUniversalTime();
|
||||
ProtocolVersion = "HTTP/" + ctx.Request.ProtocolVersion.ToString();
|
||||
Source = new SourceDetails(ctx.Request.RemoteEndPoint.Address.ToString(), ctx.Request.RemoteEndPoint.Port);
|
||||
Destination = new DestinationDetails(ctx.Request.LocalEndPoint.Address.ToString(), ctx.Request.LocalEndPoint.Port, _Uri.Host);
|
||||
Method = (HttpMethod)Enum.Parse(typeof(HttpMethod), ctx.Request.HttpMethod, true);
|
||||
Url = new UrlDetails(ctx.Request.Url.ToString().Trim(), ctx.Request.RawUrl.ToString().Trim());
|
||||
ProtocolVersion = "HTTP/" + context.Request.ProtocolVersion.ToString();
|
||||
Source = new SourceDetails(context.Request.RemoteEndPoint.Address.ToString(), context.Request.RemoteEndPoint.Port);
|
||||
Destination = new DestinationDetails(context.Request.LocalEndPoint.Address.ToString(), context.Request.LocalEndPoint.Port, _Uri.Host);
|
||||
Method = (HttpMethod)Enum.Parse(typeof(HttpMethod), context.Request.HttpMethod, true);
|
||||
Url = new UrlDetails(context.Request.Url.ToString().Trim(), context.Request.RawUrl.ToString().Trim());
|
||||
Query = new QueryDetails(Url.Full);
|
||||
|
||||
Headers = new Dictionary<string, string>();
|
||||
for (int i = 0; i < ctx.Request.Headers.Count; i++)
|
||||
for (int i = 0; i < context.Request.Headers.Count; i++)
|
||||
{
|
||||
string key = ctx.Request.Headers.GetKey(i);
|
||||
string val = ctx.Request.Headers.Get(i);
|
||||
string key = context.Request.Headers.GetKey(i);
|
||||
string val = context.Request.Headers.Get(i);
|
||||
Headers = AddToDict(key, val, Headers);
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ namespace EonaCat.Network
|
|||
}
|
||||
}
|
||||
|
||||
Data = ctx.Request.InputStream;
|
||||
Data = context.Request.InputStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -140,16 +140,16 @@ namespace EonaCat.Network
|
|||
{
|
||||
}
|
||||
|
||||
internal HttpResponse(HttpRequest req, System.Net.HttpListenerContext ctx, EonaCatWebserverSettings settings, EonaCatWebserverEvents events)
|
||||
internal HttpResponse(HttpRequest httpRequest, System.Net.HttpListenerContext context, EonaCatWebserverSettings settings, EonaCatWebserverEvents events)
|
||||
{
|
||||
if (req == null)
|
||||
if (httpRequest == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(req));
|
||||
throw new ArgumentNullException(nameof(httpRequest));
|
||||
}
|
||||
|
||||
if (ctx == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ctx));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
|
@ -162,8 +162,8 @@ namespace EonaCat.Network
|
|||
throw new ArgumentNullException(nameof(events));
|
||||
}
|
||||
|
||||
_Request = req;
|
||||
_Context = ctx;
|
||||
_Request = httpRequest;
|
||||
_Context = context;
|
||||
_Response = _Context.Response;
|
||||
_Settings = settings;
|
||||
_Events = events;
|
||||
|
|
Loading…
Reference in New Issue