Passa al contenuto principale

5. Instradamento dei messaggi

L'instradamento di un messaggio di richiesta HTTP è determinato da ciascun client in base alla risorsa di destinazione, alla configurazione proxy del client e all'instaurazione o al riutilizzo di una connessione in entrata. Il corrispondente instradamento della risposta segue la stessa catena di connessioni a ritroso fino al client.

5.1. Identificazione di una risorsa di destinazione​

HTTP è usato in un'ampia varietà di applicazioni, da computer di uso generale a elettrodomestici. In alcuni casi, le opzioni di comunicazione sono codificate in modo rigido nella configurazione di un client. Tuttavia, la maggior parte dei client HTTP si affida allo stesso meccanismo di identificazione delle risorse e alle stesse tecniche di configurazione dei browser web di uso generale.

La comunicazione HTTP è avviata da uno user agent per uno scopo. Lo scopo è una combinazione di semantica della richiesta, definita in [RFC7231], e di una risorsa di destinazione alla quale applicare quella semantica. Un riferimento URI (Sezione 2.7) è tipicamente usato come identificatore della "risorsa di destinazione", che uno user agent risolverebbe nella sua forma assoluta per ottenere il "target URI". Il target URI esclude il componente fragment del riferimento, se presente, poiché gli identificatori di fragment sono riservati all'elaborazione lato client ([RFC3986], Sezione 3.5).

5.2. Connessione in entrata​

Una volta determinato il target URI, un client deve decidere se sia necessaria una richiesta di rete per realizzare la semantica desiderata e, in tal caso, dove debba essere indirizzata tale richiesta.

Se il client dispone di una cache [RFC7234] e la richiesta può essere soddisfatta da essa, la richiesta viene di solito indirizzata lì per prima.

Se la richiesta non viene soddisfatta da una cache, un client tipico controllerà la propria configurazione per determinare se debba essere usato un proxy per soddisfare la richiesta. La configurazione del proxy dipende dall'implementazione, ma spesso si basa sulla corrispondenza del prefisso dell'URI, sulla corrispondenza selettiva dell'authority, oppure su entrambe, e il proxy stesso è di solito identificato da un URI "http" o "https". Se un proxy è applicabile, il client si connette in entrata stabilendo (o riutilizzando) una connessione a quel proxy.

Se nessun proxy è applicabile, un client tipico invocherà una routine handler, di solito specifica per lo schema del target URI, per connettersi direttamente a un'autorità per la risorsa di destinazione. Il modo in cui ciò viene realizzato dipende dallo schema del target URI ed è definito dalla specifica associata, in modo simile a come questa specifica definisce l'accesso all'origin server per la risoluzione degli schemi "http" (Sezione 2.7.1) e "https" (Sezione 2.7.2).

I requisiti HTTP relativi alla gestione delle connessioni sono definiti nella Sezione 6.

5.3. Request target​

Una volta ottenuta una connessione in entrata, il client invia un messaggio di richiesta HTTP (Sezione 3) con un request-target derivato dal target URI. Esistono quattro formati distinti per il request-target, a seconda sia del metodo richiesto sia del fatto che la richiesta sia diretta a un proxy.

request-target = origin-form
/ absolute-form
/ authority-form
/ asterisk-form

5.3.1. origin-form​

La forma più comune di request-target è la origin-form.

origin-form    = absolute-path [ "?" query ]

Quando si effettua una richiesta direttamente a un origin server, diversa da una richiesta CONNECT o da una richiesta OPTIONS a livello di server (come dettagliato di seguito), un client MUST inviare come request-target solo i componenti path assoluto e query del target URI. Se il componente path del target URI è vuoto, il client MUST inviare "/" come path all'interno della origin-form del request-target. Viene inviato anche un campo di intestazione Host, come definito nella Sezione 5.4.

Ad esempio, un client che desideri recuperare una rappresentazione della risorsa identificata come

http://www.example.org/where?q=now

direttamente dall'origin server aprirebbe (o riutilizzerebbe) una connessione TCP alla porta 80 dell'host "www.example.org" e invierebbe le righe:

GET /where?q=now HTTP/1.1
Host: www.example.org

seguite dal resto del messaggio di richiesta.

5.3.2. absolute-form​

Quando si effettua una richiesta a un proxy, diversa da una richiesta CONNECT o da una richiesta OPTIONS a livello di server (come dettagliato di seguito), un client MUST inviare il target URI in absolute-form come request-target.

absolute-form  = absolute-URI

Al proxy viene richiesto di soddisfare quella richiesta da una cache valida, se possibile, oppure di effettuare la stessa richiesta per conto del client al successivo server proxy in entrata o direttamente all'origin server indicato dal request-target. I requisiti su tale "inoltro" dei messaggi sono definiti nella Sezione 5.7.

Un esempio di request-line in absolute-form sarebbe:

GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1

Per consentire la transizione alla absolute-form per tutte le richieste in una qualche versione futura di HTTP, un server MUST accettare la absolute-form nelle richieste, anche se i client HTTP/1.1 la invieranno solo nelle richieste ai proxy.

5.3.3. authority-form​

La authority-form del request-target è usata solo per le richieste CONNECT (Sezione 4.3.6 di [RFC7231]).

authority-form = authority

Quando si effettua una richiesta CONNECT per stabilire un tunnel attraverso uno o più proxy, un client MUST inviare come request-target solo il componente authority del target URI (escludendo qualsiasi userinfo e il suo delimitatore "@"). Ad esempio,

CONNECT www.example.com:80 HTTP/1.1

5.3.4. asterisk-form​

La asterisk-form del request-target è usata solo per una richiesta OPTIONS a livello di server (Sezione 4.3.7 di [RFC7231]).

asterisk-form  = "*"

Quando un client desidera richiedere OPTIONS per il server nel suo complesso, anziché per una specifica risorsa denominata di quel server, il client MUST inviare solo "*" (%x2A) come request-target. Ad esempio,

OPTIONS * HTTP/1.1

Se un proxy riceve una richiesta OPTIONS con un request-target in absolute-form in cui l'URI ha un path vuoto e nessun componente query, allora l'ultimo proxy sulla catena di richieste MUST inviare un request-target "*" quando inoltra la richiesta all'origin server indicato.

Ad esempio, la richiesta

OPTIONS http://www.example.org:8001 HTTP/1.1

sarebbe inoltrata dal proxy finale come

OPTIONS * HTTP/1.1
Host: www.example.org:8001

dopo essersi connesso alla porta 8001 dell'host "www.example.org".

5.4. Host​

Il campo di intestazione "Host" in una richiesta fornisce le informazioni di host e porta ricavate dal target URI, consentendo all'origin server di distinguere tra le risorse mentre soddisfa richieste per più nomi di host su un singolo indirizzo IP.

Host = uri-host [ ":" port ] ; Section 2.7.1

Un client MUST inviare un campo di intestazione Host in tutti i messaggi di richiesta HTTP/1.1. Se il target URI include un componente authority, allora un client MUST inviare per Host un field-value identico a quel componente authority, escludendo qualsiasi sottocomponente userinfo e il suo delimitatore "@" (Sezione 2.7.1). Se il componente authority è mancante o indefinito per il target URI, allora un client MUST inviare un campo di intestazione Host con un field-value vuoto.

Poiché il field-value di Host è un'informazione critica per la gestione di una richiesta, uno user agent SHOULD generare Host come primo campo di intestazione dopo la request-line.

Ad esempio, una richiesta GET all'origin server per http://www.example.org/pub/WWW/ inizierebbe con:

GET /pub/WWW/ HTTP/1.1
Host: www.example.org

Un client MUST inviare un campo di intestazione Host in una richiesta HTTP/1.1 anche se il request-target è in absolute-form, poiché ciò consente di inoltrare le informazioni di Host attraverso antichi proxy HTTP/1.0 che potrebbero non aver implementato Host.

Quando un proxy riceve una richiesta con un request-target in absolute-form, il proxy MUST ignorare il campo di intestazione Host ricevuto (se presente) e sostituirlo invece con le informazioni di host del request-target. Un proxy che inoltra tale richiesta MUST generare un nuovo field-value di Host basato sul request-target ricevuto, anziché inoltrare il field-value di Host ricevuto.

Poiché il campo di intestazione Host funge da meccanismo di instradamento a livello di applicazione, è un bersaglio frequente per malware che cerca di avvelenare una cache condivisa o di reindirizzare una richiesta a un server non previsto. Un interception proxy è particolarmente vulnerabile se si basa sul field-value di Host per reindirizzare le richieste a server interni, o per usarlo come chiave di cache in una cache condivisa, senza prima verificare che la connessione intercettata sia diretta a un indirizzo IP valido per quell'host.

Un server MUST rispondere con un codice di stato 400 (Bad Request) a qualsiasi messaggio di richiesta HTTP/1.1 privo di un campo di intestazione Host e a qualsiasi messaggio di richiesta che contenga più di un campo di intestazione Host oppure un campo di intestazione Host con un field-value non valido.

5.5. Effective request URI​

Poiché il request-target contiene spesso solo una parte del target URI dello user agent, un server ricostruisce la destinazione prevista come "effective request URI" per soddisfare correttamente la richiesta. Questa ricostruzione coinvolge sia la configurazione locale del server sia le informazioni comunicate nel request-target, nel campo di intestazione Host e nel contesto della connessione.

Per uno user agent, l'effective request URI è il target URI.

Se il request-target è in absolute-form, l'effective request URI è identico al request-target. In caso contrario, l'effective request URI è costruito come segue:

Se la configurazione del server (o un gateway in uscita) fornisce uno schema URI fisso, quello schema viene usato per l'effective request URI. In caso contrario, se la richiesta è ricevuta su una connessione TCP protetta con TLS, lo schema dell'effective request URI è "https"; in caso contrario, lo schema è "http".

Se la configurazione del server (o un gateway in uscita) fornisce un componente authority URI fisso, quell'authority viene usato per l'effective request URI. In caso contrario, se il request-target è in authority-form, il componente authority dell'effective request URI è identico al request-target. Se non è così, allora se viene fornito un campo di intestazione Host con un field-value non vuoto, il componente authority è identico al field-value di Host. In caso contrario, al componente authority viene assegnato il nome predefinito configurato per il server e, se il numero di porta TCP in ingresso della connessione differisce dalla porta predefinita per lo schema dell'effective request URI, allora al componente authority vengono aggiunti i due punti (":") e il numero di porta in ingresso (in forma decimale).

Se il request-target è in authority-form o asterisk-form, il componente combinato path e query dell'effective request URI è vuoto. In caso contrario, il componente combinato path e query è identico al request-target.

I componenti dell'effective request URI, una volta determinati come sopra, possono essere combinati nella forma absolute-URI concatenando lo schema, "://", l'authority e il componente combinato path e query.

Esempio 1: il seguente messaggio ricevuto su una connessione TCP non protetta

GET /pub/WWW/TheProject.html HTTP/1.1
Host: www.example.org:8080

ha come effective request URI

http://www.example.org:8080/pub/WWW/TheProject.html

Esempio 2: il seguente messaggio ricevuto su una connessione TCP protetta con TLS

OPTIONS * HTTP/1.1
Host: www.example.org

ha come effective request URI

https://www.example.org

I destinatari di una richiesta HTTP/1.0 priva di un campo di intestazione Host potrebbero dover usare euristiche (ad es. l'esame del path dell'URI per qualcosa di univoco per un particolare host) al fine di indovinare il componente authority dell'effective request URI.

Una volta costruito l'effective request URI, un origin server deve decidere se fornire o meno il servizio per quell'URI tramite la connessione in cui la richiesta è stata ricevuta. Ad esempio, la richiesta potrebbe essere stata indirizzata male, deliberatamente o accidentalmente, cosicché le informazioni all'interno di un request-target o di un campo di intestazione Host ricevuti differiscono dall'host o dalla porta su cui è stata effettuata la connessione. Se la connessione proviene da un gateway attendibile, tale incoerenza potrebbe essere prevista; in caso contrario, potrebbe indicare un tentativo di aggirare i filtri di sicurezza, di indurre il server a fornire contenuti non pubblici, oppure di avvelenare una cache. Vedere la Sezione 9 per le considerazioni di sicurezza relative all'instradamento dei messaggi.

5.6. Associazione di una risposta a una richiesta​

HTTP non include un identificatore di richiesta per associare un dato messaggio di richiesta ai suoi corrispondenti uno o più messaggi di risposta. Pertanto, si affida all'ordine di arrivo delle risposte affinché corrisponda esattamente all'ordine in cui le richieste vengono effettuate sulla stessa connessione. Più di un messaggio di risposta per richiesta si verifica solo quando una o più risposte informative (1xx, vedere la Sezione 6.2 di [RFC7231]) precedono una risposta finale alla stessa richiesta.

Un client che ha più di una richiesta in sospeso su una connessione MUST mantenere un elenco delle richieste in sospeso nell'ordine di invio e MUST associare ciascun messaggio di risposta ricevuto su quella connessione alla richiesta di ordine più alto che non ha ancora ricevuto una risposta finale (non 1xx).

5.7. Inoltro dei messaggi​

Come descritto nella Sezione 2.3, gli intermediari possono svolgere una varietà di ruoli nell'elaborazione delle richieste e delle risposte HTTP. Alcuni intermediari sono usati per migliorare le prestazioni o la disponibilità. Altri sono usati per il controllo degli accessi o per filtrare i contenuti. Poiché un flusso HTTP ha caratteristiche simili a un'architettura pipe-and-filter, non esistono limiti intrinseci alla misura in cui un intermediario può migliorare (o interferire con) l'una o l'altra direzione del flusso.

Un intermediario che non agisce come tunnel MUST implementare il campo di intestazione Connection, come specificato nella Sezione 6.1, ed escludere dall'inoltro i campi destinati solo alla connessione in entrata.

Un intermediario MUST NOT inoltrare un messaggio a se stesso, a meno che non sia protetto da un ciclo di richieste infinito. In generale, un intermediario dovrebbe riconoscere i propri nomi di server, incluse eventuali alias, varianti locali o indirizzi IP letterali, e rispondere direttamente a tali richieste.

5.7.1. Via​

Il campo di intestazione "Via" indica la presenza di protocolli e destinatari intermedi tra lo user agent e il server (sulle richieste) o tra l'origin server e il client (sulle risposte), in modo simile al campo di intestazione "Received" nella posta elettronica (Sezione 3.6.7 di [RFC5322]). Via può essere usato per tracciare gli inoltri dei messaggi, per evitare cicli di richieste e per identificare le capacità di protocollo dei mittenti lungo la catena richiesta/risposta.

Via = 1#( received-protocol RWS received-by [ RWS comment ] )

received-protocol = [ protocol-name "/" ] protocol-version
; see Section 6.7
received-by = ( uri-host [ ":" port ] ) / pseudonym
pseudonym = token

Più valori di campo Via rappresentano ciascun proxy o gateway che ha inoltrato il messaggio. Ciascun intermediario aggiunge le proprie informazioni su come è stato ricevuto il messaggio, cosicché il risultato finale è ordinato secondo la sequenza dei destinatari di inoltro.

Un proxy MUST inviare un campo di intestazione Via appropriato, come descritto di seguito, in ciascun messaggio che inoltra. Un gateway da HTTP a HTTP MUST inviare un campo di intestazione Via appropriato in ciascun messaggio di richiesta in entrata e MAY inviare un campo di intestazione Via nei messaggi di risposta inoltrati.

Per ciascun intermediario, il received-protocol indica il protocollo e la versione del protocollo usati dal mittente a monte del messaggio. Quindi, il valore del campo Via registra le capacità di protocollo dichiarate dalla catena richiesta/risposta, in modo che rimangano visibili ai destinatari a valle; ciò può essere utile per determinare quali funzionalità retroincompatibili potrebbero essere sicure da usare in risposta, o in una richiesta successiva, come descritto nella Sezione 2.6. Per brevità, il protocol-name viene omesso quando il protocollo ricevuto è HTTP.

La porzione received-by del valore del campo è normalmente l'host e il numero di porta opzionale di un server o client destinatario che ha successivamente inoltrato il messaggio. Tuttavia, se l'host reale è considerato un'informazione sensibile, un mittente MAY sostituirlo con uno pseudonimo. Se non viene fornita una porta, un destinatario MAY interpretarlo come se significasse che il messaggio è stato ricevuto sulla porta TCP predefinita, se esiste, per il received-protocol.

Un mittente MAY generare commenti nel campo di intestazione Via per identificare il software di ciascun destinatario, in modo analogo ai campi di intestazione User-Agent e Server. Tuttavia, tutti i commenti nel campo Via sono opzionali, e un destinatario MAY rimuoverli prima di inoltrare il messaggio.

Ad esempio, un messaggio di richiesta potrebbe essere inviato da uno user agent HTTP/1.0 a un proxy interno con nome in codice "fred", che usa HTTP/1.1 per inoltrare la richiesta a un proxy pubblico su p.example.net, che completa la richiesta inoltrandola all'origin server su www.example.com. La richiesta ricevuta da www.example.com avrebbe quindi il seguente campo di intestazione Via:

Via: 1.0 fred, 1.1 p.example.net

Un intermediario usato come portale attraverso un firewall di rete SHOULD NOT inoltrare i nomi e le porte degli host all'interno della regione del firewall, a meno che non sia esplicitamente abilitato a farlo. Se non è abilitato, tale intermediario SHOULD sostituire ogni host received-by di qualsiasi host dietro il firewall con uno pseudonimo appropriato per quell'host.

Un intermediario MAY combinare una sottosequenza ordinata di voci del campo di intestazione Via in un'unica voce di questo tipo, se le voci hanno valori received-protocol identici. Ad esempio,

Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy

potrebbe essere ridotto a

Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

Un mittente SHOULD NOT combinare più voci, a meno che non siano tutte sotto lo stesso controllo organizzativo e gli host siano già stati sostituiti da pseudonimi. Un mittente MUST NOT combinare voci che hanno valori received-protocol diversi.

5.7.2. Trasformazioni​

Alcuni intermediari includono funzionalità per trasformare i messaggi e i loro payload. Un proxy potrebbe, ad esempio, convertire tra formati di immagine al fine di risparmiare spazio nella cache o ridurre la quantità di traffico su un collegamento lento. Tuttavia, possono verificarsi problemi operativi quando queste trasformazioni vengono applicate a payload destinati ad applicazioni critiche, come imaging medico o analisi di dati scientifici, in particolare quando si usano controlli di integrità o firme digitali per garantire che il payload ricevuto sia identico all'originale.

Un proxy da HTTP a HTTP è detto "transforming proxy" se è progettato o configurato per modificare i messaggi in modo semanticamente significativo (cioè modifiche, oltre a quelle richieste dalla normale elaborazione HTTP, che cambiano il messaggio in un modo che sarebbe significativo per il mittente originale o potenzialmente significativo per i destinatari a valle). Ad esempio, un transforming proxy potrebbe agire come server di annotazioni condiviso (modificando le risposte per includere riferimenti a un database di annotazioni locale), come filtro antimalware, come transcoder di formato, oppure come filtro per la privacy. Si presume che tali trasformazioni siano desiderate dal client (o dall'organizzazione del client) che ha selezionato il proxy.

Se un proxy riceve un request-target con un nome host che non è un nome di dominio completamente qualificato, MAY aggiungere il proprio dominio al nome host ricevuto quando inoltra la richiesta. Un proxy MUST NOT cambiare il nome host se il request-target contiene un nome di dominio completamente qualificato.

Un proxy MUST NOT modificare le parti "absolute-path" e "query" del request-target ricevuto quando lo inoltra al successivo server in entrata, salvo quanto sopra indicato per sostituire un path vuoto con "/" o "*".

Un proxy MAY modificare il corpo del messaggio tramite l'applicazione o la rimozione di una transfer coding (Sezione 4).

Un proxy MUST NOT trasformare il payload (Sezione 3.3 di [RFC7231]) di un messaggio che contiene una direttiva cache-control no-transform (Sezione 5.2 di [RFC7234]).

Un proxy MAY trasformare il payload di un messaggio che non contiene una direttiva cache-control no-transform. Un proxy che trasforma un payload MUST aggiungere un campo di intestazione Warning con il warn-code 214 ("Transformation Applied") se tale campo non è già presente nel messaggio (vedere la Sezione 5.5 di [RFC7234]). Un proxy che trasforma il payload di una risposta 200 (OK) può inoltre informare i destinatari a valle che è stata applicata una trasformazione cambiando il codice di stato della risposta in 203 (Non-Authoritative Information) (Sezione 6.3.4 di [RFC7231]).

Un proxy SHOULD NOT modificare i campi di intestazione che forniscono informazioni sugli endpoint della catena di comunicazione, sullo stato della risorsa o sulla rappresentazione selezionata (diversa dal payload), a meno che la definizione del campo non consenta specificamente tale modifica o la modifica non sia ritenuta necessaria per la privacy o la sicurezza.