diff options
author | vnugent <public@vaughnnugent.com> | 2023-01-08 16:01:54 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-01-08 16:01:54 -0500 |
commit | de94d788e9a47432a7630a8215896b8dd3628599 (patch) | |
tree | 666dec06eef861d101cb6948aff52a3d354c8d73 /lib/Plugins.Essentials/src/HttpEntity.cs | |
parent | be6dc557a3b819248b014992eb96c1cb21f8112b (diff) |
Reorder + analyzer cleanup
Diffstat (limited to 'lib/Plugins.Essentials/src/HttpEntity.cs')
-rw-r--r-- | lib/Plugins.Essentials/src/HttpEntity.cs | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/Plugins.Essentials/src/HttpEntity.cs b/lib/Plugins.Essentials/src/HttpEntity.cs new file mode 100644 index 0000000..ffad607 --- /dev/null +++ b/lib/Plugins.Essentials/src/HttpEntity.cs @@ -0,0 +1,178 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials +* File: HttpEntity.cs +* +* HttpEntity.cs is part of VNLib.Plugins.Essentials which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.IO; +using System.Net; +using System.Threading; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using VNLib.Net.Http; +using VNLib.Plugins.Essentials.Sessions; +using VNLib.Plugins.Essentials.Extensions; + +#nullable enable + +/* + * HttpEntity was converted to an object as during profiling + * it was almost always heap allcated due to async opertaions + * or other object tracking issues. So to reduce the number of + * allocations (at the cost of larger objects) basic profiling + * showed less GC load and less collections when SessionInfo + * remained a value type + */ +#pragma warning disable CA1051 // Do not declare visible instance fields + +namespace VNLib.Plugins.Essentials +{ + /// <summary> + /// A container for an <see cref="HttpEvent"/> with its attached session. + /// This class cannot be inherited. + /// </summary> + public sealed class HttpEntity : IHttpEvent + { + /// <summary> + /// The connection event entity + /// </summary> + private readonly IHttpEvent Entity; + public HttpEntity(IHttpEvent entity, EventProcessor root, in SessionHandle session, in CancellationToken cancellation) + { + Entity = entity; + RequestedRoot = root; + EventCancellation = cancellation; + + //See if the connection is coming from an downstream server + IsBehindDownStreamServer = root.Options.DownStreamServers.Contains(entity.Server.RemoteEndpoint.Address); + /* + * If the connection was behind a trusted downstream server, + * we can trust the x-forwarded-for header, + * otherwise use the remote ep ip address + */ + TrustedRemoteIp = entity.Server.GetTrustedIp(IsBehindDownStreamServer); + //Initialize the session + Session = session.IsSet ? new(session.SessionData, entity.Server, TrustedRemoteIp) : new(); + //Local connection + IsLocalConnection = entity.Server.LocalEndpoint.Address.IsLocalSubnet(TrustedRemoteIp); + //Cache value + IsSecure = entity.Server.IsSecure(IsBehindDownStreamServer); + } + + /// <summary> + /// A token that has a scheduled timeout to signal the cancellation of the entity event + /// </summary> + public readonly CancellationToken EventCancellation; + /// <summary> + /// The session assocaited with the event + /// </summary> + public readonly SessionInfo Session; + /// <summary> + /// A value that indicates if the connecion came from a trusted downstream server + /// </summary> + public readonly bool IsBehindDownStreamServer; + /// <summary> + /// Determines if the connection came from the local network to the current server + /// </summary> + public readonly bool IsLocalConnection; + /// <summary> + /// Gets a value that determines if the connection is using tls, locally + /// or behind a trusted downstream server that is using tls. + /// </summary> + public readonly bool IsSecure; + + /// <summary> + /// The connection info object assocated with the entity + /// </summary> + public IConnectionInfo Server => Entity.Server; + /// <summary> + /// User's ip. If the connection is behind a local proxy, returns the users actual IP. Otherwise returns the connection ip. + /// </summary> + public readonly IPAddress TrustedRemoteIp; + /// <summary> + /// The requested web root. Provides additional site information + /// </summary> + public readonly EventProcessor RequestedRoot; + /// <summary> + /// If the request has query arguments they are stored in key value format + /// </summary> + public IReadOnlyDictionary<string, string> QueryArgs => Entity.QueryArgs; + /// <summary> + /// If the request body has form data or url encoded arguments they are stored in key value format + /// </summary> + public IReadOnlyDictionary<string, string> RequestArgs => Entity.RequestArgs; + /// <summary> + /// Contains all files upladed with current request + /// </summary> + public IReadOnlyList<FileUpload> Files => Entity.Files; + ///<inheritdoc/> + HttpServer IHttpEvent.OriginServer => Entity.OriginServer; + + /// <summary> + /// Complete the session and respond to user + /// </summary> + /// <param name="code">Status code of operation</param> + /// <exception cref="InvalidOperationException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CloseResponse(HttpStatusCode code) => Entity.CloseResponse(code); + + ///<inheritdoc/> + ///<exception cref="ContentTypeUnacceptableException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CloseResponse(HttpStatusCode code, ContentType type, Stream stream) + { + Entity.CloseResponse(code, type, stream); + //Verify content type matches + if (!Server.Accepts(type)) + { + throw new ContentTypeUnacceptableException("The client does not accept the content type of the response"); + } + } + + ///<inheritdoc/> + ///<exception cref="ContentTypeUnacceptableException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CloseResponse(HttpStatusCode code, ContentType type, IMemoryResponseReader entity) + { + //Verify content type matches + if (!Server.Accepts(type)) + { + throw new ContentTypeUnacceptableException("The client does not accept the content type of the response"); + } + + Entity.CloseResponse(code, type, entity); + } + + ///<inheritdoc/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DisableCompression() => Entity.DisableCompression(); + + /* + * Do not directly expose dangerous methods, but allow them to be called + */ + + ///<inheritdoc/> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IHttpEvent.DangerousChangeProtocol(IAlternateProtocol protocolHandler) => Entity.DangerousChangeProtocol(protocolHandler); + } +} |