Custom Handler

You can roll your own IHttpHandler and injecting it when creating the builder.

Create a custom handler

Fulfill the IHttpHandler contract

Stream stream : The NetworkStream or SslStream if TLS is enabled.

Func<TContext, Task> pipeline : The pipeline that will call the middleware pipeline and route/call the endpoint, when pipeline is invoked, the route should be already set in the TContext.

/// <summary>
/// Defines a contract for handling client connections using a custom or HTTP-based protocol.
/// </summary>
public interface IHttpHandler<out TContext>
    where TContext : IContext
{
    /// <summary>
    /// Processes a client connection and dispatches one or more protocol-compliant requests.
    /// </summary>
    /// <param name="stream">The <see cref="Stream"/> representing the client connection.</param>
    /// <param name="pipeline">
    /// A delegate that executes the application's request-handling pipeline, typically consisting of middleware and endpoint logic.
    /// </param>
    /// <param name="stoppingToken">
    /// A <see cref="CancellationToken"/> used to signal cancellation, such as during server shutdown.
    /// </param>
    /// <returns>
    /// A <see cref="Task"/> that represents the asynchronous operation of handling the client session.
    /// </returns>
    Task HandleClientAsync(
        Stream stream,
        Func<TContext, Task> pipeline,
        CancellationToken stoppingToken);
}

Custom handler pseudo example

public class CustomContext : IContext
{
    // Implement your custom IContext..
}

public class CustomRequest : IRequest
{
    // Implement your custom IRequest..
}

public class CustomResponse : IResponse
{
    // Implement your custom IResponse..
}

public class CustomHttpHandler<TContext> : IHttpHandler<TContext>
    where TContext : class, IContext, new()
{
    // Pool context objects for less memory pressure (optional)
    private static readonly ObjectPool<TContext> ContextPool =
        new DefaultObjectPool<TContext>(new DefaultPooledObjectPolicy<TContext>(), 8192);

    public async Task HandleClientAsync(Stream stream, Func<TContext, Task> pipeline, CancellationToken stoppingToken)
    {
        // Get a context object from pool (or create a new instance if not pooling)
        var context = ContextPool.Get();

        // Create a new IHttpRequest or use pooling
        context.Request = new CustomRequest();

        // Set up the PipeReader and PipeWriter for the context.
        // You can also skip this and use the Stream directly to read and write from socket.
        // However, the preferred way is to use PipeReader and PipeWriter for better performance.
        // Also, if you decide to use Stream, you will need to cast the IContext passed to the endpoint
        // since IContext does not expose the Stream directly.
        context.Reader = PipeReader.Create(stream,
            new StreamPipeReaderOptions(MemoryPool<byte>.Shared, leaveOpen: true, bufferSize: 8192));

        context.Writer = PipeWriter.Create(stream,
            new StreamPipeWriterOptions(MemoryPool<byte>.Shared, leaveOpen: true));



        // The next section is typically wrapped in a loop or equivalent if the connection is persistent (keep-alive).

        // Read the received request headers and set the context's HttpMethod and Route

        // Call the pipeline callback, it will trigger the middleware pipeline and the endpoint

        // Handle Keep-Alive connections

        // Make sure to dispose managed resources and return the context to the pool


    }
}

Injecting the custom handler in the builder

Use the handler factory overload to pass the custom handler construction delegate to the CreateBuilder method.

var builder = WiredApp.CreateBuilder<CustomHttpHandler<CustomContext>, CustomContext>(() => 
    new CustomHttpHandler<CustomContext>());

Optionally, also pass the accepted SslAplicationProtocols

var builder = WiredApp.CreateBuilder<CustomHttpHandler<CustomContext>, CustomContext>(() =>
    new CustomHttpHandler<CustomContext>(), [SslApplicationProtocol.Http11]);