# Servme Un framework web HTTP de alto rendimiento escrito en Rust, construido sobre [Hyper](https://hyper.rs/). ## Primeros pasos ### Instalación Añade `servme` a tu `Cargo.toml`: ```toml [dependencies] servme = "0.1" tokio = { version = "1", features = ["full"] } ``` ### Tu primer servidor ```rust use http_body_util::Full; use hyper::{body::Bytes, Request, Response}; use servme::{Responder, Server, ServerError}; #[tokio::main] async fn main() { Server::builder() .address("127.0.0.1", 8080) .build() .run(handler) .await; } async fn handler(req: Request) -> Result>, ServerError> { Responder::ok(format!("Hola! Path: {}", req.uri())) } ``` > **Para usuarios nuevos**: Ejecuta `cargo run` y visita `http://127.0.0.1:8080` en tu navegador. --- ## Conceptos básicos ### El Handler El handler es una función asíncrona que recibe un `Request` y retorna una `Response`: ```rust use http_body_util::Full; use hyper::{body::Bytes, Request, Response}; async fn handler(req: Request) -> Result>, ServerError> { // Tu lógica aquí Responder::ok("Respuesta") } ``` ### Responder `Responder` proporciona métodos auxiliares para crear respuestas HTTP comunes: ```rust use servme::Responder; // Respuestas de éxito Responder::ok("Mensaje")?; // 200 OK Responder::html("

Título

")?; // 200 con Content-Type: text/html Responder::json(&datos)?; // 200 con Content-Type: application/json Responder::redirect("/nueva-ruta")?; // 302 Redirect Responder::no_content()?; // 204 No Content // Respuestas de error Responder::not_found()?; // 404 Responder::unauthorized()?; // 401 Responder::forbidden()?; // 403 Responder::bad_request("Datos inválidos")?; // 400 Responder::internal_error("Algo salió mal")?; // 500 ``` ### Extraer datos del Request ```rust use servme::Requester; // Extraer body JSON let data: MyStruct = Requester::extract_body(req).await?; // Extraer como texto let body_str: String = Requester::extract_body_str(req).await?; // Extraer como bytes (más eficiente) let body_bytes: Bytes = Requester::extract_body_bytes(req).await?; // Extraer parámetros de URL let url = UrlExtract::new(req.uri()); let name = url.param_str("nombre"); // ?nombre=Juan let age: Option = url.param_i64("edad"); // ?edad=25 ``` --- ## Middleware Los middleware se ejecutan antes del handler y pueden authnticar, filtrar o modificar requests. ### API Key ```rust use servme::{Server, ServerBuilder}; Server::builder() .address("127.0.0.1", 8080) .add_api_key_middleware("mi-clave-secreta") .build() .run(handler) .await; ``` Envía la clave en el header `X-API-Key`. ### JWT Authentication ```rust use servme::{Server, ServerBuilder}; Server::builder() .address("0.0.0.0", 8080) .add_jwt_middleware( rsa_public_key_pem, // Clave pública RSA en formato PEM vec!["/health".to_string()], // Rutas públicas (sin auth) ) .build() .run(handler) .await; ``` El JWT se valida desde: - Header `Authorization: Bearer ` - Cookie `access_token` ### Filtrado por IP ```rust use servme::ServerBuilder; Server::builder() .address("127.0.0.1", 8080) .add_ip_filter_middleware( vec!["192.168.1.100".to_string(), "10.0.0.1".to_string()], true, // allow_private: también permitir IPs privadas (192.168.x.x, 10.x.x.x) ) .build() .run(handler) .await; ``` ### Múltiples middlewares ```rust Server::builder() .address("0.0.0.0", 8080) .add_ip_filter_middleware(vec![], true) // Permitir todas las IPs privadas .add_jwt_middleware(pub_key, vec!["/static".to_string(), "/health".to_string()]) .build() .run(handler) .await; ``` --- ## Estado compartido Puedes compartir datos entre requests usando `.data()`: ```rust use std::sync::Arc; #[derive(Clone)] struct AppState { db: Database, } Server::builder() .address("127.0.0.1", 8080) .data(AppState { db: Database::new() }) .build() .run(|req| async move { // Accede al estado desde las extensions del request let state = req.extensions().get::>().unwrap(); // Usa state.db... Responder::ok("ok") }) .await; ``` --- ## Estructura del proyecto ``` src/ ├── lib.rs # Exports públicos ├── main.rs # Binario de ejemplo ├── builder.rs # ServerBuilder - configuración del servidor ├── config.rs # ServerConfig - estructura de configuración ├── server.rs # Servidor HTTP con graceful shutdown ├── error.rs # ServerError - tipos de errores ├── constants.rs # Constantes del framework ├── responder.rs # Helpers para construir respuestas ├── requester.rs # Helpers para extraer datos del request ├── url_extract.rs # Parsing de URLs y query params └── middleware/ ├── mod.rs # Traits y tipos comunes ├── api_key.rs # Autenticación por API Key ├── jwt.rs # Autenticación JWT (RS256) ├── ip_filter.rs # Filtrado por dirección IP └── auth_types.rs # Tipos para autenticación (Claims) ``` --- ## Construcción y testing ```bash # Compilar cargo build # Ejecutar tests cargo test # Ejecutar con logs de debug RUST_LOG=debug cargo run # Ver documentation cargo doc --open ``` --- ## Errores comunes ### "Failed to bind to address" El puerto está en uso. Prueba con otro puerto: ```rust Server::builder() .address("127.0.0.1", 8081) // Cambia el puerto .build() .run(handler) .await; ``` ### "JWT validation failed" - Verifica que la clave pública RSA sea válida - Asegúrate de que el token no esté expirado (`exp` claim) - El token debe contener el claim `sub` (subject) --- ## Constantes útiles ```rust use servme::{ DEFAULT_HOST, // "127.0.0.1" DEFAULT_PORT, // 8080 JWT_COOKIE_NAME, // "access_token" BEARER_PREFIX, // "Bearer " MAX_ALLOWED_IPS, // 1000 }; use servme::constants::FILE_EXTENSIONS; // Extensiones de archivos estáticos ``` --- ## License MIT