Web Features Rust Samples

Web Rust web framework features examples including routing, middleware, and static files

Key Facts

Category
Rust
Items
3
Format Families
text

Sample Overview

Web Rust web framework features examples including routing, middleware, and static files This sample set belongs to Rust and can be used to test related workflows inside Elysia Tools.

💻 Routing rust

🟡 intermediate ⭐⭐⭐⭐

URL routing and path parameter extraction

⏱️ 30 min 🏷️ rust, web, web features
Prerequisites: Intermediate Rust, HTTP basics
// Web Rust Routing Examples
// URL routing and path parameter extraction
//
// For production web applications, consider using:
// - Actix Web: https://actix.rs/
// - Axum: https://github.com/tokio-rs/axum
// - Rocket: https://rocket.rs/
// - Warp: https://github.com/seanmonstar/warp

use std::collections::HashMap;
use serde::{Deserialize, Serialize};

// 1. Basic Route

/// HTTP Method
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HttpMethod {
    GET,
    POST,
    PUT,
    DELETE,
    PATCH,
    HEAD,
    OPTIONS,
}

/// HTTP Request (simplified)
#[derive(Debug, Clone)]
pub struct HttpRequest {
    pub method: HttpMethod,
    pub path: String,
    pub query_params: HashMap<String, String>,
    pub headers: HashMap<String, String>,
    pub body: Option<String>,
}

/// HTTP Response (simplified)
#[derive(Debug, Clone)]
pub struct HttpResponse {
    pub status_code: u16,
    pub headers: HashMap<String, String>,
    pub body: String,
}

impl HttpResponse {
    pub fn ok(body: &str) -> Self {
        HttpResponse {
            status_code: 200,
            headers: HashMap::new(),
            body: body.to_string(),
        }
    }

    pub fn not_found() -> Self {
        HttpResponse {
            status_code: 404,
            headers: HashMap::new(),
            body: "Not Found".to_string(),
        }
    }

    pub fn json<T: Serialize>(data: &T) -> Self {
        let mut response = HttpResponse::ok("");
        response.headers.insert("Content-Type".to_string(), "application/json".to_string());
        response.body = serde_json::to_string(data).unwrap_or_default();
        response
    }
}

// 2. Route Definition

/// Route handler type
pub type RouteHandler = fn(&HttpRequest) -> HttpResponse;

/// Route definition
#[derive(Debug, Clone)]
pub struct Route {
    pub method: HttpMethod,
    pub pattern: String,
    pub handler: RouteHandler,
    pub name: Option<String>,
}

impl Route {
    pub fn new(method: HttpMethod, pattern: &str, handler: RouteHandler) -> Self {
        Route {
            method,
            pattern: pattern.to_string(),
            handler,
            name: None,
        }
    }

    pub fn with_name(mut self, name: &str) -> Self {
        self.name = Some(name.to_string());
        self
    }
}

/// Create a GET route
pub fn get(pattern: &str, handler: RouteHandler) -> Route {
    Route::new(HttpMethod::GET, pattern, handler)
}

/// Create a POST route
pub fn post(pattern: &str, handler: RouteHandler) -> Route {
    Route::new(HttpMethod::POST, pattern, handler)
}

/// Create a PUT route
pub fn put(pattern: &str, handler: RouteHandler) -> Route {
    Route::new(HttpMethod::PUT, pattern, handler)
}

/// Create a DELETE route
pub fn delete(pattern: &str, handler: RouteHandler) -> Route {
    Route::new(HttpMethod::DELETE, pattern, handler)
}

// 3. Router

/// Simple router
pub struct Router {
    routes: Vec<Route>,
}

impl Router {
    pub fn new() -> Self {
        Router {
            routes: Vec::new(),
        }
    }

    /// Add a route
    pub fn add_route(mut self, route: Route) -> Self {
        self.routes.push(route);
        self
    }

    /// Match request to route
    pub fn match_route(&self, request: &HttpRequest) -> Option<&Route> {
        self.routes.iter()
            .find(|route| {
                route.method == request.method && self.match_pattern(&route.pattern, &request.path)
            })
    }

    /// Check if path matches pattern
    fn match_pattern(&self, pattern: &str, path: &str) -> bool {
        // Simple pattern matching
        // :id style parameters
        let pattern_parts: Vec<&str> = pattern.split('/').collect();
        let path_parts: Vec<&str> = path.split('/').collect();

        if pattern_parts.len() != path_parts.len() {
            return false;
        }

        pattern_parts.iter().zip(path_parts.iter())
            .all(|(p, actual)| {
                p.starts_with(':') || *p == *actual
            })
    }

    /// Extract path parameters
    pub fn extract_params(&self, pattern: &str, path: &str) -> HashMap<String, String> {
        let mut params = HashMap::new();
        let pattern_parts: Vec<&str> = pattern.split('/').collect();
        let path_parts: Vec<&str> = path.split('/').collect();

        for (p, actual) in pattern_parts.iter().zip(path_parts.iter()) {
            if p.starts_with(':') {
                let param_name = &p[1..];
                params.insert(param_name.to_string(), actual.to_string());
            }
        }

        params
    }

    /// Handle request
    pub fn handle(&self, request: &HttpRequest) -> HttpResponse {
        if let Some(route) = self.match_route(request) {
            (route.handler)(request)
        } else {
            HttpResponse::not_found()
        }
    }
}

impl Default for Router {
    fn default() -> Self {
        Self::new()
    }
}

// 4. URL Pattern Matching

/// Match pattern with wildcards
pub fn match_wildcard(pattern: &str, path: &str) -> bool {
    let pattern_parts: Vec<&str> = pattern.split('*').collect();

    if pattern_parts.len() == 1 {
        return pattern == path;
    }

    let mut current_pos = 0;
    for (i, part) in pattern_parts.iter().enumerate() {
        if part.is_empty() {
            continue;
        }

        if let Some(pos) = path[current_pos..].find(part) {
            current_pos += pos + part.len();
        } else {
            return false;
        }

        // Last part should match to end
        if i == pattern_parts.len() - 1 && !path.ends_with(part) {
            return false;
        }
    }

    true
}

/// Extract wildcard value
pub fn extract_wildcard(pattern: &str, path: &str) -> Option<String> {
    if !pattern.contains('*') {
        return None;
    }

    let prefix = &pattern[..pattern.find('*')?];
    let suffix = &pattern[pattern.find('*')? + 1..];

    if !path.starts_with(prefix) || !path.ends_with(suffix) {
        return None;
    }

    let start = prefix.len();
    let end = path.len() - suffix.len();

    if end <= start {
        return None;
    }

    Some(path[start..end].to_string())
}

// 5. Query Parameters

/// Parse query string
pub fn parse_query(query: &str) -> HashMap<String, String> {
    let mut params = HashMap::new();

    for pair in query.split('&') {
        let parts: Vec<&str> = pair.splitn(2, '=').collect();
        if parts.len() == 2 {
            params.insert(
                url_decode(parts[0]),
                url_decode(parts[1])
            );
        }
    }

    params
}

/// URL decode (simple implementation)
pub fn url_decode(s: &str) -> String {
    let mut result = String::new();
    let mut chars = s.chars().peekable();

    while let Some(c) = chars.next() {
        if c == '%' {
            let hex1 = chars.next().unwrap_or('0');
            let hex2 = chars.next().unwrap_or('0');
            let byte = u8::from_str_radix(&format!("{}{}", hex1, hex2), 16).unwrap_or(0);
            result.push(byte as char);
        } else if c == '+' {
            result.push(' ');
        } else {
            result.push(c);
        }
    }

    result
}

/// URL encode (simple implementation)
pub fn url_encode(s: &str) -> String {
    s.chars()
        .map(|c| match c {
            'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' => c.to_string(),
            ' ' => "+".to_string(),
            _ => format!("%{:02X}", c as u8),
        })
        .collect()
}

// 6. Path Builders

/// Build URL with path parameters
pub fn build_path(template: &str, params: &HashMap<String, String>) -> String {
    let mut result = template.to_string();

    for (key, value) in params {
        let placeholder = format!(":{}", key);
        if result.contains(&placeholder) {
            result = result.replace(&placeholder, value);
        }
    }

    result
}

/// Build URL with query parameters
pub fn build_url(base: &str, params: &HashMap<String, String>) -> String {
    if params.is_empty() {
        return base.to_string();
    }

    let query: Vec<String> = params.iter()
        .map(|(k, v)| format!("{}={}", url_encode(k), url_encode(v)))
        .collect();

    format!("{}?{}", base, query.join("&"))
}

// 7. Route Groups

/// Route group with prefix
pub struct RouteGroup {
    prefix: String,
    routes: Vec<Route>,
}

impl RouteGroup {
    pub fn new(prefix: &str) -> Self {
        RouteGroup {
            prefix: prefix.to_string(),
            routes: Vec::new(),
        }
    }

    /// Add route to group
    pub fn add_route(mut self, mut route: Route) -> Self {
        route.pattern = format!("{}{}", self.prefix, route.pattern);
        self.routes.push(route);
        self
    }

    /// Get all routes with prefix applied
    pub fn routes(self) -> Vec<Route> {
        self.routes
    }
}

// 8. Named Routes

/// Route name registry
pub struct RouteRegistry {
    routes: HashMap<String, String>,
}

impl RouteRegistry {
    pub fn new() -> Self {
        RouteRegistry {
            routes: HashMap::new(),
        }
    }

    /// Register named route
    pub fn register(&mut self, name: &str, pattern: &str) {
        self.routes.insert(name.to_string(), pattern.to_string());
    }

    /// Generate URL from route name
    pub fn url(&self, name: &str, params: &HashMap<String, String>) -> Option<String> {
        let pattern = self.routes.get(name)?;
        Some(build_path(pattern, params))
    }
}

impl Default for RouteRegistry {
    fn default() -> Self {
        Self::new()
    }
}

// 9. Route Middleware

/// Route middleware type
pub type RouteMiddleware = Box<dyn Fn(&HttpRequest) -> Option<HttpResponse> + Send + Sync>;

/// Route with middleware
pub struct RouteWithMiddleware {
    route: Route,
    middleware: Vec<RouteMiddleware>,
}

impl RouteWithMiddleware {
    pub fn new(route: Route) -> Self {
        RouteWithMiddleware {
            route,
            middleware: Vec::new(),
        }
    }

    /// Add middleware
    pub fn add_middleware(mut self, middleware: RouteMiddleware) -> Self {
        self.middleware.push(middleware);
        self
    }

    /// Handle with middleware
    pub fn handle(&self, request: &HttpRequest) -> HttpResponse {
        // Run middleware
        for mw in &self.middleware {
            if let Some(response) = mw(request) {
                return response;
            }
        }

        // Run handler
        (self.route.handler)(request)
    }
}

// 10. Common Route Patterns

/// User routes
pub fn user_routes() -> Vec<Route> {
    vec![
        get("/users", |req| HttpResponse::ok("List users")),
        get("/users/:id", |req| HttpResponse::ok("Get user")),
        post("/users", |req| HttpResponse::ok("Create user")),
        put("/users/:id", |req| HttpResponse::ok("Update user")),
        delete("/users/:id", |req| HttpResponse::ok("Delete user")),
    ]
}

/// API routes
pub fn api_routes() -> Vec<Route> {
    vec![
        get("/api/status", |req| HttpResponse::ok("OK")),
        get("/api/health", |req| HttpResponse::ok("Healthy")),
        get("/api/version", |req| HttpResponse::ok("1.0.0")),
    ]
}

// Usage Examples
fn main() {
    println!("=== Web Rust Routing Examples ===\n");

    // 1. Basic routes
    println!("--- 1. Basic Routes ---");
    let home_route = get("/", |_| HttpResponse::ok("Home Page"));
    let about_route = get("/about", |_| HttpResponse::ok("About Page"));
    println!("Home route: {:?}", home_route);
    println!("About route: {:?}", about_route);

    // 2. Router setup
    println!("
--- 2. Router Setup ---");
    let router = Router::new()
        .add_route(get("/", |_| HttpResponse::ok("Home")))
        .add_route(get("/users", |_| HttpResponse::ok("Users")))
        .add_route(get("/users/:id", |_| HttpResponse::ok("User Details")));

    let request = HttpRequest {
        method: HttpMethod::GET,
        path: "/users/123".to_string(),
        query_params: HashMap::new(),
        headers: HashMap::new(),
        body: None,
    };

    let response = router.handle(&request);
    println!("Response: {}", response.status_code);

    // 3. Parameter extraction
    println!("
--- 3. Parameter Extraction ---");
    let params = router.extract_params("/users/:id", "/users/123");
    println!("Extracted params: {:?}", params);

    // 4. Query parameters
    println!("
--- 4. Query Parameters ---");
    let query = "name=John&age=30&city=NYC";
    let parsed = parse_query(query);
    println!("Parsed query: {:?}", parsed);

    // 5. URL encoding/decoding
    println!("
--- 5. URL Encoding/Decoding ---");
    let original = "hello world!";
    let encoded = url_encode(original);
    let decoded = url_decode(&encoded);
    println!("Original: {}", original);
    println!("Encoded: {}", encoded);
    println!("Decoded: {}", decoded);

    // 6. Build paths
    println!("
--- 6. Build Paths ---");
    let template = "/users/:id/posts/:post_id";
    let mut params = HashMap::new();
    params.insert("id".to_string(), "123".to_string());
    params.insert("post_id".to_string(), "456".to_string());
    let path = build_path(template, &params);
    println!("Built path: {}", path);

    // 7. Build URLs
    println!("
--- 7. Build URLs ---");
    let mut query_params = HashMap::new();
    query_params.insert("page".to_string(), "1".to_string());
    query_params.insert("limit".to_string(), "10".to_string());
    let url = build_url("/users", &query_params);
    println!("Built URL: {}", url);

    // 8. Wildcard matching
    println!("
--- 8. Wildcard Matching ---");
    let pattern = "/files/*";
    println!("Match '/files/doc.pdf': {}", match_wildcard(pattern, "/files/doc.pdf"));
    println!("Match '/files/images/photo.jpg': {}", match_wildcard(pattern, "/files/images/photo.jpg"));
    println!("Extract wildcard: {:?}", extract_wildcard(pattern, "/files/images/photo.jpg"));

    // 9. Route groups
    println!("
--- 9. Route Groups ---");
    let group = RouteGroup::new("/api/v1")
        .add_route(get("/users", |_| HttpResponse::ok("Users")))
        .add_route(get("/posts", |_| HttpResponse::ok("Posts")));

    let grouped_routes = group.routes();
    println!("Grouped routes:");
    for route in &grouped_routes {
        println!("  {}", route.pattern);
    }

    // 10. Named routes
    println!("
--- 10. Named Routes ---");
    let mut registry = RouteRegistry::new();
    registry.register("user_profile", "/users/:id");
    registry.register("post_detail", "/posts/:id/comments/:comment_id");

    let mut params = HashMap::new();
    params.insert("id".to_string(), "123".to_string());
    println!("User profile URL: {:?}", registry.url("user_profile", &params));

    let mut params = HashMap::new();
    params.insert("id".to_string(), "456".to_string());
    params.insert("comment_id".to_string(), "789".to_string());
    println!("Post detail URL: {:?}", registry.url("post_detail", &params));

    println!("
=== Note ===");
    println!("For production web applications, use:");
    println!("- Actix Web: https://actix.rs/");
    println!("- Axum: https://github.com/tokio-rs/axum");
    println!("- Rocket: https://rocket.rs/");
    println!("- Warp: https://github.com/seanmonstar/warp");

    println!("
=== All Routing Examples Completed ===");
}

💻 Static Files rust

🟡 intermediate ⭐⭐⭐

Serve static files and assets from a directory

⏱️ 25 min 🏷️ rust, web, web features
Prerequisites: Intermediate Rust, File I/O
// Web Rust Static Files Examples
// Serve static files and assets from a directory
//
// For production web applications, consider using:
// - Actix Web: https://actix.rs/
// - Axum: https://github.com/tokio-rs/axum
// - Static file serving crates: rust-embed, mime_guess

use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::fs;

// 1. File Response Types

/// File response
#[derive(Debug, Clone)]
pub struct FileResponse {
    pub path: PathBuf,
    pub content_type: String,
    pub content: Vec<u8>,
    pub status_code: u16,
    pub headers: HashMap<String, String>,
}

impl FileResponse {
    pub fn new(path: PathBuf, content: Vec<u8>, content_type: &str) -> Self {
        FileResponse {
            path,
            content_type: content_type.to_string(),
            content,
            status_code: 200,
            headers: HashMap::new(),
        }
    }

    pub fn with_cache_control(mut self, value: &str) -> Self {
        self.headers.insert("Cache-Control".to_string(), value.to_string());
        self
    }

    pub fn with_etag(mut self, etag: &str) -> Self {
        self.headers.insert("ETag".to_string(), etag.to_string());
        self
    }
}

/// File not found response
pub fn file_not_found() -> FileResponse {
    FileResponse {
        path: PathBuf::new(),
        content_type: "text/plain".to_string(),
        content: "File Not Found".to_string().into_bytes(),
        status_code: 404,
        headers: HashMap::new(),
    }
}

// 2. MIME Type Detection

/// Get MIME type from file extension
pub fn get_mime_type(path: &Path) -> &'static str {
    path.extension()
        .and_then(|ext| ext.to_str())
        .map(|ext| match ext.to_lowercase().as_str() {
            "html" | "htm" => "text/html",
            "css" => "text/css",
            "js" => "application/javascript",
            "json" => "application/json",
            "xml" => "application/xml",
            "pdf" => "application/pdf",
            "zip" => "application/zip",
            "tar" => "application/x-tar",
            "gz" => "application/gzip",
            "txt" => "text/plain",
            "md" => "text/markdown",
            "png" => "image/png",
            "jpg" | "jpeg" => "image/jpeg",
            "gif" => "image/gif",
            "svg" => "image/svg+xml",
            "webp" => "image/webp",
            "ico" => "image/x-icon",
            "bmp" => "image/bmp",
            "woff" => "font/woff",
            "woff2" => "font/woff2",
            "ttf" => "font/ttf",
            "eot" => "application/vnd.ms-fontobject",
            "mp3" => "audio/mpeg",
            "wav" => "audio/wav",
            "ogg" => "audio/ogg",
            "mp4" => "video/mp4",
            "webm" => "video/webm",
            "avi" => "video/x-msvideo",
            "mov" => "video/quicktime",
            _ => "application/octet-stream",
        })
        .unwrap_or("application/octet-stream")
}

/// Check if file is binary
pub fn is_binary_file(path: &Path) -> bool {
    let ext = path.extension()
        .and_then(|e| e.to_str())
        .map(|e| e.to_lowercase())
        .unwrap_or_default();

    matches!(ext.as_str(),
        "png" | "jpg" | "jpeg" | "gif" | "webp" | "ico" | "bmp" |
        "woff" | "woff2" | "ttf" | "eot" |
        "mp3" | "wav" | "ogg" | "mp4" | "webm" | "avi" | "mov" |
        "pdf" | "zip" | "tar" | "gz"
    )
}

// 3. Static File Server

/// Static file server
pub struct StaticFileServer {
    root_dir: PathBuf,
    index_files: Vec<String>,
    spa_mode: bool,
}

impl StaticFileServer {
    pub fn new(root_dir: &str) -> Self {
        StaticFileServer {
            root_dir: PathBuf::from(root_dir),
            index_files: vec!["index.html".to_string()],
            spa_mode: false,
        }
    }

    /// Set index files
    pub fn index_files(mut self, files: Vec<&str>) -> Self {
        self.index_files = files.iter().map(|s| s.to_string()).collect();
        self
    }

    /// Enable SPA mode (serve index.html for all routes)
    pub fn spa_mode(mut self, enabled: bool) -> Self {
        self.spa_mode = enabled;
        self
    }

    /// Serve file
    pub fn serve_file(&self, request_path: &str) -> FileResponse {
        // Sanitize path
        let clean_path = self.sanitize_path(request_path);
        let full_path = self.root_dir.join(&clean_path);

        // Check if path exists and is within root
        if !full_path.starts_with(&self.root_dir) || !full_path.exists() {
            if self.spa_mode {
                // Try serving index.html
                let index_path = self.root_dir.join("index.html");
                if index_path.exists() {
                    return self.read_file(&index_path);
                }
            }
            return file_not_found();
        }

        // If directory, try index file
        if full_path.is_dir() {
            for index in &self.index_files {
                let index_path = full_path.join(index);
                if index_path.exists() {
                    return self.read_file(&index_path);
                }
            }
            return file_not_found();
        }

        self.read_file(&full_path)
    }

    /// Read file into response
    fn read_file(&self, path: &Path) -> FileResponse {
        match fs::read(path) {
            Ok(content) => {
                let mime_type = get_mime_type(path);
                FileResponse::new(path.to_path_buf(), content, mime_type)
                    .with_cache_control("public, max-age=3600")
            },
            Err(_) => file_not_found(),
        }
    }

    /// Sanitize path to prevent directory traversal
    fn sanitize_path(&self, path: &str) -> String {
        // Remove query string
        let path = path.split('?').next().unwrap_or(path);

        // Remove leading slash
        let path = path.trim_start_matches('/');

        // Remove any .. components
        path.split('/')
            .filter(|part| *part != ".." && *part != ".")
            .collect::<Vec<_>>()
            .join("/")
    }

    /// Generate ETag for file
    pub fn generate_etag(&self, path: &Path) -> Option<String> {
        let metadata = fs::metadata(path).ok()?;
        let modified = metadata.modified().ok()?;
        let timestamp = modified.duration_since(std::time::UNIX_EPOCH).ok()?.as_secs();
        Some(format!(r#""{}""#, timestamp))
    }
}

// 4. File Caching

/// Cached file entry
#[derive(Debug, Clone)]
pub struct CachedFile {
    pub content: Vec<u8>,
    pub content_type: String,
    pub etag: String,
    pub last_modified: u64,
}

/// File cache
pub struct FileCache {
    cache: HashMap<String, CachedFile>,
    max_size: usize,
    max_age: u64,
}

impl FileCache {
    pub fn new(max_size: usize, max_age_seconds: u64) -> Self {
        FileCache {
            cache: HashMap::new(),
            max_size,
            max_age: max_age_seconds,
        }
    }

    /// Get from cache
    pub fn get(&self, path: &str) -> Option<&CachedFile> {
        self.cache.get(path)
    }

    /// Insert into cache
    pub fn insert(&mut self, path: String, file: CachedFile) {
        // Remove oldest if at capacity
        if self.cache.len() >= self.max_size {
            // Simple FIFO - in production use LRU
            if let Some(key) = self.cache.keys().next() {
                self.cache.remove(key);
            }
        }
        self.cache.insert(path, file);
    }

    /// Clear cache
    pub fn clear(&mut self) {
        self.cache.clear();
    }

    /// Remove expired entries
    pub fn remove_expired(&mut self, current_time: u64) {
        self.cache.retain(|_, file| {
            current_time - file.last_modified < self.max_age
        });
    }
}

// 5. Range Request Support

/// Parse Range header
#[derive(Debug, Clone)]
pub struct Range {
    pub start: u64,
    pub end: Option<u64>,
}

pub fn parse_range_header(range_header: &str, file_size: u64) -> Option<Range> {
    if !range_header.starts_with("bytes=") {
        return None;
    }

    let range_spec = &range_header[6..];
    let parts: Vec<&str> = range_spec.split('-').collect();

    if parts.len() != 2 {
        return None;
    }

    let start = parts[0].parse::<u64>().ok()?;
    let end = if parts[1].is_empty() {
        None
    } else {
        Some(parts[1].parse::<u64>().ok()?)
    };

    // Validate range
    if start >= file_size {
        return None;
    }

    Some(Range {
        start,
        end: end.map(|e| e.min(file_size - 1)),
    })
}

/// Serve file with range support
pub fn serve_file_range(content: &[u8], range: &Range) -> (Vec<u8>, String) {
    let end = range.end.unwrap_or(content.len() as u64 - 1) as usize;
    let start = range.start as usize;

    let range_content = content[start..=end].to_vec();
    let content_range = format!("bytes {}-{}/{}", start, end, content.len());

    (range_content, content_range)
}

// 6. File Compression

/// Compress content (simplified)
pub fn compress_content(content: &[u8], encoding: &str) -> Option<Vec<u8>> {
    match encoding {
        "gzip" => {
            // In real implementation, use flate2 crate
            Some(content.to_vec())
        },
        "deflate" => {
            // In real implementation, use flate2 crate
            Some(content.to_vec())
        },
        "br" => {
            // In real implementation, use brotli crate
            Some(content.to_vec())
        },
        _ => None,
    }
}

// 7. Virtual File System

/// Virtual file (embedded)
pub struct VirtualFile {
    pub path: String,
    pub content: &'static [u8],
    pub content_type: &'static str,
}

/// Virtual file system
pub struct VirtualFileSystem {
    files: HashMap<String, VirtualFile>,
}

impl VirtualFileSystem {
    pub fn new() -> Self {
        VirtualFileSystem {
            files: HashMap::new(),
        }
    }

    /// Add virtual file
    pub fn add_file(&mut self, file: VirtualFile) {
        self.files.insert(file.path.clone(), file);
    }

    /// Get virtual file
    pub fn get_file(&self, path: &str) -> Option<&VirtualFile> {
        self.files.get(path)
    }

    /// Serve virtual file
    pub fn serve(&self, path: &str) -> Option<FileResponse> {
        let file = self.get_file(path)?;

        Some(FileResponse {
            path: PathBuf::from(path),
            content_type: file.content_type.to_string(),
            content: file.content.to_vec(),
            status_code: 200,
            headers: HashMap::new(),
        })
    }
}

impl Default for VirtualFileSystem {
    fn default() -> Self {
        Self::new()
    }
}

// 8. Static Assets Bundle

/// Predefined asset bundle
pub struct AssetBundle {
    assets: HashMap<String, &'static [u8]>,
    mime_types: HashMap<String, &'static str>,
}

impl AssetBundle {
    pub fn new() -> Self {
        AssetBundle {
            assets: HashMap::new(),
            mime_types: HashMap::new(),
        }
    }

    /// Add asset
    pub fn add_asset(mut self, path: &str, content: &'static [u8]) -> Self {
        let mime = get_mime_type(Path::new(path));
        self.mime_types.insert(path.to_string(), mime);
        self.assets.insert(path.to_string(), content);
        self
    }

    /// Serve asset
    pub fn serve(&self, path: &str) -> Option<FileResponse> {
        let content = self.assets.get(path)?;
        let mime_type = self.mime_types.get(path)?;

        Some(FileResponse {
            path: PathBuf::from(path),
            content_type: mime_type.to_string(),
            content: content.to_vec(),
            status_code: 200,
            headers: HashMap::new(),
        })
    }
}

impl Default for AssetBundle {
    fn default() -> Self {
        Self::new()
    }
}

// 9. Directory Listing

/// Directory entry
#[derive(Debug, Clone)]
pub struct DirectoryEntry {
    pub name: String,
    pub is_dir: bool,
    pub size: Option<u64>,
}

/// List directory contents
pub fn list_directory(path: &Path) -> Vec<DirectoryEntry> {
    let mut entries = Vec::new();

    if let Ok(read_dir) = fs::read_dir(path) {
        for entry in read_dir.filter_map(|e| e.ok()) {
            let metadata = entry.metadata().ok();
            let name = entry.file_name().to_string_lossy().to_string();

            entries.push(DirectoryEntry {
                name,
                is_dir: metadata.as_ref().map(|m| m.is_dir()).unwrap_or(false),
                size: metadata.and_then(|m| if m.is_file() { Some(m.len()) } else { None }),
            });
        }
    }

    entries.sort_by(|a, b| {
        if a.is_dir && !b.is_dir {
            std::cmp::Ordering::Less
        } else if !a.is_dir && b.is_dir {
            std::cmp::Ordering::Greater
        } else {
            a.name.cmp(&b.name)
        }
    });

    entries
}

// 10. Usage Examples

fn main() {
    println!("=== Web Rust Static Files Examples ===\n");

    // 1. MIME type detection
    println!("--- 1. MIME Type Detection ---");
    let files = [
        "index.html",
        "styles.css",
        "app.js",
        "image.png",
        "data.json",
        "document.pdf",
    ];

    for file in &files {
        let path = Path::new(file);
        println!("{} -> {}", file, get_mime_type(path));
    }

    // 2. Binary file detection
    println!("
--- 2. Binary File Detection ---");
    println!("index.html is binary: {}", is_binary_file(Path::new("index.html")));
    println!("image.png is binary: {}", is_binary_file(Path::new("image.png")));

    // 3. Path sanitization
    println!("
--- 3. Path Sanitization ---");
    let server = StaticFileServer::new("/var/www");
    let paths = [
        "/index.html",
        "/../etc/passwd",
        "/files/../../secret.txt",
        "/normal/path.js",
    ];

    for path in &paths {
        println!("Original: {}", path);
        println!("Sanitized: {}", server.sanitize_path(path));
    }

    // 4. Virtual file system
    println!("
--- 4. Virtual File System ---");
    let mut vfs = VirtualFileSystem::new();
    vfs.add_file(VirtualFile {
        path: "/index.html".to_string(),
        content: b"<html><body>Hello</body></html>",
        content_type: "text/html",
    });

    if let Some(response) = vfs.serve("/index.html") {
        println!("Virtual file served: {} bytes", response.content.len());
    }

    // 5. Asset bundle
    println!("
--- 5. Asset Bundle ---");
    let bundle = AssetBundle::new()
        .add_asset("/favicon.ico", b"icon_data")
        .add_asset("/robots.txt", b"User-agent: *");

    if let Some(response) = bundle.serve("/robots.txt") {
        println!("Asset served: {}", String::from_utf8_lossy(&response.content));
    }

    // 6. Range request parsing
    println!("
--- 6. Range Request ---");
    let range_header = "bytes=0-1023";
    let file_size = 5000;
    if let Some(range) = parse_range_header(range_header, file_size) {
        let end = range.end.unwrap_or(file_size.saturating_sub(1));
        println!("Range: {}-{}", range.start, end);
    }

    // 7. Directory listing
    println!("
--- 7. Directory Listing ---");
    let entries = list_directory(Path::new("."));
    println!("Current directory contents:");
    for entry in entries.iter().take(5) {
        let type_marker = if entry.is_dir { "[DIR]" } else { "[FILE]" };
        println!("  {} {} {:?}", type_marker, entry.name, entry.size);
    }

    // 8. File cache
    println!("
--- 8. File Cache ---");
    let mut cache = FileCache::new(100, 3600);
    cache.insert("test.html".to_string(), CachedFile {
        content: b"<html></html>".to_vec(),
        content_type: "text/html".to_string(),
        etag: r#""12345""#.to_string(),
        last_modified: 1234567890,
    });

    if let Some(cached) = cache.get("test.html") {
        println!("Cached file: {}, etag: {}", cached.content_type, cached.etag);
    }

    println!("
=== Note ===");
    println!("For production static file serving, use:");
    println!("- rust-embed: https://github.com/pyros2097/rust-embed");
    println!("- mime_guess: https://github.com/sfstewa/mime_guess");
    println!("- Actix static files: https://actix.rs/docs/static-files");
    println!("- Axum static files: https://docs.rs/axum/latest/axum/struct.Router.html#method.nest_service");

    println!("
=== All Static Files Examples Completed ===");
}

💻 Middleware rust

🔴 complex ⭐⭐⭐⭐

Request processing middleware pipeline

⏱️ 35 min 🏷️ rust, web, web features
Prerequisites: Advanced Rust, HTTP knowledge
// Web Rust Middleware Examples
// Request processing middleware pipeline
//
// For production web applications, consider using:
// - Actix Web: https://actix.rs/
// - Axum: https://github.com/tokio-rs/axum
// - Tower: https://github.com/tower-rs/tower

use std::time::{SystemTime, UNIX_EPOCH};
use std::collections::HashMap;

// 1. Request/Response Types

/// HTTP Request
#[derive(Debug, Clone)]
pub struct Request {
    pub method: String,
    pub path: String,
    pub headers: HashMap<String, String>,
    pub body: Option<String>,
    pub state: HashMap<String, String>,
}

/// HTTP Response
#[derive(Debug, Clone)]
pub struct Response {
    pub status_code: u16,
    pub headers: HashMap<String, String>,
    pub body: String,
}

impl Response {
    pub fn new(status_code: u16, body: &str) -> Self {
        Response {
            status_code,
            headers: HashMap::new(),
            body: body.to_string(),
        }
    }

    pub fn ok(body: &str) -> Self {
        Response::new(200, body)
    }

    pub fn not_found() -> Self {
        Response::new(404, "Not Found")
    }

    pub fn unauthorized() -> Self {
        Response::new(401, "Unauthorized")
    }

    pub fn forbidden() -> Self {
        Response::new(403, "Forbidden")
    }

    pub fn server_error() -> Self {
        Response::new(500, "Internal Server Error")
    }

    pub fn with_header(mut self, key: &str, value: &str) -> Self {
        self.headers.insert(key.to_string(), value.to_string());
        self
    }
}

// 2. Middleware Types

/// Middleware function type
pub type Middleware = Box<dyn Fn(Request) -> Result<Request, Response> + Send + Sync>;

/// Middleware result
pub type MiddlewareResult = Result<Request, Response>;

/// Next function in chain
pub type Next = Box<dyn Fn(Request) -> Response + Send + Sync>;

/// Async-style middleware (simplified)
pub type AsyncMiddleware = Box<dyn Fn(Request, Next) -> Response + Send + Sync>;

// 3. Common Middleware

/// Logging middleware
pub fn logging_middleware() -> Middleware {
    Box::new(|mut request: Request| -> MiddlewareResult {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        println!("[{}] {} {}", timestamp, request.method, request.path);

        // Add request timing
        request.state.insert("start_time".to_string(), timestamp.to_string());

        Ok(request)
    })
}

/// CORS middleware
pub fn cors_middleware(allowed_origins: Vec<String>) -> Middleware {
    Box::new(move |request: Request| -> MiddlewareResult {
        let origin = request.headers.get("origin").map(|s| s.as_str());

        if let Some(origin) = origin {
            if allowed_origins.contains(&origin.to_string()) || allowed_origins.contains(&"*".to_string()) {
                return Ok(request);
            }
        }

        Err(Response::new(403, "Origin not allowed")
            .with_header("Access-Control-Allow-Origin", "*"))
    })
}

/// Authentication middleware
pub fn auth_middleware() -> Middleware {
    Box::new(|request: Request| -> MiddlewareResult {
        let auth_header = request.headers.get("authorization");

        match auth_header {
            Some(token) if token.starts_with("Bearer ") => {
                // Validate token (simplified)
                let token_value = &token[7..];
                if token_value == "valid_token" {
                    Ok(request)
                } else {
                    Err(Response::unauthorized())
                }
            },
            Some(_) => Err(Response::unauthorized()
                .with_header("WWW-Authenticate", "Bearer")),
            None => Err(Response::unauthorized()
                .with_header("WWW-Authenticate", "Bearer")),
        }
    })
}

/// Content-Type middleware
pub fn content_type_middleware() -> Middleware {
    Box::new(|mut request: Request| -> MiddlewareResult {
        let content_type = request.headers.get("content-type").cloned();

        if let Some(ct) = content_type {
            request.state.insert("content_type".to_string(), ct);
        }

        Ok(request)
    })
}

/// Query parser middleware
pub fn query_parser_middleware() -> Middleware {
    Box::new(|mut request: Request| -> MiddlewareResult {
        if let Some(pos) = request.path.find('?') {
            let query_string = &request.path[pos + 1..];
            let params = parse_query_string(query_string);
            request.state.insert("query_params".to_string(), serde_json::to_string(&params).unwrap());
        }

        Ok(request)
    })
}

/// Parse query string
fn parse_query_string(query: &str) -> HashMap<String, String> {
    let mut params = HashMap::new();
    for pair in query.split('&') {
        let parts: Vec<&str> = pair.splitn(2, '=').collect();
        if parts.len() == 2 {
            params.insert(parts[0].to_string(), parts[1].to_string());
        }
    }
    params
}

// 4. Rate Limiting Middleware

/// Rate limiter
pub struct RateLimiter {
    requests: Vec<u64>,
    max_requests: usize,
    window_ms: u64,
}

impl RateLimiter {
    pub fn new(max_requests: usize, window_ms: u64) -> Self {
        RateLimiter {
            requests: Vec::new(),
            max_requests,
            window_ms,
        }
    }

    pub fn check(&mut self) -> bool {
        let now = current_timestamp();
        let window_start = now - self.window_ms;

        // Clean old requests
        self.requests.retain(|&t| t > window_start);

        if self.requests.len() < self.max_requests {
            self.requests.push(now);
            true
        } else {
            false
        }
    }
}

fn current_timestamp() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis() as u64
}

/// Rate limiting middleware
pub fn rate_limit_middleware(max_requests: usize, window_ms: u64) -> Middleware {
    Box::new(move |_| -> MiddlewareResult {
        use std::sync::{Arc, Mutex};
        thread_local! {
            static LIMITER: Arc<Mutex<RateLimiter>> = Arc::new(Mutex::new(RateLimiter::new(max_requests, window_ms)));
        }

        LIMITER.with(|limiter| {
            let mut limiter = limiter.lock().unwrap();
            if limiter.check() {
                Ok(Request {
                    method: "GET".to_string(),
                    path: "/".to_string(),
                    headers: HashMap::new(),
                    body: None,
                    state: HashMap::new(),
                })
            } else {
                Err(Response::new(429, "Too Many Requests")
                    .with_header("Retry-After", &format!("{}", window_ms / 1000)))
            }
        })
    })
}

// 5. Compression Middleware

/// Compression middleware
pub fn compression_middleware() -> AsyncMiddleware {
    Box::new(|request: Request, next: Next| -> Response {
        let accepts_encoding = request.headers.get("accept-encoding")
            .map(|s| s.contains("gzip"))
            .unwrap_or(false);

        let mut response = next(request);

        if accepts_encoding {
            response.headers.insert("Content-Encoding".to_string(), "gzip".to_string());
            // In real implementation, would compress body here
        }

        response
    })
}

// 6. Security Headers Middleware

/// Security headers middleware
pub fn security_headers_middleware() -> AsyncMiddleware {
    Box::new(|request: Request, next: Next| -> Response {
        let mut response = next(request);

        response.headers.insert("X-Content-Type-Options".to_string(), "nosniff".to_string());
        response.headers.insert("X-Frame-Options".to_string(), "DENY".to_string());
        response.headers.insert("X-XSS-Protection".to_string(), "1; mode=block".to_string());
        response.headers.insert("Strict-Transport-Security".to_string(), "max-age=31536000".to_string());

        response
    })
}

// 7. Request ID Middleware

/// Request ID middleware
pub fn request_id_middleware() -> Middleware {
    Box::new(|mut request: Request| -> MiddlewareResult {
        let request_id = generate_id();
        request.state.insert("request_id".to_string(), request_id.clone());
        request.headers.insert("X-Request-ID".to_string(), request_id);

        Ok(request)
    })
}

/// Generate unique ID
fn generate_id() -> String {
    format!("{:x}", current_timestamp())
}

// 8. Timeout Middleware

/// Timeout middleware (simplified)
pub fn timeout_middleware(timeout_ms: u64) -> AsyncMiddleware {
    Box::new(move |request: Request, next: Next| -> Response {
        let start = current_timestamp();

        let response = next(request);

        let elapsed = current_timestamp() - start;

        if elapsed > timeout_ms {
            Response::new(408, "Request Timeout")
                .with_header("X-Timeout", "true")
        } else {
            response
        }
    })
}

// 9. Body Size Limit Middleware

/// Body size limit middleware
pub fn body_size_limit_middleware(max_bytes: usize) -> Middleware {
    Box::new(move |request: Request| -> MiddlewareResult {
        if let Some(body) = &request.body {
            if body.len() > max_bytes {
                return Err(Response::new(413, "Payload Too Large")
                    .with_header("Content-Type", "text/plain"));
            }
        }
        Ok(request)
    })
}

// 10. Middleware Chain

/// Middleware chain
pub struct MiddlewareChain {
    middleware: Vec<Middleware>,
    handler: Box<dyn Fn(Request) -> Response + Send + Sync>,
}

impl MiddlewareChain {
    pub fn new() -> Self {
        MiddlewareChain {
            middleware: Vec::new(),
            handler: Box::new(|_| Response::not_found()),
        }
    }

    pub fn add_middleware(mut self, mw: Middleware) -> Self {
        self.middleware.push(mw);
        self
    }

    pub fn set_handler<H>(mut self, handler: H) -> Self
    where
        H: Fn(Request) -> Response + Send + Sync + 'static,
    {
        self.handler = Box::new(handler);
        self
    }

    pub fn process(&self, mut request: Request) -> Response {
        // Run middleware
        for mw in &self.middleware {
            match mw(request) {
                Ok(req) => request = req,
                Err(response) => return response,
            }
        }

        // Run handler
        (self.handler)(request)
    }
}

impl Default for MiddlewareChain {
    fn default() -> Self {
        Self::new()
    }
}

// 11. Middleware Combiner

/// Combine multiple middleware
pub fn combine_middleware(middleware: Vec<Middleware>) -> Middleware {
    Box::new(move |request: Request| -> MiddlewareResult {
        let mut request = request;
        for mw in &middleware {
            request = mw(request)?;
        }
        Ok(request)
    })
}

// Usage Examples
fn main() {
    println!("=== Web Rust Middleware Examples ===\n");

    // 1. Basic middleware
    println!("--- 1. Basic Middleware ---");
    let request = Request {
        method: "GET".to_string(),
        path: "/test".to_string(),
        headers: HashMap::new(),
        body: None,
        state: HashMap::new(),
    };

    let logged = logging_middleware()(request).unwrap();
    println!("Logged request: {}", logged.path);

    // 2. Authentication
    println!("
--- 2. Authentication Middleware ---");
    let mut auth_request = Request {
        method: "GET".to_string(),
        path: "/protected".to_string(),
        headers: {
            let mut map = HashMap::new();
            map.insert("authorization".to_string(), "Bearer valid_token".to_string());
            map
        },
        body: None,
        state: HashMap::new(),
    };

    match auth_middleware()(auth_request) {
        Ok(_) => println!("Request authenticated"),
        Err(response) => println!("Authentication failed: {}", response.status_code),
    }

    // 3. CORS
    println!("
--- 3. CORS Middleware ---");
    let mut cors_request = Request {
        method: "GET".to_string(),
        path: "/api/data".to_string(),
        headers: {
            let mut map = HashMap::new();
            map.insert("origin".to_string(), "https://example.com".to_string());
            map
        },
        body: None,
        state: HashMap::new(),
    };

    let allowed = vec!["https://example.com".to_string()];
    match cors_middleware(allowed)(cors_request) {
        Ok(_) => println!("CORS check passed"),
        Err(response) => println!("CORS blocked: {}", response.status_code),
    }

    // 4. Middleware chain
    println!("
--- 4. Middleware Chain ---");
    let chain = MiddlewareChain::new()
        .add_middleware(logging_middleware())
        .add_middleware(request_id_middleware())
        .add_middleware(content_type_middleware())
        .set_handler(|req| Response::ok(&format!("Handled: {}", req.path)));

    let response = chain.process(Request {
        method: "GET".to_string(),
        path: "/test".to_string(),
        headers: HashMap::new(),
        body: None,
        state: HashMap::new(),
    });

    println!("Chain response: {}", response.status_code);

    // 5. Combined middleware
    println!("
--- 5. Combined Middleware ---");
    let combined = combine_middleware(vec![
        logging_middleware(),
        content_type_middleware(),
        query_parser_middleware(),
    ]);

    let test_request = Request {
        method: "GET".to_string(),
        path: "/search?q=rust&lang=en".to_string(),
        headers: HashMap::new(),
        body: None,
        state: HashMap::new(),
    };

    match combined(test_request) {
        Ok(req) => println!("All middleware passed. Query params in state: {}",
            req.state.get("query_params").is_some()),
        Err(resp) => println!("Middleware chain failed: {}", resp.status_code),
    }

    // 6. Rate limiting
    println!("
--- 6. Rate Limiting ---");
    let mut limiter = RateLimiter::new(5, 1000);
    for i in 0..7 {
        let allowed = limiter.check();
        println!("Request {} allowed: {}", i + 1, allowed);
    }

    // 7. Security headers
    println!("
--- 7. Security Headers ---");
    let sec_chain = MiddlewareChain::new()
        .set_handler(|req| Response::ok("Secure content"));

    let response = sec_chain.process(Request {
        method: "GET".to_string(),
        path: "/secure".to_string(),
        headers: HashMap::new(),
        body: None,
        state: HashMap::new(),
    });

    println!("Security headers: {:?}", response.headers.keys().collect::<Vec<_>>());

    // 8. Body size limit
    println!("
--- 8. Body Size Limit ---");
    let large_body = "x".repeat(1_000_000);
    let large_request = Request {
        method: "POST".to_string(),
        path: "/upload".to_string(),
        headers: HashMap::new(),
        body: Some(large_body),
        state: HashMap::new(),
    };

    match body_size_limit_middleware(100_000)(large_request) {
        Ok(_) => println!("Body size acceptable"),
        Err(response) => println!("Body too large: {}", response.status_code),
    }

    println!("
=== Note ===");
    println!("For production middleware, use:");
    println!("- Actix Web: https://actix.rs/");
    println!("- Axum: https://github.com/tokio-rs/axum");
    println!("- Tower: https://github.com/tower-rs/tower");

    println!("
=== All Middleware Examples Completed ===");
}