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.
Kernel events flowchart¶
The following chart shows events, listeners and attributes added to the request or its wrapping event object.
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 |
|
To view details of a service (including class, arguments and tags), run php bin/console debug:container --show-arguments <service.name>
, for example:
1 |
|
To list all services with a specific tag, run php bin/console debug:container --tag=<tag>
, for example:
1 |
|
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'ssiteaccess
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 eZ\Publish\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 EzSystems\EzPlatformAdminUi\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 eZ\Publish\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 eZ\Bundle\EzPublishCoreBundle\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 aContentView
as its argument. - A custom controller set by the matched view rule and a
View
or the request as its argument (most likely aContentView
but there is no restriction).
Permission control
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
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
- 255:
- 32:
router_listener
ezpublish.chain_router
- tag=
router
router.default
ezpublish.urlwildcard_router
ezpublish.urlalias_router
- tag=
- 16:
locale_listener
- 13:
EzSystems\EzPlatformAdminUi\EventListener\RequestListener
- 45:
- event=
kernel.controller
- 10:
ezpublish.view_controller_listener
ezpublish.view_builder.registry
- tag=
ibexa.view_builder
ezpublish.view_builder.content
ezpublish.view.configurator
- tag=
- 10:
- event=
kernel.controller_arguments
- event=
kernel.view
- 0:
ezpublish.view.renderer_listener
ezpublish.view.template_renderer
- 0:
- event=
kernel.response
- event=
kernel.finish_request
- 100:
siso_core.verify_user_policies_request_listener
- 100:
- event=
kernel.terminate
- 0:
ezpublish.search.background_indexer
- 0:
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 | @EzPublishCore/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.