Request-response cycle

Last modified
Friday, May 19, 2017 - 12:47

LedgerSMB 1.6 (master)

In contrast to what's documented below, in 1.6 (and later) the response is generated as a PSGI response triplet (response code, headers array ref, body arrayref or file handle). Entry points return a triplet like the above which is returned directly to the PSGI container for processing.

Further more has the code been cleaned up for the "new code" to no longer expect "CGI environment variables" to be defined which pass request state into the request handler. Instead, relevant processing (such as authentication) is now handled in the dispatch phase.

In the past, modules indicated whether they wanted the dispatcher to instantiate a database connection; now, they also indicate which entry-points require sessions not to be validated (so they can be reset/cleared).

LedgerSMB 1.5

Versions 1.5 and later no longer use the CGI scripts in the project root directory as described below; instead they establish a URL mapping using 'tools/starman.psgi', where various URLs ending in .pl (suggesting the existence of a Perl script) are being mapped directly to their entrypoints. For "new code", this means a mapping to the Perl modules in lib/LedgerSMB/Scripts/. For "old code" this means mapping to bin/*.pl.

LedgerSMB 1.4 & 1.3

For these versions the request/response cycle looks something like this:

  • Webserver accepts request for a URL pointing to a CGI script (e.g. login.pl)
  • The webserver discovers a toplevel *.pl script, which it invokes; there are 2 types of toplevel scripts:
    • old-style scripts (e.g. ar.pl), which:
      • delegate the request processing to old-handler.pl
      • old-handler.pl makes some environment assertions,
      • sets up an outer-most exception handler
      • sets up a database connection
      • authenticates the (user) session
      • instantiates the request object, usually called $form
      • delegates to an entry point in the equally named script in bin/ (e.g. bin/ar.pl)
    • new-style scripts (e.g. login.pl), which
      • delegate the request processing to lsmb-request.pl
      • old-handler.pl makes some environment assertions,
      • sets up an outer-most exception handler
      • sets up a database connection
      • authenticates the (user) session
      • instantiates the request object, usually called $form
      • delegates to an entry point in the equally named script (or, since 1.4 module) in LedgerSMB/Scripts/
        ​(the difference between 1.3 and 1.4 is that the file extension has been changed from .pl to .pm)
  • the invoked script generates a response, based on
    • the supplied parameters and entry point
    • (failure to do) business processing
  • the generated response consists of
    • an HTTP response code
    • associated HTML; generated in one of two ways:
      • old code: typically uses a mix of code and literal strings containing HTML, where processing results in the creation of an HTML response document
      • new code: typically sets up values to be used for template processing in code and then hands off the HTML response document creation to Template Toolkit, based on the templates located in the UI/ directory

Exception handling / non-local exits

The outer exception handler will catch any uncaught exceptions and translate those into 500 - Internal Server Error unless the code uses a plain "die;" statement, which is taken to mean an intentional -- error-free -- early-exit (in 1.4+).

Due to the above, "die", "croak" and "confess" can't be used to terminate processing early. Entry points must use "return" for their early returns. Routines used by entry points should agree on a return value protocol to signal their callers processing needs to stop or callers should catch known/agreed-upon thrown errors.

Entry points

Entry points are nothing more than Perl subroutines defined in the specified file. Which entry point in a file is to be invoked, is defined by the 'action=' parameter. This parameter is a query parameter for GET requests or part of the body-specified parameters in a POST request.

Request object problems

Due to the fact that there's no response object in the system, many entry points (especially, but not exclusively, old code entry points) have a "habit" of overwriting request values with parsed equivalents, or with values to be used for the processing of the HTML of the response ("response template").