From 4035c838c1508af0aa7e767a97431a692958ce1c Mon Sep 17 00:00:00 2001 From: vnugent Date: Sun, 12 May 2024 16:55:32 -0400 Subject: perf: Utils + http perf mods --- lib/Net.Http/src/HttpResponseCookie.cs | 226 +++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 lib/Net.Http/src/HttpResponseCookie.cs (limited to 'lib/Net.Http/src/HttpResponseCookie.cs') diff --git a/lib/Net.Http/src/HttpResponseCookie.cs b/lib/Net.Http/src/HttpResponseCookie.cs new file mode 100644 index 0000000..8fc54c2 --- /dev/null +++ b/lib/Net.Http/src/HttpResponseCookie.cs @@ -0,0 +1,226 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Net.Http +* File: HttpResponseCookie.cs +* +* HttpResponseCookie.cs is part of VNLib.Net.Http which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Net.Http 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.Net.Http 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 VNLib.Utils; +using VNLib.Utils.Memory; +using VNLib.Utils.Extensions; + +namespace VNLib.Net.Http +{ + /// + /// Represents an HTTP cookie that is set with responses. + /// + /// The cookie name + public readonly struct HttpResponseCookie(string name) : IStringSerializeable, IEquatable + { + /// + /// The default copy buffer allocated when calling the + /// family of methods. + /// + public const int DefaultCookieBufferSize = 4096; + + + /// + /// The name of the cookie to set. + /// + public readonly string Name { get; } = name; + + /// + /// The actual cookie content or value. + /// + public readonly string? Value { get; init; } + + /// + /// The domain this cookie will be sent to. + /// + public readonly string? Domain { get; init; } + + /// + /// The cookie path the client will send this cookie with. Null + /// or empty string for all paths. + /// + public readonly string? Path { get; init; } + + /// + /// Sets the duration of the cookie lifetime (in seconds), aka MaxAge + /// + public readonly TimeSpan MaxAge { get; init; } + + /// + /// Sets the cookie Samesite field. + /// + public readonly CookieSameSite SameSite { get; init; } + + /// + /// Sets the cookie Secure flag. If true only sends the cookie with requests + /// if the connection is secure. + /// + public readonly bool Secure { get; init; } + + /// + /// Sets cookie HttpOnly flag. If true denies JavaScript access to + /// + public readonly bool HttpOnly { get; init; } + + /// + /// Sets the cookie expiration to the duration of the user's session (aka no expiration) + /// + public readonly bool IsSession { get; init; } + + /// + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields + /// + /// The internal copy buffer defaults to + /// use if you need control over the buffer size + /// + /// + /// The cookie header value as a string + /// + public readonly string Compile() + { + nint bufSize = MemoryUtil.NearestPage(DefaultCookieBufferSize); + + return Compile(bufSize.ToInt32()); + } + + /// + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields. + /// + /// The size of the internal accumulator buffer + /// The cookie header value as a string + /// + public readonly string Compile(int bufferSize) + { + using UnsafeMemoryHandle cookieBuffer = MemoryUtil.UnsafeAlloc(bufferSize, false); + + ERRNO count = Compile(cookieBuffer.Span); + + return cookieBuffer.AsSpan(0, (int)count).ToString(); + } + + /// + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields. + /// + /// The character buffer to write the cookie data tor + /// The cookie header value as a string + /// + public readonly ERRNO Compile(Span buffer) + { + ForwardOnlyWriter writer = new(buffer); + Compile(ref writer); + return writer.Written; + } + + /// + /// Writes the HTTP 1.x header format for the cookie + /// + /// + public readonly void Compile(ref ForwardOnlyWriter writer) + { + writer.Append(Name); + writer.Append('='); + writer.Append(Value); + + /* + * If a session cookie is set, then do not include a max-age value + * browsers will default to session duration if not set + */ + if (!IsSession) + { + writer.AppendSmall("; Max-Age="); + writer.Append((int)MaxAge.TotalSeconds); + } + + if (!string.IsNullOrWhiteSpace(Domain)) + { + writer.AppendSmall("; Domain="); + writer.Append(Domain); + } + + if (!string.IsNullOrWhiteSpace(Path)) + { + //Set path + writer.AppendSmall("; Path="); + writer.Append(Path); + } + + writer.AppendSmall("; SameSite="); + + switch (SameSite) + { + case CookieSameSite.None: + writer.AppendSmall("None"); + break; + case CookieSameSite.Strict: + writer.AppendSmall("Strict"); + break; + case CookieSameSite.Lax: + default: + writer.AppendSmall("Lax"); + break; + } + + if (HttpOnly) + { + writer.AppendSmall("; HttpOnly"); + } + + if (Secure) + { + writer.AppendSmall("; Secure"); + } + } + + /// + public readonly override int GetHashCode() => string.GetHashCode(Name, StringComparison.OrdinalIgnoreCase); + + /// + public readonly override bool Equals(object? obj) => obj is HttpResponseCookie other && Equals(other); + + /// + public readonly bool Equals(HttpResponseCookie other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); + + /// + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields + /// + /// The internal copy buffer defaults to + /// use if you need control over the buffer size + /// + /// + /// The cookie header value as a string + public override string ToString() => Compile(); + + /// + public static bool operator ==(HttpResponseCookie left, HttpResponseCookie right) => left.Equals(right); + + /// + public static bool operator !=(HttpResponseCookie left, HttpResponseCookie right) => !(left == right); + } +} \ No newline at end of file -- cgit