Skip to content

Request lifecycle: from request to response

Beginning of HTTP request

When entering the server infrastructure, the HTTP request can be handled by several component such as a firewall, a load balancer, or a reverse proxy before arriving on the web server itself.

For an overview of what happens on a reverse proxy like Varnish or Fastly, see Context-aware HTTP cache / Request lifecycle.

When arriving at a web server, the request is filtered by Apache Virtual Host, Nginx Server Blocks or equivalent. There, requests of static resources are separated from requests to PHP interpreter.

As Ibexa DXP is a Symfony application, the handling of requests starts like in Symfony (see Symfony and HTTP Fundamentals).

If the HTTP request is to be treated by Ibexa DXP, it goes to the public/index.php of the Symfony Front Controller.

The front controller transforms the HTTP request into a PHP Request object and passes it to Symfony's Kernel to get a Response object that is transformed and sent back as an HTTP response.

The schemas start with a regular Request object from a browser that enters Symfony and Ibexa DXP. There is no ESI, no REST, and no GraphQL request performed.

Lifecycle flowcharts

Concept flowchart

The chart below introduces the logic of the request treatment.

Simplified request lifecycle flowchart

Kernel events flowchart

The following chart shows events, listeners and attributes added to the request or its wrapping event object.

Detailed request lifecycle flowchart organised around kernel events

This schema is described below event by event.

Tip

To list all listeners that listen to an event, run php bin/console debug:event-dispatcher <event.name>, for example:

1
php bin/console debug:event-dispatcher kernel.request

To view details of a service (including class, arguments and tags), run php bin/console debug:container --show-arguments <service.name>, for example:

1
php bin/console debug:container --show-arguments ezpublish.siteaccess_match_listener`

To list all services with a specific tag, run php bin/console debug:container --tag=<tag>, for example:

1
php bin/console debug:container --tag=router

Kernel's request event

When the request enters the Symfony's kernel (and goes underneath the HttpKernel, http_kernel), a kernel.request event (KernelEvents::REQUEST) is dispatched. Several listeners are called in decreasing priority.

SiteAccess matching

The FragmentListener (priority 48) handles the request first, and then it passes to the ezpublish.siteaccess_match_listener service (priority 45). This service can be either:

  • purely the SiteAccessMatchListener or
  • its UserContextSiteAccessMatchSubscriber decoration when HTTP cache is used.

The ezpublish.siteaccess_match_listener service:

  • finds the current SiteAccess using the SiteAccess\Router (ezpublish.siteaccess_router) regarding the SiteAccess Matching configurations,
  • adds the current SiteAccess to the Request object's siteaccess attribute,
  • then dispatches the ezpublish.siteaccess event (MVCEvents::SITEACCESS).

The SiteAccessListener (ezpublish.siteaccess_listener) subscribes to this ezpublish.siteaccess event with top priority (priority 255). The SiteAccessListener adds the semanticPathinfo attribute, the path without SiteAccess indications (URIElement, URIText, or Map\URI implementing the URILexer interface) to the request.

Routing

Finally, the Symfony\Component\HttpKernel\EventListener\RouterListener (router_listener) (priority 32), which also listens to the kernel.request event, calls Ibexa\Core\MVC\Symfony\Routing\ChainRouter::matchRequest and adds its returned parameters to the request.

ChainRouter

The ChainRouter is a Symfony Content Management Framework (CMF) component. Ibexa DXP makes it a service named ezpublish.chain_router. It has a collection of prioritized routers where to find one matching the request. The ChainRouter router collection is built by the ChainRoutingPass, collecting the services tagged router. The DefaultRouter is always added to the collection with top priority (priority 255).

DefaultRouter

DefaultRouter (router.default): The DefaultRouter tries to match the semanticPathinfo against routes, close to the way pure Symfony does, by extending and using Symfony\Component\Routing\Router. If a route matches, the controller associated with it is responsible for building a View or Response object.

UrlWildcardRouter

UrlWildcardRouter (ezpublish.urlwildcard_router): If URL Wildcards have been enabled, then the URLWildcardRouter is the next router tried. If a wildcard matches, the request's semanticPathinfo is updated and the router throws a ResourceNotFoundException to continue with the ChainRouter collection's next entry.

UrlAliasRouter

UrlAliasRouter (ezpublish.urlalias_router): This router uses the UrlAliasService to associate the semanticPathinfo to a Location. If it finds a Location, the request receives the attributes locationId and contentId, viewType is set to full, and the _controller is set to ez_content:viewAction for now.

The locale_listener (priority 16) sets the request's _locale attribute.

Permission control

Another kernel.request event listener is the Ibexa\AdminUi\EventListener\RequestListener (priority 13). When a route gets a siteaccess_group_whitelist parameter, this listener checks that the current SiteAccess is in one of the listed groups. For example, the Back Office sets an early protection of its routes by passing them a siteaccess_group_whitelist containing only the admin_group.

Now, when the Request knows its controller, the HttpKernel dispatches the kernel.controller event.

Kernel's controller event

View building and matching

When HttpKernel dispatches the kernel.controller event, the following things happen.

Listening to kernel.controller, the ViewControllerListener (ezpublish.view_controller_listener) (priority 10) checks if the _controller request attribute is associated with a ViewBuilder (a service tagged ibexa.view_builder) in the ViewBuilderRegistry (ezpublish.view_builder.registry). The ContentViewBuilder (ezpublish.view_builder.content) matches on controller starting with ez_content: (see Ibexa\Core\MVC\Symfony\View\Builder\ContentViewBuilder::matches). The ContentViewBuilder builds a ContentView.

First, the ContentViewBuilder loads the Location and the Content, and adds them to the ContentView object.

Permission control

content/read and/or content/view_embed permissions are controlled during this ContentView building.

Then, the ContentViewBuilder passes the ContentView to its View\Configurator (ezpublish.view.configurator). It's implemented by the View\Configurator\ViewProvider and its View\Provider\Registry. This registry receives the services tagged ezpublish.view_provider thanks to the ViewProviderPass. Among the view providers, the services using the Ibexa\Bundle\Core\View\Provider\Configured have an implementation of the MatcherFactoryInterface (ezpublish.content_view.matcher_factory). Through service decoration and class inheritance, the ClassNameMatcherFactory is responsible for the view matching. The View\Configurator\ViewProvider uses the matched view rule to add possible templateIdentifier and controllerReference to the ContentView object.

The ViewControllerListener adds the ContentView to the Request as the view attribute. The ViewControllerListener eventually updates the request's _controller attribute with the ContentView's controllerReference.

The HttpKernel then dispatches a kernel.controller_arguments (KernelEvents::CONTROLLER_ARGUMENTS) but nothing from Ibexa DXP is listening to it.

Controller execution

The HttpKernel extracts from the request the controller and the arguments to pass to the controller. Argument resolvers work in a way similar to autowiring. The HttpKernel executes the controller with those arguments.

As a reminder, the controller and its argument can be:

  • A controller set by the matched route and the request as its argument.
  • The default ez_content:viewAction controller and a ContentView as its argument.
  • A custom controller set by the matched view rule and a View or the request as its argument (most likely a ContentView but there is no restriction).

Permission control

See Permissions for custom controller.

Kernel's view event and ContentView rendering

If the controller returns something other than Response, the HttpKernel dispatches a kernel.view event (KernelEvents::VIEW). In the case of a URL Alias, the controller most likely returns a ContentView. The ViewRendererListener (ezpublish.view.renderer_listener) uses the ContentView and the TemplateRenderer (ezpublish.view.template_renderer) to get the content of the Response and attach this new Response to the event. The HttpKernel retrieves the response attached to the event and continues.

Kernel's response event and Response sending

The HttpKernel sends a kernel.response event (KernelEvents::RESPONSE). For example, if HTTP cache is used, response's headers may be enhanced.

The HttpKernel sends a kernel.finish_request event (KernelEvents::FINISH_REQUEST). The VerifyUserPoliciesRequestListener (siso_core.verify_user_policies_request_listener) (priority 100) filters routes on its policy configuration.

Permission control

See Permissions for routes.

Finally, the HttpKernel send the response.

If an exception occurs during this chain of events, the HttpKernel sends a kernel.exception and tries to get a Response from its listeners.

The HttpKernel sends the last kernel.terminate event (KernelEvents::TERMINATE). For example, the BackgroundIndexingTerminateListener (ezpublish.search.background_indexer) (priority 0) removes from the SearchService index possible content existing in the index but not in the database.

Summary

Summary of events and services

  • event=kernel.request
    • 45:ezpublish.siteaccess_match_listener
      • ezpublish.siteaccess_router
      • event=ezpublish.siteaccess
        • 255:ezpublish.siteaccess_listener
    • 32:router_listener
      • ezpublish.chain_router
        • tag=router
          • router.default
          • ezpublish.urlwildcard_router
          • ezpublish.urlalias_router
    • 16:locale_listener
    • 13:Ibexa\AdminUi\EventListener\RequestListener
  • event=kernel.controller
    • 10:ezpublish.view_controller_listener
      • ezpublish.view_builder.registry
        • tag=ibexa.view_builder
          • ezpublish.view_builder.content
            • ezpublish.view.configurator
  • event=kernel.controller_arguments
  • event=kernel.view
    • 0:ezpublish.view.renderer_listener
      • ezpublish.view.template_renderer
  • event=kernel.response
  • event=kernel.finish_request
    • 100:siso_core.verify_user_policies_request_listener
  • event=kernel.terminate
    • 0:ezpublish.search.background_indexer

Examples request attributes timeline

Event Service Request attribute Example
http_kernel pathInfo /en/about
kernel.request ezpublish.siteaccess_match_listener siteaccess en
ezpublish.siteaccess ezpublish.siteaccess_listener semanticPathinfo /about
kernel.request router.default _route N/A
kernel.request router.default _controller N/A
kernel.request ezpublish.urlalias_router _route ez_urlalias
kernel.request ezpublish.urlalias_router _controller ez_content:viewAction
kernel.request ezpublish.urlalias_router viewType full
kernel.request ezpublish.urlalias_router contentId 1
kernel.request ezpublish.urlalias_router locationId 42
kernel.request locale_listener _locale en_GB
kernel.controller ezpublish.view_builder.content view.content Content
kernel.controller ezpublish.view_builder.content view.location Location
kernel.controller ezpublish.view.configurator view.templateIdentifier @IbexaCore/default/content/full.html.twig
kernel.controller ezpublish.view.configurator view.controllerReference null
kernel.controller ezpublish.view_controller_listener view ContentView
kernel.controller ezpublish.view_controller_listener _controller ez_content:viewAction
(controller execution) http_kernel ContentView
kernel.view ezpublish.view.renderer_listener response Response

End of HTTP response

The web server outputs the HTTP response. Depending on the architecture, few things may still occur. For example, Varnish or Fastly can take specific headers into account when setting the cache or serving it.