From 3f8c2781ee57654e0aa8bccd341e29bd3660469d Mon Sep 17 00:00:00 2001 From: flashwave Date: Tue, 30 Aug 2022 17:28:46 +0200 Subject: [PATCH] Cleaned things up again. --- Hamakaze/Hamakaze.csproj | 7 - Hamakaze/Headers/HttpAcceptEncodingHeader.cs | 26 -- Hamakaze/Headers/HttpConnectionHeader.cs | 17 -- Hamakaze/Headers/HttpContentEncodingHeader.cs | 20 -- Hamakaze/Headers/HttpContentLengthHeader.cs | 30 -- Hamakaze/Headers/HttpContentTypeHeader.cs | 20 -- Hamakaze/Headers/HttpCustomHeader.cs | 13 - Hamakaze/Headers/HttpDateHeader.cs | 18 -- Hamakaze/Headers/HttpHeader.cs | 41 --- Hamakaze/Headers/HttpHostHeader.cs | 37 --- Hamakaze/Headers/HttpKeepAliveHeader.cs | 35 --- Hamakaze/Headers/HttpServerHeader.cs | 14 - Hamakaze/Headers/HttpTeHeader.cs | 26 -- .../Headers/HttpTransferEncodingHeader.cs | 20 -- Hamakaze/Headers/HttpUserAgentHeader.cs | 20 -- Hamakaze/HttpClient.cs | 118 -------- Hamakaze/HttpConnection.cs | 81 ------ Hamakaze/HttpConnectionManager.cs | 122 -------- Hamakaze/HttpEncoding.cs | 69 ----- Hamakaze/HttpException.cs | 40 --- Hamakaze/HttpMediaType.cs | 159 ----------- Hamakaze/HttpMessage.cs | 46 --- Hamakaze/HttpRequestMessage.cs | 190 ------------- Hamakaze/HttpResponseMessage.cs | 265 ------------------ Hamakaze/HttpTask.cs | 189 ------------- Hamakaze/HttpTaskManager.cs | 41 --- SharpChat.sln | 10 +- SharpChat/ChatEventManager.cs | 14 +- SharpChat/ChatUser.cs | 3 +- SharpChat/Program.cs | 56 +--- SharpChat/WebDatabase.cs | 233 --------------- 31 files changed, 5 insertions(+), 1975 deletions(-) delete mode 100644 Hamakaze/Hamakaze.csproj delete mode 100644 Hamakaze/Headers/HttpAcceptEncodingHeader.cs delete mode 100644 Hamakaze/Headers/HttpConnectionHeader.cs delete mode 100644 Hamakaze/Headers/HttpContentEncodingHeader.cs delete mode 100644 Hamakaze/Headers/HttpContentLengthHeader.cs delete mode 100644 Hamakaze/Headers/HttpContentTypeHeader.cs delete mode 100644 Hamakaze/Headers/HttpCustomHeader.cs delete mode 100644 Hamakaze/Headers/HttpDateHeader.cs delete mode 100644 Hamakaze/Headers/HttpHeader.cs delete mode 100644 Hamakaze/Headers/HttpHostHeader.cs delete mode 100644 Hamakaze/Headers/HttpKeepAliveHeader.cs delete mode 100644 Hamakaze/Headers/HttpServerHeader.cs delete mode 100644 Hamakaze/Headers/HttpTeHeader.cs delete mode 100644 Hamakaze/Headers/HttpTransferEncodingHeader.cs delete mode 100644 Hamakaze/Headers/HttpUserAgentHeader.cs delete mode 100644 Hamakaze/HttpClient.cs delete mode 100644 Hamakaze/HttpConnection.cs delete mode 100644 Hamakaze/HttpConnectionManager.cs delete mode 100644 Hamakaze/HttpEncoding.cs delete mode 100644 Hamakaze/HttpException.cs delete mode 100644 Hamakaze/HttpMediaType.cs delete mode 100644 Hamakaze/HttpMessage.cs delete mode 100644 Hamakaze/HttpRequestMessage.cs delete mode 100644 Hamakaze/HttpResponseMessage.cs delete mode 100644 Hamakaze/HttpTask.cs delete mode 100644 Hamakaze/HttpTaskManager.cs delete mode 100644 SharpChat/WebDatabase.cs diff --git a/Hamakaze/Hamakaze.csproj b/Hamakaze/Hamakaze.csproj deleted file mode 100644 index f208d30..0000000 --- a/Hamakaze/Hamakaze.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - net5.0 - - - diff --git a/Hamakaze/Headers/HttpAcceptEncodingHeader.cs b/Hamakaze/Headers/HttpAcceptEncodingHeader.cs deleted file mode 100644 index 47b60a7..0000000 --- a/Hamakaze/Headers/HttpAcceptEncodingHeader.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Hamakaze.Headers { - public class HttpAcceptEncodingHeader : HttpHeader { - public const string NAME = @"Accept-Encoding"; - - public override string Name => NAME; - public override object Value => string.Join(@", ", Encodings); - - public HttpEncoding[] Encodings { get; } - - public HttpAcceptEncodingHeader(string encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - ) { } - - public HttpAcceptEncodingHeader(string[] encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Select(HttpEncoding.Parse) - ) {} - - public HttpAcceptEncodingHeader(IEnumerable encodings) { - Encodings = (encodings ?? throw new ArgumentNullException(nameof(encodings))).ToArray(); - } - } -} diff --git a/Hamakaze/Headers/HttpConnectionHeader.cs b/Hamakaze/Headers/HttpConnectionHeader.cs deleted file mode 100644 index 50b1318..0000000 --- a/Hamakaze/Headers/HttpConnectionHeader.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpConnectionHeader : HttpHeader { - public const string NAME = @"Connection"; - - public override string Name => NAME; - public override object Value { get; } - - public const string CLOSE = @"close"; - public const string KEEP_ALIVE = @"keep-alive"; - - public HttpConnectionHeader(string mode) { - Value = mode ?? throw new ArgumentNullException(nameof(mode)); - } - } -} diff --git a/Hamakaze/Headers/HttpContentEncodingHeader.cs b/Hamakaze/Headers/HttpContentEncodingHeader.cs deleted file mode 100644 index 9972819..0000000 --- a/Hamakaze/Headers/HttpContentEncodingHeader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpContentEncodingHeader : HttpHeader { - public const string NAME = @"Content-Encoding"; - - public override string Name => NAME; - public override object Value => string.Join(@", ", Encodings); - - public string[] Encodings { get; } - - public HttpContentEncodingHeader(string encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - ) { } - - public HttpContentEncodingHeader(string[] encodings) { - Encodings = encodings ?? throw new ArgumentNullException(nameof(encodings)); - } - } -} diff --git a/Hamakaze/Headers/HttpContentLengthHeader.cs b/Hamakaze/Headers/HttpContentLengthHeader.cs deleted file mode 100644 index 53f2d31..0000000 --- a/Hamakaze/Headers/HttpContentLengthHeader.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.IO; - -namespace Hamakaze.Headers { - public class HttpContentLengthHeader : HttpHeader { - public const string NAME = @"Content-Length"; - - public override string Name => NAME; - public override object Value => Stream?.Length ?? Length; - - private Stream Stream { get; } - private long Length { get; } - - public HttpContentLengthHeader(Stream stream) { - Stream = stream ?? throw new ArgumentNullException(nameof(stream)); - if(!stream.CanRead || !stream.CanSeek) - throw new ArgumentException(@"Body must readable and seekable.", nameof(stream)); - } - - public HttpContentLengthHeader(long length) { - Length = length; - } - - public HttpContentLengthHeader(string length) { - if(!long.TryParse(length, out long ll)) - throw new ArgumentException(@"Invalid length value.", nameof(length)); - Length = ll; - } - } -} diff --git a/Hamakaze/Headers/HttpContentTypeHeader.cs b/Hamakaze/Headers/HttpContentTypeHeader.cs deleted file mode 100644 index 8ef6846..0000000 --- a/Hamakaze/Headers/HttpContentTypeHeader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpContentTypeHeader : HttpHeader { - public const string NAME = @"Content-Type"; - - public override string Name => NAME; - public override object Value => MediaType.ToString(); - - public HttpMediaType MediaType { get; } - - public HttpContentTypeHeader(string mediaType) { - MediaType = HttpMediaType.Parse(mediaType ?? throw new ArgumentNullException(nameof(mediaType))); - } - - public HttpContentTypeHeader(HttpMediaType mediaType) { - MediaType = mediaType; - } - } -} diff --git a/Hamakaze/Headers/HttpCustomHeader.cs b/Hamakaze/Headers/HttpCustomHeader.cs deleted file mode 100644 index bfd7196..0000000 --- a/Hamakaze/Headers/HttpCustomHeader.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpCustomHeader : HttpHeader { - public override string Name { get; } - public override object Value { get; } - - public HttpCustomHeader(string name, object value) { - Name = NormaliseName(name ?? throw new ArgumentNullException(nameof(name))); - Value = value; - } - } -} diff --git a/Hamakaze/Headers/HttpDateHeader.cs b/Hamakaze/Headers/HttpDateHeader.cs deleted file mode 100644 index 91c36fe..0000000 --- a/Hamakaze/Headers/HttpDateHeader.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Globalization; - -namespace Hamakaze.Headers { - public class HttpDateHeader : HttpHeader { - public const string NAME = @"Date"; - - public override string Name => NAME; - public override object Value { get; } - - public DateTimeOffset DateTime { get; } - - public HttpDateHeader(string dateString) { - Value = dateString ?? throw new ArgumentNullException(nameof(dateString)); - DateTime = DateTimeOffset.ParseExact(dateString, @"r", CultureInfo.InvariantCulture); - } - } -} diff --git a/Hamakaze/Headers/HttpHeader.cs b/Hamakaze/Headers/HttpHeader.cs deleted file mode 100644 index b9f75fa..0000000 --- a/Hamakaze/Headers/HttpHeader.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Globalization; - -namespace Hamakaze.Headers { - public abstract class HttpHeader { - public abstract string Name { get; } - public abstract object Value { get; } - - public override string ToString() { - return string.Format(@"{0}: {1}", Name, Value); - } - - public static string NormaliseName(string name) { - if(string.IsNullOrWhiteSpace(name)) - return string.Empty; - - string[] parts = name.ToLowerInvariant().Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - for(int i = 0; i < parts.Length; ++i) - parts[i] = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(parts[i]); - return string.Join('-', parts); - } - - public static HttpHeader Create(string name, object value) { - return name switch { - HttpTeHeader.NAME => new HttpTeHeader(value.ToString()), - HttpDateHeader.NAME => new HttpDateHeader(value.ToString()), - HttpHostHeader.NAME => new HttpHostHeader(value.ToString()), - HttpServerHeader.NAME => new HttpServerHeader(value.ToString()), - HttpUserAgentHeader.NAME => new HttpUserAgentHeader(value.ToString()), - HttpKeepAliveHeader.NAME => new HttpKeepAliveHeader(value.ToString()), - HttpConnectionHeader.NAME => new HttpConnectionHeader(value.ToString()), - HttpContentTypeHeader.NAME => new HttpContentTypeHeader(value.ToString()), - HttpContentLengthHeader.NAME => new HttpContentLengthHeader(value.ToString()), - HttpAcceptEncodingHeader.NAME => new HttpAcceptEncodingHeader(value.ToString()), - HttpContentEncodingHeader.NAME => new HttpContentEncodingHeader(value.ToString()), - HttpTransferEncodingHeader.NAME => new HttpTransferEncodingHeader(value.ToString()), - _ => new HttpCustomHeader(name, value), - }; - } - } -} diff --git a/Hamakaze/Headers/HttpHostHeader.cs b/Hamakaze/Headers/HttpHostHeader.cs deleted file mode 100644 index c263ff9..0000000 --- a/Hamakaze/Headers/HttpHostHeader.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace Hamakaze.Headers { - public class HttpHostHeader : HttpHeader { - public const string NAME = @"Host"; - - public override string Name => NAME; - public override object Value { - get { - StringBuilder sb = new(); - sb.Append(Host); - if(Port != -1) - sb.AppendFormat(@":{0}", Port); - return sb.ToString(); - } - } - - public string Host { get; } - public int Port { get; } - public bool IsSecure { get; } - - public HttpHostHeader(string host, int port) { - Host = host; - Port = port; - } - - public HttpHostHeader(string hostAndPort) { - string[] parts = hostAndPort.Split(':', 2, StringSplitOptions.TrimEntries); - Host = parts.ElementAtOrDefault(0) ?? throw new ArgumentNullException(nameof(hostAndPort)); - if(!ushort.TryParse(parts.ElementAtOrDefault(1), out ushort port)) - throw new FormatException(@"Host is not in valid format."); - Port = port; - } - } -} diff --git a/Hamakaze/Headers/HttpKeepAliveHeader.cs b/Hamakaze/Headers/HttpKeepAliveHeader.cs deleted file mode 100644 index d8ab2c3..0000000 --- a/Hamakaze/Headers/HttpKeepAliveHeader.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Hamakaze.Headers { - public class HttpKeepAliveHeader : HttpHeader { - public const string NAME = @"Keep-Alive"; - - public override string Name => NAME; - public override object Value { - get { - List parts = new(); - if(MaxIdle != TimeSpan.MaxValue) - parts.Add(string.Format(@"timeout={0}", MaxIdle.TotalSeconds)); - if(MaxRequests >= 0) - parts.Add(string.Format(@"max={0}", MaxRequests)); - return string.Join(@", ", parts); - } - } - - public TimeSpan MaxIdle { get; } = TimeSpan.MaxValue; - public int MaxRequests { get; } = -1; - - public HttpKeepAliveHeader(string value) { - IEnumerable kvps = (value ?? throw new ArgumentNullException(nameof(value))).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - - foreach(string kvp in kvps) { - string[] parts = kvp.Split('=', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - if(parts[0] == @"timeout" && int.TryParse(parts[1], out int timeout)) - MaxIdle = TimeSpan.FromSeconds(timeout); - else if(parts[0] == @"max" && int.TryParse(parts[1], out int max)) - MaxRequests = max; - } - } - } -} diff --git a/Hamakaze/Headers/HttpServerHeader.cs b/Hamakaze/Headers/HttpServerHeader.cs deleted file mode 100644 index c2d665f..0000000 --- a/Hamakaze/Headers/HttpServerHeader.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpServerHeader : HttpHeader { - public const string NAME = @"Server"; - - public override string Name => NAME; - public override object Value { get; } - - public HttpServerHeader(string server) { - Value = server ?? throw new ArgumentNullException(nameof(server)); - } - } -} diff --git a/Hamakaze/Headers/HttpTeHeader.cs b/Hamakaze/Headers/HttpTeHeader.cs deleted file mode 100644 index 0ccc4c3..0000000 --- a/Hamakaze/Headers/HttpTeHeader.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Hamakaze.Headers { - public class HttpTeHeader : HttpHeader { - public const string NAME = @"TE"; - - public override string Name => NAME; - public override object Value => string.Join(@", ", Encodings); - - public HttpEncoding[] Encodings { get; } - - public HttpTeHeader(string encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - ) { } - - public HttpTeHeader(string[] encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Select(HttpEncoding.Parse) - ) { } - - public HttpTeHeader(IEnumerable encodings) { - Encodings = (encodings ?? throw new ArgumentNullException(nameof(encodings))).ToArray(); - } - } -} diff --git a/Hamakaze/Headers/HttpTransferEncodingHeader.cs b/Hamakaze/Headers/HttpTransferEncodingHeader.cs deleted file mode 100644 index a62939a..0000000 --- a/Hamakaze/Headers/HttpTransferEncodingHeader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpTransferEncodingHeader : HttpHeader { - public const string NAME = @"Transfer-Encoding"; - - public override string Name => NAME; - public override object Value => string.Join(@", ", Encodings); - - public string[] Encodings { get; } - - public HttpTransferEncodingHeader(string encodings) : this( - (encodings ?? throw new ArgumentNullException(nameof(encodings))).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - ) {} - - public HttpTransferEncodingHeader(string[] encodings) { - Encodings = encodings ?? throw new ArgumentNullException(nameof(encodings)); - } - } -} diff --git a/Hamakaze/Headers/HttpUserAgentHeader.cs b/Hamakaze/Headers/HttpUserAgentHeader.cs deleted file mode 100644 index b8aa5ec..0000000 --- a/Hamakaze/Headers/HttpUserAgentHeader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Hamakaze.Headers { - public class HttpUserAgentHeader : HttpHeader { - public const string NAME = @"User-Agent"; - - public override string Name => NAME; - public override object Value { get; } - - public HttpUserAgentHeader(string userAgent) { - if(userAgent == null) - throw new ArgumentNullException(nameof(userAgent)); - - if(string.IsNullOrWhiteSpace(userAgent) || userAgent.Equals(HttpClient.USER_AGENT)) - Value = HttpClient.USER_AGENT; - else - Value = string.Format(@"{0} {1}", userAgent, HttpClient.USER_AGENT); - } - } -} diff --git a/Hamakaze/HttpClient.cs b/Hamakaze/HttpClient.cs deleted file mode 100644 index be009b5..0000000 --- a/Hamakaze/HttpClient.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Hamakaze.Headers; -using System; -using System.Collections.Generic; - -namespace Hamakaze { - public class HttpClient : IDisposable { - public const string PRODUCT_STRING = @"HMKZ"; - public const string VERSION_MAJOR = @"1"; - public const string VERSION_MINOR = @"0"; - public const string USER_AGENT = PRODUCT_STRING + @"/" + VERSION_MAJOR + @"." + VERSION_MINOR; - - private static HttpClient InstanceValue { get; set; } - public static HttpClient Instance { - get { - if(InstanceValue == null) - InstanceValue = new HttpClient(); - return InstanceValue; - } - } - - private HttpConnectionManager Connections { get; } - private HttpTaskManager Tasks { get; } - - public string DefaultUserAgent { get; set; } = USER_AGENT; - public bool ReuseConnections { get; set; } = true; - public IEnumerable AcceptedEncodings { get; set; } = new[] { HttpEncoding.GZip, HttpEncoding.Deflate, HttpEncoding.Brotli }; - - public HttpClient() { - Connections = new HttpConnectionManager(); - Tasks = new HttpTaskManager(); - } - - public HttpTask CreateTask( - HttpRequestMessage request, - Action onComplete = null, - Action onError = null, - Action onCancel = null, - Action onDownloadProgress = null, - Action onUploadProgress = null, - Action onStateChange = null, - bool disposeRequest = true, - bool disposeResponse = true - ) { - if(request == null) - throw new ArgumentNullException(nameof(request)); - if(string.IsNullOrWhiteSpace(request.UserAgent)) - request.UserAgent = DefaultUserAgent; - if(!request.HasHeader(HttpAcceptEncodingHeader.NAME)) - request.AcceptedEncodings = AcceptedEncodings; - request.Connection = ReuseConnections ? HttpConnectionHeader.KEEP_ALIVE : HttpConnectionHeader.CLOSE; - - HttpTask task = new(Connections, request, disposeRequest, disposeResponse); - - if(onComplete != null) - task.OnComplete += onComplete; - if(onError != null) - task.OnError += onError; - if(onCancel != null) - task.OnCancel += onCancel; - if(onDownloadProgress != null) - task.OnDownloadProgress += onDownloadProgress; - if(onUploadProgress != null) - task.OnUploadProgress += onUploadProgress; - if(onStateChange != null) - task.OnStateChange += onStateChange; - - return task; - } - - public void RunTask(HttpTask task) { - Tasks.RunTask(task); - } - - public void SendRequest( - HttpRequestMessage request, - Action onComplete = null, - Action onError = null, - Action onCancel = null, - Action onDownloadProgress = null, - Action onUploadProgress = null, - Action onStateChange = null, - bool disposeRequest = true, - bool disposeResponse = true - ) { - RunTask(CreateTask(request, onComplete, onError, onCancel, onDownloadProgress, onUploadProgress, onStateChange, disposeRequest, disposeResponse)); - } - - public static void Send( - HttpRequestMessage request, - Action onComplete = null, - Action onError = null, - Action onCancel = null, - Action onDownloadProgress = null, - Action onUploadProgress = null, - Action onStateChange = null, - bool disposeRequest = true, - bool disposeResponse = true - ) { - Instance.SendRequest(request, onComplete, onError, onCancel, onDownloadProgress, onUploadProgress, onStateChange, disposeRequest, disposeResponse); - } - - private bool IsDisposed; - ~HttpClient() - => DoDispose(); - public void Dispose() { - DoDispose(); - GC.SuppressFinalize(this); - } - private void DoDispose() { - if(IsDisposed) - return; - IsDisposed = true; - - Tasks.Dispose(); - Connections.Dispose(); - } - } -} diff --git a/Hamakaze/HttpConnection.cs b/Hamakaze/HttpConnection.cs deleted file mode 100644 index 8bb90d0..0000000 --- a/Hamakaze/HttpConnection.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Authentication; -using System.Threading; - -namespace Hamakaze { - public class HttpConnection : IDisposable { - public IPEndPoint EndPoint { get; } - public Stream Stream { get; } - - public Socket Socket { get; } - public NetworkStream NetworkStream { get; } - public SslStream SslStream { get; } - - public string Host { get; } - public bool IsSecure { get; } - - public bool HasTimedOut => MaxRequests == 0 || (DateTimeOffset.Now - LastOperation) > MaxIdle; - - public int MaxRequests { get; set; } = -1; - public TimeSpan MaxIdle { get; set; } = TimeSpan.MaxValue; - public DateTimeOffset LastOperation { get; private set; } = DateTimeOffset.Now; - - public bool InUse { get; private set; } - - public HttpConnection(string host, IPEndPoint endPoint, bool secure) { - Host = host ?? throw new ArgumentNullException(nameof(host)); - EndPoint = endPoint ?? throw new ArgumentNullException(nameof(endPoint)); - IsSecure = secure; - - if(endPoint.AddressFamily is not AddressFamily.InterNetwork and not AddressFamily.InterNetworkV6) - throw new ArgumentException(@"Address must be an IPv4 or IPv6 address.", nameof(endPoint)); - - Socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) { - NoDelay = true, - Blocking = true, - }; - Socket.Connect(endPoint); - - NetworkStream = new NetworkStream(Socket, true); - - if(IsSecure) { - SslStream = new SslStream(NetworkStream, false, (s, ce, ch, e) => e == SslPolicyErrors.None, null); - Stream = SslStream; - SslStream.AuthenticateAsClient(Host, null, SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, true); - } else - Stream = NetworkStream; - } - - public void MarkUsed() { - LastOperation = DateTimeOffset.Now; - if(MaxRequests > 0) - --MaxRequests; - } - - public bool Acquire() { - return !InUse && (InUse = true); - } - - public void Release() { - InUse = false; - } - - private bool IsDisposed; - ~HttpConnection() - => DoDispose(); - public void Dispose() { - DoDispose(); - GC.SuppressFinalize(this); - } - private void DoDispose() { - if(IsDisposed) - return; - IsDisposed = true; - Stream.Dispose(); - } - } -} diff --git a/Hamakaze/HttpConnectionManager.cs b/Hamakaze/HttpConnectionManager.cs deleted file mode 100644 index a0c5853..0000000 --- a/Hamakaze/HttpConnectionManager.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; - -namespace Hamakaze { - public class HttpConnectionManager : IDisposable { - private List Connections { get; } = new(); - private Mutex Lock { get; } = new(); - - public HttpConnectionManager() { - } - - private void AcquireLock() { - if(!Lock.WaitOne(10000)) - throw new HttpConnectionManagerLockException(); - } - - private void ReleaseLock() { - Lock.ReleaseMutex(); - } - - public HttpConnection CreateConnection(string host, IPEndPoint endPoint, bool secure) { - if(host == null) - throw new ArgumentNullException(nameof(host)); - if(endPoint == null) - throw new ArgumentNullException(nameof(endPoint)); - HttpConnection conn = null; - AcquireLock(); - try { - conn = CreateConnectionInternal(host, endPoint, secure); - } finally { - ReleaseLock(); - } - return conn; - } - - private HttpConnection CreateConnectionInternal(string host, IPEndPoint endPoint, bool secure) { - HttpConnection conn = new(host, endPoint, secure); - Connections.Add(conn); - return conn; - } - - public HttpConnection GetConnection(string host, IPEndPoint endPoint, bool secure) { - if(host == null) - throw new ArgumentNullException(nameof(host)); - if(endPoint == null) - throw new ArgumentNullException(nameof(endPoint)); - HttpConnection conn = null; - AcquireLock(); - try { - conn = GetConnectionInternal(host, endPoint, secure); - } finally { - ReleaseLock(); - } - return conn; - } - - private HttpConnection GetConnectionInternal(string host, IPEndPoint endPoint, bool secure) { - CleanConnectionsInternal(); - HttpConnection conn = Connections.FirstOrDefault(c => host.Equals(c.Host) && endPoint.Equals(c.EndPoint) && c.IsSecure == secure && c.Acquire()); - if(conn == null) { - conn = CreateConnectionInternal(host, endPoint, secure); - conn.Acquire(); - } - return conn; - } - - public void EndConnection(HttpConnection conn) { - if(conn == null) - throw new ArgumentNullException(nameof(conn)); - AcquireLock(); - try { - EndConnectionInternal(conn); - } finally { - ReleaseLock(); - } - } - - private void EndConnectionInternal(HttpConnection conn) { - Connections.Remove(conn); - conn.Dispose(); - } - - public void CleanConnection() { - AcquireLock(); - try { - CleanConnectionsInternal(); - } finally { - ReleaseLock(); - } - } - - private void CleanConnectionsInternal() { - IEnumerable conns = Connections.Where(x => x.HasTimedOut).ToArray(); - foreach(HttpConnection conn in conns) { - Connections.Remove(conn); - conn.Dispose(); - } - } - - private bool IsDisposed; - ~HttpConnectionManager() - => DoDispose(); - public void Dispose() { - DoDispose(); - GC.SuppressFinalize(this); - } - private void DoDispose() { - if(IsDisposed) - return; - IsDisposed = true; - - Lock.Dispose(); - - foreach(HttpConnection conn in Connections) - conn.Dispose(); - Connections.Clear(); - } - } -} diff --git a/Hamakaze/HttpEncoding.cs b/Hamakaze/HttpEncoding.cs deleted file mode 100644 index 5b6e3a5..0000000 --- a/Hamakaze/HttpEncoding.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Globalization; -using System.Text; - -namespace Hamakaze { - public readonly struct HttpEncoding : IComparable, IEquatable { - public const string DEFLATE = @"deflate"; - public const string GZIP = @"gzip"; - public const string XGZIP = @"x-gzip"; - public const string BROTLI = @"br"; - public const string IDENTITY = @"identity"; - public const string CHUNKED = @"chunked"; - public const string ANY = @"*"; - - public static readonly HttpEncoding Any = new(ANY); - public static readonly HttpEncoding None = new(ANY, 0f); - public static readonly HttpEncoding Deflate = new(DEFLATE); - public static readonly HttpEncoding GZip = new(GZIP); - public static readonly HttpEncoding Brotli = new(BROTLI); - public static readonly HttpEncoding Identity = new(IDENTITY); - - public string Name { get; } - public float Quality { get; } - - public HttpEncoding(string name, float quality = 1f) { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Quality = quality; - } - - public HttpEncoding WithQuality(float quality) { - return new HttpEncoding(Name, quality); - } - - public static HttpEncoding Parse(string encoding) { - string[] parts = encoding.Split(';', StringSplitOptions.TrimEntries); - float quality = 1f; - encoding = parts[0]; - - for(int i = 1; i < parts.Length; ++i) - if(parts[i].StartsWith(@"q=")) { - if(!float.TryParse(parts[i], out quality)) - quality = 1f; - break; - } - - return new HttpEncoding(encoding, quality); - } - - public override string ToString() { - StringBuilder sb = new(); - sb.Append(Name); - if(Quality is >= 0f and < 1f) - sb.AppendFormat(CultureInfo.InvariantCulture, @";q={0:0.0}", Quality); - return sb.ToString(); - } - - public int CompareTo(HttpEncoding? other) { - if(!other.HasValue || other.Value.Quality < Quality) - return -1; - if(other.Value.Quality > Quality) - return 1; - return 0; - } - - public bool Equals(HttpEncoding? other) { - return other.HasValue && Name.Equals(other.Value.Name) && Quality.Equals(other.Value.Quality); - } - } -} diff --git a/Hamakaze/HttpException.cs b/Hamakaze/HttpException.cs deleted file mode 100644 index d6e0bce..0000000 --- a/Hamakaze/HttpException.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -namespace Hamakaze { - public class HttpException : Exception { - public HttpException(string message) : base(message) { } - } - - public class HttpConnectionManagerException : HttpException { - public HttpConnectionManagerException(string message) : base(message) { } - } - public class HttpConnectionManagerLockException : HttpConnectionManagerException { - public HttpConnectionManagerLockException() : base(@"Failed to lock the connection manager in time.") { } - } - - public class HttpTaskException : HttpException { - public HttpTaskException(string message) : base(message) { } - } - public class HttpTaskAlreadyStartedException : HttpTaskException { - public HttpTaskAlreadyStartedException() : base(@"Task has already started.") { } - } - public class HttpTaskInvalidStateException : HttpTaskException { - public HttpTaskInvalidStateException() : base(@"Task has ended up in an invalid state.") { } - } - public class HttpTaskNoAddressesException : HttpTaskException { - public HttpTaskNoAddressesException() : base(@"Could not find any addresses for this host.") { } - } - public class HttpTaskNoConnectionException : HttpTaskException { - public HttpTaskNoConnectionException() : base(@"Was unable to create a connection with this host.") { } - } - public class HttpTaskRequestFailedException : HttpTaskException { - public HttpTaskRequestFailedException() : base(@"Request failed for unknown reasons.") { } - } - - public class HttpTaskManagerException : HttpException { - public HttpTaskManagerException(string message) : base(message) { } - } - public class HttpTaskManagerLockException : HttpTaskManagerException { - public HttpTaskManagerLockException() : base(@"Failed to reserve a thread.") { } - } -} diff --git a/Hamakaze/HttpMediaType.cs b/Hamakaze/HttpMediaType.cs deleted file mode 100644 index e3ca1e6..0000000 --- a/Hamakaze/HttpMediaType.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Hamakaze { - public readonly struct HttpMediaType : IComparable, IEquatable { - public const string TYPE_APPLICATION = @"application"; - public const string TYPE_AUDIO = @"audio"; - public const string TYPE_IMAGE = @"image"; - public const string TYPE_MESSAGE = @"message"; - public const string TYPE_MULTIPART = @"multipart"; - public const string TYPE_TEXT = @"text"; - public const string TYPE_VIDEO = @"video"; - - public static readonly HttpMediaType OctetStream = new(TYPE_APPLICATION, @"octet-stream"); - public static readonly HttpMediaType FWIF = new(TYPE_APPLICATION, @"x.fwif"); - public static readonly HttpMediaType JSON = new(TYPE_APPLICATION, @"json"); - public static readonly HttpMediaType HTML = new(TYPE_TEXT, @"html", args: new[] { Param.UTF8 }); - - public string Type { get; } - public string Subtype { get; } - public string Suffix { get; } - public IEnumerable Params { get; } - - public HttpMediaType(string type, string subtype, string suffix = null, IEnumerable args = null) { - Type = type ?? throw new ArgumentNullException(nameof(type)); - Subtype = subtype ?? throw new ArgumentNullException(nameof(subtype)); - Suffix = suffix ?? string.Empty; - Params = args ?? Enumerable.Empty(); - } - - public string GetParamValue(string name) { - foreach(Param param in Params) - if(param.Name.ToLowerInvariant() == name) - return param.Value; - return null; - } - - public static explicit operator HttpMediaType(string mediaTypeString) => Parse(mediaTypeString); - - public static HttpMediaType Parse(string mediaTypeString) { - if(mediaTypeString == null) - throw new ArgumentNullException(nameof(mediaTypeString)); - - int slashIndex = mediaTypeString.IndexOf('/'); - if(slashIndex == -1) - return OctetStream; - - string type = mediaTypeString[..slashIndex]; - string subtype = mediaTypeString[(slashIndex + 1)..]; - string suffix = null; - IEnumerable args = null; - - int paramIndex = subtype.IndexOf(';'); - if(paramIndex != -1) { - args = subtype[(paramIndex + 1)..] - .Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .Select(Param.Parse); - subtype = subtype[..paramIndex]; - } - - int suffixIndex = subtype.IndexOf('+'); - if(suffixIndex != -1) { - suffix = subtype[(suffixIndex + 1)..]; - subtype = subtype[..suffixIndex]; - } - - return new HttpMediaType(type, subtype, suffix, args); - } - - public override string ToString() { - StringBuilder sb = new(); - sb.AppendFormat(@"{0}/{1}", Type, Subtype); - if(!string.IsNullOrWhiteSpace(Suffix)) - sb.AppendFormat(@"+{0}", Suffix); - if(Params.Any()) - sb.AppendFormat(@";{0}", string.Join(';', Params)); - return sb.ToString(); - } - - public int CompareTo(HttpMediaType? other) { - if(!other.HasValue) - return -1; - int type = Type.CompareTo(other.Value.Type); - if(type != 0) - return type; - int subtype = Subtype.CompareTo(other.Value.Subtype); - if(subtype != 0) - return subtype; - int suffix = Suffix.CompareTo(other.Value.Suffix); - if(suffix != 0) - return suffix; - int paramCount = Params.Count(); - int args = paramCount - other.Value.Params.Count(); - if(args != 0) - return args; - for(int i = 0; i < paramCount; ++i) { - args = Params.ElementAt(i).CompareTo(other.Value.Params.ElementAt(i)); - if(args != 0) - return args; - } - return 0; - } - - public bool Equals(HttpMediaType? other) { - if(!other.HasValue) - return false; - if(!Type.Equals(other.Value.Type) || !Subtype.Equals(other.Value.Subtype) || !Suffix.Equals(other.Value.Suffix)) - return false; - int paramCount = Params.Count(); - if(paramCount != other.Value.Params.Count()) - return false; - for(int i = 0; i < paramCount; ++i) - if(!Params.ElementAt(i).Equals(other.Value.Params.ElementAt(i))) - return false; - return true; - } - - public readonly struct Param : IComparable, IEquatable { - public const string CHARSET = @"charset"; - - public static readonly Param ASCII = new(CHARSET, @"us-ascii"); - public static readonly Param UTF8 = new(CHARSET, @"utf-8"); - - public string Name { get; } - public string Value { get; } - - public Param(string name, string value) { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Value = value ?? throw new ArgumentNullException(nameof(name)); - } - - public override string ToString() { - return string.Format(@"{0}={1}", Name, Value); - } - - public static explicit operator Param(string paramStr) => Parse(paramStr); - - public static Param Parse(string paramStr) { - string[] parts = (paramStr ?? throw new ArgumentNullException(nameof(paramStr))).Split('=', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - return new Param(parts[0], parts[1]); - } - - public int CompareTo(Param? other) { - if(!other.HasValue) - return -1; - int name = Name.CompareTo(other.Value.Name); - return name != 0 - ? name - : Value.CompareTo(other.Value.Value); - } - - public bool Equals(Param? other) { - return other.HasValue && Name.Equals(other.Value.Name) && Value.Equals(other.Value.Value); - } - } - } -} diff --git a/Hamakaze/HttpMessage.cs b/Hamakaze/HttpMessage.cs deleted file mode 100644 index cbfc22e..0000000 --- a/Hamakaze/HttpMessage.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Hamakaze.Headers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Hamakaze { - public abstract class HttpMessage : IDisposable { - public abstract string ProtocolVersion { get; } - public abstract IEnumerable Headers { get; } - public abstract Stream Body { get; } - - public virtual bool HasBody => Body != null; - - protected bool OwnsBodyStream { get; set; } - - public virtual IEnumerable GetHeader(string header) { - header = HttpHeader.NormaliseName(header); - return Headers.Where(h => h.Name == header); - } - - public virtual bool HasHeader(string header) { - header = HttpHeader.NormaliseName(header); - return Headers.Any(h => h.Name == header); - } - - public virtual string GetHeaderLine(string header) { - return string.Join(@", ", GetHeader(header).Select(h => h.Value)); - } - - private bool IsDisposed; - ~HttpMessage() - => DoDispose(); - public void Dispose() { - DoDispose(); - GC.SuppressFinalize(this); - } - protected void DoDispose() { - if(IsDisposed) - return; - IsDisposed = true; - if(OwnsBodyStream && Body != null) - Body.Dispose(); - } - } -} diff --git a/Hamakaze/HttpRequestMessage.cs b/Hamakaze/HttpRequestMessage.cs deleted file mode 100644 index 6ce3ce3..0000000 --- a/Hamakaze/HttpRequestMessage.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Hamakaze.Headers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace Hamakaze { - public class HttpRequestMessage : HttpMessage { - public const string GET = @"GET"; - public const string PUT = @"PUT"; - public const string HEAD = @"HEAD"; - public const string POST = @"POST"; - public const string DELETE = @"DELETE"; - - public override string ProtocolVersion => @"1.1"; - - public string Method { get; } - public string RequestTarget { get; } - - public bool IsSecure { get; } - - public string Host { get; } - public ushort Port { get; } - public bool IsDefaultPort { get; } - - public override IEnumerable Headers => HeaderList; - private List HeaderList { get; } = new(); - - private Stream BodyStream { get; set; } - public override Stream Body { - get { - if(BodyStream == null) { - OwnsBodyStream = true; - SetBody(new MemoryStream()); - } - return BodyStream; - } - } - - private static readonly string[] HEADERS_READONLY = new[] { - HttpHostHeader.NAME, HttpContentLengthHeader.NAME, - }; - private static readonly string[] HEADERS_SINGLE = new[] { - HttpUserAgentHeader.NAME, HttpConnectionHeader.NAME, HttpAcceptEncodingHeader.NAME, - }; - - public IEnumerable AcceptedEncodings { - get => HeaderList.Where(x => x.Name == HttpAcceptEncodingHeader.NAME).Cast().FirstOrDefault()?.Encodings - ?? Enumerable.Empty(); - - set { - HeaderList.RemoveAll(x => x.Name == HttpAcceptEncodingHeader.NAME); - HeaderList.Add(new HttpAcceptEncodingHeader(value)); - } - } - - public string UserAgent { - get => HeaderList.FirstOrDefault(x => x.Name == HttpUserAgentHeader.NAME)?.Value.ToString() - ?? string.Empty; - set { - HeaderList.RemoveAll(x => x.Name == HttpUserAgentHeader.NAME); - HeaderList.Add(new HttpUserAgentHeader(value)); - } - } - - public string Connection { - get => HeaderList.FirstOrDefault(x => x.Name == HttpConnectionHeader.NAME)?.Value.ToString() - ?? string.Empty; - set { - HeaderList.RemoveAll(x => x.Name == HttpConnectionHeader.NAME); - HeaderList.Add(new HttpConnectionHeader(value)); - } - } - - public HttpMediaType ContentType { - get => HeaderList.Where(x => x.Name == HttpContentTypeHeader.NAME).Cast().FirstOrDefault()?.MediaType - ?? HttpMediaType.OctetStream; - set { - HeaderList.RemoveAll(x => x.Name == HttpContentTypeHeader.NAME); - HeaderList.Add(new HttpContentTypeHeader(value)); - } - } - - public HttpRequestMessage(string method, string uri) : this( - method, new Uri(uri) - ) {} - - public const ushort HTTP = 80; - public const ushort HTTPS = 443; - - public HttpRequestMessage(string method, Uri uri) { - Method = method ?? throw new ArgumentNullException(nameof(method)); - RequestTarget = uri.PathAndQuery; - IsSecure = uri.Scheme.Equals(@"https", StringComparison.InvariantCultureIgnoreCase); - Host = uri.Host; - ushort defaultPort = (IsSecure ? HTTPS : HTTP); - Port = uri.Port == -1 ? defaultPort : (ushort)uri.Port; - IsDefaultPort = Port == defaultPort; - HeaderList.Add(new HttpHostHeader(Host, IsDefaultPort ? -1 : Port)); - } - - public static bool IsHeaderReadOnly(string name) - => HEADERS_READONLY.Contains(name ?? throw new ArgumentNullException(nameof(name))); - public static bool IsHeaderSingleInstance(string name) - => HEADERS_SINGLE.Contains(name ?? throw new ArgumentNullException(nameof(name))); - - public void SetHeader(string name, object value) { - name = HttpHeader.NormaliseName(name ?? throw new ArgumentNullException(nameof(name))); - if(IsHeaderReadOnly(name)) - throw new ArgumentException(@"This header is read-only.", nameof(name)); - HeaderList.RemoveAll(x => x.Name == name); - HeaderList.Add(HttpHeader.Create(name, value)); - } - - public void AddHeader(string name, object value) { - name = HttpHeader.NormaliseName(name ?? throw new ArgumentNullException(nameof(name))); - if(IsHeaderReadOnly(name)) - throw new ArgumentException(@"This header is read-only.", nameof(name)); - if(IsHeaderSingleInstance(name)) - HeaderList.RemoveAll(x => x.Name == name); - HeaderList.Add(HttpHeader.Create(name, value)); - } - - public void RemoveHeader(string name) { - name = HttpHeader.NormaliseName(name ?? throw new ArgumentNullException(nameof(name))); - if(IsHeaderReadOnly(name)) - throw new ArgumentException(@"This header is read-only.", nameof(name)); - HeaderList.RemoveAll(x => x.Name == name); - } - - public void SetBody(Stream stream) { - if(stream == null) { - if(OwnsBodyStream) - BodyStream?.Dispose(); - OwnsBodyStream = false; - BodyStream = null; - HeaderList.RemoveAll(x => x.Name == HttpContentLengthHeader.NAME); - } else { - if(!stream.CanRead || !stream.CanSeek) - throw new ArgumentException(@"Body must readable and seekable.", nameof(stream)); - if(OwnsBodyStream) - BodyStream?.Dispose(); - OwnsBodyStream = false; - BodyStream = stream; - HeaderList.Add(new HttpContentLengthHeader(BodyStream)); - } - } - - public void SetBody(byte[] buffer) { - SetBody(new MemoryStream(buffer)); - OwnsBodyStream = true; - } - - public void SetBody(string str, Encoding encoding = null) { - SetBody((encoding ?? Encoding.UTF8).GetBytes(str)); - } - - public void WriteTo(Stream stream, Action onProgress = null) { - using(StreamWriter sw = new(stream, new ASCIIEncoding(), leaveOpen: true)) { - sw.NewLine = "\r\n"; - sw.Write(Method); - sw.Write(' '); - sw.Write(RequestTarget); - sw.Write(@" HTTP/"); - sw.WriteLine(ProtocolVersion); - foreach(HttpHeader header in Headers) - sw.WriteLine(header); - sw.WriteLine(); - sw.Flush(); - } - - if(BodyStream != null) { - const int bufferSize = 8192; - byte[] buffer = new byte[bufferSize]; - int read; - long totalRead = 0; - - onProgress?.Invoke(totalRead, BodyStream.Length); - - BodyStream.Seek(0, SeekOrigin.Begin); - while((read = BodyStream.Read(buffer, 0, bufferSize)) > 0) { - stream.Write(buffer, 0, read); - totalRead += read; - onProgress?.Invoke(totalRead, BodyStream.Length); - } - } - } - } -} diff --git a/Hamakaze/HttpResponseMessage.cs b/Hamakaze/HttpResponseMessage.cs deleted file mode 100644 index c041e93..0000000 --- a/Hamakaze/HttpResponseMessage.cs +++ /dev/null @@ -1,265 +0,0 @@ -using Hamakaze.Headers; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; - -namespace Hamakaze { - public class HttpResponseMessage : HttpMessage { - public override string ProtocolVersion { get; } - public int StatusCode { get; } - public string StatusMessage { get; } - - public override IEnumerable Headers { get; } - - public override Stream Body { get; } - - public string Connection - => Headers.FirstOrDefault(x => x.Name == HttpConnectionHeader.NAME)?.Value.ToString() ?? string.Empty; - public string Server - => Headers.FirstOrDefault(x => x.Name == HttpServerHeader.NAME)?.Value.ToString() ?? string.Empty; - public DateTimeOffset Date - => Headers.Where(x => x.Name == HttpDateHeader.NAME).Cast().FirstOrDefault()?.DateTime ?? DateTimeOffset.MinValue; - public HttpMediaType ContentType - => Headers.Where(x => x.Name == HttpContentTypeHeader.NAME).Cast().FirstOrDefault()?.MediaType - ?? HttpMediaType.OctetStream; - public Encoding ResponseEncoding - => Encoding.GetEncoding(ContentType.GetParamValue(@"charset") ?? @"iso8859-1"); - public IEnumerable ContentEncodings - => Headers.Where(x => x.Name == HttpContentEncodingHeader.NAME).Cast().FirstOrDefault()?.Encodings - ?? Enumerable.Empty(); - public IEnumerable TransferEncodings - => Headers.Where(x => x.Name == HttpTransferEncodingHeader.NAME).Cast().FirstOrDefault()?.Encodings - ?? Enumerable.Empty(); - - public HttpResponseMessage( - int statusCode, string statusMessage, string protocolVersion, - IEnumerable headers, Stream body - ) { - ProtocolVersion = protocolVersion ?? throw new ArgumentNullException(nameof(protocolVersion)); - StatusCode = statusCode; - StatusMessage = statusMessage ?? string.Empty; - Headers = (headers ?? throw new ArgumentNullException(nameof(headers))).ToArray(); - OwnsBodyStream = true; - Body = body; - } - - public byte[] GetBodyBytes() { - if(Body == null) - return null; - if(Body is MemoryStream msBody) - return msBody.ToArray(); - using MemoryStream ms = new(); - if(Body.CanSeek) - Body.Seek(0, SeekOrigin.Begin); - Body.CopyTo(ms); - return ms.ToArray(); - } - - public string GetBodyString() { - byte[] bytes = GetBodyBytes(); - return bytes == null || bytes.Length < 1 - ? string.Empty - : ResponseEncoding.GetString(bytes); - } - - // there's probably a less stupid way to do this, be my guest and call me an idiot - private static void ProcessEncoding(Stack encodings, Stream stream, bool transfer) { - using MemoryStream temp = new(); - bool inTemp = false; - - while(encodings.TryPop(out string encoding)) { - Stream target = (inTemp = !inTemp) ? temp : stream, - source = inTemp ? stream : temp; - - target.SetLength(0); - source.Seek(0, SeekOrigin.Begin); - - switch(encoding) { - case HttpEncoding.GZIP: - case HttpEncoding.XGZIP: - using(GZipStream gzs = new(source, CompressionMode.Decompress, true)) - gzs.CopyTo(target); - break; - - case HttpEncoding.DEFLATE: - using(DeflateStream def = new(source, CompressionMode.Decompress, true)) - def.CopyTo(target); - break; - - case HttpEncoding.BROTLI: - if(transfer) - goto default; - using(BrotliStream br = new(source, CompressionMode.Decompress, true)) - br.CopyTo(target); - break; - - case HttpEncoding.IDENTITY: - break; - - case HttpEncoding.CHUNKED: - if(!transfer) - goto default; - throw new IOException(@"Invalid use of chunked encoding type in Transfer-Encoding header."); - - default: - throw new IOException(@"Unsupported encoding supplied."); - } - } - - if(inTemp) { - stream.SetLength(0); - temp.Seek(0, SeekOrigin.Begin); - temp.CopyTo(stream); - } - } - - public static HttpResponseMessage ReadFrom(Stream stream, Action onProgress = null) { - // ignore this function, it doesn't exist - string readLine() { - const ushort crlf = 0x0D0A; - using MemoryStream ms = new(); - int byt; ushort lastTwo = 0; - - for(; ; ) { - byt = stream.ReadByte(); - if(byt == -1 && ms.Length == 0) - return null; - - ms.WriteByte((byte)byt); - - lastTwo <<= 8; - lastTwo |= (byte)byt; - if(lastTwo == crlf) { - ms.SetLength(ms.Length - 2); - break; - } - } - - return Encoding.ASCII.GetString(ms.ToArray()); - } - - long contentLength = -1; - Stack transferEncodings = null; - Stack contentEncodings = null; - - // Read initial header - string line = readLine(); - if(line == null) - throw new IOException(@"Failed to read initial HTTP header."); - if(!line.StartsWith(@"HTTP/")) - throw new IOException(@"Response is not a valid HTTP message."); - string[] parts = line[5..].Split(' ', 3); - if(!int.TryParse(parts.ElementAtOrDefault(1), out int statusCode)) - throw new IOException(@"Invalid HTTP status code format."); - string protocolVersion = parts.ElementAtOrDefault(0); - string statusMessage = parts.ElementAtOrDefault(2); - - // Read header key-value pairs - List headers = new(); - - while((line = readLine()) != null) { - if(string.IsNullOrWhiteSpace(line)) - break; - - parts = line.Split(':', 2, StringSplitOptions.TrimEntries); - if(parts.Length < 2) - throw new IOException(@"Invalid HTTP header in response."); - - string hName = HttpHeader.NormaliseName(parts.ElementAtOrDefault(0) ?? string.Empty), - hValue = parts.ElementAtOrDefault(1); - if(string.IsNullOrEmpty(hName)) - throw new IOException(@"Invalid HTTP header name."); - - HttpHeader header = HttpHeader.Create(hName, hValue); - - if(header is HttpContentLengthHeader hclh) - contentLength = (long)hclh.Value; - else if(header is HttpTransferEncodingHeader hteh) - transferEncodings = new Stack(hteh.Encodings); - else if(header is HttpContentEncodingHeader hceh) - contentEncodings = new Stack(hceh.Encodings); - - headers.Add(header); - } - - if(statusCode is < 200 or 201 or 204 or 205) - contentLength = 0; - - Stream body = null; - long totalRead = 0; - const int buffer_size = 8192; - byte[] buffer = new byte[buffer_size]; - int read; - - void readBuffer(long length = -1) { - if(length == 0) - return; - long remaining = length; - int bufferRead = buffer_size; - if(bufferRead > length) - bufferRead = (int)length; - - if(totalRead < 1) - onProgress?.Invoke(0, contentLength); - - while((read = stream.Read(buffer, 0, bufferRead)) > 0) { - body.Write(buffer, 0, read); - - totalRead += read; - onProgress?.Invoke(totalRead, contentLength); - - if(length >= 0) { - remaining -= read; - if(remaining < 1) - break; - if(bufferRead > remaining) - bufferRead = (int)remaining; - } - } - } - - // Read body - if(transferEncodings != null && transferEncodings.Any() && transferEncodings.Peek() == HttpEncoding.CHUNKED) { - // oh no the poop is chunky - transferEncodings.Pop(); - body = new MemoryStream(); - - while((line = readLine()) != null) { - if(string.IsNullOrWhiteSpace(line)) - break; - if(!int.TryParse(line, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int chunkLength)) - throw new IOException(@"Failed to decode chunk length."); - if(chunkLength == 0) // final chunk - break; - readBuffer(chunkLength); - readLine(); - } - readLine(); - } else if(contentLength != 0) { - body = new MemoryStream(); - readBuffer(contentLength); - readLine(); - } - - if(body != null) - // Check if body is empty and null it again if so - if(body.Length == 0) { - body.Dispose(); - body = null; - } else { - if(transferEncodings != null) - ProcessEncoding(transferEncodings, body, true); - if(contentEncodings != null) - ProcessEncoding(contentEncodings, body, false); - - body.Seek(0, SeekOrigin.Begin); - } - - return new HttpResponseMessage(statusCode, statusMessage, protocolVersion, headers, body); - } - } -} diff --git a/Hamakaze/HttpTask.cs b/Hamakaze/HttpTask.cs deleted file mode 100644 index ddcd212..0000000 --- a/Hamakaze/HttpTask.cs +++ /dev/null @@ -1,189 +0,0 @@ -using Hamakaze.Headers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; - -namespace Hamakaze { - public class HttpTask { - public TaskState State { get; private set; } = TaskState.Initial; - - public bool IsStarted - => State != TaskState.Initial; - public bool IsFinished - => State == TaskState.Finished; - public bool IsCancelled - => State == TaskState.Cancelled; - public bool IsErrored - => Exception != null; - - public Exception Exception { get; private set; } - - public HttpRequestMessage Request { get; } - public HttpResponseMessage Response { get; private set; } - private HttpConnectionManager Connections { get; } - - private IEnumerable Addresses { get; set; } - private HttpConnection Connection { get; set; } - - public bool DisposeRequest { get; set; } - public bool DisposeResponse { get; set; } - - public event Action OnComplete; - public event Action OnError; - public event Action OnCancel; - public event Action OnUploadProgress; - public event Action OnDownloadProgress; - public event Action OnStateChange; - - public HttpTask(HttpConnectionManager conns, HttpRequestMessage request, bool disposeRequest, bool disposeResponse) { - Connections = conns ?? throw new ArgumentNullException(nameof(conns)); - Request = request ?? throw new ArgumentNullException(nameof(request)); - DisposeRequest = disposeRequest; - DisposeResponse = disposeResponse; - } - - public void Run() { - if(IsStarted) - throw new HttpTaskAlreadyStartedException(); - while(NextStep()); - } - - public void Cancel() { - State = TaskState.Cancelled; - OnStateChange?.Invoke(this, State); - OnCancel?.Invoke(this); - if(DisposeResponse) - Response?.Dispose(); - if(DisposeRequest) - Request?.Dispose(); - } - - private void Error(Exception ex) { - Exception = ex; - OnError?.Invoke(this, ex); - Cancel(); - } - - public bool NextStep() { - if(IsCancelled) - return false; - - switch(State) { - case TaskState.Initial: - State = TaskState.Lookup; - OnStateChange?.Invoke(this, State); - DoLookup(); - break; - case TaskState.Lookup: - State = TaskState.Request; - OnStateChange?.Invoke(this, State); - DoRequest(); - break; - case TaskState.Request: - State = TaskState.Response; - OnStateChange?.Invoke(this, State); - DoResponse(); - break; - case TaskState.Response: - State = TaskState.Finished; - OnStateChange?.Invoke(this, State); - OnComplete?.Invoke(this, Response); - if(DisposeResponse) - Response?.Dispose(); - if(DisposeRequest) - Request?.Dispose(); - return false; - default: - Error(new HttpTaskInvalidStateException()); - return false; - } - - return true; - } - - private void DoLookup() { - try { - Addresses = Dns.GetHostAddresses(Request.Host); - } catch(Exception ex) { - Error(ex); - return; - } - - if(!Addresses.Any()) - Error(new HttpTaskNoAddressesException()); - } - - private void DoRequest() { - Exception exception = null; - - try { - foreach(IPAddress addr in Addresses) { - int tries = 0; - IPEndPoint endPoint = new(addr, Request.Port); - - exception = null; - Connection = Connections.GetConnection(Request.Host, endPoint, Request.IsSecure); - - retry: - ++tries; - try { - Request.WriteTo(Connection.Stream, (p, t) => OnUploadProgress?.Invoke(this, p, t)); - break; - } catch(IOException ex) { - Connection.Dispose(); - Connection = Connections.GetConnection(Request.Host, endPoint, Request.IsSecure); - - if(tries < 2) - goto retry; - - exception = ex; - continue; - } finally { - Connection.MarkUsed(); - } - } - } catch(Exception ex) { - Error(ex); - } - - if(exception != null) - Error(exception); - else if(Connection == null) - Error(new HttpTaskNoConnectionException()); - } - - private void DoResponse() { - try { - Response = HttpResponseMessage.ReadFrom(Connection.Stream, (p, t) => OnDownloadProgress?.Invoke(this, p, t)); - } catch(Exception ex) { - Error(ex); - return; - } - - if(Response.Connection == HttpConnectionHeader.CLOSE) - Connection.Dispose(); - if(Response == null) - Error(new HttpTaskRequestFailedException()); - - HttpKeepAliveHeader hkah = Response.Headers.Where(x => x.Name == HttpKeepAliveHeader.NAME).Cast().FirstOrDefault(); - if(hkah != null) { - Connection.MaxIdle = hkah.MaxIdle; - Connection.MaxRequests = hkah.MaxRequests; - } - - Connection.Release(); - } - - public enum TaskState { - Initial = 0, - Lookup = 10, - Request = 20, - Response = 30, - Finished = 40, - - Cancelled = -1, - } - } -} diff --git a/Hamakaze/HttpTaskManager.cs b/Hamakaze/HttpTaskManager.cs deleted file mode 100644 index ef12439..0000000 --- a/Hamakaze/HttpTaskManager.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Threading; - -namespace Hamakaze { - public class HttpTaskManager : IDisposable { - private Semaphore Lock { get; set; } - - public HttpTaskManager(int maxThreads = 5) { - Lock = new Semaphore(maxThreads, maxThreads); - } - - public void RunTask(HttpTask task) { - if(task == null) - throw new ArgumentNullException(nameof(task)); - if(!Lock.WaitOne()) - throw new HttpTaskManagerLockException(); - new Thread(() => { - try { - task.Run(); - } finally { - Lock?.Release(); - } - }).Start(); - } - - private bool IsDisposed; - ~HttpTaskManager() - => DoDispose(); - public void Dispose() { - DoDispose(); - GC.SuppressFinalize(this); - } - private void DoDispose() { - if(IsDisposed) - return; - IsDisposed = true; - Lock.Dispose(); - Lock = null; - } - } -} diff --git a/SharpChat.sln b/SharpChat.sln index 3230db2..75c6f6e 100644 --- a/SharpChat.sln +++ b/SharpChat.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29025.244 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32630.192 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpChat", "SharpChat\SharpChat.csproj", "{DDB24C19-B802-4C96-AC15-0449C6FC77F2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpChatTest", "SharpChatTest\SharpChatTest.csproj", "{6CD6DB9D-E0FF-4DEB-8E28-0C3EA6BA26B2}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,10 +15,6 @@ Global {DDB24C19-B802-4C96-AC15-0449C6FC77F2}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDB24C19-B802-4C96-AC15-0449C6FC77F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {DDB24C19-B802-4C96-AC15-0449C6FC77F2}.Release|Any CPU.Build.0 = Release|Any CPU - {6CD6DB9D-E0FF-4DEB-8E28-0C3EA6BA26B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CD6DB9D-E0FF-4DEB-8E28-0C3EA6BA26B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CD6DB9D-E0FF-4DEB-8E28-0C3EA6BA26B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CD6DB9D-E0FF-4DEB-8E28-0C3EA6BA26B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SharpChat/ChatEventManager.cs b/SharpChat/ChatEventManager.cs index 53593a7..35bc3a3 100644 --- a/SharpChat/ChatEventManager.cs +++ b/SharpChat/ChatEventManager.cs @@ -15,7 +15,7 @@ namespace SharpChat { public ChatEventManager(ChatContext context) { Context = context; - if (!Database.HasDatabase && !WebDatabase.IsAvailable) + if (!Database.HasDatabase) Events = new List(); } @@ -27,9 +27,6 @@ namespace SharpChat { lock(Events) Events.Add(evt); - if(WebDatabase.IsAvailable) - WebDatabase.LogEvent(evt); - if(Database.HasDatabase) Database.LogEvent(evt); } @@ -42,9 +39,6 @@ namespace SharpChat { lock (Events) Events.Remove(evt); - if(WebDatabase.IsAvailable) - WebDatabase.DeleteEvent(evt); - if (Database.HasDatabase) Database.DeleteEvent(evt); @@ -55,9 +49,6 @@ namespace SharpChat { if (seqId < 1) return null; - if(WebDatabase.IsAvailable) - return WebDatabase.GetEvent(seqId); - if (Database.HasDatabase) return Database.GetEvent(seqId); @@ -69,9 +60,6 @@ namespace SharpChat { } public IEnumerable GetTargetLog(IPacketTarget target, int amount = 20, int offset = 0) { - if(WebDatabase.IsAvailable) - return WebDatabase.GetEvents(target, amount, offset); - if (Database.HasDatabase) return Database.GetEvents(target, amount, offset).Reverse(); diff --git a/SharpChat/ChatUser.cs b/SharpChat/ChatUser.cs index 37735dc..70b2121 100644 --- a/SharpChat/ChatUser.cs +++ b/SharpChat/ChatUser.cs @@ -82,7 +82,6 @@ namespace SharpChat { public string TargetName => @"@log"; - [Obsolete] public ChatChannel Channel { get { lock(Channels) @@ -94,7 +93,7 @@ namespace SharpChat { public ChatChannel CurrentChannel { get; private set; } public bool IsSilenced - => SilencedUntil != null && DateTimeOffset.UtcNow - SilencedUntil <= TimeSpan.Zero; + => DateTimeOffset.UtcNow - SilencedUntil <= TimeSpan.Zero; public bool HasSessions { get { diff --git a/SharpChat/Program.cs b/SharpChat/Program.cs index 34c815b..e460bee 100644 --- a/SharpChat/Program.cs +++ b/SharpChat/Program.cs @@ -19,66 +19,12 @@ namespace SharpChat { Console.WriteLine(@"============================================ DEBUG =="); #endif -#if DEBUG - Console.WriteLine(@"HOLD A KEY TO START A TEST NOW"); - Thread.Sleep(1000); - if (Console.KeyAvailable) - switch (Console.ReadKey(true).Key) { - case ConsoleKey.F: - TestMisuzuAuth(); - return; - } -#endif - - WebDatabase.ReadConfig(); - if(!WebDatabase.IsAvailable) - Database.ReadConfig(); + Database.ReadConfig(); using ManualResetEvent mre = new ManualResetEvent(false); using SockChatServer scs = new SockChatServer(PORT); Console.CancelKeyPress += (s, e) => { e.Cancel = true; mre.Set(); }; mre.WaitOne(); } - -#if DEBUG - private static void TestMisuzuAuth() { - Console.WriteLine($@"Enter token found on {FlashiiUrls.BASE_URL}/login:"); - string[] token = Console.ReadLine().Split(new[] { '_' }, 2); - - System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient(); - httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(@"SharpChat"); - - FlashiiAuth authRes = FlashiiAuth.Attempt(httpClient, new FlashiiAuthRequest { - UserId = int.Parse(token[0]), Token = token[1], IPAddress = @"1.2.4.8" - }).GetAwaiter().GetResult(); - - if(authRes.Success) { - Console.WriteLine(@"Auth success!"); - Console.WriteLine($@" User ID: {authRes.UserId}"); - Console.WriteLine($@" Username: {authRes.Username}"); - Console.WriteLine($@" Colour: {authRes.ColourRaw:X8}"); - Console.WriteLine($@" Hierarchy: {authRes.Rank}"); - Console.WriteLine($@" Silenced: {authRes.SilencedUntil}"); - Console.WriteLine($@" Perms: {authRes.Permissions}"); - } else { - Console.WriteLine($@"Auth failed: {authRes.Reason}"); - return; - } - - Console.WriteLine(@"Bumping last seen..."); - FlashiiBump.Submit(httpClient, new[] { new ChatUser(authRes) }); - - Console.WriteLine(@"Fetching ban list..."); - IEnumerable bans = FlashiiBan.GetList(httpClient).GetAwaiter().GetResult(); - Console.WriteLine($@"{bans.Count()} BANS"); - foreach(FlashiiBan ban in bans) { - Console.WriteLine($@"BAN INFO"); - Console.WriteLine($@" User ID: {ban.UserId}"); - Console.WriteLine($@" Username: {ban.Username}"); - Console.WriteLine($@" IP Address: {ban.UserIP}"); - Console.WriteLine($@" Expires: {ban.Expires}"); - } - } -#endif } } diff --git a/SharpChat/WebDatabase.cs b/SharpChat/WebDatabase.cs deleted file mode 100644 index 80e22d6..0000000 --- a/SharpChat/WebDatabase.cs +++ /dev/null @@ -1,233 +0,0 @@ -using SharpChat.Events; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace SharpChat { - public static class WebDatabase { - private static string EndPoint = null; - - public static bool IsAvailable - => !string.IsNullOrWhiteSpace(EndPoint); - - public static void ReadConfig() { - if(!File.Exists(@"webdb.txt")) - return; - string[] config = File.ReadAllLines(@"webdb.txt"); - if(config.Length < 1 && !string.IsNullOrWhiteSpace(config[0])) - return; - Init(config[0]); - } - - public static void Init(string endPoint) { - EndPoint = endPoint; - } - - public static void Deinit() { - EndPoint = null; - } - - public static void LogEvent(IChatEvent evt) { - if(evt.SequenceId < 1) - evt.SequenceId = Database.GenerateId(); - - string sId = evt.SequenceId.ToString(); - string sCreated = evt.DateTime.ToUnixTimeSeconds().ToString(); - string sType = evt.GetType().FullName; - string sTarget = evt.Target.TargetName ?? string.Empty; - string sFlags = ((byte)evt.Flags).ToString(); - string sData = JsonSerializer.Serialize(evt, evt.GetType()); - string sSender = evt.Sender?.UserId < 1 ? string.Empty : evt.Sender.UserId.ToString(); - string sSenderName = evt.Sender?.Username.ToString() ?? string.Empty; - string sSenderColour = evt.Sender?.Colour.Raw.ToString() ?? string.Empty; - string sSenderRank = evt.Sender?.Rank.ToString() ?? string.Empty; - string sSenderNick = evt.Sender?.Nickname?.ToString() ?? string.Empty; - string sSenderPerms = evt.Sender == null ? string.Empty : ((int)evt.Sender.Permissions).ToString(); - string sHash = string.Join('#', @"crev", sId, sCreated, sType, sTarget, sFlags, sSender, sSenderColour, sSenderRank, sSenderPerms, sSenderName, sSenderNick, sData); - - MultipartFormDataContent mfdc = new(); - mfdc.Add(new StringContent(sId), @"i"); - mfdc.Add(new StringContent(sCreated), @"c"); - mfdc.Add(new StringContent(sType), @"t"); - mfdc.Add(new StringContent(sTarget), @"l"); - mfdc.Add(new StringContent(sFlags), @"f"); - mfdc.Add(new StringContent(sData), @"d"); - mfdc.Add(new StringContent(sSender), @"s"); - mfdc.Add(new StringContent(sSenderName), @"su"); - mfdc.Add(new StringContent(sSenderColour), @"sc"); - mfdc.Add(new StringContent(sSenderRank), @"sr"); - mfdc.Add(new StringContent(sSenderNick), @"sn"); - mfdc.Add(new StringContent(sSenderPerms), @"sp"); - - HttpRequestMessage request = new(HttpMethod.Post, EndPoint + @"?m=crev") { - Content = mfdc, - Headers = { - { @"X-SharpChat-Signature", sHash.GetSignedHash() }, - } - }; - - SockChatServer.HttpClient.SendAsync(request).ContinueWith(x => { - if(x.IsCompleted) { - if(x.Result.Headers.TryGetValues(@"X-SharpChat-Error", out IEnumerable values)) - Logger.Write($@"DBS - LogEvent: {values.FirstOrDefault()}"); - } - - if(x.IsFaulted) - Logger.Write($@"DBS - LogEvent: {x.Exception}"); - }).Wait(); - } - - public static void DeleteEvent(IChatEvent evt) { - string msgId = evt.SequenceId.ToString(); - - using HttpRequestMessage request = new(HttpMethod.Delete, EndPoint + @"?m=deev&i=" + msgId) { - Headers = { - { @"X-SharpChat-Signature", (@"deev#" + msgId).GetSignedHash() }, - } - }; - - SockChatServer.HttpClient.SendAsync(request).ContinueWith(x => { - if(x.IsCompleted) { - if(x.Result.Headers.TryGetValues(@"X-SharpChat-Error", out IEnumerable values)) - Logger.Write($@"DBS - DeleteEvent: {values.FirstOrDefault()}"); - } - - if(x.IsFaulted) - Logger.Write($@"DBS - DeleteEvent: {x.Exception}"); - }).Wait(); - } - - private class DatabaseRow { - [JsonPropertyName(@"i")] - public long EventId { get; set; } - - [JsonPropertyName(@"s")] - public long EventSenderId { get; set; } - - [JsonPropertyName(@"su")] - public string EventSenderName { get; set; } - - [JsonPropertyName(@"sc")] - public int EventSenderColour { get; set; } - - [JsonPropertyName(@"sr")] - public int EventSenderRank { get; set; } - - [JsonPropertyName(@"sn")] - public string EventSenderNick { get; set; } - - [JsonPropertyName(@"sp")] - public int EventSenderPerms { get; set; } - - [JsonPropertyName(@"c")] - public int EventCreated { get; set; } - - [JsonPropertyName(@"t")] - public string EventType { get; set; } - - [JsonPropertyName(@"l")] - public string EventTarget { get; set; } - - [JsonPropertyName(@"f")] - public byte EventFlags { get; set; } - - [JsonPropertyName(@"d")] - public string EventData { get; set; } - } - - private static IChatEvent ReadEvent(DatabaseRow row, IPacketTarget target = null) { - Type evtType = Type.GetType(row.EventType); - IChatEvent evt = JsonSerializer.Deserialize(row.EventData, evtType) as IChatEvent; - evt.SequenceId = row.EventId; - evt.Target = target; - evt.TargetName = target?.TargetName ?? row.EventTarget; - evt.Flags = (ChatMessageFlags)row.EventFlags; - evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(row.EventCreated); - - if(row.EventSenderId > 0) { - evt.Sender = new BasicUser { - UserId = row.EventSenderId, - Username = row.EventSenderName, - Colour = new ChatColour(row.EventSenderColour), - Rank = row.EventSenderRank, - Nickname = string.IsNullOrEmpty(row.EventSenderNick) ? null : row.EventSenderNick, - Permissions = (ChatUserPermissions)row.EventSenderPerms - }; - } - - return evt; - } - - public static IChatEvent GetEvent(long seqId) { - string sSeqId = seqId.ToString(); - - using HttpRequestMessage request = new(HttpMethod.Get, EndPoint + @"?m=geev&i=" + sSeqId) { - Headers = { - { @"X-SharpChat-Signature", (@"geev#" + sSeqId).GetSignedHash() }, - } - }; - - // jesus christ what is this - System.Runtime.CompilerServices.TaskAwaiter? awaiter = null; - HttpResponseMessage response = null; - - try { - awaiter = SockChatServer.HttpClient.SendAsync(request).GetAwaiter(); - response = awaiter.Value.GetResult(); - - byte[] garbage = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); - - return ReadEvent( - (DatabaseRow)JsonSerializer.Deserialize(garbage, typeof(DatabaseRow)) - ); - } catch(Exception ex) { - Logger.Write($@"DBS - GetEvent: {ex}"); - } finally { - if(awaiter.HasValue && awaiter.Value.IsCompleted) { - if(response != null && response.Headers.TryGetValues(@"X-SharpChat-Error", out IEnumerable values)) - Logger.Write($@"DBS - GetEvent: {values.FirstOrDefault()}"); - } - } - - return null; - } - - public static IEnumerable GetEvents(IPacketTarget target, int amount, int offset) { - List events = new(); - - using HttpRequestMessage request = new(HttpMethod.Get, EndPoint + @"?m=feev&l=" + target.TargetName + @"&a=" + amount + @"&o=" + offset) { - Headers = { - { @"X-SharpChat-Signature", string.Join('#', @"feev", amount, offset, target.TargetName).GetSignedHash() }, - } - }; - - System.Runtime.CompilerServices.TaskAwaiter? awaiter = null; - HttpResponseMessage response = null; - - try { - awaiter = SockChatServer.HttpClient.SendAsync(request).GetAwaiter(); - response = awaiter.Value.GetResult(); - - byte[] trash = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); - - IEnumerable rows = (IEnumerable)JsonSerializer.Deserialize(trash, typeof(IEnumerable)); - - foreach(DatabaseRow row in rows) - events.Add(ReadEvent(row, target)); - } catch(Exception ex) { - Logger.Write($@"DBS - GetEvents: {ex}"); - } finally { - if(awaiter.HasValue && awaiter.Value.IsCompleted) { - if(response != null && response.Headers.TryGetValues(@"X-SharpChat-Error", out IEnumerable values)) - Logger.Write($@"DBS - GetEvents: {values.FirstOrDefault()}"); - } - } - - return events; - } - } -}