Understanding the HTTP request lifecycle
There are a number of components involved in performing the HTTP request. The components are as follows:
- Client: This is where the request starts and the response ends (for example, the browser).
- Network: The requests and responses go through this and it connects the server and the client.
- Proxy: This is an optional component that could perform some tasks before the request reaches the web server, such as caching, rewriting and/or altering the request, and forwarding the request to the right web server.
- Web server: This receives the request and is responsible for selecting the correct resource.
- PHP: The language, or more generally in the case of server-side languages, the language-specific engine that is used and involved. In this case, the PHP interpreter is used. The PHP interpreter can be activated mainly in two ways: as a web server module or as a separate process. In the latter case, a technology called FastCGI Process Manager (FPM) is used. We will see how this mechanism works later in more detail. For now, it is useful to know that the web server somehow invokes the server-side language interpreter. By doing this, our server is able to interpret the language. If the invoked resource is a PHP-type file with the specific PHP syntax, the resource file requested is interpreted by the PHP engine. The output is sent back in the form of a response to the web server, the network, and then the browser.
- Framework: In the case that the application is written with PHP and a framework is used, as a developer, you can access classes, methods, and helpers to build your application faster.
The components are called sequentially in the HTTP request flow. The HTTP request starts from the browser, then goes through the network (optionally passing through via the proxy), until it reaches the web server that invokes the PHP engine and the framework is bootstrapped.
From a performance point of view, if you want to bring some improvement, you have to take some action or implement some solution depending on the elements of this architecture.
For example, on the browser side, you could work on caching assets in the browser or on the optimization of your JavaScript code. On the networking side, one solution could be resource optimization, for example, reducing the weight of assets or introducing architectural elements such as a CDN. In the case of the web server, an effective first-level improvement could be to avoid loading the PHP engine for the static assets (non-PHP files).
All such fine-tuning will be addressed in the final chapters, where we will deal with the configuration and optimization of production elements. Most of the book covers the optimization of the framework. For example, in Chapters 2 and 3, topics such as the use of Octane with tools such as Swoole and RoadRunner, which enable more efficient and effective loading of resources (shared objects and structures), are addressed. Other points of performance improvement on the framework side include the introduction of an asynchronous approach through the use of queuing systems (Chapters 6 and 7).
Now that you have an idea of the components involved in an HTTP request, let’s look at the structure of an HTTP request.
The structure of an HTTP request
To understand in detail what happens in a typical HTTP request, we start by analyzing what is sent from the browser to the server during a request. A request is mainly characterized by methods (GET
, POST
, PUT
, and so on), the URL, and HTTP headers.
The URL is visible in the browser’s address bar, whereas the headers are handled automatically by the browser and are not directly visible to the user.
The following describes the structure of an HTTP request:
- The HTTP method (or HTTP verbs) in an HTTP request represents the operation the frontend side wants to perform on the server side with the requested resource:
- The
GET
method: Reads and retrieves a resource - The
POST
method: Creates a new resource - The
PUT
method: Replaces a resource - The
PATCH
method: Edits the resource - The
DELETE
method: Deletes the resource
- The
- The URL identifies the resource. We’ll explain the URL structure in the next section (Handling an HTTP request).
- The headers include additional information that allows the server to understand how to handle the resource. This information can comprise authentication information, the required format of the resource, and so on.
- The body payload is additional data, for example, the data sent when a form is submitted to the server.
Now that you have an idea of the structure of an HTTP request, let’s see how such requests are handled.
Handling an HTTP request
A URL is made up of the protocol, the hostname, the port, the path, and the parameters. A typical URL is as follows:
<
protocol>://<hostname>:<port>/<path>?<parameters>
For example, a URL could be the following:
https://127.0.0.1:8000/home?cache=12345
Each part that makes up the HTTP request is used specifically by the various software involved in handling the HTTP request:
- A protocol is used by the browser to determine the communication encryption (encrypted via HTTPS or non-encrypted via HTTP).
- A hostname is used by the DNS to resolve the hostname into an IP address, and by the web server to involve the right virtual host.
- A port is used by the operating system of the server to access the right process.
- A path is used by the web server to call the right resource and for the framework to activate the routing mechanism.
- Parameters are used by the application to control the behavior of the logic (server-side for query parameters and client-side for the anchor parameters). For example, the query parameters are defined after the
?
character, and the anchor parameters are defined after the#
character in the URL:https://127.0.0.1:8000/?queryparam=1#anchorparam
.
First, the protocol (typically HTTP or HTTPS) is defined. Next, the hostname, which is useful for figuring out which server to contact, is specified. Then, there is a part that is not normally specified, which is the port number; typically, it is port 80
for HTTP and 443
for HTTPS. Also present is the path that identifies the resource we are requesting from the server. Finally, two other optional parts deal with parameters. The first concerns server-side parameters (query string), and the second concerns client-side or JavaScript parameters (parameters with anchors).
In addition to the URL, another characteristic element of the request is the HTTP header, which is very important for the server reached by the request to better understand how to handle the request.
HTTP headers are automatically handled by the browser based on some information and browsing state. Typically, the headers specify the format and other information about the resource; for example, they specify the MIME type, user agent, and so on.
They also specify any access tokens in case the requested resource is protected. The elements to manage the state are also present in the headers as cookies and references for the session. This information is useful for the server to understand and relate consecutive requests.
Why is it so important to understand how a request is composed? Because in analyzing optimization elements regarding performance, the structure of the URL and the parts that make up the headers determine the behavior of different elements within the web architecture (browser, network, web server, server-side language, and framework).
For example, an element such as a hostname is useful to the DNS (network) to be able to resolve the hostname into the IP address. Knowing this is useful in deciding whether to do caching, for example, for name resolution.
Each element involved has its own characteristics that can be optimized to be able to get better performance.
One of the characterizing elements of a typical request to a classic PHP application is that each request is independent of any other request. This means that if your PHP script instantiates an object, this operation is repeated with each request. This has little impact if your script is called only a few times and your script is simple.
Let’s try to think of a scenario in which we have a framework-based application, with the application having to deal with a high load of concurrent requests.
A framework-based application has numerous objects at its disposal, which must be instantiated and configured at startup. In the classic case of PHP, the startup of the framework corresponds to a request.
Laravel Octane, on the other hand, introduces a new way of starting the application.
In a classic Laravel web application, it is sufficient to have a web server (such as nginx) or the internal web server provided by Laravel in the case of development on the developer’s local computer. A classic web server can handle requests without any kind of resource-sharing unless these resources are external resources such as a database or a cache manager.
In contrast to what happens with a classic web server, an application server has the task of starting and managing the executions of multiple workers. Each worker will be able to handle multiple requests by reusing objects and parts of the logic of your application.
This has one benefit, which is that the actual startup of your application and the setting up of the various objects occur on the first request received from the worker and not on each individual request.
HTTP requests and Laravel
From the Laravel application perspective, the parts involved directly in the HTTPS requests are typically routes and controllers.
Handling a request through a Laravel application typically means having to implement the routing part and implement the logic to manage the request in the controller. Routing allows us to define which code to execute within our Laravel application against a specific path in the URL. For example, we might want to define that the code of a method in a specific class such as HomeController::home()
must be invoked against a request that has a /home
path in the URL. In the classic Laravel definition, we would write something like this in the routes/web.php
file:
Route::get('/home', [HomeController::class, 'home'])->name("home");
Now we have to implement the logic to manage the request in the HomeController
class (that we have to create) and implement the home
method. So, in a new app/Http/Controllers/HomeController.php
file, you have to implement the HomeController
class extending the basic controller class:
<?php namespace App\Http\Controllers; class HomeController extends Controller { public function home(): string { return "this is the Home page"; } }
Now that you have an understanding of how web servers handle requests, let us learn more about the application servers that Laravel Octane integrates with.