diff --git a/src/Entity/HttpHeaderCollection.cs b/src/Entity/HttpHeaderCollection.cs
index 30f3c80..dbb6367 100644
--- a/src/Entity/HttpHeaderCollection.cs
+++ b/src/Entity/HttpHeaderCollection.cs
@@ -13,7 +13,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection.PortableExecutable;
using System.Text;
-using Header = Sisk.Core.Internal.HttpKnownHeaderNames;
+using Header = Sisk.Core.Http.HttpKnownHeaderNames;
namespace Sisk.Core.Entity;
diff --git a/src/Entity/MultipartFormReader.cs b/src/Entity/MultipartFormReader.cs
index 3352635..056d00e 100644
--- a/src/Entity/MultipartFormReader.cs
+++ b/src/Entity/MultipartFormReader.cs
@@ -7,6 +7,7 @@
// File name: MultipartFormReader.cs
// Repository: https://github.com/sisk-http/core
+using Sisk.Core.Http;
using Sisk.Core.Internal;
using System;
using System.Collections.Generic;
diff --git a/src/Entity/MultipartObject.cs b/src/Entity/MultipartObject.cs
index b3c422b..e80357b 100644
--- a/src/Entity/MultipartObject.cs
+++ b/src/Entity/MultipartObject.cs
@@ -8,7 +8,6 @@
// Repository: https://github.com/sisk-http/core
using Sisk.Core.Http;
-using Sisk.Core.Internal;
using System.Collections.Specialized;
using System.Text;
diff --git a/src/Http/CookieHelpers.cs b/src/Http/CookieHelpers.cs
index feb05e8..068b8cc 100644
--- a/src/Http/CookieHelpers.cs
+++ b/src/Http/CookieHelpers.cs
@@ -7,7 +7,6 @@
// File name: CookieHelpers.cs
// Repository: https://github.com/sisk-http/core
-using Sisk.Core.Internal;
using System.Web;
namespace Sisk.Core.Http;
diff --git a/src/Http/Handlers/AsyncHttpServerHandler.cs b/src/Http/Handlers/AsyncHttpServerHandler.cs
new file mode 100644
index 0000000..53770d3
--- /dev/null
+++ b/src/Http/Handlers/AsyncHttpServerHandler.cs
@@ -0,0 +1,134 @@
+// The Sisk Framework source code
+// Copyright (c) 2023 PROJECT PRINCIPIUM
+//
+// The code below is licensed under the MIT license as
+// of the date of its publication, available at
+//
+// File name: AsyncHttpServerHandler.cs
+// Repository: https://github.com/sisk-http/core
+
+using Sisk.Core.Entity;
+using Sisk.Core.Routing;
+
+namespace Sisk.Core.Http.Handlers;
+
+///
+/// Represents an asynchronous event handler for the , router, and related events.
+///
+public abstract class AsyncHttpServerHandler : HttpServerHandler
+{
+ ///
+ /// Method that is called immediately before starting the .
+ ///
+ /// The HTTP server entity which is starting.
+ protected virtual Task OnServerStartingAsync(HttpServer server) => Task.CompletedTask;
+
+ ///
+ /// Method that is called immediately after starting the , when it's
+ /// ready and listening.
+ ///
+ /// The HTTP server entity which is ready.
+ protected virtual Task OnServerStartedAsync(HttpServer server) => Task.CompletedTask;
+
+ ///
+ /// Method that is called before the stop, when it is
+ /// stopping from listening requests.
+ ///
+ /// The HTTP server entity which is stopping.
+ protected virtual Task OnServerStoppingAsync(HttpServer server) => Task.CompletedTask;
+
+ ///
+ /// Method that is called after the is stopped, meaning
+ /// it has stopped from listening to requests.
+ ///
+ /// The HTTP server entity which has stopped.
+ protected virtual Task OnServerStoppedAsync(HttpServer server) => Task.CompletedTask;
+
+ ///
+ /// Method that is called when an is binded to the HTTP server.
+ ///
+ /// The router entity which is binded.
+ protected virtual Task OnSetupRouterAsync(Router router) => Task.CompletedTask;
+
+ ///
+ /// Method that is called when an HTTP context is created within an
+ /// object.
+ ///
+ /// The creating context bag.
+ protected virtual Task OnContextBagCreatedAsync(TypedValueDictionary contextBag) => Task.CompletedTask;
+
+ ///
+ /// Method that is called when an is received in the
+ /// HTTP server.
+ ///
+ /// The connecting HTTP request entity.
+ protected virtual Task OnHttpRequestOpenAsync(HttpRequest request) => Task.CompletedTask;
+
+ ///
+ /// Method that is called when an is closed in the
+ /// HTTP server.
+ ///
+ /// The result of the execution of the request.
+ protected virtual Task OnHttpRequestCloseAsync(HttpServerExecutionResult result) => Task.CompletedTask;
+
+ ///
+ /// Method that is called when an exception is caught in the HTTP server. This method is called
+ /// regardless of whether is enabled or not.
+ ///
+ /// The exception object.
+ protected virtual Task OnExceptionAsync(Exception exception) => Task.CompletedTask;
+
+ ///
+ protected sealed override void OnContextBagCreated(TypedValueDictionary contextBag)
+ {
+ OnContextBagCreatedAsync(contextBag).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnException(Exception exception)
+ {
+ OnExceptionAsync(exception).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnHttpRequestClose(HttpServerExecutionResult result)
+ {
+ OnHttpRequestCloseAsync(result).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnHttpRequestOpen(HttpRequest request)
+ {
+ OnHttpRequestOpenAsync(request).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnServerStarted(HttpServer server)
+ {
+ OnServerStartedAsync(server).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnServerStarting(HttpServer server)
+ {
+ OnServerStartingAsync(server).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnServerStopped(HttpServer server)
+ {
+ OnServerStoppedAsync(server).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnServerStopping(HttpServer server)
+ {
+ OnServerStoppingAsync(server).GetAwaiter().GetResult();
+ }
+
+ ///
+ protected sealed override void OnSetupRouter(Router router)
+ {
+ OnSetupRouterAsync(router).GetAwaiter().GetResult();
+ }
+}
diff --git a/src/Http/Handlers/HttpServerHandler.cs b/src/Http/Handlers/HttpServerHandler.cs
index 9116575..0a40fd3 100644
--- a/src/Http/Handlers/HttpServerHandler.cs
+++ b/src/Http/Handlers/HttpServerHandler.cs
@@ -7,6 +7,7 @@
// File name: HttpServerHandler.cs
// Repository: https://github.com/sisk-http/core
+using Sisk.Core.Entity;
using Sisk.Core.Routing;
namespace Sisk.Core.Http.Handlers;
@@ -52,16 +53,15 @@ protected virtual void OnServerStopped(HttpServer server) { }
///
/// The router entity which is binded.
protected virtual void OnSetupRouter(Router router) { }
-
internal void InvokeOnSetupRouter(Router router) => OnSetupRouter(router);
///
- /// Method that is called when an is created within an
+ /// Method that is called when an HTTP context is created within an
/// object.
///
/// The creating context bag.
- protected virtual void OnContextBagCreated(HttpContextBagRepository contextBag) { }
- internal void InvokeOnContextBagCreated(HttpContextBagRepository contextBag) => OnContextBagCreated(contextBag);
+ protected virtual void OnContextBagCreated(TypedValueDictionary contextBag) { }
+ internal void InvokeOnContextBagCreated(TypedValueDictionary contextBag) => OnContextBagCreated(contextBag);
///
/// Method that is called when an is received in the
diff --git a/src/Http/Handlers/HttpServerHandlerRepository.cs b/src/Http/Handlers/HttpServerHandlerRepository.cs
index 8878479..462e271 100644
--- a/src/Http/Handlers/HttpServerHandlerRepository.cs
+++ b/src/Http/Handlers/HttpServerHandlerRepository.cs
@@ -7,6 +7,7 @@
// File name: HttpServerHandlerRepository.cs
// Repository: https://github.com/sisk-http/core
+using Sisk.Core.Entity;
using Sisk.Core.Routing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -56,7 +57,7 @@ private void CallEvery(Action action)
internal void ServerStarting(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStarting(val));
internal void ServerStarted(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStarted(val));
internal void SetupRouter(Router val) => CallEvery(handler => handler.InvokeOnSetupRouter(val));
- internal void ContextBagCreated(HttpContextBagRepository val) => CallEvery(handler => handler.InvokeOnContextBagCreated(val));
+ internal void ContextBagCreated(TypedValueDictionary val) => CallEvery(handler => handler.InvokeOnContextBagCreated(val));
internal void HttpRequestOpen(HttpRequest val) => CallEvery(handler => handler.InvokeOnHttpRequestOpen(val));
internal void HttpRequestClose(HttpServerExecutionResult val) => CallEvery(handler => handler.InvokeOnHttpRequestClose(val));
internal void Exception(Exception val) => CallEvery(handler => handler.InvokeOnException(val));
diff --git a/src/Http/HttpContext.cs b/src/Http/HttpContext.cs
index c4774ad..ace99cf 100644
--- a/src/Http/HttpContext.cs
+++ b/src/Http/HttpContext.cs
@@ -32,7 +32,7 @@ public sealed class HttpContext
///
/// Gets or sets a managed object that is accessed and modified by request handlers.
///
- public HttpContextBagRepository RequestBag { get; set; } = new HttpContextBagRepository();
+ public TypedValueDictionary RequestBag { get; set; } = new TypedValueDictionary();
///
/// Gets the context HTTP Server instance.
diff --git a/src/Http/HttpContextBagRepository.cs b/src/Http/HttpContextBagRepository.cs
deleted file mode 100644
index 3f13c80..0000000
--- a/src/Http/HttpContextBagRepository.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// The Sisk Framework source code
-// Copyright (c) 2023 PROJECT PRINCIPIUM
-//
-// The code below is licensed under the MIT license as
-// of the date of its publication, available at
-//
-// File name: HttpContextBagRepository.cs
-// Repository: https://github.com/sisk-http/core
-
-using Sisk.Core.Entity;
-
-namespace Sisk.Core.Http;
-
-///
-/// Represents a repository of information stored over the lifetime of a request.
-///
-public sealed class HttpContextBagRepository : TypedValueDictionary
-{
-}
diff --git a/src/Http/HttpKnownHeaderNames.cs b/src/Http/HttpKnownHeaderNames.cs
new file mode 100644
index 0000000..3d3f68c
--- /dev/null
+++ b/src/Http/HttpKnownHeaderNames.cs
@@ -0,0 +1,531 @@
+// The Sisk Framework source code
+// Copyright (c) 2023 PROJECT PRINCIPIUM
+//
+// The code below is licensed under the MIT license as
+// of the date of its publication, available at
+//
+// File name: HttpKnownHeaderNames.cs
+// Repository: https://github.com/sisk-http/core
+
+// The source code below was forked from the official dotnet runtime
+//
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Sisk.Core.Http;
+
+///
+/// Provides most of the most commonly known HTTP headers for constants.
+///
+public static class HttpKnownHeaderNames
+{
+ ///
+ /// The HTTP Accept header.
+ /// Specifies the media types that are acceptable for the response, allowing the client to indicate its preferences.
+ ///
+ public const string Accept = "Accept";
+
+ ///
+ /// The HTTP Accept-Charset header.
+ /// Indicates the character sets that are acceptable for the response, allowing the client to specify its preferred encoding.
+ ///
+ public const string AcceptCharset = "Accept-Charset";
+
+ ///
+ /// The HTTP Accept-Encoding header.
+ /// Specifies the content encodings that are acceptable for the response, allowing the client to indicate its preferences for compression.
+ ///
+ public const string AcceptEncoding = "Accept-Encoding";
+
+ ///
+ /// The HTTP Accept-Language header.
+ /// Indicates the natural languages that are preferred for the response, allowing the client to specify its language preferences.
+ ///
+ public const string AcceptLanguage = "Accept-Language";
+
+ ///
+ /// The HTTP Accept-Patch header.
+ /// Indicates the patch document formats that are acceptable for the response, allowing the client to specify its preferences for patching resources.
+ ///
+ public const string AcceptPatch = "Accept-Patch";
+
+ ///
+ /// The HTTP Accept-Ranges header.
+ /// Indicates that the server supports range requests for the resource, allowing clients to request specific byte ranges.
+ ///
+ public const string AcceptRanges = "Accept-Ranges";
+
+ ///
+ /// The HTTP Access-Control-Allow-Credentials header.
+ /// Indicates whether the response to the request can expose credentials, allowing cross-origin requests to include credentials.
+ ///
+ public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
+
+ ///
+ /// The HTTP Access-Control-Allow-Headers header.
+ /// Specifies which headers can be used when making the actual request in a cross-origin resource sharing (CORS) context.
+ ///
+ public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
+
+ ///
+ /// The HTTP Access-Control-Allow-Methods header.
+ /// Specifies the methods that are allowed when accessing the resource in a CORS context.
+ ///
+ public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
+
+ ///
+ /// The HTTP Access-Control-Allow-Origin header.
+ /// Specifies which origins are allowed to access the resource in a CORS context, helping to control cross-origin requests.
+ ///
+ public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
+
+ ///
+ /// The HTTP Access-Control-Expose-Headers header.
+ /// Indicates which headers can be exposed as part of the response to a cross-origin request.
+ ///
+ public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
+
+ ///
+ /// The HTTP Access-Control-Max-Age header.
+ /// Specifies how long the results of a preflight request can be cached, reducing the number of preflight requests made.
+ ///
+ public const string AccessControlMaxAge = "Access-Control-Max-Age";
+
+ ///
+ /// The HTTP Age header.
+ /// Indicates the age of the object in a cache, helping clients understand how fresh the cached response is.
+ ///
+ public const string Age = "Age";
+
+ ///
+ /// The HTTP Allow header.
+ /// Lists the HTTP methods that are supported by the resource, informing clients about the available actions.
+ ///
+ public const string Allow = "Allow";
+
+ ///
+ /// The HTTP Alt-Svc header.
+ /// Indicates that an alternative service is available for the resource, allowing clients to connect to a different server or protocol.
+ ///
+ public const string AltSvc = "Alt-Svc";
+
+ ///
+ /// The HTTP Authorization header.
+ /// Contains credentials for authenticating the client with the server, often used for basic or bearer token authentication.
+ ///
+ public const string Authorization = "Authorization";
+
+ ///
+ /// The HTTP Cache-Control header.
+ /// Directs caching mechanisms on how to cache the response, including directives for expiration and revalidation.
+ ///
+ public const string CacheControl = "Cache-Control";
+
+ ///
+ /// The HTTP Connection header.
+ /// Controls whether the network connection stays open after the current transaction finishes, allowing for persistent connections.
+ ///
+ public const string Connection = "Connection";
+
+ ///
+ /// The HTTP Content-Disposition header.
+ /// Indicates if the content should be displayed inline in the browser or treated as an attachment to be downloaded.
+ ///
+ public const string ContentDisposition = "Content-Disposition";
+
+ ///
+ /// The HTTP Content-Encoding header.
+ /// Specifies the encoding transformations that have been applied to the response body, such as gzip or deflate.
+ ///
+ public const string ContentEncoding = "Content-Encoding";
+
+ ///
+ /// The HTTP Content-Language header.
+ /// Indicates the natural language(s) of the intended audience for the response, helping clients understand the content's language.
+ ///
+ public const string ContentLanguage = "Content-Language";
+
+ ///
+ /// The HTTP Content-Length header.
+ /// Indicates the size of the response body in bytes, allowing the client to know how much data to expect.
+ ///
+ public const string ContentLength = "Content-Length";
+
+ ///
+ /// The HTTP Content-Location header.
+ /// Indicates an alternate location for the returned data, often used for redirecting clients to a different resource.
+ ///
+ public const string ContentLocation = "Content-Location";
+
+ ///
+ /// The HTTP Content-MD5 header.
+ /// Contains the MD5 hash of the response body, allowing clients to verify the integrity of the received data.
+ ///
+ public const string ContentMD5 = "Content-MD5";
+
+ ///
+ /// The HTTP Content-Range header.
+ /// Indicates the part of a document that the server is returning, used in range requests to specify byte ranges.
+ ///
+ public const string ContentRange = "Content-Range";
+
+ ///
+ /// The HTTP Content-Security-Policy header.
+ /// Defines security policies for the content, helping to prevent cross-site scripting (XSS) and other code injection attacks.
+ ///
+ public const string ContentSecurityPolicy = "Content-Security-Policy";
+
+ ///
+ /// The HTTP Content-Type header.
+ /// Indicates the media type of the resource, allowing the client to understand how to process the response body.
+ ///
+ public const string ContentType = "Content-Type";
+
+ ///
+ /// The HTTP Cookie header.
+ /// Contains stored HTTP cookies previously sent by the server, allowing the server to identify the client on subsequent requests.
+ ///
+ public const string Cookie = "Cookie";
+
+ ///
+ /// The HTTP Cookie2 header.
+ /// Used to send cookies in a more advanced format, primarily for compatibility with older versions of HTTP.
+ ///
+ public const string Cookie2 = "Cookie2";
+
+ ///
+ /// The HTTP Date header.
+ /// Indicates the date and time at which the message was sent, helping clients understand the freshness of the response.
+ ///
+ public const string Date = "Date";
+
+ ///
+ /// The HTTP ETag header.
+ /// Provides a unique identifier for a specific version of a resource, allowing clients to cache and validate resources efficiently.
+ ///
+ public const string ETag = "ETag";
+
+ ///
+ /// The HTTP Expect header.
+ /// Indicates that the client expects certain behaviors from the server, such as support for specific features or conditions.
+ ///
+ public const string Expect = "Expect";
+
+ ///
+ /// The HTTP Expires header.
+ /// Indicates the date and time after which the response is considered stale, helping clients manage caching.
+ ///
+ public const string Expires = "Expires";
+
+ ///
+ /// The HTTP From header.
+ /// Contains the email address of the user making the request, often used for identifying the requester.
+ ///
+ public const string From = "From";
+
+ ///
+ /// The HTTP Host header.
+ /// Specifies the domain name of the server and the TCP port number on which the server is listening, allowing for virtual hosting.
+ ///
+ public const string Host = "Host";
+
+ ///
+ /// The HTTP If-Match header.
+ /// Used to make a conditional request, allowing the client to specify that the request should only be processed if the resource matches the given ETag.
+ ///
+ public const string IfMatch = "If-Match";
+
+ ///
+ /// The HTTP If-Modified-Since header.
+ /// Used to make a conditional request, allowing the client to specify that the resource should only be returned if it has been modified since the given date.
+ ///
+ public const string IfModifiedSince = "If-Modified-Since";
+
+ ///
+ /// The HTTP If-None-Match header.
+ /// Used to make a conditional request, allowing the client to specify that the resource should only be returned if it does not match the given ETag.
+ ///
+ public const string IfNoneMatch = "If-None-Match";
+
+ ///
+ /// The HTTP If-Range header.
+ /// Used to make a conditional range request, allowing the client to specify that the range should only be returned if the resource has not changed.
+ ///
+ public const string IfRange = "If-Range";
+
+ ///
+ /// The HTTP If-Unmodified-Since header.
+ /// Used to make a conditional request, allowing the client to specify that the resource should only be returned if it has not been modified since the given date.
+ ///
+ public const string IfUnmodifiedSince = "If-Unmodified-Since";
+
+ ///
+ /// The HTTP Keep-Alive header.
+ /// Used to specify parameters for persistent connections, allowing the client and server to maintain an open connection for multiple requests.
+ ///
+ public const string KeepAlive = "Keep-Alive";
+
+ ///
+ /// The HTTP Last-Modified header.
+ /// Indicates the date and time at which the resource was last modified, helping clients determine if they need to refresh their cached version.
+ ///
+ public const string LastModified = "Last-Modified";
+
+ ///
+ /// The HTTP Link header.
+ /// Used to provide relationships between the current resource and other resources, often used for navigation and linking.
+ ///
+ public const string Link = "Link";
+
+ ///
+ /// The HTTP Location header.
+ /// Used in redirection responses to indicate the URL to which the client should redirect.
+ ///
+ public const string Location = "Location";
+
+ ///
+ /// The HTTP Max-Forwards header.
+ /// Used in OPTIONS requests to limit the number of times the request can be forwarded by proxies.
+ ///
+ public const string MaxForwards = "Max-Forwards";
+
+ ///
+ /// The HTTP Origin header.
+ /// Indicates the origin of the request, helping servers implement CORS and manage cross-origin requests.
+ ///
+ public const string Origin = "Origin";
+
+ ///
+ /// The HTTP P3P header.
+ /// Used to indicate the privacy policy of the server, allowing clients to understand how their data will be handled.
+ ///
+ public const string P3P = "P3P";
+
+ ///
+ /// The HTTP Pragma header.
+ /// Used to include implementation-specific directives that might apply to any recipient along the request/response chain.
+ ///
+ public const string Pragma = "Pragma";
+
+ ///
+ /// The HTTP Proxy-Authenticate header.
+ /// Used by a proxy server to request authentication from the client, indicating the authentication method required.
+ ///
+ public const string ProxyAuthenticate = "Proxy-Authenticate";
+
+ ///
+ /// The HTTP Proxy-Authorization header.
+ /// Contains credentials for authenticating the client with a proxy server, allowing access to the requested resource.
+ ///
+ public const string ProxyAuthorization = "Proxy-Authorization";
+
+ ///
+ /// The HTTP Proxy-Connection header.
+ /// Used to control whether the network connection to the proxy server should be kept open after the current transaction.
+ ///
+ public const string ProxyConnection = "Proxy-Connection";
+
+ ///
+ /// The HTTP Public-Key-Pins header.
+ /// Used to prevent man-in-the-middle attacks by specifying which public keys are valid for the server's certificate.
+ ///
+ public const string PublicKeyPins = "Public-Key-Pins";
+
+ ///
+ /// The HTTP Range header.
+ /// Used to request a specific range of bytes from a resource, allowing clients to download large files in parts.
+ ///
+ public const string Range = "Range";
+
+ ///
+ /// The HTTP Referer header.
+ /// Indicates the URL of the resource from which the request originated, helping servers understand the source of traffic.
+ ///
+ public const string Referer = "Referer";
+
+ ///
+ /// The HTTP Retry-After header.
+ /// Indicates how long the client should wait before making a follow-up request, often used in rate limiting scenarios.
+ ///
+ public const string RetryAfter = "Retry-After";
+
+ ///
+ /// The HTTP Sec-WebSocket-Accept header.
+ /// Used in the WebSocket handshake to confirm the server's acceptance of the connection request.
+ ///
+ public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
+
+ ///
+ /// The HTTP Sec-WebSocket-Extensions header.
+ /// Used to negotiate WebSocket extensions during the handshake, allowing for additional features and capabilities.
+ ///
+ public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
+
+ ///
+ /// The HTTP Sec-WebSocket-Key header.
+ /// Contains a base64-encoded value used to establish a WebSocket connection, ensuring the request is valid.
+ ///
+ public const string SecWebSocketKey = "Sec-WebSocket-Key";
+
+ ///
+ /// The HTTP Sec-WebSocket-Protocol header.
+ /// Used to specify subprotocols that the client wishes to use during the WebSocket connection.
+ ///
+ public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
+
+ ///
+ /// The HTTP Sec-WebSocket-Version header.
+ /// Indicates the version of the WebSocket protocol that the client wishes to use.
+ ///
+ public const string SecWebSocketVersion = "Sec-WebSocket-Version";
+
+ ///
+ /// The HTTP Server header.
+ /// Contains information about the server software handling the request, often used for informational purposes.
+ ///
+ public const string Server = "Server";
+
+ ///
+ /// The HTTP Set-Cookie header.
+ /// Used to send cookies from the server to the client, allowing the server to store state information on the client.
+ ///
+ public const string SetCookie = "Set-Cookie";
+
+ ///
+ /// The HTTP Set-Cookie2 header.
+ /// Used to send cookies in a more advanced format, primarily for compatibility with older versions of HTTP.
+ ///
+ public const string SetCookie2 = "Set-Cookie2";
+
+ ///
+ /// The HTTP Strict-Transport-Security header.
+ /// Enforces secure (HTTPS) connections to the server, helping to prevent man-in-the-middle attacks.
+ ///
+ public const string StrictTransportSecurity = "Strict-Transport-Security";
+
+ ///
+ /// The HTTP TE header.
+ /// Indicates the transfer encodings that are acceptable for the response, allowing for content negotiation.
+ ///
+ public const string TE = "TE";
+
+ ///
+ /// The HTTP TSV header.
+ /// Used to indicate the type of data being sent in a transaction, often used in specific applications or protocols.
+ ///
+ public const string TSV = "TSV";
+
+ ///
+ /// The HTTP Trailer header.
+ /// Indicates that the sender will include additional fields in the message trailer, which can be used for metadata.
+ ///
+ public const string Trailer = "Trailer";
+
+ ///
+ /// The HTTP Transfer-Encoding header.
+ /// Specifies the form of encoding used to safely transfer the payload body to the user.
+ ///
+ public const string TransferEncoding = "Transfer-Encoding";
+
+ ///
+ /// The HTTP Upgrade header.
+ /// Indicates that the client prefers to upgrade to a different protocol, such as switching from HTTP/1.1 to HTTP/2.
+ ///
+ public const string Upgrade = "Upgrade";
+
+ ///
+ /// The HTTP Upgrade-Insecure-Requests header.
+ /// Indicates that the client prefers to receive an upgraded version of the resource over HTTPS instead of HTTP.
+ ///
+ public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
+
+ ///
+ /// The HTTP User-Agent header.
+ /// Contains information about the user agent (browser or application) making the request, including its version and platform.
+ ///
+ public const string UserAgent = "User-Agent";
+
+ ///
+ /// The HTTP Vary header.
+ /// Indicates that the response varies based on the value of the specified request headers, allowing for content negotiation.
+ ///
+ public const string Vary = "Vary";
+
+ ///
+ /// The HTTP Via header.
+ /// Used to track message forwards and proxies, indicating the intermediate protocols and recipients involved in the request/response chain.
+ ///
+ public const string Via = "Via";
+
+ ///
+ /// The HTTP WWW-Authenticate header.
+ /// Used in response to a request for authentication, indicating the authentication method that should be used to access the resource.
+ ///
+ public const string WWWAuthenticate = "WWW-Authenticate";
+
+ ///
+ /// The HTTP Warning header.
+ /// Provides additional information about the status or transformation of a message, often used for caching and validation.
+ ///
+ public const string Warning = "Warning";
+
+ ///
+ /// The HTTP X-AspNet-Version header.
+ /// Indicates the version of ASP.NET that the server is using to process the request.
+ ///
+ public const string XAspNetVersion = "X-AspNet-Version";
+
+ ///
+ /// The HTTP X-Content-Duration header.
+ /// Specifies the duration of the content in seconds, often used for media files.
+ ///
+ public const string XContentDuration = "X-Content-Duration";
+
+ ///
+ /// The HTTP X-Content-Type-Options header.
+ /// Used to prevent MIME type sniffing, ensuring that the browser respects the declared content type.
+ ///
+ public const string XContentTypeOptions = "X-Content-Type-Options";
+
+ ///
+ /// The HTTP X-Frame-Options header.
+ /// Used to control whether a browser should be allowed to render a page in a iframe, frame, embed or object tag, helping to prevent clickjacking attacks.
+ ///
+ public const string XFrameOptions = "X-Frame-Options";
+
+ ///
+ /// The HTTP X-MSEdge-Ref header.
+ /// Used by Microsoft Edge to provide information about the request context, often for analytics and debugging purposes.
+ ///
+ public const string XMSEdgeRef = "X-MSEdge-Ref";
+
+ ///
+ /// The HTTP X-Powered-By header.
+ /// Indicates the technology or framework that powers the web application, often used for informational purposes.
+ ///
+ public const string XPoweredBy = "X-Powered-By";
+
+ ///
+ /// The HTTP X-Forwarded-Host header.
+ /// Used to identify the original host requested by the client in the Host HTTP request header, often used in proxy setups.
+ ///
+ public const string XForwardedHost = "X-Forwarded-Host";
+
+ ///
+ /// The HTTP X-Forwarded-For header.
+ /// Used to identify the originating IP address of a client connecting to a web server through an HTTP proxy or load balancer.
+ ///
+ public const string XForwardedFor = "X-Forwarded-For";
+
+ ///
+ /// The HTTP X-Request-ID header.
+ /// Used to uniquely identify a request for tracking and debugging purposes, often generated by the client or server.
+ ///
+ public const string XRequestID = "X-Request-ID";
+
+ ///
+ /// The HTTP X-UA-Compatible header.
+ /// Used to specify the document mode that Internet Explorer should use to render the page, helping to ensure compatibility with older versions.
+ ///
+ public const string XUACompatible = "X-UA-Compatible";
+}
\ No newline at end of file
diff --git a/src/Http/HttpRequest.cs b/src/Http/HttpRequest.cs
index 0e5b9c1..786b95e 100644
--- a/src/Http/HttpRequest.cs
+++ b/src/Http/HttpRequest.cs
@@ -45,8 +45,7 @@ public sealed class HttpRequest
private NameValueCollection? cookies = null;
private StringValueCollection? query = null;
private StringValueCollection? form = null;
-
- private IPAddress remoteAddr;
+ private IPAddress? remoteAddr;
private int currentFrame = 0;
@@ -60,18 +59,6 @@ internal HttpRequest(
listenerResponse = context.Response;
listenerRequest = context.Request;
RequestedAt = DateTime.Now;
-
- // the listenerRequest.RemoteEndPoint property is disposed after the
- // response is sent to the server, resulting in an null exception when
- // getting it in an OnConnectionClose handler
- if (contextServerConfiguration.ForwardingResolver is { } fr)
- {
- remoteAddr = fr.OnResolveClientAddress(this, listenerRequest.RemoteEndPoint);
- }
- else
- {
- remoteAddr = new IPAddress(listenerRequest.RemoteEndPoint.Address.GetAddressBytes());
- }
}
internal string mbConvertCodepage(string input, Encoding inEnc, Encoding outEnc)
@@ -201,7 +188,7 @@ public NameValueCollection Cookies
///
/// This property is an shortcut for property.
///
- public HttpContextBagRepository Bag => Context.RequestBag;
+ public TypedValueDictionary Bag => Context.RequestBag;
///
/// Get the requested host header with the port from this HTTP request.
@@ -216,11 +203,11 @@ public string Authority
///
public string Path
{
- get => listenerRequest.Url?.AbsolutePath ?? "/";
+ get => HttpUtility.UrlDecode(listenerRequest.Url?.AbsolutePath ?? "/");
}
///
- /// Gets the full HTTP request path with the query string.
+ /// Gets the raw, full HTTP request path with the query string.
///
public string FullPath
{
@@ -307,7 +294,21 @@ public StringValueCollection Query
///
public IPAddress RemoteAddress
{
- get => remoteAddr;
+ get
+ {
+ if (remoteAddr is null)
+ {
+ if (contextServerConfiguration.ForwardingResolver is { } fr)
+ {
+ remoteAddr = fr.OnResolveClientAddress(this, listenerRequest.RemoteEndPoint);
+ }
+ else
+ {
+ remoteAddr = new IPAddress(listenerRequest.RemoteEndPoint.Address.GetAddressBytes());
+ }
+ }
+ return remoteAddr;
+ }
}
///
diff --git a/src/Http/ListeningPort.cs b/src/Http/ListeningPort.cs
index 24cfdb0..9f25de1 100644
--- a/src/Http/ListeningPort.cs
+++ b/src/Http/ListeningPort.cs
@@ -111,34 +111,22 @@ public ListeningPort(bool secure, string hostname, ushort port)
/// The URI component that will be parsed to the listening port format.
public ListeningPort(string uri)
{
- int schemeIndex = uri.IndexOf(":");
- if (schemeIndex == -1) throw new ArgumentException(SR.ListeningPort_Parser_UndefinedScheme);
- int portIndex = uri.IndexOf(":", schemeIndex + 3);
- if (portIndex == -1) throw new ArgumentException(SR.ListeningPort_Parser_UndefinedPort);
- int endIndex = uri.IndexOf("/", schemeIndex + 3);
- if (endIndex == -1 || !uri.EndsWith('/')) throw new ArgumentException(SR.ListeningPort_Parser_UriNotTerminatedSlash);
-
- string schemePart = uri.Substring(0, schemeIndex);
- string hostnamePart = uri.Substring(schemeIndex + 3, portIndex - (schemeIndex + 3));
- string portPart = uri.Substring(portIndex + 1, endIndex - (portIndex + 1));
-
- if (schemePart == "http")
+ if (ushort.TryParse(uri, out ushort port))
{
- Secure = false;
+ Hostname = "localhost";
+ Port = port;
+ Secure = port == 443;
}
- else if (schemePart == "https")
+ else if (Uri.TryCreate(uri, UriKind.RelativeOrAbsolute, out var uriResult))
{
- Secure = true;
+ Hostname = uriResult.Host;
+ Port = (ushort)uriResult.Port;
+ Secure = string.Compare(uriResult.Scheme, "https", true) == 0;
}
else
{
- throw new ArgumentException(SR.ListeningPort_Parser_InvalidScheme);
+ throw new ArgumentException(SR.ListeningPort_Parser_InvalidInput);
}
-
- if (!ushort.TryParse(portPart, out ushort port)) throw new ArgumentException(SR.ListeningPort_Parser_InvalidPort);
-
- Port = port;
- Hostname = hostnamePart;
}
///
diff --git a/src/Http/Streams/HttpResponseStream.cs b/src/Http/Streams/HttpResponseStream.cs
index 3935ef0..e4d6c0a 100644
--- a/src/Http/Streams/HttpResponseStream.cs
+++ b/src/Http/Streams/HttpResponseStream.cs
@@ -7,7 +7,6 @@
// File name: HttpResponseStream.cs
// Repository: https://github.com/sisk-http/core
-using Sisk.Core.Internal;
using System.Net;
namespace Sisk.Core.Http.Streams;
@@ -73,20 +72,25 @@ public void SetContentLength(long contentLength)
///
/// The HTTP header name.
/// The HTTP header value.
- public void SetHeader(string headerName, string value)
+ public void SetHeader(string headerName, object? value)
{
+ string? _value = value?.ToString();
+ if (_value is null)
+ {
+ return;
+ }
if (hasSentData) throw new InvalidOperationException(SR.Httpserver_Commons_HeaderAfterContents);
if (string.Compare(headerName, HttpKnownHeaderNames.ContentLength, true) == 0)
{
- SetContentLength(long.Parse(value));
+ SetContentLength(long.Parse(_value));
}
else if (string.Compare(headerName, HttpKnownHeaderNames.ContentType, true) == 0)
{
- listenerResponse.ContentType = value;
+ listenerResponse.ContentType = _value;
}
else
{
- listenerResponse.AddHeader(headerName, value);
+ listenerResponse.AddHeader(headerName, _value);
}
}
diff --git a/src/Internal/HttpKnownHeaderNames.cs b/src/Internal/HttpKnownHeaderNames.cs
deleted file mode 100644
index 6927751..0000000
--- a/src/Internal/HttpKnownHeaderNames.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// The Sisk Framework source code
-// Copyright (c) 2023 PROJECT PRINCIPIUM
-//
-// The code below is licensed under the MIT license as
-// of the date of its publication, available at
-//
-// File name: HttpKnownHeaderNames.cs
-// Repository: https://github.com/sisk-http/core
-
-// The source code below was forked from the official dotnet runtime
-//
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Sisk.Core.Internal;
-
-internal static partial class HttpKnownHeaderNames
-{
- public const string Accept = "Accept";
- public const string AcceptCharset = "Accept-Charset";
- public const string AcceptEncoding = "Accept-Encoding";
- public const string AcceptLanguage = "Accept-Language";
- public const string AcceptPatch = "Accept-Patch";
- public const string AcceptRanges = "Accept-Ranges";
- public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
- public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
- public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
- public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
- public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
- public const string AccessControlMaxAge = "Access-Control-Max-Age";
- public const string Age = "Age";
- public const string Allow = "Allow";
- public const string AltSvc = "Alt-Svc";
- public const string Authorization = "Authorization";
- public const string CacheControl = "Cache-Control";
- public const string Connection = "Connection";
- public const string ContentDisposition = "Content-Disposition";
- public const string ContentEncoding = "Content-Encoding";
- public const string ContentLanguage = "Content-Language";
- public const string ContentLength = "Content-Length";
- public const string ContentLocation = "Content-Location";
- public const string ContentMD5 = "Content-MD5";
- public const string ContentRange = "Content-Range";
- public const string ContentSecurityPolicy = "Content-Security-Policy";
- public const string ContentType = "Content-Type";
- public const string Cookie = "Cookie";
- public const string Cookie2 = "Cookie2";
- public const string Date = "Date";
- public const string ETag = "ETag";
- public const string Expect = "Expect";
- public const string Expires = "Expires";
- public const string From = "From";
- public const string Host = "Host";
- public const string IfMatch = "If-Match";
- public const string IfModifiedSince = "If-Modified-Since";
- public const string IfNoneMatch = "If-None-Match";
- public const string IfRange = "If-Range";
- public const string IfUnmodifiedSince = "If-Unmodified-Since";
- public const string KeepAlive = "Keep-Alive";
- public const string LastModified = "Last-Modified";
- public const string Link = "Link";
- public const string Location = "Location";
- public const string MaxForwards = "Max-Forwards";
- public const string Origin = "Origin";
- public const string P3P = "P3P";
- public const string Pragma = "Pragma";
- public const string ProxyAuthenticate = "Proxy-Authenticate";
- public const string ProxyAuthorization = "Proxy-Authorization";
- public const string ProxyConnection = "Proxy-Connection";
- public const string PublicKeyPins = "Public-Key-Pins";
- public const string Range = "Range";
- public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
- public const string RetryAfter = "Retry-After";
- public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
- public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
- public const string SecWebSocketKey = "Sec-WebSocket-Key";
- public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
- public const string SecWebSocketVersion = "Sec-WebSocket-Version";
- public const string Server = "Server";
- public const string SetCookie = "Set-Cookie";
- public const string SetCookie2 = "Set-Cookie2";
- public const string StrictTransportSecurity = "Strict-Transport-Security";
- public const string TE = "TE";
- public const string TSV = "TSV";
- public const string Trailer = "Trailer";
- public const string TransferEncoding = "Transfer-Encoding";
- public const string Upgrade = "Upgrade";
- public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
- public const string UserAgent = "User-Agent";
- public const string Vary = "Vary";
- public const string Via = "Via";
- public const string WWWAuthenticate = "WWW-Authenticate";
- public const string Warning = "Warning";
- public const string XAspNetVersion = "X-AspNet-Version";
- public const string XContentDuration = "X-Content-Duration";
- public const string XContentTypeOptions = "X-Content-Type-Options";
- public const string XFrameOptions = "X-Frame-Options";
- public const string XMSEdgeRef = "X-MSEdge-Ref";
- public const string XPoweredBy = "X-Powered-By";
- public const string XForwardedHost = "X-Forwarded-Host";
- public const string XForwardedFor = "X-Forwarded-For";
- public const string XRequestID = "X-Request-ID";
- public const string XUACompatible = "X-UA-Compatible";
-}
\ No newline at end of file
diff --git a/src/Internal/PathUtility.cs b/src/Internal/PathUtility.cs
index 636016b..5b83469 100644
--- a/src/Internal/PathUtility.cs
+++ b/src/Internal/PathUtility.cs
@@ -45,4 +45,58 @@ public static string CombinePaths(params string[] paths)
return sb.ToString();
}
+
+ public static string NormalizedCombine(bool allowReturn, char environmentPathChar, ReadOnlySpan paths)
+ {
+ if (paths.Length == 0) return "";
+
+ bool startsWithSepChar = paths[0].StartsWith("/") || paths[0].StartsWith("\\");
+ List tokens = new List();
+
+ for (int ip = 0; ip < paths.Length; ip++)
+ {
+ string path = paths[ip]
+ ?? throw new ArgumentNullException($"The path string at index {ip} is null.");
+
+ string normalizedPath = path
+ .Replace('/', environmentPathChar)
+ .Replace('\\', environmentPathChar)
+ .Trim(environmentPathChar);
+
+ string[] pathIdentities = normalizedPath.Split(
+ environmentPathChar,
+ StringSplitOptions.RemoveEmptyEntries
+ );
+
+ tokens.AddRange(pathIdentities);
+ }
+
+ Stack insertedIndexes = new Stack();
+ StringBuilder pathBuilder = new StringBuilder();
+ foreach (string token in tokens)
+ {
+ if (token == ".")
+ {
+ continue;
+ }
+ else if (token == "..")
+ {
+ if (allowReturn)
+ {
+ pathBuilder.Length = insertedIndexes.Pop();
+ }
+ }
+ else
+ {
+ insertedIndexes.Push(pathBuilder.Length);
+ pathBuilder.Append(token);
+ pathBuilder.Append(environmentPathChar);
+ }
+ }
+
+ if (startsWithSepChar)
+ pathBuilder.Insert(0, environmentPathChar);
+
+ return pathBuilder.ToString().TrimEnd(environmentPathChar);
+ }
}
diff --git a/src/Internal/SR.cs b/src/Internal/SR.cs
index a9e9932..e3b6b27 100644
--- a/src/Internal/SR.cs
+++ b/src/Internal/SR.cs
@@ -39,11 +39,7 @@ static partial class SR
public const string ListeningHostRepository_Duplicate = "This ListeningHost has already been defined in this collection with identical definitions.";
- public const string ListeningPort_Parser_UndefinedScheme = "Scheme was not defined in the URI.";
- public const string ListeningPort_Parser_UndefinedPort = "The URI port must be explicitly defined.";
- public const string ListeningPort_Parser_UriNotTerminatedSlash = "The URI must terminate with /.";
- public const string ListeningPort_Parser_InvalidScheme = "The URI scheme must be http or https.";
- public const string ListeningPort_Parser_InvalidPort = "The URI port is invalid.";
+ public const string ListeningPort_Parser_InvalidInput = "Invalid ListeningPort syntax.";
public const string LogStream_NotBuffering = "This LogStream is not buffering. To peek lines, call StartBuffering() first.";
public const string LogStream_NoOutput = "No output writter was set up for this LogStream.";
diff --git a/src/Routing/AsyncRequestHandler.cs b/src/Routing/AsyncRequestHandler.cs
index da2c1cb..384d48f 100644
--- a/src/Routing/AsyncRequestHandler.cs
+++ b/src/Routing/AsyncRequestHandler.cs
@@ -14,13 +14,8 @@ namespace Sisk.Core.Routing;
///
/// Represents a class that implements and its execution method is asynchronous.
///
-public abstract class AsyncRequestHandler : IRequestHandler
+public abstract class AsyncRequestHandler : RequestHandler
{
- ///
- /// Gets or sets when this RequestHandler should run.
- ///
- public abstract RequestHandlerExecutionMode ExecutionMode { get; init; }
-
///
/// This method is called by the before executing a request when the instantiates an object that implements this interface. If it returns
/// a object, the route action is not called and all execution of the route is stopped. If it returns "null", the execution is continued.
@@ -29,17 +24,9 @@ public abstract class AsyncRequestHandler : IRequestHandler
/// The HTTP request context. It may contain information from other .
public abstract Task ExecuteAsync(HttpRequest request, HttpContext context);
- ///
- /// Returns an null reference, which points to the next request handler or route action.
- ///
- public HttpResponse? Next()
- {
- return null;
- }
-
///
///
- public HttpResponse? Execute(HttpRequest request, HttpContext context)
+ public sealed override HttpResponse? Execute(HttpRequest request, HttpContext context)
{
return ExecuteAsync(request, context).GetAwaiter().GetResult();
}
diff --git a/src/Routing/Router.cs b/src/Routing/Router.cs
index 54f4ac9..5f44ffc 100644
--- a/src/Routing/Router.cs
+++ b/src/Routing/Router.cs
@@ -58,6 +58,40 @@ public static string CombinePaths(params string[] paths)
return PathUtility.CombinePaths(paths);
}
+ ///
+ /// Normalizes and combines the specified file-system paths into one.
+ ///
+ /// Specifies if relative paths should be merged and ".." returns should be respected.
+ /// Specifies the path separator character.
+ /// Specifies the array of paths to combine.
+ public static string FilesystemCombinePaths(bool allowRelativeReturn, char separator, ReadOnlySpan paths)
+ {
+ return PathUtility.NormalizedCombine(allowRelativeReturn, separator, paths);
+ }
+
+ ///
+ /// Normalizes and combines the specified file-system paths into one, using the default environment directory separator char.
+ ///
+ /// Specifies the array of paths to combine.
+ public static string FilesystemCombinePaths(params string[] paths)
+ {
+ return PathUtility.NormalizedCombine(false, Path.DirectorySeparatorChar, paths);
+ }
+
+ ///
+ /// Normalize the given path to use the specified directory separator, trim the last separator and
+ /// remove empty entries.
+ ///
+ /// The path to normalize.
+ /// The directory separator.
+ public static string NormalizePath(string path, char directorySeparator = '/')
+ {
+ string[] parts = path.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
+ string result = string.Join(directorySeparator, parts);
+ if (path.StartsWith('/') || path.StartsWith('\\')) result = directorySeparator + result;
+ return result;
+ }
+
///
/// Gets an boolean indicating where this is read-only or not.
///
diff --git a/src/Sisk.Core.csproj b/src/Sisk.Core.csproj
index c8eb8f5..935f719 100644
--- a/src/Sisk.Core.csproj
+++ b/src/Sisk.Core.csproj
@@ -89,8 +89,4 @@
-
-
-
-
\ No newline at end of file