redb.Route.Http
4.2.0
Prefix Reserved
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
<PackageReference Include="redb.Route.Http" Version="4.2.0" />
<PackageVersion Include="redb.Route.Http" Version="4.2.0" />
<PackageReference Include="redb.Route.Http" />
paket add redb.Route.Http --version 4.2.0
#r "nuget: redb.Route.Http, 4.2.0"
#:package redb.Route.Http@4.2.0
#addin nuget:?package=redb.Route.Http&version=4.2.0
#tool nuget:?package=redb.Route.Http&version=4.2.0
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.
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 withouthttp:///https://— the scheme is set byHttp.vsHttps..
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
IExpressionfor 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:
- Concrete/literal paths (
/api/echo) before route-parameter paths (/api/{id}). - Fewer route parameters before more.
- Catch-all templates (
/{**path}) are tried last. - 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:
- Response fields never come in.
Set-Cookie,Location,WWW-Authenticate,Proxy-Authenticate,Authentication-Info,Proxy-Authentication-Info,Retry-After,Server,Age,ETag,Accept-RangesandVaryin 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. - The request is not echoed. A header still holding the value the client sent is not written back.
- 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-Controlcannot remove the route'sCache-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
Serverspan 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'straceparent; without one it is a root. The caller's baggage is back on it, and the route's spans are its children. It carriesredb.route.endpointandhttp.response.status_code, and is an error for a 5xx. - Producer. One
Clientspan per call, namedHTTP {method}, withhttp.response.status_code; an error for a 4xx, a 5xx or a failed call. The request carries the context of this span: atraceparentthe header bridge copied from an incoming request is replaced, as it names the previous hop. RouteEngineOptions.EnableTelemetry = falseopens neither span. A context that came in still goes out.
| Product | Versions 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. |
-
net10.0
- redb.Route (>= 4.2.0)
- redb.Route.Http.Hosting (>= 4.2.0)
- redb.Route.Xml (>= 4.2.0)
-
net8.0
- redb.Route (>= 4.2.0)
- redb.Route.Http.Hosting (>= 4.2.0)
- redb.Route.Xml (>= 4.2.0)
-
net9.0
- redb.Route (>= 4.2.0)
- redb.Route.Http.Hosting (>= 4.2.0)
- redb.Route.Xml (>= 4.2.0)
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 |