Livt packages
Livt.Web
Version 1.0.0
Bounded HTTP routing, request parsing, response encoding, and Ethernet application composition for Livt.
Published files (63)
- HttpAllowedMethods.lvt
- HttpApplication.lvt
- HttpApplicationState.lvt
- HttpContentType.lvt
- HttpHeaderName.lvt
- HttpMatchResult.lvt
- HttpMethod.lvt
- HttpNetworkEndpoint.lvt
- HttpNetworkResponse.lvt
- HttpPath.lvt
- HttpPreparation.lvt
- HttpRequestCursor.lvt
- HttpRequestHeaders.lvt
- HttpRequestLine.lvt
- HttpRequestParser.lvt
- HttpRequestResult.lvt
- HttpRequestSyntax.lvt
- HttpResponse.lvt
- HttpResponseEncoder.lvt
- HttpResponseSyntax.lvt
- HttpRouteFallback.lvt
- HttpRouteMethods.lvt
- HttpRouter.lvt
- HttpServer.lvt
- HttpStatus.lvt
- HttpTcpFrame.lvt
- HttpTcpSequence.lvt
- HttpTcpState.lvt
- HttpTcpTransport.lvt
- HttpTransportPayload.lvt
- HttpTransportState.lvt
- HttpVersion.lvt
- IHttpApplication.lvt
- IHttpHandler.lvt
- IHttpRequest.lvt
- IHttpResponse.lvt
- IHttpRoute.lvt
- IHttpRouteMatcher.lvt
- Route.lvt
- RouteChain.lvt
- RouteDispatch.lvt
- RouteSelection.lvt
- StaticContent.lvt
src/http/RouteChain.lvt
namespace Livt.Web.Http
using Livt.Net
/**
* Nestable ordered subtree. Select the first path-and-method match. A path-only
* match never hides a later matching method. Owns no parser, encoder or body.
*/
component RouteChain<A: IHttpRoute, B: IHttpRoute> : IHttpRoute
{
// Exclusive borrowed route subtrees, sharing the same parsed request.
first: A
second: B
// Selection and Allow union are frozen until End.
selection: RouteSelection
dispatch: RouteDispatch
matchedMethods: HttpAllowedMethods
/**
* Borrows ordered subtrees over the same request. First matching method wins;
* construction neither selects a route nor starts any handler.
*/
new(first: A, second: B)
{
this.first = first
this.second = second
}
/**
* Selects over the shared published request without beginning a handler.
* NotReady may retry; other outcomes remain frozen until End.
*/
override fn Select() RouteDispatch
{
if (dispatch != RouteDispatch.Idle && dispatch != RouteDispatch.NotReady) {
return dispatch
}
matchedMethods = HttpAllowedMethods.None
var firstResult = first.Select()
if (firstResult == RouteDispatch.Selected) {
selection = RouteSelection.First
dispatch = firstResult
return dispatch
}
if (firstResult != RouteDispatch.NotFound && firstResult != RouteDispatch.MethodNotAllowed) {
dispatch = firstResult
return dispatch
}
var secondResult = second.Select()
if (secondResult == RouteDispatch.Selected) {
selection = RouteSelection.Second
dispatch = secondResult
return dispatch
}
if (secondResult != RouteDispatch.NotFound && secondResult != RouteDispatch.MethodNotAllowed) {
dispatch = secondResult
return dispatch
}
var firstMethods = first.GetMatchedMethods()
var secondMethods = second.GetMatchedMethods()
var combined = HttpRouteMethods.Union(firstMethods, secondMethods)
matchedMethods = combined
dispatch = RouteDispatch.NotFound
if (combined != HttpAllowedMethods.None) {
dispatch = RouteDispatch.MethodNotAllowed
}
return dispatch
}
/**
* Returns the supported-method union for matching paths.
* This describes route selection, not a prepared response or a wildcard method.
*/
override fn GetMatchedMethods() HttpAllowedMethods
{
if (selection == RouteSelection.First) {
return first.GetMatchedMethods()
}
if (selection == RouteSelection.Second) {
return second.GetMatchedMethods()
}
return matchedMethods
}
/**
* Ends both child subtrees to clear even unselected cached outcomes.
* Requires stopped response consumers; only started child handlers perform cleanup.
*/
override fn End()
{
// Clear even unselected/mismatched children's cached dispatch outcomes.
first.End()
second.End()
selection = RouteSelection.None
dispatch = RouteDispatch.Idle
matchedMethods = HttpAllowedMethods.None
}
/**
* Starts preparation once for the accepted selection; repeated calls must not
* repeat accepted application actions. Pending work resumes through Poll.
*/
override fn Begin() HttpPreparation
{
if (selection == RouteSelection.First) {
return first.Begin()
}
if (selection == RouteSelection.Second) {
return second.Begin()
}
return HttpPreparation.Idle
}
/**
* Resumes pending preparation without restarting accepted actions.
* Prepared or Failed remains terminal until End.
*/
override fn Poll() HttpPreparation
{
if (selection == RouteSelection.First) {
return first.Poll()
}
if (selection == RouteSelection.Second) {
return second.Poll()
}
return HttpPreparation.Idle
}
/**
* Reports current publication availability, not a reservation.
* Keep the borrowed providers stable until all consumers finish.
*/
override fn IsReady() bool
{
if (selection == RouteSelection.First) {
return first.IsReady()
}
if (selection == RouteSelection.Second) {
return second.IsReady()
}
return false
}
/**
* Returns the selected response status; use only while IsReady is true.
*/
override fn GetStatus() HttpStatus
{
if (selection == RouteSelection.First) {
return first.GetStatus()
}
if (selection == RouteSelection.Second) {
return second.GetStatus()
}
return HttpStatus.Unknown
}
/**
* Returns the selected body media type; use only while IsReady is true.
*/
override fn GetContentType() HttpContentType
{
if (selection == RouteSelection.First) {
return first.GetContentType()
}
if (selection == RouteSelection.Second) {
return second.GetContentType()
}
return HttpContentType.Unknown
}
/**
* Returns the selected response Allow set, not the routing tree's match set.
* Use only while IsReady is true; None omits the Allow header.
*/
override fn GetAllowedMethods() HttpAllowedMethods
{
if (selection == RouteSelection.First) {
return first.GetAllowedMethods()
}
if (selection == RouteSelection.Second) {
return second.GetAllowedMethods()
}
return HttpAllowedMethods.None
}
/**
* Returns the currently advertised byte extent, or zero without publication.
* The extent is a snapshot; it does not reserve the backing storage.
*/
override fn GetAvailableLength() int
{
if (selection == RouteSelection.First) {
return first.GetAvailableLength()
}
if (selection == RouteSelection.Second) {
return second.GetAvailableLength()
}
return 0
}
/**
* Reads a zero-based byte within the published extent. Check PacketDataResult
* before using value; keep all borrowed providers stable throughout the read.
*/
override fn TryRead(index: int, value: out byte) PacketDataResult
{
value = 0x00
if (selection == RouteSelection.First) {
return first.TryRead(index, value)
}
if (selection == RouteSelection.Second) {
return second.TryRead(index, value)
}
return PacketDataResult.NotReady
}
}