Skip to content

Commit

Permalink
lazy load fields
Browse files Browse the repository at this point in the history
  • Loading branch information
CypherPotato committed Dec 21, 2023
1 parent a8ee5e9 commit e11a748
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 126 deletions.
194 changes: 97 additions & 97 deletions src/Http/HttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public sealed class HttpRequest
private byte[]? contentBytes;
internal bool isStreaming;
private HttpRequestEventSource? activeEventSource;
private bool isContentAvailable = false;
private NameValueCollection headers = null!;
private NameValueCollection? headers = null;
private NameValueCollection? cookies = null;
private int currentFrame = 0;

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
Expand All @@ -63,92 +63,14 @@ internal HttpRequest(
ListeningHost host,
HttpListenerContext context)
{

this.context = context;
this.baseServer = server;
this.contextServerConfiguration = baseServer.ServerConfiguration;
this.listenerResponse = context.Response;
this.listenerRequest = context.Request;
this.RequestedAt = DateTime.Now;
this.Query = listenerRequest.QueryString;
this.RequestId = Guid.NewGuid();
this.Cookies = new NameValueCollection();
this.hostContext = host;
this.Origin = listenerRequest.LocalEndPoint.Address;

Task resolveOriginAddress = new Task(() =>
{
if (contextServerConfiguration.ResolveForwardedOriginAddress)
{
string? forwardedIp = listenerRequest.Headers["X-Forwarded-For"];
if (forwardedIp != null)
{
string forwardedIpLiteralStr = forwardedIp.Contains(',') ? forwardedIp.Substring(forwardedIp.IndexOf(',') + 1) : forwardedIp;
bool ok = IPAddress.TryParse(forwardedIpLiteralStr, out IPAddress? forwardedAddress);
if (!ok || forwardedAddress == null)
{
throw new HttpRequestException(SR.HttpRequest_InvalidForwardedIpAddress);
}
else
{
this.Origin = forwardedAddress;
}
}
}
});

Task parseCookies = new Task(() =>
{
string? cookieHeader = listenerRequest.Headers["cookie"];
if (cookieHeader != null)
{
string[] cookieParts = cookieHeader.Split(';');
foreach (string cookieExpression in cookieParts)
{
int eqPos = cookieExpression.IndexOf("=");
if (eqPos < 0)
{
throw new HttpRequestException(SR.HttpRequest_InvalidCookieSyntax);
}
string key = cookieExpression.Substring(0, eqPos).Trim();
string value = cookieExpression.Substring(eqPos + 1).Trim();
if (string.IsNullOrEmpty(key))
{
throw new HttpRequestException(SR.HttpRequest_InvalidCookieSyntax);
}
this.Cookies[key] = WebUtility.UrlDecode(value);
}
}
});

Task normalizeHeaderNames = new Task(() =>
{
if (contextServerConfiguration.Flags.NormalizeHeadersEncodings)
{
headers = new NameValueCollection();
Encoding entryCodepage = Encoding.GetEncoding("ISO-8859-1");
foreach (string headerName in listenerRequest.Headers)
{
string headerValue = listenerRequest.Headers[headerName]!;
headers.Add(
headerName,
mbConvertCodepage(headerValue, entryCodepage, listenerRequest.ContentEncoding)
);
}
}
else
{
headers = listenerRequest.Headers;
}
});

resolveOriginAddress.Start();
parseCookies.Start();
normalizeHeaderNames.Start();

Task.WaitAll(resolveOriginAddress, parseCookies, normalizeHeaderNames);
}

internal string mbConvertCodepage(string input, Encoding inEnc, Encoding outEnc)
Expand All @@ -167,7 +89,7 @@ internal string mbConvertCodepage(string input, Encoding inEnc, Encoding outEnc)
/// <type>
/// Property
/// </type>
public Guid RequestId { get; private set; }
public Guid RequestId { get => listenerRequest.RequestTraceIdentifier; }

/// <summary>
/// Gets a boolean indicating whether this request was made by an secure transport context (SSL/TLS) or not.
Expand All @@ -189,7 +111,7 @@ internal string mbConvertCodepage(string input, Encoding inEnc, Encoding outEnc)
/// <type>
/// Property
/// </type>
public bool IsContentAvailable { get => isContentAvailable; }
public bool IsContentAvailable { get => contentBytes != null; }

/// <summary>
/// Gets a boolean indicating whether this request has contents.
Expand All @@ -213,7 +135,31 @@ internal string mbConvertCodepage(string input, Encoding inEnc, Encoding outEnc)
/// </type>
public NameValueCollection Headers
{
get => headers;
get
{
if (headers == null)
{
if (contextServerConfiguration.Flags.NormalizeHeadersEncodings)
{
headers = new NameValueCollection();
Encoding entryCodepage = Encoding.GetEncoding("ISO-8859-1");
foreach (string headerName in listenerRequest.Headers)
{
string headerValue = listenerRequest.Headers[headerName]!;
headers.Add(
headerName,
mbConvertCodepage(headerValue, entryCodepage, listenerRequest.ContentEncoding)
);
}
}
else
{
headers = listenerRequest.Headers;
}
}

return headers;
}
}

/// <summary>
Expand All @@ -225,7 +171,40 @@ public NameValueCollection Headers
/// <type>
/// Property
/// </type>
public NameValueCollection Cookies { get; private set; } //= new NameValueCollection();
public NameValueCollection Cookies
{
get
{
if (cookies == null)
{
cookies = new NameValueCollection();
string? cookieHeader = listenerRequest.Headers["cookie"];
if (cookieHeader != null)
{
string[] cookieParts = cookieHeader.Split(';');
foreach (string cookieExpression in cookieParts)
{
int eqPos = cookieExpression.IndexOf("=");
if (eqPos < 0)
{
throw new HttpRequestException(SR.HttpRequest_InvalidCookieSyntax);
}
string key = cookieExpression.Substring(0, eqPos).Trim();
string value = cookieExpression.Substring(eqPos + 1).Trim();

if (string.IsNullOrEmpty(key))
{
throw new HttpRequestException(SR.HttpRequest_InvalidCookieSyntax);
}

cookies[key] = WebUtility.UrlDecode(value);
}
}
}

return cookies;
}
}

/// <summary>
/// Get the requested host header (without port) from this HTTP request.
Expand Down Expand Up @@ -351,7 +330,11 @@ public string Body
/// </type>
public byte[] RawBody
{
get => contentBytes ?? Array.Empty<byte>();
get
{
ReadRequestStreamContents();
return contentBytes!;
}
}

/// <summary>
Expand Down Expand Up @@ -401,7 +384,28 @@ public long ContentLength
/// </type>
public IPAddress Origin
{
get; internal set;
get
{
if (contextServerConfiguration.ResolveForwardedOriginAddress)
{
string? forwardedIp = listenerRequest.Headers["X-Forwarded-For"];
if (forwardedIp != null)
{
string forwardedIpLiteralStr = forwardedIp.Contains(',') ? forwardedIp.Substring(forwardedIp.IndexOf(',') + 1) : forwardedIp;
bool ok = IPAddress.TryParse(forwardedIpLiteralStr, out IPAddress? forwardedAddress);
if (!ok || forwardedAddress == null)
{
throw new HttpRequestException(SR.HttpRequest_InvalidForwardedIpAddress);
}
else
{
return forwardedAddress;
}
}
}

return listenerRequest.RemoteEndPoint.Address;
}
}

/// <summary>
Expand Down Expand Up @@ -666,7 +670,7 @@ public void Send(HttpStatusCode statusCode)
/// <since>0.15</since>
public Stream GetRequestStream()
{
if (isContentAvailable)
if (contentBytes != null)
{
throw new InvalidOperationException(SR.HttpRequest_InputStreamAlreadyLoaded);
}
Expand Down Expand Up @@ -705,17 +709,13 @@ public HttpResponseStream GetResponseStream()
/// </type>
public void ReadRequestStreamContents()
{
if (isContentAvailable)
{
if (this.contentBytes == null)
this.contentBytes = Array.Empty<byte>();
return;
}
using (var memoryStream = new MemoryStream())
if (this.contentBytes == null)
{
listenerRequest.InputStream.CopyTo(memoryStream);
this.contentBytes = memoryStream.ToArray();
isContentAvailable = true;
using (var memoryStream = new MemoryStream())
{
listenerRequest.InputStream.CopyTo(memoryStream);
this.contentBytes = memoryStream.ToArray();
}
}
}

Expand Down
17 changes: 0 additions & 17 deletions src/Http/HttpServerFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,23 +201,6 @@ public class HttpServerFlags
/// </type>
public TimeSpan RouteActionTimeout = TimeSpan.Zero;

/// <summary>
/// Determines if the request input stream should be read into an byte array after running all <see cref="RequestHandlerExecutionMode.BeforeContents"/>
/// request handlers.
/// </summary>
/// <docs>
/// <p>
/// Default value: <code>true</code>
/// </p>
/// </docs>
/// <definition>
/// public bool AutoReadRequestStream;
/// </definition>
/// <type>
/// Field
/// </type>
public bool AutoReadRequestStream = true;

/// <summary>
/// Creates an new <see cref="HttpServerFlags"/> instance with default flags values.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Http/HttpServer__Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ private async Task ProcessRequest(HttpListenerContext context)

bool isPayloadStreamable =
response.Content is StreamContent ||
responseContentLength > 1024;
responseContentLength > 2 * UnitMb;

if (isPayloadStreamable)
{
Expand Down
5 changes: 3 additions & 2 deletions src/Internal/WildcardMatching.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ namespace Sisk.Core.Internal
{
internal static class HttpStringInternals
{
public record PathMatchResult(bool IsMatched, NameValueCollection Query);
public record PathMatchResult(bool IsMatched, NameValueCollection? Query);

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static PathMatchResult IsPathMatch(string pathPattern, string requestPath, bool ignoreCase)
{
NameValueCollection query = new NameValueCollection();
NameValueCollection? query = null;
pathPattern = pathPattern.TrimEnd('/');
requestPath = requestPath.TrimEnd('/');

Expand All @@ -41,6 +41,7 @@ public static PathMatchResult IsPathMatch(string pathPattern, string requestPath

if (pathPtt.StartsWith('<') && pathPtt.EndsWith('>'))
{
if (query == null) query = new NameValueCollection();
string queryValueName = pathPtt.Substring(1, pathPtt.Length - 2);
query.Add(queryValueName, reqsPtt);
}
Expand Down
13 changes: 5 additions & 8 deletions src/Routing/Router__CoreInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ internal async Task<RouterExecutionResult> Execute(HttpContext context)

HttpServerFlags flag = ParentServer!.ServerConfiguration.Flags;
bool hasGlobalHandlers = this.GlobalRequestHandlers?.Length > 0;

foreach (Route route in _routes)
{
// test path
Expand Down Expand Up @@ -145,10 +145,10 @@ internal async Task<RouterExecutionResult> Execute(HttpContext context)

if (isMethodMatched)
{
foreach (string routeParam in pathTest.Query)
{
request.Query.Add(routeParam, HttpUtility.UrlDecode(pathTest.Query[routeParam]));
}
if (pathTest.Query != null)
foreach (string routeParam in pathTest.Query)
request.Query[routeParam] = HttpUtility.UrlDecode(pathTest.Query[routeParam]);

matchResult = RouteMatchResult.FullyMatched;
matchedRoute = route;
break;
Expand Down Expand Up @@ -213,9 +213,6 @@ internal async Task<RouterExecutionResult> Execute(HttpContext context)
}
#endregion

if (flag.AutoReadRequestStream)
request.ReadRequestStreamContents();

#region Before-response global handlers
if (hasGlobalHandlers)
{
Expand Down
1 change: 0 additions & 1 deletion src/Sisk.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>symbols.nupkg</SymbolPackageFormat>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<ServerGarbageCollection>true</ServerGarbageCollection>

<OutputType>Library</OutputType>
<ImplicitUsings>disable</ImplicitUsings>
Expand Down

0 comments on commit e11a748

Please sign in to comment.