"""
Mini servidor HTTP/1.1 en Python desde socket puro.
Sin http.server, sin frameworks. Compatible Python 3.8+
Uso: python code_1910_http_server.py [puerto] [directorio]
"""
import socket
import os
import sys
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
ROOT = sys.argv[2] if len(sys.argv) > 2 else '.'
MIME = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.txt': 'text/plain; charset=utf-8',
}
def parse_request(raw: bytes):
"""Parsear request HTTP, devolver (method, path, headers, body)."""
header_end = raw.find(b'rnrn')
if header_end == -1:
return None, None, {}, b''
header_part = raw[:header_end].decode('utf-8', errors='replace')
body = raw[header_end + 4:]
lines = header_part.split('rn')
request_line = lines[0].split()
if len(request_line) < 2:
return None, None, {}, b''
method = request_line[0]
path = request_line[1]
headers = {}
for line in lines[1:]:
if ':' in line:
k, _, v = line.partition(':')
headers[k.strip().lower()] = v.strip()
return method, path, headers, body
def build_response(status: int, content_type: str, body: bytes) -> bytes:
reasons = {200: 'OK', 404: 'Not Found', 405: 'Method Not Allowed', 500: 'Internal Server Error'}
reason = reasons.get(status, 'Unknown')
headers = (
f'HTTP/1.1 {status} {reason}rn'
f'Content-Type: {content_type}rn'
f'Content-Length: {len(body)}rn'
f'Connection: closern'
'rn'
)
return headers.encode() + body
def handle(conn: socket.socket) -> None:
try:
raw = b''
conn.settimeout(5.0)
while True:
chunk = conn.recv(4096)
if not chunk:
break
raw += chunk
if b'rnrn' in raw:
break
method, path, headers, _ = parse_request(raw)
if method is None:
conn.sendall(build_response(400, 'text/plain', b'Bad Request'))
return
if method != 'GET':
conn.sendall(build_response(405, 'text/plain', b'Method Not Allowed'))
return
# Limpiar path y evitar directory traversal
safe_path = os.path.normpath(path.split('?')[0].lstrip('/'))
if safe_path.startswith('..'):
safe_path = ''
full_path = os.path.join(ROOT, safe_path)
# Si es directorio, buscar index.html
if os.path.isdir(full_path):
full_path = os.path.join(full_path, 'index.html')
if not os.path.isfile(full_path):
body = b'404 Not Found
'
conn.sendall(build_response(404, 'text/html', body))
return
ext = os.path.splitext(full_path)[1].lower()
content_type = MIME.get(ext, 'application/octet-stream')
with open(full_path, 'rb') as f:
body = f.read()
conn.sendall(build_response(200, content_type, body))
except Exception as e:
try:
conn.sendall(build_response(500, 'text/plain', f'Error: {e}'.encode()))
except Exception:
pass
finally:
conn.close()
def serve() -> None:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('', PORT))
server.listen(10)
print(f'Servidor en http://localhost:{PORT} (root: {os.path.abspath(ROOT)})')
print('Ctrl+C para detenern')
try:
while True:
conn, addr = server.accept()
print(f' {addr[0]}:{addr[1]}')
handle(conn)
except KeyboardInterrupt:
print('nServidor detenido.')
finally:
server.close()
if __name__ == '__main__':
serve()
Mini servidor HTTP en Python desde socket puro
Servidor HTTP/1.1 básico en Python usando sockets raw sin frameworks ni librerías. Parsea request line y headers, sirve ficheros estáticos con Content-Type correcto, devuelve 404 cuando no existe el recurso y soporta index.html como directorio raíz.
Descargar adjuntos
COMPARTE ESTE TUTORIAL
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP