Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
CypherPotato committed Apr 5, 2024
1 parent 91fa64a commit e02e0d8
Show file tree
Hide file tree
Showing 16 changed files with 804 additions and 495 deletions.
478 changes: 478 additions & 0 deletions src/Entity/HttpHeaderCollection.cs

Large diffs are not rendered by default.

403 changes: 35 additions & 368 deletions src/Ext/CircularBuffer.cs

Large diffs are not rendered by default.

19 changes: 18 additions & 1 deletion src/Http/Handlers/HttpServerHandlerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ namespace Sisk.Core.Http.Handlers;

internal class HttpServerHandlerRepository
{
private readonly HttpServer parent;
private readonly List<HttpServerHandler> handlers = new List<HttpServerHandler>();

public HttpServerHandlerRepository(HttpServer parent)
{
this.parent = parent;
}

public void RegisterHandler(HttpServerHandler handler)
{
handlers.Add(handler);
Expand All @@ -23,7 +29,18 @@ public void RegisterHandler(HttpServerHandler handler)
private void CallEvery(Action<HttpServerHandler> action)
{
foreach (HttpServerHandler handler in handlers)
action(handler);
try
{
action(handler);
}
catch (Exception ex)
{
if (parent.ServerConfiguration.ThrowExceptions)
{
parent.ServerConfiguration.ErrorsLogsStream?.WriteException(ex);
}
else throw;
}
}

internal void ServerStarting(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStarting(val));
Expand Down
14 changes: 7 additions & 7 deletions src/Http/HttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public sealed class HttpRequest
private byte[]? contentBytes;
internal bool isStreaming;
private HttpRequestEventSource? activeEventSource;
private NameValueCollection? headers = null;
private HttpHeaderCollection? headers = null;
private NameValueCollection? cookies = null;
private StringValueCollection? query = null;
private StringValueCollection? form = null;
Expand Down Expand Up @@ -84,7 +84,7 @@ void ReadRequestStreamContents()
{
if (contentBytes == null)
{
using (var memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream((int)ContentLength))
{
listenerRequest.InputStream.CopyTo(memoryStream);
contentBytes = memoryStream.ToArray();
Expand Down Expand Up @@ -140,20 +140,20 @@ void ReadRequestStreamContents()
/// Gets the HTTP request headers.
/// </summary>
/// <definition>
/// public NameValueCollection Headers { get; }
/// public HttpHeaderCollection Headers { get; }
/// </definition>
/// <type>
/// Property
/// </type>
public NameValueCollection Headers
public HttpHeaderCollection Headers
{
get
{
if (headers == null)
{
if (contextServerConfiguration.Flags.NormalizeHeadersEncodings)
{
headers = new NameValueCollection();
headers = new HttpHeaderCollection();
Encoding entryCodepage = Encoding.GetEncoding("ISO-8859-1");
foreach (string headerName in listenerRequest.Headers)
{
Expand All @@ -166,7 +166,7 @@ public NameValueCollection Headers
}
else
{
headers = listenerRequest.Headers;
headers = new HttpHeaderCollection(listenerRequest.Headers);
}
}

Expand Down Expand Up @@ -708,7 +708,7 @@ public HttpResponse Close()
/// cached in this object.
/// </summary>
/// <definition>
/// public Stream GetInputStream()
/// public Stream GetRequestStream()
/// </definition>
/// <type>
/// Method
Expand Down
57 changes: 27 additions & 30 deletions src/Http/HttpResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Sisk.Core.Http
/// </summary>
/// <definition>
/// public class HttpResponse : CookieHelper
/// </definition>
/// </definition>
/// <type>
/// Class
/// </type>
Expand Down Expand Up @@ -84,16 +84,15 @@ public static HttpResponse CreateRedirectResponse(RouteAction action)
}

/// <summary>
/// Gets or sets an custom HTTP status code and description for this HTTP response. If this property ins't null, it will overwrite
/// the <see cref="Status"/> property in this class.
/// Gets or sets the HTTP status code and description for this HTTP response.
/// </summary>
/// <definition>
/// public HttpStatusInformation? CustomStatus { get; set; }
/// public HttpStatusInformation StatusInformation { get; set; }
/// </definition>
/// <type>
/// Property
/// </type>
public HttpStatusInformation? CustomStatus { get; set; } = null;
public HttpStatusInformation StatusInformation { get; set; } = new HttpStatusInformation();

/// <summary>
/// Gets or sets the HTTP response status code.
Expand All @@ -104,7 +103,7 @@ public static HttpResponse CreateRedirectResponse(RouteAction action)
/// <type>
/// Property
/// </type>
public HttpStatusCode Status { get; set; } = HttpStatusCode.OK;
public HttpStatusCode Status { get => (HttpStatusCode)StatusInformation.StatusCode; set => StatusInformation = new HttpStatusInformation(value); }

/// <summary>
/// Gets a <see cref="NameValueCollection"/> instance of the HTTP response headers.
Expand Down Expand Up @@ -146,12 +145,6 @@ internal HttpResponse(byte internalStatus)
this.internalStatus = internalStatus;
}

internal HttpResponse(HttpListenerResponse res)
{
Status = (HttpStatusCode)res.StatusCode;
Headers.Add(res.Headers);
}

/// <summary>
/// Gets the raw HTTP response message.
/// </summary>
Expand All @@ -166,16 +159,23 @@ internal HttpResponse(HttpListenerResponse res)
public string GetRawHttpResponse(bool includeBody = true)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"HTTP/1.1 {(int)Status}");
sb.AppendLine($"HTTP/1.1 {StatusInformation}");
foreach (string header in Headers)
{
sb.Append(header + ": ");
sb.Append(Headers[header]);
sb.Append('\n');
}
if (Content?.Headers is not null)
foreach (var header in Content.Headers)
{
sb.Append(header.Key + ": ");
sb.Append(string.Join(", ", header.Value));
sb.Append('\n');
}
sb.Append('\n');

if (includeBody)
if (includeBody && Content is not StreamContent)
{
sb.Append(Content?.ReadAsStringAsync().Result);
}
Expand Down Expand Up @@ -283,8 +283,7 @@ public HttpResponse WithStatus(int status)
/// </type>
public HttpResponse WithStatus(HttpStatusInformation statusInformation)
{
Status = default;
CustomStatus = statusInformation;
StatusInformation = statusInformation;
return this;
}

Expand Down Expand Up @@ -340,8 +339,6 @@ public HttpResponse WithCookie(string name, string value, DateTime? expires = nu
/// </type>
public HttpResponse()
{
Status = HttpStatusCode.OK;
Content = null;
}

/// <summary>
Expand Down Expand Up @@ -388,6 +385,18 @@ public HttpResponse(int status, HttpContent? content) : this((HttpStatusCode)sta
/// </type>
public HttpResponse(HttpContent? content) : this(HttpStatusCode.OK, content) { }

/// <summary>
/// Creates an new <see cref="HttpResponse"/> instanec with given string content and status code as 200 OK.
/// </summary>
/// <param name="stringContent">The UTF-8 string content.</param>
/// <definition>
/// public HttpResponse(string stringContent)
/// </definition>
/// <type>
/// Constructor
/// </type>
public HttpResponse(string stringContent) : this(HttpStatusCode.OK, new StringContent(stringContent)) { }

/// <summary>
/// Creates an new <see cref="HttpResponse"/> instance with given status code and HTTP contents.
/// </summary>
Expand All @@ -403,18 +412,6 @@ public HttpResponse(HttpStatusCode status, HttpContent? content)
Content = content;
}

internal string? GetHeader(string headerName)
{
foreach (string header in Headers.Keys)
{
if (string.Compare(header, headerName, StringComparison.OrdinalIgnoreCase) == 0)
{
return Headers[header];
}
}
return null;
}

/// <inheritdoc/>
protected override void SetCookieHeader(String name, String value)
{
Expand Down
3 changes: 2 additions & 1 deletion src/Http/HttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public partial class HttpServer : IDisposable
internal HttpEventSourceCollection _eventCollection = new HttpEventSourceCollection();
internal HttpWebSocketConnectionCollection _wsCollection = new HttpWebSocketConnectionCollection();
internal List<string>? listeningPrefixes;
internal HttpServerHandlerRepository handler = new HttpServerHandlerRepository();
internal HttpServerHandlerRepository handler;

static HttpServer()
{
Expand Down Expand Up @@ -257,6 +257,7 @@ public HttpServer(HttpServerConfiguration configuration)
{
_listenerCallback = new AsyncCallback(ListenerCallback);
ServerConfiguration = configuration;
handler = new HttpServerHandlerRepository(this);
handler.RegisterHandler(new DefaultHttpServerHandler());
}

Expand Down
10 changes: 2 additions & 8 deletions src/Http/HttpServerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,8 @@ public class HttpServerConfiguration : IDisposable
public CultureInfo? DefaultCultureInfo { get; set; }

/// <summary>
/// Gets or sets the <see cref="TextWriter"/> object which the HTTP server will write HTTP server access messages to.
/// Gets or sets the <see cref="LogStream"/> object which the HTTP server will write HTTP server access messages to.
/// </summary>
/// <remarks>
/// This property defaults to Console.Out.
/// </remarks>
/// <definition>
/// public TextWriter? AccessLogsStream { get; set; }
/// </definition>
Expand All @@ -71,11 +68,8 @@ public class HttpServerConfiguration : IDisposable
public LogStream? AccessLogsStream { get; set; } = null;

/// <summary>
/// Gets or sets the <see cref="TextWriter"/> object which the HTTP server will write HTTP server error transcriptions to.
/// Gets or sets the <see cref="LogStream"/> object which the HTTP server will write HTTP server error transcriptions to.
/// </summary>
/// <remarks>
/// This stream can be empty if ThrowExceptions is true.
/// </remarks>
/// <definition>
/// public TextWriter? ErrorsLogsStream { get; set; }
/// </definition>
Expand Down
62 changes: 30 additions & 32 deletions src/Http/HttpServer__Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,16 +280,8 @@ private async Task ProcessRequest(HttpListenerContext context)
goto finishSending;
}

if (response.CustomStatus != null)
{
baseResponse.StatusCode = response.CustomStatus.Value.StatusCode;
baseResponse.StatusDescription = response.CustomStatus.Value.Description;
}
else
{
baseResponse.StatusCode = (int)response.Status;
}

baseResponse.StatusCode = response.StatusInformation.StatusCode;
baseResponse.StatusDescription = response.StatusInformation.Description;
baseResponse.KeepAlive = ServerConfiguration.KeepAlive;

#endregion
Expand All @@ -310,19 +302,39 @@ private async Task ProcessRequest(HttpListenerContext context)
baseResponse.Headers[incameHeader] = value;
}

if (response.Content != null)
if (response.Content is not null)
{
Stream? contentStream = null;
// determines the content type
baseResponse.ContentType = resHeaders[HttpKnownHeaderNames.ContentType] ?? response.Content.Headers.ContentType?.ToString();

// determines if the response should be sent as chunked or normal
if (response.SendChunked || responseContentLength == null)
// determines if the response should be sent as chunked or normal
if (response.SendChunked)
{
baseResponse.SendChunked = true;
}
else if (responseContentLength is long contentLength)
{
baseResponse.ContentLength64 = contentLength;
}
else if (response.Content is StreamContent stmContent)
{
contentStream = await stmContent.ReadAsStreamAsync();
if (!contentStream.CanSeek)
{
throw new InvalidOperationException(SR.HttpResponse_Stream_ContentLenghtNotSet);
}
else
{
contentStream.Position = 0;
baseResponse.ContentLength64 = contentStream.Length;
}
}
else
{
baseResponse.ContentLength64 = responseContentLength.Value;
// the content-length wasn't informed and the user didn't set the request to
// send as chunked. so it will throw an exception.
throw new InvalidOperationException(SR.HttpResponse_Stream_ContentLenghtNotSet);
}

// write the output buffer
Expand All @@ -336,23 +348,8 @@ response.Content is StreamContent ||

if (isPayloadStreamable)
{
Stream contentStream = await response.Content.ReadAsStreamAsync();

if (contentStream.CanSeek)
{
contentStream.Position = 0;
if (!baseResponse.SendChunked)
baseResponse.ContentLength64 = contentStream.Length;
}
else
{
if (baseResponse.ContentLength64 == 0)
{
// the content-length wasn't informed and the user didn't set the request to
// send as chunked. so it will throw an exception.
throw new InvalidOperationException(SR.HttpResponse_Stream_ContentLenghtNotSet);
}
}
if (contentStream is null)
contentStream = await response.Content.ReadAsStreamAsync();

await contentStream.CopyToAsync(baseResponse.OutputStream, flag.RequestStreamCopyBufferSize);
}
Expand Down Expand Up @@ -420,6 +417,7 @@ response.Content is StreamContent ||
{
if (!ServerConfiguration.ThrowExceptions)
{
baseResponse.StatusCode = 500;
executionResult.ServerException = ex;
executionResult.Status = HttpServerExecutionStatus.ExceptionThrown;
}
Expand All @@ -443,7 +441,7 @@ response.Content is StreamContent ||
catch (Exception)
{
baseResponse.Abort();
}
}
}

if (OnConnectionClose != null)
Expand Down
Loading

0 comments on commit e02e0d8

Please sign in to comment.