redb.Route.Http 4.2.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package redb.Route.Http --version 4.2.0
                    
NuGet\Install-Package redb.Route.Http -Version 4.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="redb.Route.Http" Version="4.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="redb.Route.Http" Version="4.2.0" />
                    
Directory.Packages.props
<PackageReference Include="redb.Route.Http" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add redb.Route.Http --version 4.2.0
                    
#r "nuget: redb.Route.Http, 4.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package redb.Route.Http@4.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=redb.Route.Http&version=4.2.0
                    
Install as a Cake Addin
#tool nuget:?package=redb.Route.Http&version=4.2.0
                    
Install as a Cake Tool

redb.Route.Http

HTTP/HTTPS transport for redb.Route. HttpClient-based producer (outbound requests) and Kestrel-based consumer (webhook receiver) with CORS, auth, and streaming.

NuGet License: Apache 2.0

Installation

dotnet add package redb.Route.Http

Usage

Fluent DSL

using redb.Route.Http.Fluent;

// Outbound HTTP call (producer)
From("direct://send")
    .To(Http.Post("api.example.com/orders")
        .Timeout(5000)
        .BearerAuth()
        .ContentType("application/json"));

// Webhook receiver (consumer)
From(Http.Listen("/webhooks/orders")
        .Host("0.0.0.0").Port(8080)
        .Methods("POST")
        .Cors("https://app.example.com")
        .MaxRequestBodySize(1_048_576))
    .Log("Webhook received: ${body}")
    .To("direct://process");

// REST methods shorthand
From("direct://get-data")
    .To(Http.Get("api.example.com/status").NoThrowOnError());

From("direct://update")
    .To(Http.Put("api.example.com/orders/${header.orderId}"));

From("direct://remove")
    .To(Http.Delete("api.example.com/orders/${header.orderId}"));

// HTTPS
From("direct://secure-call")
    .To(Https.Post("api.example.com/data")
        .BearerAuth()
        .AuthToken("${property.jwt}"));

// Named parameters — {name} in URL resolved from .Param() at runtime
From("direct://get-order")
    .To(Http.Get("api.example.com/orders/{orderId}")
        .Param("orderId", Header("orderId")));

// Multiple named parameters + IExpression values
From("direct://user-orders")
    .To(Http.Get("api.example.com/users/{userId}/orders/{status}")
        .Param("userId", Header("userId"))
        .Param("status", Constant("active")));

${...} expressions in URL and options are resolved per message at runtime. {name} placeholders are resolved from .Param() bindings — values are URL-encoded automatically. In fluent DSL, pass the path without http:// / https:// — the scheme is set by Http. vs Https..

Raw URI (non-fluent)

// Raw URI strings include the full scheme — ${...} resolved per message
From("direct://update")
    .To("https://api.example.com/orders/${header.orderId}?method=PUT");

// Fully dynamic URL — host, port, path all from expressions
From("direct://proxy")
    .To("https://${header.targetHost}:${header.targetPort}/api/${header.resource}?method=POST");

Fluent Builder API

Category Methods
HTTP Methods Http.Get(), Http.Post(), Http.Put(), Http.Delete(), Http.Patch(), Http.Head()
Consumer Http.Listen(), .Host(), .Port(), .Methods(), .Cors(), .CorsCredentials(), .MaxRequestBodySize(), .Protocol(), .ResponseCode(), .InOut(), .StreamRequest()
Auth .BasicAuth(user, pass), .BearerAuth(), .AuthToken()
SSL .SslCert(path, pass?)
Producer .Timeout(), .ContentType(), .NoThrowOnError(), .NoBridgeHeaders(), .NoFollowRedirects(), .MaxRedirects(), .NoCopyResponseHeaders(), .PreserveHostHeader()
Parameters .Param(name, value), .Param(name, IExpression) — bind {name} URL placeholders

Most builder methods (Timeout, BasicAuth, AuthToken, MaxRedirects, Host, Port, MaxRequestBodySize, SslCert, ResponseCode) accept both constant values and IExpression for runtime resolution.

Schemes

Both http and https schemes are supported. Use Https.Get(...) / Https.Post(...) for TLS endpoints.

Routing precedence (shared server)

Multiple consumers can register routes on the same (host, port) — they share one Kestrel server. When several routes match the same request path, the most specific path wins, not the first one registered:

  1. Concrete/literal paths (/api/echo) before route-parameter paths (/api/{id}).
  2. Fewer route parameters before more.
  3. Catch-all templates (/{**path}) are tried last.
  4. Registration order breaks ties (first-registered wins among equal specificity).

This means a concrete path and a catch-all fallback can coexist on one port — e.g. a dispatcher mounted on /{**path} plus a dedicated /api/echo route — and /api/echo is routed to the specific handler. The same ordering drives per-route CORS dispatch.

REST DSL

A declarative facade over the same consumer and shared host (Apache Camel rest() parity):

this.Rest("/api/orders", o => { o.Port = 8080; o.BindingMode = RestBindingMode.Json; })
    .Get("/{id}").Produces("application/json").OutType<Order>().To("direct:get-order")   // header.id, header.query.page
    .Post().Consumes("application/json").Type<Order>().To("direct:create-order")          // 415 on another Content-Type
    .Put("/{id}/status").To("direct:set-status")
    .Delete("/{id}").Route().Process(e => e.In.Body = null);                              // inline steps, 204

Every verb is an ordinary route From("http://host:port/base/path?methods=GET&inOut=true"). Path parameters arrive as header.id, query as header.query.*; the status code is the consumer's redbHttp.ResponseCode header; no body and no status is 204. An OpenAPI 3.0.3 document is served at {basePath}/openapi.json (RestOptions.OpenApi / OpenApiPath). Several Rest(...) declarations share a port.

Request validation

Parameters are declared on the verb with Param(...) and always go to the OpenAPI document. They are enforced only when ClientRequestValidation is on (Camel's clientRequestValidation), for the whole declaration or per verb, so parameters written down for the document alone never start refusing requests:

this.Rest("/api/items", o => { o.Port = 8080; o.ClientRequestValidation = true; o.ErrorHandler = "#restErrors"; })
    .Get("/{id}").Produces("application/json")
        .Param("id", RestParamType.Path, dataType: RestParamDataType.Integer)
        .Param("limit", RestParamType.Query, required: true, dataType: RestParamDataType.Integer, description: "Page size")
        .Param("X-Tenant", RestParamType.Header, required: true)
        .To("direct:get-item");

In order, before the route runs:

Check Answer
Content-Type against Consumes (runs with validation off too) 415
Accept against Produces: no header admits everything; the most specific matching range decides; q=0 refuses 406
a required parameter is missing, or a value does not convert to its dataType (integer, number, boolean; invariant culture) 400, naming the parameter

A path parameter must be a segment of the template and is always required. The refusal body is the reason as text/plain. ErrorHandler names a registered IProcessor that writes it instead: it finds the code, reason and parameter in the RestErrorProperties exchange properties, and may change the status. A name that is not registered stops the start.

In Route-XML:

<rest path="/api/items" port="8080" clientRequestValidation="true" errorHandler="#restErrors">
  <get path="/{id}" produces="application/json" to="direct:get-item">
    <param name="id" type="path" dataType="integer"/>
    <param name="limit" required="true" dataType="integer" description="Page size"/>
    <param name="X-Tenant" type="header" required="true"/>
  </get>
</rest>

Part of

redb.Route — ESB & EIP Framework for .NET

Named connection factory

Keep credentials out of the route URI: register a factory in the context registry and reference it by name. A set-but-unknown name fails loud at startup — a typo can never silently fall back to inline URI parameters.

context.AddToRegistry("prod", new HttpConnectionFactory
{
    AuthScheme = "Bearer",
    AuthToken = secrets.ApiToken,
});
// http://api.internal/orders?connectionFactory=prod

Credentials: outbound and inbound

authScheme, username, password and authToken are what a producer sends. Written on a consumer URI they are refused at startup — naming the parameters, never their values — instead of starting an endpoint that looks protected and accepts everyone. A connection factory a consumer references for its TLS certificate may still carry producer credentials; only what the consumer URI writes itself is taken as intent.

A consumer checks its callers with inboundAuth (refused on a producer the same way):

inboundAuth Also needs A refused request
basic inboundUsername, inboundPassword (from configuration: {{api.password}}) 401, WWW-Authenticate: Basic realm="…", charset="UTF-8"
bearer tokenValidator=#name, a registered IHttpTokenValidator 401, WWW-Authenticate: Bearer realm="…", with error="invalid_token" when the validator refused the token
context.AddToRegistry("tokens", new MyTokenValidator());     // IHttpTokenValidator
r.From("http://0.0.0.0:8080/orders?inboundAuth=bearer&tokenValidator=#tokens&inboundRealm=orders")
    .Process(e => { var who = ExchangePrincipal.Get(e)?.Identity?.Name; /* ... */ });

A refused request never reaches the route. An accepted one reaches it with the principal on the exchange (ExchangePrincipal.Get) and without the Authorization header, so a route that logs or forwards its headers does not carry the credentials on. Basic compares both halves in constant time. inboundRealm (default redb) is the realm of the challenge. A half-declared check — credentials without inboundAuth, basic without a password, bearer without a validator, an unregistered validator name — stops the start.

Boundary. The connector does not read tokens. JWT parsing, signing keys and their rotation, audiences, scopes and roles belong to the identity provider's library (redb.Identity, or any OpenID Connect client), plugged in through IHttpTokenValidator; access policies beyond "who is this" belong to the route. Do not add a token parser here.

Rest(...) takes the same check for every route of the declaration (RestOptions.InboundAuth, InboundUsername, InboundPassword, InboundRealm, TokenValidator; in Route-XML the attributes of the same names on <rest>). The OpenAPI document is behind it too.

Request headers and the response

A consumer puts the request's headers on the exchange and, for inOut=true, writes the message's headers into the response. Three rules keep a client from shaping that response:

  1. Response fields never come in. Set-Cookie, Location, WWW-Authenticate, Proxy-Authenticate, Authentication-Info, Proxy-Authentication-Info, Retry-After, Server, Age, ETag, Accept-Ranges and Vary in a request are dropped before the exchange is built (HttpHeaders.ResponseOnlyHeaders). Stricter than Camel's inbound filter, which removes only its own headers: a request has no use for them.
  2. The request is not echoed. A header still holding the value the client sent is not written back.
  3. What the route wrote goes out. A header the route set is written even when the client sent one with the same name: a request carrying Cache-Control cannot remove the route's Cache-Control: no-store.

A processor that builds a response header from a request value — copies a header, reflects a parameter — answers for that value itself: validate or encode it before writing. Writing the request's own value back under its own name is an echo and is held back by rule 2.

Concurrency limits

Kestrel executes as many handlers as requests arrive; without a limit a route has no ceiling. The admission limit caps concurrent pipeline executions per endpoint and sheds the overflow BEFORE any pipeline work (load shedding, not backpressure):

Parameter Default Description
maxConcurrentRequests 0 (unlimited) Max concurrent pipeline executions
requestQueueLimit 0 Requests over the limit that WAIT (FIFO) instead of being rejected
rejectStatusCode 429 Status for a shed request
retryAfterSeconds 1 Retry-After header value; 0 = do not send

A shed request is answered before an exchange exists: it appears in the endpoint's Rejected counter, not in MessagesIn or Errors. The limit is strictly per endpoint — other routes on the same listener keep their own budget. For "slow down but do not drop" semantics use .Threads(n) in the route instead; the two compose (the limit sheds at the door, Threads paces inside).

Tracing

On the redb.Route activity source (AddSource("redb.Route")):

  • Consumer. One Server span per request, named {method} {path}, over the whole request, refused ones included. Its parent is the host's ASP.NET Core span when the application instruments ASP.NET Core, otherwise the caller's traceparent; without one it is a root. The caller's baggage is back on it, and the route's spans are its children. It carries redb.route.endpoint and http.response.status_code, and is an error for a 5xx.
  • Producer. One Client span per call, named HTTP {method}, with http.response.status_code; an error for a 4xx, a 5xx or a failed call. The request carries the context of this span: a traceparent the header bridge copied from an incoming request is replaced, as it names the previous hop.
  • RouteEngineOptions.EnableTelemetry = false opens neither span. A context that came in still goes out.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on redb.Route.Http:

Package Downloads
redb.Tsak.Core

Kernel of redb.Tsak — runtime container for redb.Route contexts. Provides hot-reload module loading, REST management API, scheduler, monitoring, security and pluggable cluster bootstrap.

redb.Identity.Core

OAuth 2.1 / OpenID Connect engine for redb.Identity — OpenIddict pipeline on redb.Route, redb-backed stores, MFA, WebAuthn, federation, DataProtection and signing keys.

redb.Identity.Http

HTTP / HTTPS facade for redb.Identity — OIDC discovery, token, authorize, userinfo, introspect, revoke, JWKS, PAR, DCR, SCIM, /me and management endpoints.

redb.Identity.Core.Module

redb.Tsak .tpkg host glue for redb.Identity.Core — IRouteModule entry point, configuration binding and named-redb wiring.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.2.3 55 10/8/2026
4.2.2 197 10/5/2026
4.2.0 209 9/30/2026
4.1.1 213 9/25/2026
4.1.0 220 9/21/2026
4.0.1 219 9/18/2026
4.0.0 228 9/11/2026
3.7.2 239 8/26/2026
3.7.1 243 8/26/2026
3.6.0 208 8/13/2026
3.5.1 212 8/9/2026
3.5.0 223 8/6/2026
3.4.0 241 7/27/2026
3.3.3 267 7/16/2026
3.3.1 497 7/10/2026
3.3.0 128 7/8/2026
3.2.0 225 6/29/2026
3.1.0 178 6/6/2026
3.0.1 149 6/3/2026
Loading failed