Capa de aplicación — HTTP, DNS, TLS
"Si entendés HTTP y DNS, entendés cómo funciona el 90% de Internet."
Qué vas a aprender en este capítulo
La capa de aplicación es la que tu programa habla. Vas a aprender HTTP (protocolo de la web), DNS (cómo se traducen nombres a IPs), y TLS (la magia detrás del candadito en el navegador). Vas a construir un mini servidor HTTP desde cero — el proyecto-hilo del libro — y al cerrar, vas a entender qué pasa, byte por byte, cuando hacés una petición a un sitio web.
5.1 La idea: protocolos en texto
💡 Intuición
Los protocolos de aplicación clásicos (HTTP, SMTP, FTP) son texto plano legible. Eso era radical en los 90s — antes los protocolos eran binarios y opacos.
Cuando entendés HTTP, podés escribir un cliente HTTP en cualquier lenguaje, en menos de 10 líneas, sin biblioteca. Solo abriendo un socket TCP y mandando texto.
Cliente envía:
GET / HTTP/1.1
Host: example.com
Servidor responde:
HTTP/1.1 200 OK
Content-Type: text/html
<html>...</html>
Eso es una página web. Es así de simple, en su esencia.
Conociendo este protocolo, podés:
- Diagnosticar problemas (curl, telnet).
- Implementar APIs REST.
- Optimizar rendimiento (caching, keep-alive).
- Hackear (consensuadamente — en CTFs).
DNS y TLS son más complejos pero igualmente fundamentales.
5.2 HTTP — anatomía
📐 Fundamento
HTTP (HyperText Transfer Protocol) es stateless (sin estado por defecto), texto, request-response.
Request
GET /productos/queso HTTP/1.1
Host: pupuseria.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: session=abc123
Estructura:
- Línea de request:
MÉTODO RUTA VERSIÓN. - Headers: clave-valor, uno por línea.
- Línea en blanco (separador).
- Cuerpo opcional (POST, PUT con body).
Response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Set-Cookie: nuevo=xyz
Server: nginx/1.21
<html>...</html>
Estructura:
- Línea de status:
VERSIÓN CÓDIGO MENSAJE. - Headers.
- Línea en blanco.
- Cuerpo opcional.
Métodos HTTP
| Método | Para qué | Idempotente | Body |
|---|---|---|---|
| GET | Obtener recurso | Sí | No |
| POST | Crear / acción | No | Sí |
| PUT | Actualizar (reemplazar) | Sí | Sí |
| PATCH | Actualizar (parcial) | No siempre | Sí |
| DELETE | Eliminar | Sí | No |
| HEAD | Como GET pero sin body | Sí | No |
| OPTIONS | Capacidades del servidor | Sí | No |
Idempotente = ejecutar varias veces da el mismo resultado. Importante para reintentos.
Códigos de estado
1xx Informativos (raros en HTTP/1.1).
2xx Éxito:
200 OK— todo bien.201 Created— recurso creado (típico tras POST).204 No Content— éxito sin contenido.
3xx Redirecciones:
301 Moved Permanently— el recurso se movió, actualizá tu link.302 Found— temporal.304 Not Modified— no cambió desde tu última visita (caching).
4xx Errores del cliente:
400 Bad Request— la request está malformada.401 Unauthorized— necesitás autenticarte.403 Forbidden— autenticado pero sin permiso.404 Not Found— recurso no existe.429 Too Many Requests— rate limit.
5xx Errores del servidor:
500 Internal Server Error— el servidor reventó.502 Bad Gateway— proxy/load balancer no puede hablar con el backend.503 Service Unavailable— servidor saturado o en mantenimiento.504 Gateway Timeout— proxy esperó demasiado.
Memorizá: 200=OK, 301=movido, 404=no existe, 500=server roto. Los demás se buscan.
5.3 HTTP/1.1, HTTP/2, HTTP/3
📐 Fundamento
HTTP/1.0 (1996)
Una conexión TCP por request. Lento — abrir/cerrar TCP cada vez.
HTTP/1.1 (1997)
Keep-alive por defecto: una conexión TCP sirve varias requests. Pipelining: mandar varias requests sin esperar (poco usado en práctica).
Aún así, una request a la vez por conexión — head-of-line blocking.
HTTP/2 (2015)
- Binario (no texto) — más eficiente de parsear.
- Multiplexación: muchas requests en paralelo sobre la misma conexión TCP.
- Compresión de headers (HPACK).
- Server push: el servidor puede enviar recursos antes de que el cliente los pida.
Resuelve el head-of-line blocking de HTTP/1.1, aunque hereda el de TCP.
HTTP/3 (2022)
Reemplaza TCP por QUIC (sobre UDP). Resuelve definitivamente HOL blocking — cada stream es independiente.
- Más rápido para abrir conexión (0-RTT en vuelta de cliente conocido).
- Mejor en redes inestables (mobile, WiFi).
- TLS 1.3 obligatorio.
Hoy en 2026, los grandes sites (Google, Facebook, Cloudflare) usan HTTP/3. El navegador lo negocia automáticamente.
Para escribir tu servidor
En la práctica, escribís contra HTTP/1.1 y un proxy frontal (nginx, Caddy) maneja HTTP/2 y HTTP/3 hacia los clientes.
5.4 DNS — del nombre a la IP
📐 Fundamento
Internet rutea por IPs, pero los humanos recordamos nombres. DNS (Domain Name System) traduce.
Estructura jerárquica
. (raíz)
/ | \
com org sv ← TLDs
/ \ \
google wiki gob
↓
google.com ← dominio
↓
www.google.com ← FQDN
Cada nivel está delegado al siguiente: la raíz no sabe IPs de Google, sabe quién es responsable del .com. El servidor de .com sabe quién es responsable de google.com. Y así.
Tipos de registros
| Tipo | Contenido |
|---|---|
| A | IPv4 (google.com → 142.250.x.x) |
| AAAA | IPv6 |
| CNAME | Alias (www.google.com → google.com) |
| MX | Servidor de email |
| NS | Servidor DNS responsable de un dominio |
| TXT | Texto arbitrario (verificación, SPF, DKIM) |
| PTR | Reverse (IP → nombre) |
| SRV | Servicios (XMPP, SIP, etc.) |
Resolución típica
Tu PC quiere www.google.com:
- Caché local. Si tu PC ya lo resolvió hace poco, listo.
- DNS resolver del ISP (típicamente). Le preguntás "¿IP de www.google.com?".
- El resolver, si no la tiene cacheada:
- Pregunta a la raíz: "¿quién maneja .com?"
- La raíz responde: "los servidores de .com están en estas IPs".
- Pregunta a
.com: "¿quién maneja google.com?" - .com responde: "los NS de google.com son ns1.google.com, ns2.google.com".
- Pregunta a
ns1.google.com: "¿qué IP tiene www.google.com?" - ns1 responde: "142.250.x.x".
- Resolver te lo devuelve y lo cachea.
- Tu PC abre TCP a esa IP.
Latencia típica: primer DNS lookup ~30-100 ms. Después, cachéa todo, instantáneo.
Inspeccionar DNS
$ dig www.google.com
;; ANSWER SECTION:
www.google.com. 300 IN A 142.250.10.139
;; Query time: 18 msec
;; SERVER: 192.168.1.1#53
Trace (ver toda la cadena):
$ dig +trace google.com
. 518400 IN NS a.root-servers.net.
...
com. 172800 IN NS a.gtld-servers.net.
...
google.com. 172800 IN NS ns1.google.com.
...
www.google.com. 300 IN A 142.250.10.139
Ahí ves cada nivel de la jerarquía respondiendo.
DNS-over-HTTPS (DoH)
DNS clásico es sin cifrar — tu ISP puede ver qué sitios resolvés. DoH envía DNS por HTTPS, ocultándolo. Cloudflare (1.1.1.1), Google (8.8.8.8) lo soportan.
Trade-off: privacidad mayor, pero centralizás la confianza en pocos resolvers.
5.5 TLS / HTTPS
📐 Fundamento
TLS (Transport Layer Security, antes SSL) es el protocolo que cifra HTTP. HTTPS = HTTP + TLS.
Tres garantías
- Confidencialidad. Nadie en el medio puede leer.
- Integridad. Nadie puede modificar sin que se note.
- Autenticidad. Sabés que estás hablando con el servidor real (no un impostor).
Handshake (simplificado)
Cliente Servidor
| |
|─ "ClientHello" (versiones, ciphers)──────────>|
| |
|<─ "ServerHello" + Certificado ────────────────|
| |
| (cliente verifica el certificado) |
| |
|─ Intercambio de claves (ECDHE) ──────────────>|
| |
|─── Cambio a cifrado ──────────────────────────|
| |
|── Datos HTTP cifrados ───────────────────── ─>|
Tras el handshake, todos los bytes van cifrados simétricamente con la clave acordada.
Certificados
Un certificado dice: "este servidor afirma ser google.com, y la entidad X (CA) lo respalda con su firma".
CAs (Certificate Authorities) confiables vienen pre-instaladas en tu navegador y SO. Cuando ves un certificado:
- ¿Lo firma una CA conocida? Si no → alerta.
- ¿Está vigente? Si no → alerta.
- ¿El nombre del cert coincide con el dominio que pediste? Si no → alerta.
- ¿Está revocado (CRL u OCSP)? Si sí → alerta.
Let's Encrypt revolucionó esto al hacer certificados gratis y automatizados. Hoy >90% de la web usa HTTPS.
TLS 1.3
Versión actual (2018). Más simple, más rápida, más segura:
- 1-RTT handshake (vs 2-RTT antes).
- 0-RTT en reconexiones.
- Solo cifradores modernos (eliminados los rotos).
Ver el certificado
En el navegador: clic en el candado. Línea de comandos:
$ openssl s_client -connect google.com:443 -servername google.com
...
subject=/CN=*.google.com
issuer=/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3
...
-servername es crítico — habilita SNI, que permite múltiples sitios HTTPS en una sola IP.
5.6 Mini servidor HTTP
🛠️ Avance del proyecto
Cierre del proyecto-hilo. Un servidor HTTP que sirve páginas:
# mini-server.py - servidor HTTP educativo
import socket
from datetime import datetime
from urllib.parse import unquote
HOST, PORT = '', 8080
def parse_request(raw: bytes):
"""Parsea una request HTTP/1.1 muy básica."""
text = raw.decode('utf-8', errors='replace')
if '\r\n\r\n' not in text:
return None
head, body = text.split('\r\n\r\n', 1)
lines = head.split('\r\n')
if not lines:
return None
metodo, ruta, version = lines[0].split(' ', 2)
headers = {}
for linea in lines[1:]:
if ':' in linea:
k, v = linea.split(':', 1)
headers[k.strip().lower()] = v.strip()
return {
'metodo': metodo,
'ruta': unquote(ruta),
'version': version,
'headers': headers,
'body': body,
}
def build_response(status: int, body: str, content_type='text/html'):
"""Construye una response HTTP/1.1."""
mensajes = {200: 'OK', 404: 'Not Found', 500: 'Internal Server Error'}
msg = mensajes.get(status, '')
body_bytes = body.encode('utf-8')
headers = [
f"HTTP/1.1 {status} {msg}",
f"Content-Type: {content_type}; charset=utf-8",
f"Content-Length: {len(body_bytes)}",
f"Date: {datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')}",
"Server: mini-server/0.1",
"Connection: close",
"",
"",
]
return ('\r\n'.join(headers)).encode('utf-8') + body_bytes
def routear(req):
"""Ruteador trivial."""
if req['ruta'] == '/':
return build_response(200, """
<h1>Pupuseria La Esquina</h1>
<p>Bienvenidos a nuestro mini-servidor.</p>
<ul>
<li><a href='/menu'>Menu</a></li>
<li><a href='/contacto'>Contacto</a></li>
</ul>
""")
if req['ruta'] == '/menu':
return build_response(200, """
<h1>Menu</h1>
<ul>
<li>Pupusa de queso ........ $0.50</li>
<li>Pupusa revuelta ........ $0.60</li>
<li>Pupusa de chicharron ... $0.60</li>
</ul>
""")
if req['ruta'] == '/contacto':
return build_response(200, "<h1>Contacto</h1><p>San Miguel</p>")
return build_response(404, "<h1>404</h1><p>Recurso no encontrado.</p>")
def manejar(cliente):
try:
raw = cliente.recv(8192)
req = parse_request(raw)
if req is None:
cliente.send(build_response(400, "<h1>400 Bad Request</h1>"))
return
print(f"[{req['metodo']}] {req['ruta']}")
respuesta = routear(req)
cliente.send(respuesta)
except Exception as e:
print(f"Error: {e}")
cliente.send(build_response(500, "<h1>500</h1>"))
finally:
cliente.close()
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(50)
print(f"Mini-server escuchando en http://localhost:{PORT}")
while True:
cliente, addr = s.accept()
manejar(cliente)
if __name__ == '__main__':
main()
Probalo:
$ python mini-server.py
Mini-server escuchando en http://localhost:8080
Visitá http://localhost:8080 en tu navegador. Funciona. Sirve HTML, hace ruteo básico, devuelve códigos de estado correctos.
Lo que sigue para hacerlo "real":
- Multi-cliente (threads o async).
- Servir archivos estáticos.
- Métodos POST con form data.
- Cookies y sesiones.
- HTTPS (envolvé el socket con
ssl.wrap_socket).
Pero conceptualmente, esto es un servidor HTTP. nginx, Apache y caddy hacen lo mismo, optimizado.
5.7 Resumen de lo aprendido
📐 Fundamento
Cuando hacés curl https://www.google.com:
- Tu cliente consulta DNS para resolver
www.google.com→142.250.x.x(UDP/53). - TCP three-way handshake con esa IP en puerto 443.
- TLS handshake — intercambio de certificados, claves.
- HTTP request cifrada:
GET / HTTP/1.1. - Servidor de Google procesa, busca el HTML.
- HTTP response cifrada:
200 OK, html, etc. - Cierre TCP (FIN-ACK).
- Tu cliente muestra el contenido.
Cada paso involucra:
- Capa física (cobre, fibra, radio).
- Capa enlace (Ethernet, WiFi, MAC, switches).
- Capa red (IP, routing, NAT, ICMP).
- Capa transporte (TCP).
- Capa aplicación (DNS, TLS, HTTP).
Todo eso, en menos de un segundo, miles de millones de veces por hora, alrededor del planeta.
5.8 Cierre del libro
Llegaste al final de Redes I. Repaso:
- Modelos y capas — el truco arquitectónico.
- Capa física y enlace — cables, frames, switches.
- Capa de red — IP, routing, NAT.
- Capa de transporte — TCP, UDP, sockets.
- Capa de aplicación — HTTP, DNS, TLS.
Ya entendés Internet. Quizás no en cada detalle, pero la arquitectura entera es accesible para vos.
Próximos pasos:
- Redes II: routing avanzado, multicast, IPv6 a fondo, MPLS, SDN.
- Seguridad de redes: ataques, firewalls, IDS/IPS, VPNs.
- Sistemas distribuidos: consenso, replicación, escalabilidad.
- Cloud / DevOps: la red en la era de Kubernetes.
5.9 Ejercicios
✏️ Ejercicio 5.1 — HTTP a mano con netcat
Hacé una request HTTP a mano:
$ nc google.com 80
GET / HTTP/1.1
Host: google.com
(Después de "Host:", presioná Enter dos veces.)
¿Qué respuesta obtenés?
Solución
Probablemente recibás 301 Moved Permanently redirigiéndote a https://www.google.com/. Eso es porque Google fuerza HTTPS.
HTTP/1.1 301 Moved Permanently
Location: https://www.google.com/
...
Para hacerlo en HTTPS, necesitás openssl s_client u otra herramienta TLS, ya que nc no cifra.
✏️ Ejercicio 5.2 — DNS trace
Ejecutá dig +trace tu-universidad.edu.sv (reemplazá por un dominio real). ¿Cuántos niveles ves? Identificá: raíz, TLD (.sv), dominio.
Solución
Vas a ver:
- Raíz (
.) → te dice los servidores de.sv. sv.→ te dice los servidores detu-universidad.edu.sv.- Servidor de la universidad → te da la IP final.
Cada paso es una pregunta a un servidor distinto. Total: ~3-4 saltos. Cada uno es un par UDP/TCP por encima de DNS.
✏️ Ejercicio 5.3 — Examinar certificados
Visitá un sitio HTTPS, hacé clic en el candadito, mirá el certificado. Identificá:
a. ¿Quién firmó (issuer)? b. ¿Cuándo expira? c. ¿Para qué dominios vale?
Después, ejecutá:
$ openssl s_client -connect <sitio>:443 -showcerts < /dev/null
¿Coincide con lo que viste?
Solución
La mayoría de sitios usa Let's Encrypt (R3 / R10) o Google Trust Services o Cloudflare. Validez típica: 90 días (Let's Encrypt) — hay que renovar regularmente.
El campo "Subject Alternative Name" lista los dominios cubiertos. Frecuentemente cubre domain.com y *.domain.com (wildcard).
✏️ Ejercicio 5.4 — Extender el mini-server
Tomá el mini-server de la sección 5.6 y agregá:
a. Servir archivos estáticos desde una carpeta ./public/.
b. Soporte para POST con form data.
c. Multi-cliente con threads.
Solución
import os, threading
def servir_archivo(ruta):
archivo = os.path.join('./public', ruta.lstrip('/'))
if not os.path.isfile(archivo):
return None
with open(archivo, 'rb') as f:
contenido = f.read()
# Determinar Content-Type
ext = os.path.splitext(archivo)[1].lower()
types = {'.html': 'text/html', '.css': 'text/css',
'.js': 'application/javascript', '.png': 'image/png',
'.jpg': 'image/jpeg'}
ctype = types.get(ext, 'application/octet-stream')
headers = (f"HTTP/1.1 200 OK\r\n"
f"Content-Type: {ctype}\r\n"
f"Content-Length: {len(contenido)}\r\n\r\n").encode()
return headers + contenido
def routear(req):
# Primero: archivos estáticos
archivo = servir_archivo(req['ruta'])
if archivo is not None:
return archivo
# Después: rutas dinámicas
if req['metodo'] == 'POST' and req['ruta'] == '/orden':
return build_response(201, f"<h1>Orden recibida</h1><pre>{req['body']}</pre>")
# ... lo de antes
return build_response(404, "<h1>404</h1>")
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(50)
while True:
cliente, _ = s.accept()
threading.Thread(target=manejar, args=(cliente,), daemon=True).start()
Eso ya es un servidor real funcional. Más optimizaciones (async, file caching, gzip) lo hacen production-ready.
5.10 Para profundizar
- Tanenbaum cap. 7 (Capa de aplicación).
- Kurose & Ross cap. 2.
- Ilya Grigorik, High Performance Browser Networking (gratis online). Best-in-class.
- MDN HTTP docs: https://developer.mozilla.org/es/docs/Web/HTTP
Definiciones nuevas: HTTP, request, response, método (GET/POST/etc.), código de estado, headers, HTTP/1.1, HTTP/2, HTTP/3, DNS, TLD, FQDN, registro DNS, resolver, CA, certificado, TLS, HTTPS, SNI, mini-server.