Skip to content

Commit

Permalink
Use DI more correctly; New Raw HyperSerializerDelegate, intending for…
Browse files Browse the repository at this point in the history
… passing byte arrays as responses; HyperStatus now includes the hyper serializer to use
  • Loading branch information
OoLunar committed Jul 30, 2024
1 parent 9c1bd06 commit e5d4083
Show file tree
Hide file tree
Showing 80 changed files with 2,937 additions and 389 deletions.
2 changes: 1 addition & 1 deletion benchmarks/Responders/OkResponder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ namespace HyperSharp.Benchmarks.Responders
{
public static Type[] Needs => Type.EmptyTypes;

public Result<HyperStatus> Respond(HyperContext context, CancellationToken cancellationToken = default) => Result.Success(new HyperStatus(HttpStatusCode.OK));
public Result<HyperStatus> Respond(HyperContext context, CancellationToken cancellationToken = default) => Result.Success(HyperStatus.OK());
}
}
2 changes: 1 addition & 1 deletion benchmarks/Responders/OkTaskResponder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace HyperSharp.Benchmarks.Responders

public async Task<Result<HyperStatus>> RespondAsync(HyperContext context, CancellationToken cancellationToken = default)
{
await context.RespondAsync(new HyperStatus(HttpStatusCode.OK), HyperSerializers.PlainTextAsync, cancellationToken);
await context.RespondAsync(HyperStatus.OK(), HyperSerializers.PlainTextAsync, cancellationToken);
return Result.Success(default(HyperStatus));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,57 @@ namespace HyperSharp.Protocol
public readonly partial record struct HyperStatus
{
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
public static HyperStatus {{Code}}() => new(global::System.Net.HttpStatusCode.{{Code}}, new HyperHeaderCollection(), null);
public static HyperStatus {{Code}}() => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}}
};
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
/// <param name="body">The body of the response.</param>
public static HyperStatus {{Code}}(object? body) => new(global::System.Net.HttpStatusCode.{{Code}}, new HyperHeaderCollection(), body);
public static HyperStatus {{Code}}(object? body) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}},
Body = body
};
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
/// <param name="body">The body of the response.</param>
/// <param name="serializer">Which serializer to use to serialize the body.</param>
public static HyperStatus {{Code}}(object? body, HyperSerializerDelegate serializer) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}},
Body = body,
Serializer = serializer
};
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
/// <param name="headers">The headers of the response.</param>
public static HyperStatus {{Code}}(HyperHeaderCollection headers) => new(global::System.Net.HttpStatusCode.{{Code}}, headers, null);
public static HyperStatus {{Code}}(HyperHeaderCollection headers) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}},
Headers = headers
};
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
/// <param name="headers">The headers of the response.</param>
/// <param name="body">The body of the response.</param>
public static HyperStatus {{Code}}(HyperHeaderCollection headers, object? body) => new(global::System.Net.HttpStatusCode.{{Code}}, headers, body);
public static HyperStatus {{Code}}(HyperHeaderCollection headers, object? body) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}},
Headers = headers,
Body = body
};
/// <inheritdoc cref="global::System.Net.HttpStatusCode.{{Code}}" />
/// <param name="headers">The headers of the response.</param>
/// <param name="body">The body of the response.</param>
public static HyperStatus {{Code}}(HyperHeaderCollection headers, object? body, HyperSerializerDelegate serializer) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.{{Code}},
Headers = headers,
Body = body,
Serializer = serializer
};
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/HyperSharp/HyperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,30 @@ public sealed record HyperConfiguration
/// <summary>
/// Creates a new <see cref="HyperConfiguration"/> with the default values.
/// </summary>
public HyperConfiguration() : this(new ServiceCollection().AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)).AddHyperSharp(), new HyperConfigurationBuilder()) { }
public HyperConfiguration() : this(new ServiceCollection().AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)).AddHyperSharp().BuildServiceProvider(), new HyperConfigurationBuilder()) { }

/// <summary>
/// Creates a new <see cref="HyperConfiguration"/> with the specified values.
/// </summary>
/// <param name="builder">The <see cref="HyperConfigurationBuilder"/> to use.</param>
public HyperConfiguration(HyperConfigurationBuilder builder) : this(new ServiceCollection().AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)).AddHyperSharp(), builder) { }
public HyperConfiguration(HyperConfigurationBuilder builder) : this(new ServiceCollection().AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)).AddHyperSharp().BuildServiceProvider(), builder) { }

/// <summary>
/// Creates a new <see cref="HyperConfiguration"/> using the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="serviceDescriptors">The <see cref="IServiceCollection"/> to use when creating the responders.</param>
public HyperConfiguration(IServiceCollection serviceDescriptors) : this(serviceDescriptors, new HyperConfigurationBuilder()) { }
public HyperConfiguration(IServiceCollection serviceDescriptors) : this(serviceDescriptors.BuildServiceProvider(), new HyperConfigurationBuilder()) { }

/// <summary>
/// Creates a new <see cref="HyperConfiguration"/> using the specified <see cref="IServiceCollection"/> and <see cref="HyperConfigurationBuilder"/>.
/// </summary>
/// <param name="serviceDescriptors">The <see cref="IServiceCollection"/> to use when creating the responders.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to use.</param>
/// <param name="builder">The <see cref="HyperConfigurationBuilder"/> to use.</param>
public HyperConfiguration(IServiceCollection serviceDescriptors, HyperConfigurationBuilder builder)
public HyperConfiguration(IServiceProvider serviceProvider, HyperConfigurationBuilder builder)
{
ArgumentNullException.ThrowIfNull(serviceDescriptors, nameof(serviceDescriptors));
ArgumentNullException.ThrowIfNull(serviceProvider, nameof(serviceProvider));
ArgumentNullException.ThrowIfNull(builder, nameof(builder));

IServiceProvider serviceProvider = serviceDescriptors.BuildServiceProvider();
if (!Uri.TryCreate($"http://{builder.ListeningEndpoint}/", UriKind.Absolute, out Uri? host))
{
throw new ArgumentException("The listening endpoint is invalid.", nameof(builder));
Expand Down
2 changes: 1 addition & 1 deletion src/HyperSharp/HyperServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private async Task HandleConnectionAsync(Ulid id, NetworkStream networkStream)
_ => throw new NotImplementedException("Unimplemented result status, please open a GitHub issue as this is a bug.")
};

await context.Value.RespondAsync(response, HyperSerializers.JsonAsync, cancellationTokenSource.Token);
await context.Value.RespondAsync(response, cancellationTokenSource.Token);
HyperLogging.HttpResponded(_logger, connection.Id, response, null);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/HyperSharp/Protocol/HyperContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public HyperContext(HttpMethod method, Uri route, Version version, HyperHeaderCo
}

/// <inheritdoc cref="RespondAsync(HyperStatus, HyperSerializerDelegate, CancellationToken)"/>
public ValueTask RespondAsync(HyperStatus status, CancellationToken cancellationToken = default) => RespondAsync(status, HyperSerializers.JsonAsync, cancellationToken);
public ValueTask RespondAsync(HyperStatus status, CancellationToken cancellationToken = default) => RespondAsync(status, status.Serializer, cancellationToken);

/// <summary>
/// Responds to the request with the specified status in plain text.
Expand Down
17 changes: 17 additions & 0 deletions src/HyperSharp/Protocol/HyperHeaderCollection.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;

Expand All @@ -10,6 +12,7 @@ namespace HyperSharp.Protocol
/// <summary>
/// Represents a collection of headers with string keys and lists of string values.
/// </summary>
[DebuggerDisplay("{ToString(),nq}")]
public sealed partial class HyperHeaderCollection : IList<KeyValuePair<string, byte[]>>
{
private readonly List<KeyValuePair<string, byte[]>> _headers;
Expand Down Expand Up @@ -548,5 +551,19 @@ public bool TryGetValues(string key, [NotNullWhen(true)] out List<byte[]>? value
/// <inheritdoc />
public IEnumerator<KeyValuePair<string, byte[]>> GetEnumerator() => _headers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _headers.GetEnumerator();

public override string ToString()
{
StringBuilder stringBuilder = new();
foreach (KeyValuePair<string, byte[]> header in _headers.OrderBy(x => x.Key))
{
stringBuilder.Append(header.Key);
stringBuilder.Append(": ");
stringBuilder.Append(Encoding.ASCII.GetString(header.Value));
stringBuilder.Append("\r\n");
}

return stringBuilder.ToString();
}
}
}
2 changes: 1 addition & 1 deletion src/HyperSharp/Protocol/HyperHeaderName.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated/>
// Last modified at 2024-06-28.
// Last modified at 2024-07-30.

namespace HyperSharp.Protocol
{
Expand Down
5 changes: 3 additions & 2 deletions src/HyperSharp/Protocol/HyperSerializers/JsonAsync.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
Expand All @@ -9,7 +10,7 @@ namespace HyperSharp.Protocol
{
public static partial class HyperSerializers
{
private static readonly byte[] _contentTypeJsonEncodingHeader = "Content-Type: application/json; charset=utf-8\r\nContent-Length: "u8.ToArray();
private static readonly byte[] _contentTypeJsonEncodingHeader = "Content-Type: application/json\r\nContent-Length: "u8.ToArray();

/// <summary>
/// Serializes the body to the client as JSON using the <see cref="JsonSerializer.SerializeToUtf8Bytes{TValue}(TValue, JsonSerializerOptions?)"/> method with the <see cref="HyperConfiguration.JsonSerializerOptions"/> options.
Expand All @@ -28,7 +29,7 @@ public static ValueTask<bool> JsonAsync(HyperContext context, HyperStatus status
byte[] body = JsonSerializer.SerializeToUtf8Bytes(status.Body ?? new object(), context.Connection.Server.Configuration.JsonSerializerOptions);

// Finish the Content-Length header
context.Connection.StreamWriter.Write<byte>(Encoding.ASCII.GetBytes(body.Length.ToString()));
context.Connection.StreamWriter.Write<byte>(Encoding.ASCII.GetBytes(body.Length.ToString(CultureInfo.InvariantCulture)));
context.Connection.StreamWriter.Write<byte>(_newLine);

// Write body
Expand Down
3 changes: 2 additions & 1 deletion src/HyperSharp/Protocol/HyperSerializers/PlainTextAsync.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -29,7 +30,7 @@ public static ValueTask<bool> PlainTextAsync(HyperContext context, HyperStatus s
byte[] body = Encoding.UTF8.GetBytes(status.Body?.ToString() ?? "");

// Write Content-Length header
context.Connection.StreamWriter.Write<byte>(Encoding.ASCII.GetBytes(body.Length.ToString()));
context.Connection.StreamWriter.Write<byte>(Encoding.UTF8.GetBytes(body.Length.ToString(CultureInfo.InvariantCulture)));
context.Connection.StreamWriter.Write<byte>(_newLine);

// Write body
Expand Down
41 changes: 41 additions & 0 deletions src/HyperSharp/Protocol/HyperSerializers/RawAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Toolkit.HighPerformance;

namespace HyperSharp.Protocol
{
/// <summary>
/// Holds a collection of static methods implementing <see cref="HyperSerializerDelegate"/> for the most common of Content-Types.
/// </summary>
public static partial class HyperSerializers
{
private static readonly byte[] _contentLengthHeader = "Content-Length: "u8.ToArray();

/// <summary>
/// Serializes the body to the client as plain text using the <see cref="object.ToString"/> method with the <see cref="Encoding.UTF8"/> encoding.
/// </summary>
/// <inheritdoc cref="HyperSerializerDelegate"/>
public static ValueTask<bool> RawAsync(HyperContext context, HyperStatus status, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(status);

byte[] body = status.Body as byte[] ?? Encoding.UTF8.GetBytes(status.Body?.ToString() ?? "");
int bodyLength = body.Length;

// Write Content-Length header
context.Connection.StreamWriter.Write<byte>(_contentLengthHeader);
context.Connection.StreamWriter.Write<byte>(Encoding.UTF8.GetBytes(bodyLength.ToString(CultureInfo.InvariantCulture)));
context.Connection.StreamWriter.Write<byte>(_newLine);

// Write body
context.Connection.StreamWriter.Write<byte>(_newLine);
context.Connection.StreamWriter.Write<byte>(body);

return ValueTask.FromResult(true);
}
}
}
47 changes: 42 additions & 5 deletions src/HyperSharp/Protocol/HyperStatus/HyperStatus.Accepted.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
// <auto-generated/>
// Last modified at 2024-06-28.
// Last modified at 2024-07-30.

#nullable enable
namespace HyperSharp.Protocol
{
public readonly partial record struct HyperStatus
{
/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
public static HyperStatus Accepted() => new(global::System.Net.HttpStatusCode.Accepted, new HyperHeaderCollection(), null);
public static HyperStatus Accepted() => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted
};

/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
/// <param name="body">The body of the response.</param>
public static HyperStatus Accepted(object? body) => new(global::System.Net.HttpStatusCode.Accepted, new HyperHeaderCollection(), body);
public static HyperStatus Accepted(object? body) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted,
Body = body
};

/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
/// <param name="body">The body of the response.</param>
/// <param name="serializer">Which serializer to use to serialize the body.</param>
public static HyperStatus Accepted(object? body, HyperSerializerDelegate serializer) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted,
Body = body,
Serializer = serializer
};

/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
/// <param name="headers">The headers of the response.</param>
public static HyperStatus Accepted(HyperHeaderCollection headers) => new(global::System.Net.HttpStatusCode.Accepted, headers, null);
public static HyperStatus Accepted(HyperHeaderCollection headers) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted,
Headers = headers
};

/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
/// <param name="headers">The headers of the response.</param>
/// <param name="body">The body of the response.</param>
public static HyperStatus Accepted(HyperHeaderCollection headers, object? body) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted,
Headers = headers,
Body = body
};

/// <inheritdoc cref="global::System.Net.HttpStatusCode.Accepted" />
/// <param name="headers">The headers of the response.</param>
/// <param name="body">The body of the response.</param>
public static HyperStatus Accepted(HyperHeaderCollection headers, object? body) => new(global::System.Net.HttpStatusCode.Accepted, headers, body);
public static HyperStatus Accepted(HyperHeaderCollection headers, object? body, HyperSerializerDelegate serializer) => new HyperStatus()
{
Code = global::System.Net.HttpStatusCode.Accepted,
Headers = headers,
Body = body,
Serializer = serializer
};
}
}
Loading

0 comments on commit e5d4083

Please sign in to comment.