From 3f7bde8d302f351249fbad63d235a26df722d0d9 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Mon, 8 Jul 2024 16:34:04 +0400 Subject: [PATCH] added some clarification docs --- STRUCTURE.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 STRUCTURE.md diff --git a/STRUCTURE.md b/STRUCTURE.md new file mode 100644 index 0000000..b757529 --- /dev/null +++ b/STRUCTURE.md @@ -0,0 +1,129 @@ +# Landline library and application structure + +## File layout + +- `/landline/lib/*.rb` - core of the library +- `/landline/lib/dsl/` - module namespaces for use by executable contexts +- `/landline/lib/path/` - nodes that extend the Path class (traversable + node/setup execution context) +- `/landline/lib/probe/` - nodes that extend the Probe class (executable + nodes/per-request execution context) +- `/landline/lib/template/` - template engine adapters +- `/landline/lib/util/` - utility classes and modules +- `/landline/lib/pattern_matching/` - classes that implement path pattern + matching (i.e. globs, regex and static traversal paths) + +## Architecture overview + +The following chapter defines information that pertains to the overall +structure and interaction of landline's core abstractions - Nodes, Paths +and Probes. + +For context: Node is an element of the path tree, and is another name for a +Probe (which is a leaf of the tree) or a path (which is an internal vertex +of the tree) + +```plaintext +Path +-> Path +-> Probe + | | + | +-> Probe + +-> Probe + | + |-> Path -> Probe +``` + +### Execution contexts + +In Sinatra, which this library takes most of its influence from, every +request creates its own instance of the Sinatra application. This seemed +like an excessive measure to solve a trivial problem of race conditions on a +shared execution context, so to solve that, Landline introduces per-request +execution contexts, which have a life time that spans the duration of +request processing. + +In short, there are two distinct execution contexts: + +- Per-request execution context, which is used by blocks that process the + request and which is bound to that given request +- Setup execution context, in which the Landline node tree is constructed + +For every Path-like node, the supplied block is executed in the setup +context bound to that path. + +For every Probe-like node, the supplied block is executed in the per-request +context of the request that is passed to the probe. + +These execution contexts also affect the receivers of the instance variable +write and read operations. Additionally, the two types have different +sets of available methods, and every DSL method has a description attached +which describes in which context that method can be used (this is due to the +limitations of the YARD parser and due to the need of showing method +descriptions in the IDE) + +### Request traversal + +Requests in Landline traverse through a series of paths that determine +which route the request would take. All nodes are organized in a tree +structure, where the root of the tree is a Server object that handles +things likes localized jumps, request/response conversion and response +finalization, and root-level error/`die()` handling. + +Every path-like node can be supplied with callbacks that perform +the following functions: + +- filters - callbacks that filter incoming request objects +- preprocessors - callbacks that don't usually affect the routing decision + but can modify requests before they come through +- postprocessors - callbacks that execute either when a request exits a + given path after not finding any suitable probe-like node or when a + response to a request is being finalized +- pipeline - single callback that is injected in the execution stack that + can be used to catch throws and exceptions + +(see `/examples/logging.ru`, `/examples/norxondor_gorgonax/config.ru`) + +The execution contexts for every callback (except pipeline) are shared with +those of the probe-like execution contexts, meaning that an instance +variable written to in a filter or a preprocessor can be accessed from the +probe node or a postprocessor callback. + +Once all preprocessors and filters are finished successfully, the request +is passed to all subsequent graph elements (either other Paths or Probes) +to check whether a given Node accepts that element. + +If the subtree of the path contains at least one probe that accepts the +request, that probe finishes processing the request and either exits +the tree immediately by throwing the `:finish` signal with a response +or by returning a valid (not nil) response from the `process` method, +in which case the response ascends from the probe back to the root. + +If none of the subsequent elements of a path accepted the request, the +path executes `die(404)` if `bounce` is not enabled, and if `bounce` is +enabled, the request exits from the path and proceeds to sibling +(adjacent) paths. (see `/examples/dirbounce.ru`) + +A probe can finish the request by either throwing a response, by executing a +jump to another path, by explicitly dying with a status code or by throwing +an exception (which effectively causes `die(500)`) +(see `/examples/errorpages.ru`, `/examples/jumps.ru`) + +If a jump is executed, the path of the request is rewritten locally, and the +request is fed through the server again for processing. This is effectively +a localized redirect which retains accumulated request processing +information (i.e. instance variables) +(see `/examples/jumps.ru`) + +The tree can be changed at runtime, but it's generally not advisable to +depend on that fact. If, eventually, some solution will be found to +optimizing trees into linear routing paths, and if that solution is proven +to improve performance, this statement might become false. + +```plaintext + +--------------------------------------+ + v | +run (obj) -> App#call -> Server#call +-> Path#go -> ... -> Probe#go + + | | (if app acts as a wrapper for another + <-----------------------+ | Rack server and if response is 404 + (returns back to Rack server | with x-cascade header set) + if no jumps occured) +-> passthrough#call +```