MiniJWS sigue una arquitectura modular donde cada módulo es un proyecto Maven independiente con una responsabilidad bien definida. Cada módulo es autocontenido con su propio POM.
miniJWS-core (sin dependencias)
|
├── miniJWS-demo ──► miniJWS-core
├── miniStaticServer ──► miniJWS-core, miniQR (opcional)
|
miniApkReader (usa net.dongliu:apk-parser)
|
miniQR (usa com.google.zxing, org.jfree:jfreesvg)
┌───────────────────────────────────────────────────────────────┐
│ HttpServer │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ Rutas │ │ Pool │ │ServerSocket│ │ Lista de │ │
│ │ (Map) │ │ Hilos │ │ │ │ Middleware │ │
│ └────┬─────┘ └──────────┘ └────────────┘ └──────┬───────┘ │
│ │ │ │
│ ┌────▼──────────────────────────────────────────────▼──────┐ │
│ │ Ciclo de Vida de Solicitud │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┐ │ │
│ │ │ Decoder │──►│Middleware│──►│ Runner │──►│Encoder│ │ │
│ │ │ (parsear)│ │ (cadena) │ │ (ruta) │ │(escr.)│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
- Aceptar:
HttpServeracepta una conexión TCP medianteServerSocket - Bucle Keep-Alive: Si es HTTP/1.1 sin
Connection: close, el socket se reutiliza - Decodificar:
HttpDecoderparsea la solicitud HTTP cruda en un objetoHttpRequest - Cadena de middleware: Cada middleware registrado se ejecuta en orden (logging, CORS, limitación de tasa, etc.)
- Ruta: Orden de coincidencia: exacta → parámetros de ruta (
:id) → comodín simple (*) → comodín global (**) - Ejecutar: El
RequestRunnercorrespondiente se invoca con la solicitud - Codificar:
HttpEncoderserializa elHttpResponsede vuelta al cliente - Repetir: Si keep-alive, volver al paso 3; de lo contrario cerrar conexión
io.github.blacknoize404.miniJWS/
├── HttpServer.java # Clase principal del servidor
├── DemoServer.java # Ejemplo básico (legado)
├── primitives/
│ ├── HttpMethod.java # Enum de métodos HTTP
│ ├── HttpStatusCode.java # Definiciones de códigos de estado
│ ├── ContentType.java # Enum de tipos MIME
│ ├── RequestRunner.java # Interfaz de manejador de ruta
│ ├── Middleware.java # Interfaz de middleware
│ └── MiddlewareChain.java # Interfaz de cadena
├── requests/
│ ├── HttpRequest.java # Modelo de solicitud (Builder, parseo de cuerpo, cookies)
│ └── HttpDecoder.java # Parseador de solicitudes
├── responses/
│ ├── HttpResponse.java # Modelo de respuesta (Builder, fábrica redirect, cookies)
│ └── HttpEncoder.java # Serializador de respuestas
├── middleware/
│ ├── CorsMiddleware.java # Cabeceras CORS y preflight
│ ├── AccessLogMiddleware.java # Logging de solicitudes estilo Apache
│ ├── GzipMiddleware.java # Compresión de respuestas
│ └── RateLimitMiddleware.java # Limitación de tasa por IP
├── handlers/
│ └── StaticFileHandler.java # Servicio de archivos basado en directorios
├── headers/
│ ├── Header.java # Modelo de cabecera HTTP
│ ├── Field.java # Parseador de campos de cabecera
│ └── Parameter.java # Parseador de parámetros de cabecera
└── content/
└── ContentTypes.java # Mapeo extensión-a-MIME
El middleware se ejecuta en orden de registro. Cada middleware puede:
- Inspeccionar y modificar la solicitud
- Cortocircuitar la cadena (devolver una respuesta inmediatamente)
- Inspeccionar y modificar la respuesta
- Ejecutar código antes y después del manejador
Solicitud ──► Middleware 1 ──► Middleware 2 ──► ... ──► Manejador ──► Respuesta
│ │ │
▼ ▼ ▼
(log solicitud) (verificar CORS) (manejar ruta)
│ │ │
◄────────────────┼──────────────────────────┘
│
▼
(agregar cabeceras CORS)
Las conexiones HTTP/1.1 son persistentes por defecto. El servidor:
- Reutiliza el mismo
BufferedInputStreama través de múltiples solicitudes en el mismo socket - Cierra después de la cabecera
Connection: close, tiempo de inactividad (10s), o 100 solicitudes - Establece
Connection: keep-aliveen cada respuesta
Las rutas pueden usar comodines para coincidencias flexibles:
*— coincide con un segmento de ruta (p.ej./*coincide con/cualquier-segmento)**— coincide con todos los segmentos restantes (p.ej./archivos/**coincide con/archivos/a/b/c)
Usa addStaticRoute() por conveniencia al servir archivos estáticos con /*:
server.addStaticRoute("/*", new StaticFileHandler("./public"));- Las rutas usan
ConcurrentHashMap— seguras para modificar en tiempo de ejecución - La lista de middleware usa
CopyOnWriteArrayList— segura para cargas de trabajo de mucha lectura - Cada conexión se ejecuta en un hilo separado
- El pool de hilos usa
Executors.newFixedThreadPool() - El apagado gradual usa
CountDownLatch—idle()espera el latch,stop()lo decrementa