diff options
author | vnugent <public@vaughnnugent.com> | 2024-05-12 16:55:32 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-05-12 16:55:32 -0400 |
commit | 4035c838c1508af0aa7e767a97431a692958ce1c (patch) | |
tree | 9c8f719db15364296fb9b18cbe559a001d925d73 /lib/Net.Http/src/HttpResponseCookie.cs | |
parent | f4f0d4f74250257991c57bfae74c4852c7e1ae46 (diff) |
perf: Utils + http perf mods
Diffstat (limited to 'lib/Net.Http/src/HttpResponseCookie.cs')
-rw-r--r-- | lib/Net.Http/src/HttpResponseCookie.cs | 226 |
1 files changed, 226 insertions, 0 deletions
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 +{ + /// <summary> + /// Represents an HTTP cookie that is set with responses. + /// </summary> + /// <param name="name">The cookie name</param> + public readonly struct HttpResponseCookie(string name) : IStringSerializeable, IEquatable<HttpResponseCookie> + { + /// <summary> + /// The default copy buffer allocated when calling the <see cref="Compile()"/> + /// family of methods. + /// </summary> + public const int DefaultCookieBufferSize = 4096; + + + /// <summary> + /// The name of the cookie to set. + /// </summary> + public readonly string Name { get; } = name; + + /// <summary> + /// The actual cookie content or value. + /// </summary> + public readonly string? Value { get; init; } + + /// <summary> + /// The domain this cookie will be sent to. + /// </summary> + public readonly string? Domain { get; init; } + + /// <summary> + /// The cookie path the client will send this cookie with. Null + /// or empty string for all paths. + /// </summary> + public readonly string? Path { get; init; } + + /// <summary> + /// Sets the duration of the cookie lifetime (in seconds), aka MaxAge + /// </summary> + public readonly TimeSpan MaxAge { get; init; } + + /// <summary> + /// Sets the cookie Samesite field. + /// </summary> + public readonly CookieSameSite SameSite { get; init; } + + /// <summary> + /// Sets the cookie Secure flag. If true only sends the cookie with requests + /// if the connection is secure. + /// </summary> + public readonly bool Secure { get; init; } + + /// <summary> + /// Sets cookie HttpOnly flag. If true denies JavaScript access to + /// </summary> + public readonly bool HttpOnly { get; init; } + + /// <summary> + /// Sets the cookie expiration to the duration of the user's session (aka no expiration) + /// </summary> + public readonly bool IsSession { get; init; } + + /// <summary> + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields + /// <para> + /// The internal copy buffer defaults to <see cref="DefaultCookieBufferSize"/> + /// use <see cref="Compile(int)"/> if you need control over the buffer size + /// </para> + /// </summary> + /// <returns>The cookie header value as a string</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public readonly string Compile() + { + nint bufSize = MemoryUtil.NearestPage(DefaultCookieBufferSize); + + return Compile(bufSize.ToInt32()); + } + + /// <summary> + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields. + /// </summary> + /// <param name="bufferSize">The size of the internal accumulator buffer</param> + /// <returns>The cookie header value as a string</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public readonly string Compile(int bufferSize) + { + using UnsafeMemoryHandle<char> cookieBuffer = MemoryUtil.UnsafeAlloc<char>(bufferSize, false); + + ERRNO count = Compile(cookieBuffer.Span); + + return cookieBuffer.AsSpan(0, (int)count).ToString(); + } + + /// <summary> + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields. + /// </summary> + /// <param name="buffer">The character buffer to write the cookie data tor</param> + /// <returns>The cookie header value as a string</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public readonly ERRNO Compile(Span<char> buffer) + { + ForwardOnlyWriter<char> writer = new(buffer); + Compile(ref writer); + return writer.Written; + } + + /// <summary> + /// Writes the HTTP 1.x header format for the cookie + /// </summary> + /// <param name="writer"></param> + public readonly void Compile(ref ForwardOnlyWriter<char> 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"); + } + } + + ///<inheritdoc/> + public readonly override int GetHashCode() => string.GetHashCode(Name, StringComparison.OrdinalIgnoreCase); + + ///<inheritdoc/> + public readonly override bool Equals(object? obj) => obj is HttpResponseCookie other && Equals(other); + + ///<inheritdoc/> + public readonly bool Equals(HttpResponseCookie other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); + + /// <summary> + /// Creates an HTTP 1.x spec cookie header value from the + /// cookie fields + /// <para> + /// The internal copy buffer defaults to <see cref="DefaultCookieBufferSize"/> + /// use <see cref="Compile(int)"/> if you need control over the buffer size + /// </para> + /// </summary> + /// <returns>The cookie header value as a string</returns> + public override string ToString() => Compile(); + + ///<inheritdoc/> + public static bool operator ==(HttpResponseCookie left, HttpResponseCookie right) => left.Equals(right); + + ///<inheritdoc/> + public static bool operator !=(HttpResponseCookie left, HttpResponseCookie right) => !(left == right); + } +}
\ No newline at end of file |