El RFC que define el protocolo en su versión 1.1 es el 2616 [1]. Los mensajes se pueden clasificar en dos tipos:
Sintaxis:
<linea de request>
<headers>
CRLF
<body>
<linea de request>
: se compone de tres partes:<metodo> <URI> <version de HTTP>
:<metodo>
debe ser alguno de los siguientes :- OPTIONS
- GET
- HEAD
- POST
- PUT
- TRACE
- CONNECT
<version de HTTP>
: puede ser por ejemploHTTP/1.1
oHTTP/1.0
.
<headers>
: es una lista en donde cada línea (header) se forma con un par<clave>: <valor>
y se finaliza con CRLF. Alguno de los headers mas comunes son : Host, Accept y Connection.<body>
: es el cuerpo del mensaje (por ejemplo los datos enviados al completar un formulario en una página web).
Cada linea se separa con los caracteres CRLF, o sea la concatenación del caracter de carriage-return y end-of-line (ver https://tools.ietf.org/html/rfc2616#page-35).
- Establecer una connección TCP con
telnet
a home.mcom.com: home.mcom.com:telnet home.mcom.com 80
. - Pegar el siguiente mensaje en la consola:
GET / HTTP/1.1
Host: home.mcom.com
Sintaxis:
<linea de status>
<headers>
CRLF
<body>
El status se representa por un valor numérico de tres dígitos. Se pueden clasificar por su primer dígito en alguna de las siguientes categorías:
- 1xx : se utiliza con motivo de información, por ejemplo indicando que la request fue recibida o que continua siendo procesada.
- 2xx : se utiliza cuando la request fue correctamente recibida, entendida y aceptada.
- 3xx : se utiliza para indicar algún tipo de redirección por la cual alguna acción tiene que ser tomada posteriormente.
- 4xx : se utiliza para indicar que la request contiene algun error de sintaxis o le faltan datos.
- 5xx : se utliza para indicar que el servidor falló siendo aparentemente la request válida.
HTTP/1.1 200 OK
Date: Sun, 02 Dec 2018 16:07:22 GMT
Server: Apache
Last-Modified: Fri, 21 Oct 1994 19:00:00 GMT
ETag: "2f80859-f1-2c7ebe95f4c00"
Accept-Ranges: bytes
Content-Length: 241
Cache-Control: max-age=31536000
Expires: Mon, 02 Dec 2019 16:07:22 GMT
Content-Type: text/html
<TITLE>Welcome to Mosaic Communications Corporation!</TITLE>
<CENTER>
<A HREF="MCOM/index2.html"><IMG SRC="MCOM/images/mcomwelcome1.gif" BORDER=1></A>
<H3>
<A HREF="MCOM/index2.html">Click on the Image or here to advance</A>
</H3>
</CENTER>
Normalmente los servidores asumen que la conección TCP debe quedar abierta [2]:
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection. That is, unless otherwise indicated, the client SHOULD assume that the server will maintain a persistent connection, even after error responses from the server.
Esto se puede ver en el ejemplo anterior. Para hacer que la conección sea persistente se debe agregar el header Connection: keep-alive
y para que sea no-persistente Connection: close
[3].
GET /home/welcome.html HTTP/1.1
Host: home.mcom.com
Connection: close
A veces las requests mal formadas (en este caso le falta el header Host: tools.ietf.org
) provocan que la conección se cierre incluso teniendo el header Connection: keep-alive
.
GET /html/rfc2616 HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: es,en-US;q=0.9,en;q=0.8
Connection: keep-alive
HTTP/1.0 400 Bad request
Cache-Control: no-cache
Connection: close
Content-Type: text/html
<html><body><h1>400 Bad request</h1>
Your browser sent an invalid request.
</body></html>
Notar que el servidor devuelve close
en el header Connection
de la respuesta.
Con el comando nc
(Linux/MacOS) es posible abrir un puerto TCP de escucha para recibir y responder mensajes HTTP.
En MacOS :
nc -l localhost 80
- Los parámetros para
nc
en Linux no son los mismos- Puede ser necesario invocarlo con
sudo
Luego, abrir cualquier navegador en la dirección localhost y volver a la consola para ver la request recibida.
- Ejecutar
nc
:nc -l localhost 80
- Ir a http://localhost en cualquier navegador.
- Pegar el siguiente mensaje de respuesta en la terminal donde se invocó
nc
:
HTTP/1.1 200 OK
Content-Length: 51
<html>
<body>
<h1>Hola Mundo!</h1>
</body>
</html>
- Volver al navegador.
Aclaraciones:
- Luego de la línea con el último header y antes de comenzar el cuerpo de la response debe haber un CRLF.
- La respuesta finaliza con un CRLF.
- Al ser la conección persistente por defecto, se debe cerrar mediante Ctrl+C en la consola donde fue invocado
nc
- El header
Content-Length
indica el tamaño en bytes del cuerpo de la response. Cuando no se define, el navegador mantendrá la conección abierta a la espera de recibir mas datos.
Para recibir request encriptadas mediante SSL se puede seguir un procedimiento similar utilizando el puerto 443 como puerto TCP de escucha :
nc -l localhost 443
Luego, en el navegador, en lugar de ingresar simplemente a localhost, se debe ingresar mediante HTTPS a la URL https://localhost
Para hacer un redireccionamiento permanente se deben cumplir dos reglas en la respuesta :
- Devolver el status 301 (Moved Permanently)
- Colocar el header
Location
para indicar la URL a seguir
Esto se detalla en la sección 10.3.2 del RFC.
- Ejecutar
nc
:nc -l localhost 80
- Ir a http://localhost en cualquier navegador.
- Pegar el siguiente mensaje de respuesta en la terminal donde se invocó
nc
:
HTTP/1.1 301 Moved Permanently
Location: https://www.google.com
- Volver al navegador
Aclaraciones:
- La respuesta finaliza con un CRLF.
- Los navegadores normalmente cachearan la request por lo que es recomendable ingresar en modo incógnito.
El objetivo del caching en HTTP/1.1, es eliminar la necesidad de enviar request en muchos casos y eliminar la necesidad de enviar respuestas completas en muchos otros. Para lo primero se propone un mecanismo de expiración y para lo segundo uno de validación. La idea es que los servidores HTTP respondan con la versión mas reciente y apropiada que se mantiene en el cache [4].
Generalmente los navegadores proveen mecanismos de caching pero también se pueden implementar en un servidor de proxy. Para hacer que un navegador o un servidor de caching guarde un recurso, se debe agregar el header Cache-Control
o Expires
[4][5].
- Ejecutar
nc
:nc -l localhost 80
- Ir a http://localhost en cualquier navegador.
- Pegar el siguiente mensaje de respuesta en la terminal donde se invocó
nc
:
HTTP/1.1 200 OK
Content-Length: 77
Cache-Control: max-age=10
Content-Type: text/html; charset=UTF-8
<html>
<body>
<a href="http://localhost">Recargar página</a>
</body>
</html>
- Ir al enlace "Recargar página"
Aclaraciones:
- Puede que en Google Chrome, al recargar la página con F5 o algún atajo de teclado, se genere otra request ignorando el header
Cache-Control
. Para eso, se agregó un anchor en el cuerpo HTML de la respuesta con un enlace hacia http://localhost.- El header
Content-Type: text/html; charset=UTF-8
permite utilizar tildes y otros caracteres incluidos en el charset UTF-8
[1]: RFC 2116, https://tools.ietf.org/html/rfc2616#section-13
[2]: Funcionamiento general de conecciones persistentes, https://tools.ietf.org/html/rfc2616#section-8.1.2
[3]: Negociación de conecciones persistentes, https://tools.ietf.org/html/rfc2616#section-8.1.2.1
[4]: Caching, https://tools.ietf.org/html/rfc2616#section-13
[5]: Caching, https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching