Skip to content

HBartosch/Effinitive

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

EffinitiveFramework

CI Tests NuGet NuGet .NET License HTTP/2 Support HPACK Compression

A high-performance C# web framework designed to outperform FastEndpoints and compete with GenHTTP.

βœ… Mission Accomplished:

  • 1.11x faster than GenHTTP - Beating another custom HTTP server framework (46.5ΞΌs vs 51.7ΞΌs)
  • 16x faster than FastEndpoints - Delivering on the performance promise (46.5ΞΌs vs 739ΞΌs)
  • 16x faster than ASP.NET Core - Outperforming Microsoft's Minimal API (46.5ΞΌs vs 722ΞΌs)
  • Sub-50ΞΌs response times - The fastest C# web framework tested
  • 6KB allocations - Minimal memory footprint per request

πŸš€ Performance Goals

  • Zero-allocation routing using Span<T> and Memory<T>
  • Minimal overhead for request/response handling
  • Optimized hot paths with aggressive inlining
  • Efficient memory management using ArrayPool<T>
  • Compile-time optimization for endpoint registration

πŸ“¦ Features

  • Simple, intuitive API similar to FastEndpoints
  • Type-safe endpoints with generic request/response handling
  • Multiple endpoint types - Optimized for sync, async I/O, and streaming operations
    • EndpointBase<TRequest, TResponse> - Synchronous/cached operations (ValueTask)
    • AsyncEndpointBase<TRequest, TResponse> - Async I/O operations (Task)
    • NoRequestEndpointBase<TResponse> - Endpoints without request body (GET, health checks)
    • NoRequestAsyncEndpointBase<TResponse> - Async endpoints without request body
  • Server-Sent Events (SSE) - Real-time streaming with three endpoint patterns:
    • NoRequestSseEndpointBase - Simple streaming without request body
    • SseEndpointBase<TRequest> - Streaming with request parsing
    • SseEndpointBase<TRequest, TEventData> - Strongly-typed event streaming
  • Custom HTTP server with direct socket handling for maximum performance
  • HTTP/2 support via ALPN negotiation with binary framing and HPACK compression
  • HTTP/1.1 protocol - Battle-tested and optimized for speed
  • TLS/HTTPS support with configurable certificates and modern protocol support
  • Minimal allocations in hot code paths
  • Benchmark suite included for performance validation

🌐 Protocol Support

  • βœ… HTTP/1.1 - Fully supported with custom parser (sub-50ΞΌs response times)
  • βœ… HTTPS/TLS - Full TLS 1.2/1.3 support with certificate configuration
  • βœ… HTTP/2 - Complete implementation with binary framing, HPACK, stream multiplexing, and ALPN
  • ⏳ HTTP/3/QUIC - Under consideration

HTTP/2 Implementation

EffinitiveFramework includes a complete from-scratch HTTP/2 implementation:

  • Binary framing layer - All 9 frame types (DATA, HEADERS, SETTINGS, PING, GOAWAY, etc.)
  • HPACK compression - Static table (61 entries) + dynamic table + Huffman encoding
  • Stream multiplexing - Multiple concurrent requests over single TCP connection
  • Flow control - Per-stream and connection-level window management
  • ALPN negotiation - Automatic protocol selection during TLS handshake ("h2" or "http/1.1")
  • Settings management - Dynamic configuration via SETTINGS frames

HTTP/2 is automatically enabled for HTTPS connections when clients negotiate it via ALPN. See HTTP/2 Implementation Guide for details.

πŸ—οΈ Architecture

Core Components

  1. EffinitiveApp - Main application bootstrap
  2. Router - High-performance routing engine using zero-allocation techniques
  3. EndpointBase<TRequest, TResponse> - Base class for synchronous/cached operations (ValueTask)
  4. AsyncEndpointBase<TRequest, TResponse> - Base class for I/O operations (Task)
  5. IEndpoint - Core endpoint interfaces

Performance Optimizations

  • Span-based routing - Routes are matched using ReadOnlySpan<char> to avoid string allocations
  • Smart async handling - ValueTask<T> for sync operations, Task<T> for I/O
  • Struct types - Key data structures use structs where appropriate
  • ArrayPool - Reuses arrays for temporary operations
  • Unsafe blocks enabled - Allows for low-level optimizations where needed

πŸ“– Quick Start

1. Create an Endpoint

For simple GET endpoints without request body (use NoRequestEndpointBase):

using EffinitiveFramework.Core;

public class HealthCheckEndpoint : NoRequestEndpointBase<HealthResponse>
{
    protected override string Method => "GET";
    protected override string Route => "/api/health";

    public override ValueTask<HealthResponse> HandleAsync(
        CancellationToken cancellationToken = default)
    {
        return ValueTask.FromResult(new HealthResponse 
        { 
            Status = "Healthy",
            Timestamp = DateTime.UtcNow,
            Version = "1.1.0"
        });
    }
}

For in-memory/cached operations with request body (use EndpointBase):

using EffinitiveFramework.Core;

public class GetUsersEndpoint : EndpointBase<EmptyRequest, UsersResponse>
{
    protected override string Method => "GET";
    protected override string Route => "/api/users";

    public override ValueTask<UsersResponse> HandleAsync(
        EmptyRequest request, 
        CancellationToken cancellationToken = default)
    {
        var users = new List<User>
        {
            new User { Id = 1, Name = "Alice", Email = "alice@example.com" },
            new User { Id = 2, Name = "Bob", Email = "bob@example.com" }
        };

        return ValueTask.FromResult(new UsersResponse { Users = users });
    }
}

For database/I/O operations (use AsyncEndpointBase):

using EffinitiveFramework.Core;

public class CreateUserEndpoint : AsyncEndpointBase<CreateUserRequest, UserResponse>
{
    protected override string Method => "POST";
    protected override string Route => "/api/users";

    public override async Task<UserResponse> HandleAsync(
        CreateUserRequest request, 
        CancellationToken cancellationToken = default)
    {
        // True async I/O - database insert
        var user = await _dbContext.Users.AddAsync(new User 
        { 
            Name = request.Name, 
            Email = request.Email 
        }, cancellationToken);
        
        await _dbContext.SaveChangesAsync(cancellationToken);
        
        return new UserResponse { User = user, Success = true };
    }
}

For real-time Server-Sent Events streaming (use SSE endpoints):

using EffinitiveFramework.Core.Http.ServerSentEvents;

public class ServerTimeEndpoint : NoRequestSseEndpointBase
{
    protected override string Method => "GET";
    protected override string Route => "/api/stream/time";

    protected override async Task HandleStreamAsync(
        SseStream stream, 
        CancellationToken cancellationToken)
    {
        // Start automatic keep-alive pings
        _ = stream.StartKeepAliveAsync(TimeSpan.FromSeconds(15), cancellationToken);
        
        await stream.WriteAsync("connected", "Server time stream started");
        
        while (!cancellationToken.IsCancellationRequested)
        {
            var timeData = new { Time = DateTime.UtcNow, Zone = "UTC" };
            await stream.WriteJsonAsync(timeData, cancellationToken);
            await Task.Delay(1000, cancellationToken);
        }
    }
}

Define your DTOs:

public record UsersResponse
{
    public List<User> Users { get; init; } = new();
}

public record User
{
    public int Id { get; init; }
    public string Name { get; init; } = string.Empty;
    public string Email { get; init; } = string.Empty;
}

πŸ’‘ See Endpoint Selection Guide for detailed guidance on choosing between EndpointBase and AsyncEndpointBase

2. Bootstrap the Application

using EffinitiveFramework.Core;

var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); };

// Create the app, configure ports, TLS, services and endpoints, then build
var app = EffinitiveApp
    .Create()
    .UsePort(5000)           // HTTP on port 5000
    .UseHttpsPort(5001)      // HTTPS on port 5001 (HTTP/2 enabled via ALPN)
    .ConfigureTls(tls =>
    {
        tls.CertificatePath = "localhost.pfx";
        tls.CertificatePassword = "dev-password";
    })
    .MapEndpoints() // Automatically discovers and registers all endpoints
    .Build();

// Run the server until cancelled
await app.RunAsync(cts.Token);

Dependency Injection (Configure services)

EffinitiveFramework exposes a light-weight DI integration via ConfigureServices on the builder. Use it to register DbContexts, services and middleware dependencies.

var app = EffinitiveApp.Create()
    .ConfigureServices(services =>
    {
        // Register a scoped EF Core DbContext
        services.AddScoped<AppDbContext>(sp =>
        {
            var options = new DbContextOptionsBuilder<AppDbContext>()
                .UseSqlite("Data Source=products.db")
                .Options;
            return new AppDbContext(options);
        });

        // Register application services
        services.AddScoped<IProductService, ProductService>();
        services.AddScoped<IOrderService, OrderService>();
    })
    .MapEndpoints(typeof(Program).Assembly)
    .Build();

// Resolve a scope for initialization or background work
using var scope = ((EffinitiveFramework.Core.DependencyInjection.ServiceProvider)app.Services!).CreateScope();
var ctx = scope.ServiceProvider.GetService<AppDbContext>();

3. Run the Application

dotnet run --project samples/EffinitiveFramework.Sample

The API will be available at http://localhost:5000 (or as configured).

πŸ§ͺ Running Benchmarks

dotnet run --project benchmarks/EffinitiveFramework.Benchmarks -c Release

The benchmark suite compares:

  • Route matching performance
  • Endpoint invocation overhead
  • Memory allocations
  • Request/response throughput

🎯 Benchmark Results

Framework Comparison (HTTP End-to-End)

Framework GET Mean POST Mean vs EffinitiveFramework
EffinitiveFramework 44.37 ΞΌs 44.89 ΞΌs Baseline
GenHTTP 54.58 ΞΌs 57.04 ΞΌs 1.23-1.27x slower
FastEndpoints 726.72 ΞΌs 725.10 ΞΌs 16.2-16.4x slower
ASP.NET Core Minimal API 725.19 ΞΌs 715.01 ΞΌs 15.9-16.4x slower

Key Performance Metrics

βœ… Fastest C# web framework tested
βœ… 1.23-1.27x faster than GenHTTP (another custom HTTP server)
βœ… ~16x faster than FastEndpoints and ASP.NET Core Minimal API
βœ… Sub-50ΞΌs response times for both GET and POST
βœ… 4.5-5.5 KB memory per request (minimal allocations)

See BENCHMARK_RESULTS.md for detailed results and analysis.

πŸ“š Project Structure

EffinitiveFramework/
β”œβ”€β”€ src/
β”‚   └── EffinitiveFramework.Core/       # Core framework library
β”‚       β”œβ”€β”€ EffinitiveApp.cs             # Main application class
β”‚       β”œβ”€β”€ Router.cs                    # High-performance router
β”‚       β”œβ”€β”€ EndpointBase.cs              # Base endpoint class
β”‚       β”œβ”€β”€ IEndpoint.cs                 # Endpoint interfaces
β”‚       └── HttpMethodAttribute.cs       # HTTP method attributes
β”œβ”€β”€ samples/
β”‚   └── EffinitiveFramework.Sample/      # Sample API project
β”‚       β”œβ”€β”€ Program.cs                   # Application entry point
β”‚       └── Endpoints/
β”‚           └── UserEndpoints.cs         # Example endpoints
β”œβ”€β”€ benchmarks/
β”‚   └── EffinitiveFramework.Benchmarks/  # Performance benchmarks
β”‚       └── Program.cs                   # BenchmarkDotNet tests
└── tests/
    └── EffinitiveFramework.Tests/       # Unit tests

πŸ”§ Development

Prerequisites

  • .NET 8 SDK or later
  • Visual Studio 2022 / VS Code / Rider

Build

dotnet build

Run Tests

dotnet test

Run Sample

dotnet run --project samples/EffinitiveFramework.Sample

🎨 Design Principles

  1. Performance First - Every feature is evaluated for its performance impact
  2. Zero Allocations - Hot paths should allocate as little as possible
  3. Simple API - Easy to use, hard to misuse
  4. Type Safety - Leverage C# type system for compile-time guarantees
  5. Minimal Dependencies - Only depend on ASP.NET Core fundamentals

πŸ”¬ Performance Techniques Used

  • Span and Memory - For zero-copy string operations
  • ArrayPool - For temporary buffer allocations
  • ValueTask - For reduced async allocations
  • Aggressive Inlining - [MethodImpl(MethodImplOptions.AggressiveInlining)]
  • Struct Types - Value types for small, frequently-used data
  • Unsafe Code - Low-level optimizations where beneficial

πŸ“Š Comparison with FastEndpoints

Feature EffinitiveFramework FastEndpoints
Routing Engine Custom zero-allocation ASP.NET Core
Endpoint Definition Class-based Class-based
Request Binding JSON deserialization Multiple strategies
Performance Focus Maximum High
Dependencies Minimal More features

🚧 Roadmap

  • Route parameter extraction (e.g., /users/{id}) βœ… IMPLEMENTED
  • Query string binding βœ… IMPLEMENTED (API Key auth)
  • Header/cookie binding βœ… IMPLEMENTED (Auth handlers)
  • Request validation βœ… IMPLEMENTED (Routya.ResultKit integration)
  • Middleware pipeline βœ… IMPLEMENTED (High-performance pipeline)
  • Dependency injection integration βœ… IMPLEMENTED (Full DI support)
  • Server-Sent Events (SSE) βœ… IMPLEMENTED v1.1.0 (Real-time streaming)
  • Response caching
  • OpenAPI/Swagger integration
  • Response compression (gzip, br, deflate)
  • Rate limiting
  • WebSocket support
  • HTTP/3 / QUIC protocol

🀝 Contributing

Contributions are welcome! Please ensure:

  • All benchmarks pass with improved or comparable performance
  • Code follows existing patterns
  • Tests are included for new features
  • Performance-critical code includes comments explaining optimizations

πŸ“„ License

MIT License - see LICENSE file for details

πŸ™ Acknowledgments

  • Inspired by FastEndpoints
  • Performance techniques from GenHTTP
  • Built on ASP.NET Core

Note: This is a performance-focused framework. Always benchmark your specific use case to ensure it meets your requirements.