L’HyperText Transfer Protocol è un protocollo di livello applicazione er lo scambio di ipermedia.
Segue l’architettura client/server, dove il client (browser) chiede un documento e il server HTTP risponde con la risorsa.
È un protocollo stateless, dove il server non tiene traccia delle precedenti connessioni, e ogni scambio di richiesta/risposta è indipendente dai precedenti.
Nella prima versione 1.0 (1996) le comunicazioni erano non presistenti, ma già un anno dopo HTTP 1.1 prevedeva comunicazioni persistenti per le risorse,
Con l’aumento dei dispositivi monbili e dell’utilizzo di contenuti multimediali e della richiesta di esperienze web più veloci ed efficienti, si è implementata una nuova versione del protocollo HTTP 2 che ha introdotto una serie di nuove tecnologie, come Server Push, Multiplexing, Priorità del flusso, Compressione header, Protocollo binario, …
Nel 2022 l’evoluzione in HTTP 3 ha mirato a migliorare l’efficienza, passando da una semplice richiesta GET ad un protocollo QUIC, un protocollo avanzato basato su UDP con multiplexing e crittografia integrata. QUIC riduce la latenza e aumenta la sicurezza.
Vediamo degli esempi di messaggi HTTP:
Richiesta
:method: GET
:scheme: https
:authority: www.example.com
:path: /index.html
user-agent: curl/7.79.1
accept: */*
Risposta
HTTP/3 200 OK
Date: Wed, 26 Jan 2024 12:34:56 GMT
Server: Apache/2.4.46 (Unix)
Last-Modified: Mon, 10 Jan 2024 10:15:20 GMT
Content-Length: 438
Content-Type: text/html; charset=UTF-8
<qui ci sono i dati>
Apache è un server che permette di hostare server web, implementato tramite apache2.
sudo apt update
sudo apt upgrade
sudo apt install apache2
Dopo l’installazione dovrebbe avviarsi automaticamente, ma è comunque possibile verificare tramite:
systemctl status apache2
È possibile verificare anche http://localhost in un qualsiasi browser sulla stessa macchina.
In questo caso verrà mostrata la pagina che si trova in /var/www/html/index.html.
Per interagire con un server APCHE è possibile:
sudo apache2ctl <comando> # start, stop, restart, status, configtest
# opuure #
sudo service apache2 <comando> # start, stop, restart, reload
La directory principale si trova in /etc/apache2.
Al suo interno il file di configurazione principale è /etc/apache2/apache2.conf.
La configurazione si specifica attraverso delle direttive, eventualmente raggruppate da direttive contenutore:
# Commento descrittivo
Direttiva1 valore
Direttiva2 valore
<Contenutore valore>
Direttiva3 valore
Direttiva4 valore
</Contenutore> # Fine contenutore
In generale si trovano altre configurazioni aggiuntive in:
/etc/apache2/conf-available: contiene pezzi di configurazione non attive/etc/apache2/conf-enabled: contiene link a configurazioni aggiuntive che sono attive/etc/apache2/mods-available: contiene link a moduli non attivi, ovvero configurazioni complesse che specificano come il server deve gestire le richieste da più client/etc/apache2/mods-enabled: contiene link a moduli attivi, configurazioni complesse che specificano come il server deve gestire le richieste da più client/etc/apache2/ports.conf: descrive quali porte sono disponibili/etc/apache2/sites-available: contiene link a file di configurazioni specifici a siti non però abilitati/etc/apache2/sites-enabled: contiene link a file di configurazioni specifici a siti abilitatiIl sistema modulare funziona così:
# File apache2.conf
...
# Includo la lista di porta sulle quali stare in ascolto
Include ports.conf
# ----------------------- #
# File ports.conf
Listen 80
Per (dis)abilitare un file si utilizza il comando:
a2enconf <nome_file> # abilito
a2disconf <nome_file> # disabilito
Questo comando crea un soft link nella directory /etc/apache2/conf-enabled, file he viene incluso di default all’interno di apache2.conf.
Dopo una qualsiasi modifica è necessario riavviare il server per ricaricare la configurazione.
Per i moduli si (dis)abilitano:
a2enmod <nome_file> # abilito
a2dismod <nome_file> # disabilito
Se due moduli adassero in conflitto, il server ritorna un messaggio di errore durante il caricamento della nuova configurazione.
Sono configurazioni generali che vengono applicate al server Apache nel suo complesso, indipendentemente dai siti web o dai virtual host ospitati.
Sono impostate nel file apache2.conf e possono riguardare:
A meno che non vengano sovrascritte a livello di virtual host o dorectory, vengono applicate a tutte le richieste e a tutte le risorse servite dal server.
La prima direttiva che vediamo è la server root. Di default la root del server è:
ServerRoot /etc/apache2
Questa specifica la directory principale contenente i file di configurazione di Apache, e rappresenta la base sulla quale sono risolti tutti i path relativi.
Questa direttiva è configurata automaticamente all’avvio di apache2ctl, infatti nel file apache2.conf è commentata.
Altre direttive sono:
KeepAlive on # se offrire o meno le connessioni persistenti HTTP 1.1
KeepAliveTimeout 5 # quanti secondi attendere la richiesta successiva dal client prima di chiudere la connessione
MaxKeepAliveRequest 100 # qauante richieste può fare al massimo un client sulla stessa connessione prima di chiuderla
Queste direttive sono ad oggi deprecate, ma comunque presenti nel file quindi è bene sapere cosa fanno.
In particolare un valore troppo alto per la KeepAliveTimeout potrebbe bloccare inutilmente un processo che sta servendo un client lento o disconnesso.
Un altra direttiva importante è la seguente:
Listen 80
Listen 8080
Questa direttiva obbligatoria, presente in /etc/apache2/ports.conf che viene incluso in apache2.conf, permette di specificare le porte che userà Apache per mettersi in ascolto di connessioni.
Per recuperare gli errori è possibile utilizzare un file di log, specificandolo:
ErrorLog /var/log/apache2/error.log # directory di default
Il formato e la verbosità dei messaggi possono essere definiti con le altre direttive ErrorLogFormat e LogLevel.
Nel caso più semplice, un server web è in esecuzione su una macchina con un unico indirizzo IP offrendo un solo sito Web.
In questo caso, per avere $n$ siti web saranno necessarie $n$ macchine, utilizzando quindi tantissime risorse.
COn il name-based Virtual Hosting si possono configurare più siti sullo stesso server Web, sulla stessa macchina con lo stesso indirizzo IP.
Il server Web discrimina le richieste dei client in base al campo Host della richiesta HTTP:
GET /index.html HTTP/1.1
Host: www.mysite1.com
...
I virtual host si mettono in /etc/apache2/sites-available:
a2ensite <nome_file> # abilita un sito al riavvio del server
a2dissite <nome_file> # disabilita un sito al riavvio del server
Per definire un virtual host si utilizza la seguente sintassi:
<VirtualHost ip:porta>
...
</VirtualHost>
Nel caso più semlice Apache ha un Virtual Host abilitato di default in /etc/apache2/sites-available/000-default.conf:
<VirtualHost *:80>
#ServerName www.example.com Nome simbolico del sito
ServerAdmin webmaster@localhost
...
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log # uguale alla direttiva globale
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Questa configurazione risponde a tutti gli IP sulla porta 80.
Il modulo /etc/apache2/mods-availaable/dir.conf, abilitato didefualt, permette di specificare attraverso la direttiva DirectoryIndex il file da prelevare in caso non venga specificato dal client.
DirectoryIndex index.html index.cgi index.pl index.php ...
In questo modo quando l’utente digita www.mysite.com riceverà il file www.mysite.com/index.html
Il modulo /etc/apache2/mods-availaable/userdir.conf, NON abilitato didefualt, permette invece ai singoli utenti di mettere i propri file nella cartella speciicfata e renderli accessibili tramite l’indirizzo www.mysite.com/~username/.
UserDir public_html
Affinché il server possa accettare e servire più richieste contemporaneamente Apache ricorre ad un Multi-Processing Module MPM, responsabile di gestire i socket, fare binfig delle porte, accettare connessioni e servirle, eventualmente usande processi e thread figli.
Su UNIX si può scegliere tre:
Implementa un server multiprocesso senza thread. All’avvio, un processo padre lancia un certo numero di processi figli (preforking) che restano in ascolto accettando e servendo connessioni. Dopo aver servito una connessione tornano disponibili per una nuova connessione.
Il padre gestisce il pool dei figli, cercando di mantenere sempre alcuni figli disponibili.
Il preforking permette di diminuire l’overhead della fork() ad ogni connessione.
ogni figlio viene riciclato per MaxConnectionsPerChild connessioni, e poi viene ucciso per evitare memory leak accidentali.
Il server genera all’avvio StartServer n figli. Il numero massimo di figli creabili dalle connessioni aggiuntive è MaxRequestWorkers.
I figli inattivi devono essere compresi nell’intervallo [MinSpareServers; MaxSpareServers].
Tutte queste variabili si trovano in /etc/apache2/mods-available/prefork.conf
Questo approccio ha come vantaggio:
Ha però anche degli svantaggi:
Permette di avere un server-multiprocesso e multi-thread riducendo l’overhead.
Il processo padre genera un certo numero di figli, e ogni figlio genera:
Anche in questo caso il server genera StartServer n figli, che verranno riciclati per MaxConnectionsPerChild connessioni.
I figli generano all’inizio ThreadsPerChild, e il numero massimo di thread totali è MaxRequestWorkers, e anche in questo caso i thread devono essere compresi tra [MinSpareThreads, MaxSpareThreads],,
Il numero massimo di figli è necessariamente MaxRequestWorkers/ThreadsPerChild.
È una versione migliorata di MPM worker, ed è il modulo di defautl dalla versione 2.4.
Oltre ad accettare le connessioni, il thread listener gestisce le connessioni temporaneamente inattive. Ad esempio:
In questo modo aumentiamo il numero di connessioni servibili contemporaneamente a parità di numero di thread, eliminando i tempi morti.
Alcuni limiti globali sono:
ThreadLimit: limite massimo configurabile per il numero di thread attivi per processo.
Worker(thread): ThreadsPerChild <= ThreadLimitServerLimit: limite massimo configurabile per il numero di processi figli attivi.
Prefork: MaxRequestWorkers <= ServerLimitWorker(thread): MaxRequestWorkers <= ServerLimit * ThreadsPerChild