Recursos Web macOS Swift - Exemplos

Exemplos de recursos web macOS Swift incluindo roteamento URL, pipeline de middleware e serviço de arquivos estáticos

💻 Roteamento de Requisições HTTP swift

🟡 intermediate ⭐⭐⭐

Implementar roteamento flexível de requisições HTTP com correspondência de padrões, extração de parâmetros e suporte RESTful API

⏱️ 30 min 🏷️ swift, macos, web, routing, http
Prerequisites: Swift basics, HTTP protocol, Regular expressions
// macOS Swift Web Routing Examples
// Using Foundation and Vapor-inspired routing patterns

import Foundation

// MARK: - HTTP Models

// HTTP Request representation
struct HTTPRequest {
    let method: HTTPMethod
    let path: String
    let headers: [String: String]
    let body: Data?
    var queryParameters: [String: String] = [:]
    var pathParameters: [String: String] = [:]
}

enum HTTPMethod: String {
    case GET = "GET"
    case POST = "POST"
    case PUT = "PUT"
    case DELETE = "DELETE"
    case PATCH = "PATCH"
    case HEAD = "HEAD"
    case OPTIONS = "OPTIONS"
    case ANY = "*"
}

// HTTP Response representation
struct HTTPResponse {
    var statusCode: HTTPStatusCode = .ok
    var headers: [String: String] = [:]
    var body: Data?

    init(statusCode: HTTPStatusCode = .ok, headers: [String: String] = [:], body: Data? = nil) {
        self.statusCode = statusCode
        self.headers = headers
        self.body = body
    }

    func setContentType(_ contentType: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = contentType
        return response
    }

    func setJSON(_ json: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = "application/json"
        response.body = json.data(using: .utf8)
        return response
    }

    func setHTML(_ html: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = "text/html"
        response.body = html.data(using: .utf8)
        return response
    }

    func setText(_ text: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = "text/plain"
        response.body = text.data(using: .utf8)
        return response
    }
}

enum HTTPStatusCode: Int {
    case ok = 200
    case created = 201
    case accepted = 202
    case noContent = 204
    case movedPermanently = 301
    case found = 302
    case notModified = 304
    case badRequest = 400
    case unauthorized = 401
    case forbidden = 403
    case notFound = 404
    case methodNotAllowed = 405
    case conflict = 409
    case internalServerError = 500
    case notImplemented = 501
    case badGateway = 502
    case serviceUnavailable = 503
}

// MARK: - Route Handler

typealias RouteHandler = (HTTPRequest) -> HTTPResponse

// MARK: - Route

class Route {
    let method: HTTPMethod
    let pattern: String
    let handler: RouteHandler
    let regex: NSRegularExpression
    let parameterNames: [String]

    init(method: HTTPMethod, pattern: String, handler: @escaping RouteHandler) {
        self.method = method
        self.pattern = pattern
        self.handler = handler

        // Convert route pattern to regex
        let (regexString, params) = Route.compilePattern(pattern)
        self.regex = try! NSRegularExpression(pattern: regexString, options: [])
        self.parameterNames = params
    }

    // Check if route matches request
    func matches(request: HTTPRequest) -> Bool {
        if method != .ANY && method != request.method {
            return false
        }

        let range = NSRange(location: 0, length: request.path.utf16.count)
        return regex.firstMatch(in: request.path, options: [], range: range) != nil
    }

    // Extract path parameters from request
    func extractParameters(from request: HTTPRequest) -> [String: String] {
        var parameters: [String: String] = [:]

        let range = NSRange(location: 0, length: request.path.utf16.count)
        if let match = regex.firstMatch(in: request.path, options: [], range: range) {
            for (index, name) in parameterNames.enumerated() {
                let captureRange = match.range(at: index + 1)
                if captureRange.location != NSNotFound,
                   let range = Range(captureRange, in: request.path) {
                    parameters[name] = String(request.path[range])
                }
            }
        }

        return parameters
    }

    // Compile route pattern to regex
    private static func compilePattern(_ pattern: String) -> (String, [String]) {
        var regexPattern = "^"
        var params: [String] = []
        var current = pattern.startIndex

        while current < pattern.endIndex {
            if pattern[current] == ":" {
                // Parameter found
                let paramStart = pattern.index(after: current)
                if let paramEnd = pattern[paramStart...].firstIndex(where: { $0 == "/" || $0 == "." }) {
                    let paramName = String(pattern[paramStart..<paramEnd])
                    params.append(paramName)
                    regexPattern += "([^/\.]+)"
                    current = paramEnd
                } else {
                    let paramName = String(pattern[paramStart...])
                    params.append(paramName)
                    regexPattern += "([^/]+)"
                    current = pattern.endIndex
                }
            } else if pattern[current] == "*" {
                // Wildcard
                regexPattern += ".*"
                current = pattern.index(after: current)
            } else {
                // Regular character
                regexPattern += NSRegularExpression.escapedPattern(for: String(pattern[current]))
                current = pattern.index(after: current)
            }
        }

        regexPattern += "$"
        return (regexPattern, params)
    }
}

// MARK: - Router

class Router {
    private var routes: [Route] = []

    // Add route
    @discardableResult
    func add(method: HTTPMethod, path: String, handler: @escaping RouteHandler) -> Router {
        let route = Route(method: method, pattern: path, handler: handler)
        routes.append(route)
        print("Added route: \(method.rawValue) \(path)")
        return self
    }

    // Convenience methods
    func get(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .GET, path: path, handler: handler)
    }

    func post(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .POST, path: path, handler: handler)
    }

    func put(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .PUT, path: path, handler: handler)
    }

    func delete(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .DELETE, path: path, handler: handler)
    }

    func patch(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .PATCH, path: path, handler: handler)
    }

    func any(_ path: String, handler: @escaping RouteHandler) -> Router {
        add(method: .ANY, path: path, handler: handler)
    }

    // Route request
    func route(request: HTTPRequest) -> HTTPResponse {
        for route in routes {
            if route.matches(request: request) {
                var routedRequest = request
                routedRequest.pathParameters = route.extractParameters(from: request)

                print("Routing: \(request.method.rawValue) \(request.path) -> \(route.pattern)")

                do {
                    return route.handler(routedRequest)
                } catch {
                    return HTTPResponse(statusCode: .internalServerError, body: "Internal Server Error".data(using: .utf8))
                }
            }
        }

        // No matching route
        return HTTPResponse(statusCode: .notFound).setText("404 Not Found")
    }

    // Group routes with prefix
    func group(prefix: String, configure: (Router) -> Void) -> Router {
        let groupRouter = Router()
        configure(groupRouter)

        // Add all routes from group with prefix
        for route in groupRouter.routes {
            let fullPath = prefix + route.pattern
            let newRoute = Route(method: route.method, pattern: fullPath, handler: route.handler)
            routes.append(newRoute)
        }

        return self
    }
}

// MARK: - RESTful API Builder

class RESTfulAPI {
    let router = Router()
    private let resourceName: String

    init(resourceName: String) {
        self.resourceName = resourceName
        setupDefaultRoutes()
    }

    private func setupDefaultRoutes() {
        let path = "/\(resourceName)"

        // List all resources
        router.get(path) { request in
            let response = HTTPResponse()
            let json = """
            {
                "resource": "\(self.resourceName)",
                "items": [
                    {"id": 1, "name": "Item 1"},
                    {"id": 2, "name": "Item 2"},
                    {"id": 3, "name": "Item 3"}
                ],
                "count": 3
            }
            """
            return response.setJSON(json)
        }

        // Get specific resource
        router.get("\(path)/:id") { request in
            let id = request.pathParameters["id"] ?? "0"
            let response = HTTPResponse()
            let json = """
            {
                "id": \(id),
                "name": "Item \(id)",
                "description": "This is item \(id)"
            }
            """
            return response.setJSON(json)
        }

        // Create new resource
        router.post(path) { request in
            let response = HTTPResponse(statusCode: .created)
            let json = """
            {
                "id": 4,
                "status": "created",
                "message": "Resource created successfully"
            }
            """
            return response.setJSON(json)
        }

        // Update resource
        router.put("\(path)/:id") { request in
            let id = request.pathParameters["id"] ?? "0"
            let response = HTTPResponse()
            let json = """
            {
                "id": \(id),
                "status": "updated",
                "message": "Resource \(id) updated successfully"
            }
            """
            return response.setJSON(json)
        }

        // Delete resource
        router.delete("\(path)/:id") { request in
            let id = request.pathParameters["id"] ?? "0"
            let response = HTTPResponse()
            let json = """
            {
                "id": \(id),
                "status": "deleted",
                "message": "Resource \(id) deleted successfully"
            }
            """
            return response.setJSON(json)
        }
    }

    // Customize routes
    func customize(using builder: (Router) -> Void) -> RESTfulAPI {
        builder(router)
        return self
    }

    // Get router
    func build() -> Router {
        return router
    }
}

// MARK: - Route Middleware

class RouteMiddleware {
    let handler: (HTTPRequest, @escaping (HTTPRequest) -> HTTPResponse) -> HTTPResponse

    init(handler: @escaping (HTTPRequest, @escaping (HTTPRequest) -> HTTPResponse) -> HTTPResponse) {
        self.handler = handler
    }

    func process(request: HTTPRequest, next: @escaping (HTTPRequest) -> HTTPResponse) -> HTTPResponse {
        return handler(request, next)
    }
}

// Middleware implementations
extension RouteMiddleware {
    // Logging middleware
    static func logging() -> RouteMiddleware {
        return RouteMiddleware { request, next in
            print("[\(Date())] \(request.method.rawValue) \(request.path)")
            let response = next(request)
            print("[Response] \(response.statusCode.rawValue)")
            return response
        }
    }

    // Authentication middleware
    static func authenticate() -> RouteMiddleware {
        return RouteMiddleware { request, next in
            if let auth = request.headers["Authorization"], auth.hasPrefix("Bearer ") {
                return next(request)
            } else {
                return HTTPResponse(statusCode: .unauthorized).setJSON("{"error": "Unauthorized"}")
            }
        }
    }

    // CORS middleware
    static func cors() -> RouteMiddleware {
        return RouteMiddleware { request, next in
            var response = next(request)

            if request.method == .OPTIONS {
                response.headers["Access-Control-Allow-Origin"] = "*"
                response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
                response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
                return response
            }

            response.headers["Access-Control-Allow-Origin"] = "*"
            return response
        }
    }
}

// MARK: - Demonstration

func demonstrateWebRouting() {
    print("=== macOS Swift Web Routing Examples ===\n")

    // 1. Basic router setup
    print("--- 1. Basic Router Setup ---")
    let router = Router()

    // Home page
    router.get("/") { request in
        let html = """
        <!DOCTYPE html>
        <html>
        <head><title>Swift Routing Demo</title></head>
        <body>
            <h1>Welcome to Swift Routing!</h1>
            <p>Try these routes:</p>
            <ul>
                <li><a href="/api/hello">GET /api/hello</a></li>
                <li><a href="/api/hello/John">GET /api/hello/John</a></li>
                <li><a href="/api/users">GET /api/users</a></li>
                <li><a href="/api/users/123">GET /api/users/123</a></li>
                <li><a href="/api/search?q=swift">GET /api/search?q=swift</a></li>
            </ul>
        </body>
        </html>
        """
        return HTTPResponse().setHTML(html)
    }

    // Simple API endpoint
    router.get("/api/hello") { request in
        return HTTPResponse().setJSON("{"message": "Hello, World!"}")
    }

    // Parameterized route
    router.get("/api/hello/:name") { request in
        let name = request.pathParameters["name"] ?? "Guest"
        return HTTPResponse().setJSON("{"message": "Hello, \(name)!"}")
    }

    // Multiple parameters
    router.get("/api/users/:userId/posts/:postId") { request in
        let userId = request.pathParameters["userId"] ?? "0"
        let postId = request.pathParameters["postId"] ?? "0"
        return HTTPResponse().setJSON("{"userId": \(userId), "postId": \(postId)}")
    }

    // Wildcard route
    router.get("/api/files/*") { request in
        return HTTPResponse().setJSON("{"path": "\(request.path)"}")
    }

    // 2. RESTful API
    print("\n--- 2. RESTful API ---")
    let userAPI = RESTfulAPI(resourceName: "users")

    // Customize with additional routes
    userAPI.customize { router in
        router.get("/users/:id/posts") { request in
            let id = request.pathParameters["id"] ?? "0"
            return HTTPResponse().setJSON("{"userId": \(id), "posts": []}")
        }
    }

    // Add RESTful routes to main router
    let apiRouter = userAPI.build()
    for route in apiRouter.routes {
        router.routes.append(route)
    }

    // 3. Route groups
    print("\n--- 3. Route Groups ---")
    router.group(prefix: "/api/v1") { group in
        group.get("/status") { request in
            return HTTPResponse().setJSON("{"version": "v1", "status": "ok"}")
        }

        group.get("/info") { request in
            return HTTPResponse().setJSON("{"version": "v1", "info": "API v1"}")
        }
    }

    // 4. Test routing
    print("\n--- 4. Testing Routes ---")

    let testRequests = [
        HTTPRequest(method: .GET, path: "/", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/hello", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/hello/John", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/users/123/posts/456", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/files/documents/report.pdf", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/users", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/users/42", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/api/v1/status", headers: [:], body: nil),
        HTTPRequest(method: .GET, path: "/nonexistent", headers: [:], body: nil)
    ]

    for request in testRequests {
        print("\nRequest: \(request.method.rawValue) \(request.path)")
        let response = router.route(request: request)
        print("Response: \(response.statusCode.rawValue)")

        if let body = response.body,
           let bodyString = String(data: body, encoding: .utf8),
           bodyString.count < 200 {
            print("Body: \(bodyString)")
        }
    }

    print("\n=== Web Routing Demo Completed ===")
}

// Run demonstration
demonstrateWebRouting()

💻 Middleware de Processamento de Requisições swift

🟡 intermediate ⭐⭐⭐⭐

Implementar pipeline de middleware para logging, autenticação, CORS e tratamento de erros

⏱️ 35 min 🏷️ swift, macos, web, middleware, pipeline
Prerequisites: Swift basics, HTTP protocol, Functional programming
// macOS Swift Middleware Pipeline Examples
// Using Foundation and async/await patterns

import Foundation

// MARK: - HTTP Context

struct HTTPContext {
    var request: HTTPRequest
    var response: HTTPResponse
    var metadata: [String: Any] = [:]

    init(request: HTTPRequest) {
        self.request = request
        self.response = HTTPResponse()
    }
}

// MARK: - Middleware Protocol

protocol Middleware {
    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext
}

// MARK: - Middleware Pipeline

class MiddlewarePipeline {
    private var middlewares: [Middleware] = []

    // Add middleware to pipeline
    @discardableResult
    func use(_ middleware: Middleware) -> MiddlewarePipeline {
        middlewares.append(middleware)
        print("Added middleware: \(type(of: middleware))")
        return self
    }

    // Process request through pipeline
    func process(request: HTTPRequest, finalHandler: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        var context = HTTPContext(request: request)

        // Build middleware chain
        let chain = buildChain(index: 0, finalHandler: finalHandler)

        return chain(context)
    }

    // Build middleware chain recursively
    private func buildChain(index: Int, finalHandler: @escaping (HTTPContext) -> HTTPContext) -> (HTTPContext) -> HTTPContext {
        if index < middlewares.count {
            let middleware = middlewares[index]
            return { context in
                let next = self.buildChain(index: index + 1, finalHandler: finalHandler)
                return middleware.process(context: context, next: next)
            }
        } else {
            return finalHandler
        }
    }
}

// MARK: - Middleware Implementations

// 1. Logging Middleware
class LoggingMiddleware: Middleware {
    let logLevel: LogLevel

    init(logLevel: LogLevel = .info) {
        self.logLevel = logLevel
    }

    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        let requestId = UUID().uuidString.prefix(8)
        let startTime = Date()

        log(.info, "[\(requestId]) \(context.request.method.rawValue) \(context.request.path) -> Started")

        // Store metadata
        context.metadata["requestId"] = String(requestId)
        context.metadata["startTime"] = startTime

        // Process next middleware
        let result = next(context)

        // Log completion
        let duration = Date().timeIntervalSince(startTime)
        log(.info, "[\(requestId)] \(context.request.method.rawValue) \(context.request.path) -> \(result.response.statusCode.rawValue) (\(String(format: "%.0f", duration * 1000))ms)")

        return result
    }

    private func log(_ level: LogLevel, _ message: String) {
        if level.rawValue >= logLevel.rawValue {
            print("[\(level)] \(message)")
        }
    }
}

enum LogLevel: Int, Comparable {
    case debug = 0
    case info = 1
    case warning = 2
    case error = 3

    static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

// 2. Authentication Middleware
class AuthenticationMiddleware: Middleware {
    let authPaths: Set<String>
    let excludePaths: Set<String>

    init(authPaths: Set<String>, excludePaths: Set<String> = []) {
        self.authPaths = authPaths
        self.excludePaths = excludePaths
    }

    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        let path = context.request.path

        // Check if path requires authentication
        if authPaths.contains(where: { path.hasPrefix($0) }) &&
           !excludePaths.contains(where: { path.hasPrefix($0) }) {

            // Check for Authorization header
            guard let authHeader = context.request.headers["Authorization"],
                  authHeader.hasPrefix("Bearer ") else {

                log(.warning, "Authentication required for: \(path)")
                var unauthorizedContext = context
                unauthorizedContext.response = HTTPResponse(statusCode: .unauthorized)
                    .setJSON("{"error": "Authentication required", "code": 401}")
                return unauthorizedContext
            }

            // Validate token (simplified)
            let token = String(authHeader.dropFirst(7))
            if !isValidToken(token) {
                log(.warning, "Invalid token for: \(path)")
                var forbiddenContext = context
                forbiddenContext.response = HTTPResponse(statusCode: .forbidden)
                    .setJSON("{"error": "Invalid token", "code": 403}")
                return forbiddenContext
            }

            // Store user info in context
            context.metadata["authenticated"] = true
            context.metadata["userId"] = extractUserId(from: token)

            log(.info, "User authenticated: \(context.metadata["userId"] ?? "unknown")")
        }

        return next(context)
    }

    private func isValidToken(_ token: String) -> Bool {
        // Simplified token validation
        return token.count > 20
    }

    private func extractUserId(from token: String) -> String {
        // Extract user ID from token (simplified)
        return "user_\(token.prefix(8))"
    }

    private func log(_ level: LogLevel, _ message: String) {
        print("[\(level)] \(message)")
    }
}

// 3. CORS Middleware
class CORSMiddleware: Middleware {
    let allowedOrigins: [String]
    let allowedMethods: [String]
    let allowedHeaders: [String]

    init(allowedOrigins: [String] = ["*"],
         allowedMethods: [String] = ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
         allowedHeaders: [String] = ["Content-Type", "Authorization"]) {
        self.allowedOrigins = allowedOrigins
        self.allowedMethods = allowedMethods
        self.allowedHeaders = allowedHeaders
    }

    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        // Handle preflight request
        if context.request.method == .OPTIONS {
            var preflightContext = context
            preflightContext.response.statusCode = .ok
            preflightContext.response.headers["Access-Control-Allow-Origin"] = allowedOrigins.first ?? "*"
            preflightContext.response.headers["Access-Control-Allow-Methods"] = allowedMethods.joined(separator: ", ")
            preflightContext.response.headers["Access-Control-Allow-Headers"] = allowedHeaders.joined(separator: ", ")
            preflightContext.response.headers["Access-Control-Max-Age"] = "86400"
            return preflightContext
        }

        // Add CORS headers to response
        let result = next(context)
        result.response.headers["Access-Control-Allow-Origin"] = allowedOrigins.first ?? "*"
        result.response.headers["Access-Control-Allow-Methods"] = allowedMethods.joined(separator: ", ")
        result.response.headers["Access-Control-Allow-Headers"] = allowedHeaders.joined(separator: ", ")

        if let origin = context.request.headers["Origin"] {
            result.response.headers["Access-Control-Allow-Origin"] = origin
            result.response.headers["Vary"] = "Origin"
        }

        return result
    }
}

// 4. Rate Limiting Middleware
class RateLimitingMiddleware: Middleware {
    private var requestCounts: [String: [(Date, Int)]] = [:]
    private let maxRequests: Int
    private let windowInSeconds: Int
    private let cleanupInterval: Int

    init(maxRequests: Int = 100, windowInSeconds: Int = 60, cleanupInterval: Int = 300) {
        self.maxRequests = maxRequests
        self.windowInSeconds = windowInSeconds
        self.cleanupInterval = cleanupInterval
    }

    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        let clientId = getClientId(from: context.request)
        let now = Date()

        // Clean up old entries
        if Int.random(in: 1...100) == 1 { // Occasionally cleanup
            cleanup()
        }

        // Check rate limit
        if isRateLimited(clientId: clientId, at: now) {
            log(.warning, "Rate limit exceeded for: \(clientId)")
            var rateLimitedContext = context
            rateLimitedContext.response = HTTPResponse(statusCode: .serviceUnavailable)
                .setJSON("{"error": "Rate limit exceeded", "code": 429}")
            rateLimitedContext.response.headers["Retry-After"] = String(windowInSeconds)
            return rateLimitedContext
        }

        // Record request
        recordRequest(clientId: clientId, at: now)

        return next(context)
    }

    private func getClientId(from request: HTTPRequest) -> String {
        // Use IP or User-Agent as client identifier
        return request.headers["X-Forwarded-For"] ??
               request.headers["X-Real-IP"] ??
               request.headers["User-Agent"] ??
               "unknown"
    }

    private func isRateLimited(clientId: String, at now: Date) -> Bool {
        guard let requests = requestCounts[clientId] else {
            return false
        }

        let cutoff = now.addingTimeInterval(-Double(windowInSeconds))
        let recentRequests = requests.filter { $0.0 > cutoff }

        return recentRequests.count >= maxRequests
    }

    private func recordRequest(clientId: String, at now: Date) {
        if requestCounts[clientId] == nil {
            requestCounts[clientId] = []
        }
        requestCounts[clientId]?.append((now, 1))
    }

    private func cleanup() {
        let cutoff = Date().addingTimeInterval(-Double(cleanupInterval))
        for clientId in requestCounts.keys {
            requestCounts[clientId]?..removeAll { $0.0 < cutoff }
        }
    }

    private func log(_ level: LogLevel, _ message: String) {
        print("[\(level)] \(message)")
    }
}

// 5. Request Body Parsing Middleware
class BodyParsingMiddleware: Middleware {
    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        guard let body = context.request.body else {
            return next(context)
        }

        // Check content type
        let contentType = context.request.headers["Content-Type"] ?? ""

        if contentType.contains("application/json") {
            // Parse JSON body
            if let json = try? JSONSerialization.jsonObject(with: body, options: []) as? [String: Any] {
                context.metadata["parsedBody"] = json
                log(.info, "JSON body parsed: \(json.keys.count) fields")
            }
        } else if contentType.contains("application/x-www-form-urlencoded") {
            // Parse form data
            if let bodyString = String(data: body, encoding: .utf8) {
                let formData = parseFormData(bodyString)
                context.metadata["parsedBody"] = formData
                log(.info, "Form data parsed: \(formData.keys.count) fields")
            }
        } else {
            // Store raw body
            context.metadata["rawBody"] = body
        }

        return next(context)
    }

    private func parseFormData(_ string: String) -> [String: String] {
        var result: [String: String] = [:]
        let pairs = string.components(separatedBy: "&")

        for pair in pairs {
            let components = pair.components(separatedBy: "=")
            if components.count == 2,
               let key = components[0].removingPercentEncoding,
               let value = components[1].removingPercentEncoding {
                result[key] = value
            }
        }

        return result
    }

    private func log(_ level: LogLevel, _ message: String) {
        print("[\(level)] \(message)")
    }
}

// 6. Error Handling Middleware
class ErrorHandlingMiddleware: Middleware {
    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        do {
            return next(context)
        } catch {
            log(.error, "Unhandled error: \(error)")

            var errorContext = context
            let requestId = context.metadata["requestId"] as? String ?? "unknown"

            let errorJson = """
            {
                "error": "Internal Server Error",
                "message": "\(error.localizedDescription)",
                "requestId": "\(requestId)"
            }
            """

            errorContext.response = HTTPResponse(statusCode: .internalServerError)
                .setJSON(errorJson)

            return errorContext
        }
    }

    private func log(_ level: LogLevel, _ message: String) {
        print("[\(level)] \(message)")
    }
}

// 7. Response Compression Middleware
class CompressionMiddleware: Middleware {
    let minSize: Int

    init(minSize: Int = 1024) {
        self.minSize = minSize
    }

    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        let result = next(context)

        // Check if response should be compressed
        guard let body = result.response.body,
              body.count > minSize,
              shouldCompress(for: context.request) else {
            return result
        }

        // Compress body (simplified - just remove whitespace for JSON)
        if let contentType = result.response.headers["Content-Type"],
           contentType.contains("application/json"),
           let bodyString = String(data: body, encoding: .utf8) {

            let compressed = bodyString.split(separator: " ").joined()
            if let compressedData = compressed.data(using: .utf8) {
                result.response.body = compressedData
                result.response.headers["Content-Encoding"] = "gzip"
                result.response.headers["X-Original-Size"] = String(body.count)
                result.response.headers["X-Compressed-Size"] = String(compressedData.count)

                log(.info, "Compressed response: \(body.count) -> \(compressedData.count) bytes")
            }
        }

        return result
    }

    private func shouldCompress(for request: HTTPRequest) -> Bool {
        // Check Accept-Encoding header
        if let acceptEncoding = request.headers["Accept-Encoding"] {
            return acceptEncoding.contains("gzip") || acceptEncoding.contains("*")
        }
        return false
    }

    private func log(_ level: LogLevel, _ message: String) {
        print("[\(level)] \(message)")
    }
}

// 8. Timing Middleware
class TimingMiddleware: Middleware {
    func process(context: HTTPContext, next: @escaping (HTTPContext) -> HTTPContext) -> HTTPContext {
        let startTime = Date()

        let result = next(context)

        let duration = Date().timeIntervalSince(startTime)
        result.response.headers["X-Response-Time"] = String(format: "%.3f", duration)

        return result
    }
}

// MARK: - HTTP Types (from previous example)

struct HTTPRequest {
    let method: HTTPMethod
    let path: String
    let headers: [String: String]
    let body: Data?
}

enum HTTPMethod: String {
    case GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, ANY
}

struct HTTPResponse {
    var statusCode: HTTPStatusCode = .ok
    var headers: [String: String] = [:]
    var body: Data?

    func setJSON(_ json: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = "application/json"
        response.body = json.data(using: .utf8)
        return response
    }
}

enum HTTPStatusCode: Int {
    case ok = 200
    case unauthorized = 401
    case forbidden = 403
    case notFound = 404
    case serviceUnavailable = 503
    case internalServerError = 500
}

// MARK: - Demonstration

func demonstrateMiddleware() {
    print("=== macOS Swift Middleware Pipeline Examples ===\n")

    // 1. Create pipeline with multiple middlewares
    print("--- 1. Creating Middleware Pipeline ---")
    let pipeline = MiddlewarePipeline()

    pipeline
        .use(LoggingMiddleware(logLevel: .info))
        .use(ErrorHandlingMiddleware())
        .use(CORSMiddleware())
        .use(TimingMiddleware())

    // 2. Test public route (no authentication)
    print("\n--- 2. Testing Public Route ---")
    let publicRequest = HTTPRequest(
        method: .GET,
        path: "/api/status",
        headers: ["User-Agent": "TestClient"],
        body: nil
    )

    let publicContext = pipeline.process(request: publicRequest) { context in
        var ctx = context
        ctx.response = HTTPResponse().setJSON("{"status": "ok", "message": "Public access"}")
        return ctx
    }

    print("Public route response: \(publicContext.response.statusCode.rawValue)")

    // 3. Test authenticated route
    print("\n--- 3. Testing Authenticated Route ---")
    let authPipeline = MiddlewarePipeline()
    authPipeline
        .use(LoggingMiddleware())
        .use(AuthenticationMiddleware(authPaths: ["/api/protected"]))
        .use(ErrorHandlingMiddleware())

    let unauthRequest = HTTPRequest(
        method: .GET,
        path: "/api/protected/data",
        headers: ["User-Agent": "TestClient"],
        body: nil
    )

    let unauthContext = authPipeline.process(request: unauthRequest) { context in
        var ctx = context
        ctx.response = HTTPResponse().setJSON("{"data": "sensitive"}")
        return ctx
    }

    print("Unauthorized access: \(unauthContext.response.statusCode.rawValue)")

    // 4. Test with valid token
    let authRequest = HTTPRequest(
        method: .GET,
        path: "/api/protected/data",
        headers: ["Authorization": "Bearer valid_token_here_12345678"],
        body: nil
    )

    let authContext = authPipeline.process(request: authRequest) { context in
        var ctx = context
        let userId = context.metadata["userId"] as? String ?? "unknown"
        ctx.response = HTTPResponse().setJSON("{"data": "sensitive", "user": "\(userId)"}")
        return ctx
    }

    print("Authorized access: \(authContext.response.statusCode.rawValue)")

    // 5. Test rate limiting
    print("\n--- 4. Testing Rate Limiting ---")
    let rateLimitPipeline = MiddlewarePipeline()
    rateLimitPipeline
        .use(RateLimitingMiddleware(maxRequests: 3, windowInSeconds: 60))
        .use(LoggingMiddleware())

    // Make multiple requests
    for i in 1...5 {
        let request = HTTPRequest(
            method: .GET,
            path: "/api/test",
            headers: ["User-Agent": "TestClient"],
            body: nil
        )

        let context = rateLimitPipeline.process(request: request) { context in
            var ctx = context
            ctx.response = HTTPResponse().setJSON("{"request": \(i)}")
            return ctx
        }

        print("Request \(i): \(context.response.statusCode.rawValue)")
    }

    // 6. Test body parsing
    print("\n--- 5. Testing Body Parsing ---")
    let bodyPipeline = MiddlewarePipeline()
    bodyPipeline
        .use(BodyParsingMiddleware())
        .use(LoggingMiddleware())

    let jsonBody = """
    {
        "name": "John Doe",
        "email": "[email protected]"
    }
    """.data(using: .utf8)

    let postRequest = HTTPRequest(
        method: .POST,
        path: "/api/users",
        headers: ["Content-Type": "application/json"],
        body: jsonBody
    )

    let postContext = bodyPipeline.process(request: postRequest) { context in
        var ctx = context
        if let body = context.metadata["parsedBody"] as? [String: Any] {
            ctx.response = HTTPResponse().setJSON("{"received": \(body.keys.count) fields}")
        }
        return ctx
    }

    print("POST request: \(postContext.response.statusCode.rawValue)")

    print("\n=== Middleware Pipeline Demo Completed ===")
}

// Run demonstration
demonstrateMiddleware()

💻 Serviço de Arquivos Estáticos swift

🟡 intermediate ⭐⭐⭐

Implementar serviço eficiente de arquivos estáticos com detecção de tipos MIME, headers de cache e características de segurança

⏱️ 25 min 🏷️ swift, macos, web, static files, server
Prerequisites: Swift basics, File system operations, HTTP protocol
// macOS Swift Static File Serving Examples
// Using Foundation and URL handling

import Foundation

// MARK: - Static File Server Configuration

struct StaticFileConfig {
    var rootDirectory: String
    var enableDirectoryBrowsing: Bool = false
    var enableDefaultFiles: Bool = true
    var defaultFileNames: [String] = ["index.html", "default.html", "home.html"]
    var enableETag: Bool = true
    var enableLastModified: Bool = true
    var cacheMaxAge: Int = 3600 // 1 hour
    var enableCompression: Bool = true
    var allowedExtensions: [String] = [
        "html", "htm", "css", "js", "json", "xml", "txt", "md",
        "png", "jpg", "jpeg", "gif", "bmp", "svg", "ico",
        "pdf", "zip", "woff", "woff2", "ttf"
    ]

    var indexFileName: String {
        return defaultFileNames.first ?? "index.html"
    }
}

// MARK: - MIME Type Detector

class MIMETypeDetector {
    static let shared = MIMETypeDetector()

    private let mimeTypes: [String: String] = [
        // Text files
        "html": "text/html",
        "htm": "text/html",
        "css": "text/css",
        "js": "application/javascript",
        "json": "application/json",
        "xml": "application/xml",
        "txt": "text/plain",
        "md": "text/markdown",
        "csv": "text/csv",

        // Images
        "png": "image/png",
        "jpg": "image/jpeg",
        "jpeg": "image/jpeg",
        "gif": "image/gif",
        "bmp": "image/bmp",
        "svg": "image/svg+xml",
        "ico": "image/x-icon",
        "webp": "image/webp",

        // Fonts
        "woff": "font/woff",
        "woff2": "font/woff2",
        "ttf": "font/ttf",
        "otf": "font/otf",
        "eot": "application/vnd.ms-fontobject",

        // Documents
        "pdf": "application/pdf",
        "zip": "application/zip",
        "tar": "application/x-tar",
        "gz": "application/gzip",

        // Audio/Video
        "mp3": "audio/mpeg",
        "wav": "audio/wav",
        "mp4": "video/mp4",
        "webm": "video/webm"
    ]

    func detectMIMEType(for extension: String) -> String {
        let ext = extension.lowercased()
        return mimeTypes[ext] ?? "application/octet-stream"
    }

    func detectMIMEType(for url: URL) -> String {
        return detectMIMEType(for: url.pathExtension)
    }
}

// MARK: - Static File Server

class StaticFileServer {
    let config: StaticFileConfig
    private let fileManager = FileManager.default
    private let mimeDetector = MIMETypeDetector.shared

    init(config: StaticFileConfig) {
        self.config = config
    }

    convenience init(rootDirectory: String) {
        var config = StaticFileConfig(rootDirectory: rootDirectory)
        self.init(config: config)
    }

    // Serve file for request path
    func serveFile(for path: String) -> HTTPResponse {
        // Normalize and validate path
        let normalizedPath = normalizePath(path)

        // Security check: prevent path traversal
        if isPathTraversal(normalizedPath) {
            return HTTPResponse(statusCode: .forbidden)
                .setText("Forbidden: Path traversal detected")
        }

        // Resolve full path
        let fullPath = resolvePath(normalizedPath)

        // Check if path exists
        var isDirectory: ObjCBool = false
        guard fileManager.fileExists(atPath: fullPath, isDirectory: &isDirectory) else {
            return HTTPResponse(statusCode: .notFound)
                .setText("404 - File not found: \(path)")
        }

        // Handle directory
        if isDirectory.boolValue {
            return serveDirectory(at: fullPath, path: normalizedPath)
        }

        // Check file extension
        let fileExtension = (fullPath as NSString).pathExtension.lowercased()
        if !config.allowedExtensions.contains(fileExtension) {
            return HTTPResponse(statusCode: .forbidden)
                .setText("Forbidden: File type not allowed")
        }

        // Serve file
        return serveFile(at: fullPath)
    }

    // Serve individual file
    private func serveFile(at fullPath: String) -> HTTPResponse {
        do {
            // Get file attributes
            let attributes = try fileManager.attributesOfItem(atPath: fullPath)
            let fileSize = attributes[.size] as? UInt64 ?? 0
            let modificationDate = attributes[.modificationDate] as? Date ?? Date()

            // Read file content
            let fileData = try Data(contentsOf: URL(fileURLWithPath: fullPath))

            // Create response
            var response = HTTPResponse()
            response.body = fileData

            // Set content type
            let fileExtension = (fullPath as NSString).pathExtension.lowercased()
            let mimeType = mimeDetector.detectMIMEType(for: fileExtension)
            response.headers["Content-Type"] = mimeType

            // Set content length
            response.headers["Content-Length"] = String(fileSize)

            // Set cache headers
            if config.enableETag {
                let eTag = generateETag(for: fullPath, size: fileSize, modifiedDate: modificationDate)
                response.headers["ETag"] = eTag
                response.headers["Cache-Control"] = "public, max-age=\(config.cacheMaxAge)"
            }

            if config.enableLastModified {
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
                dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
                response.headers["Last-Modified"] = dateFormatter.string(from: modificationDate)
            }

            // Set security headers
            response.headers["X-Content-Type-Options"] = "nosniff"
            response.headers["X-Frame-Options"] = "SAMEORIGIN"

            print("Served: \(fullPath) (\(fileSize) bytes, \(mimeType))")

            return response

        } catch {
            print("Error serving file: \(error)")
            return HTTPResponse(statusCode: .internalServerError)
                .setText("Error reading file")
        }
    }

    // Serve directory
    private func serveDirectory(at fullPath: String, path: String) -> HTTPResponse {
        // Try to serve default file
        if config.enableDefaultFiles {
            for defaultFile in config.defaultFileNames {
                let defaultFilePath = (fullPath as NSString).appendingPathComponent(defaultFile)
                if fileManager.fileExists(atPath: defaultFilePath) {
                    return serveFile(at: defaultFilePath)
                }
            }
        }

        // Directory browsing
        if config.enableDirectoryBrowsing {
            return serveDirectoryListing(at: fullPath, path: path)
        }

        // No default file and browsing disabled
        return HTTPResponse(statusCode: .forbidden)
            .setText("Forbidden: Directory browsing disabled")
    }

    // Serve directory listing
    private func serveDirectoryListing(at fullPath: String, path: String) -> HTTPResponse {
        do {
            let contents = try fileManager.contentsOfDirectory(atPath: fullPath)

            var directories: [String] = []
            var files: [(name: String, size: UInt64, modified: Date)] = []

            for item in contents {
                let itemPath = (fullPath as NSString).appendingPathComponent(item)
                var isDirectory: ObjCBool = false
                fileManager.fileExists(atPath: itemPath, isDirectory: &isDirectory)

                if isDirectory.boolValue {
                    directories.append(item)
                } else {
                    if let attributes = try? fileManager.attributesOfItem(atPath: itemPath) {
                        let size = attributes[.size] as? UInt64 ?? 0
                        let modified = attributes[.modificationDate] as? Date ?? Date()
                        files.append((item, size, modified))
                    }
                }
            }

            // Generate HTML listing
            let html = generateDirectoryListingHTML(path: path, directories: directories, files: files)

            var response = HTTPResponse()
            response.headers["Content-Type"] = "text/html"
            response.body = html.data(using: .utf8)

            return response

        } catch {
            return HTTPResponse(statusCode: .internalServerError)
                .setText("Error reading directory")
        }
    }

    // Generate directory listing HTML
    private func generateDirectoryListingHTML(path: String, directories: [String], files: [(name: String, size: UInt64, modified: Date)]) -> String {
        var html = """
        <!DOCTYPE html>
        <html>
        <head>
            <title>Directory Listing: \(path)</title>
            <style>
                body { font-family: Arial, sans-serif; margin: 20px; }
                h1 { color: #333; }
                table { border-collapse: collapse; width: 100%; }
                th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
                th { background-color: #f4f4f4; }
                a { text-decoration: none; color: #0066cc; }
                a:hover { text-decoration: underline; }
                .icon { margin-right: 10px; }
            </style>
        </head>
        <body>
            <h1>Directory listing for \(path.isEmpty ? "/" : path)</h1>
            <table>
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Size</th>
                        <th>Modified</th>
                    </tr>
                </thead>
                <tbody>
        """

        // Parent directory link
        if path != "/" && !path.isEmpty {
            let parentPath = (path as NSString).deletingLastPathComponent
            html += "\n<tr><td><span class=\"icon\">📁</span><a href=\"\(parentPath)\">../</a></td><td>-</td><td>-</td></tr>"
        }

        // Directories
        for dir in directories.sorted() {
            let dirPath = path.isEmpty ? "/\(dir)" : "\(path)/\(dir)"
            html += "\n<tr><td><span class=\"icon\">📁</span><a href=\"\(dirPath)/\">\(dir)/</a></td><td>-</td><td>-</td></tr>"
        }

        // Files
        for file in files.sorted(by: { $0.name < $1.name }) {
            let filePath = path.isEmpty ? "/\(file.name)" : "\(path)/\(file.name)"
            let icon = getFileIcon(for: file.name)
            let size = formatBytes(file.size)
            let modified = formatDate(file.modified)
            html += "\n<tr><td><span class=\"icon\">\(icon)</span><a href=\"\(filePath)\">\(file.name)</a></td><td>\(size)</td><td>\(modified)</td></tr>"
        }

        html += """
                </tbody>
            </table>
            <p>Total: \(directories.count) directories, \(files.count) files</p>
        </body>
        </html>
        """

        return html
    }

    // Generate ETag
    private func generateETag(for path: String, size: UInt64, modifiedDate: Date) -> String {
        let hash = "\(modifiedDate.timeIntervalSince1970)-\(size)"
        return "W/\"\(hash)\""
    }

    // Normalize path
    private func normalizePath(_ path: String) -> String {
        var normalized = path

        // Remove leading slash
        if normalized.hasPrefix("/") {
            normalized = String(normalized.dropFirst())
        }

        // Replace backslashes with forward slashes
        normalized = normalized.replacingOccurrences(of: "\\", with: "/")

        // Remove query string
        if let queryRange = normalized.range(of: "?") {
            normalized = String(normalized[..<queryRange.lowerBound])
        }

        // Remove fragment
        if let fragmentRange = normalized.range(of: "#") {
            normalized = String(normalized[..<fragmentRange.lowerBound])
        }

        return normalized.isEmpty ? "/" : "/\(normalized)"
    }

    // Resolve full path
    private func resolvePath(_ normalizedPath: String) -> String {
        let relativePath = String(normalizedPath.dropFirst())
        return (config.rootDirectory as NSString).appendingPathComponent(relativePath)
    }

    // Check for path traversal
    private func isPathTraversal(_ path: String) -> Bool {
        return path.contains("..") || path.contains("./") || path.contains("\\")
    }

    // Get file icon
    private func getFileIcon(for fileName: String) -> String {
        let ext = ((fileName as NSString).pathExtension).lowercased()

        switch ext {
        case "html", "htm": return "🌐"
        case "css": return "🎨"
        case "js": return "⚡"
        case "json": return "📋"
        case "png", "jpg", "jpeg", "gif", "svg", "webp": return "🖼️"
        case "pdf": return "📕"
        case "zip", "tar", "gz": return "📦"
        case "woff", "woff2", "ttf": return "🔤"
        default: return "📄"
        }
    }

    // Format bytes
    private func formatBytes(_ bytes: UInt64) -> String {
        let kb = Double(bytes) / 1024
        let mb = kb / 1024
        let gb = mb / 1024

        if gb >= 1 {
            return String(format: "%.2f GB", gb)
        } else if mb >= 1 {
            return String(format: "%.2f MB", mb)
        } else if kb >= 1 {
            return String(format: "%.2f KB", kb)
        } else {
            return "\(bytes) B"
        }
    }

    // Format date
    private func formatDate(_ date: Date) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return formatter.string(from: date)
    }
}

// MARK: - HTTP Response

struct HTTPResponse {
    var statusCode: HTTPStatusCode = .ok
    var headers: [String: String] = [:]
    var body: Data?

    func setText(_ text: String) -> HTTPResponse {
        var response = self
        response.headers["Content-Type"] = "text/plain"
        response.body = text.data(using: .utf8)
        return response
    }
}

enum HTTPStatusCode: Int {
    case ok = 200
    case forbidden = 403
    case notFound = 404
    case internalServerError = 500
}

// MARK: - File Cache

class FileCache {
    private var cache: [String: CachedFile] = [:]
    private let maxSize: Int
    private let maxAge: TimeInterval

    init(maxSize: Int = 100, maxAge: TimeInterval = 3600) {
        self.maxSize = maxSize
        self.maxAge = maxAge
    }

    func get(_ key: String) -> Data? {
        guard let cached = cache[key] else { return nil }

        // Check if expired
        if Date().timeIntervalSince(cached.timestamp) > maxAge {
            cache.removeValue(forKey: key)
            return nil
        }

        return cached.data
    }

    func set(_ key: String, data: Data) {
        // Evict oldest if necessary
        if cache.count >= maxSize {
            let oldest = cache.min(by: { $0.value.timestamp < $1.value.timestamp })
            if let oldestKey = oldest?.key {
                cache.removeValue(forKey: oldestKey)
            }
        }

        cache[key] = CachedFile(data: data, timestamp: Date())
    }

    func clear() {
        cache.removeAll()
    }

    private struct CachedFile {
        let data: Data
        let timestamp: Date
    }
}

// MARK: - Demonstration

func demonstrateStaticFileServing() {
    print("=== macOS Swift Static File Serving Examples ===\n")

    // Create test directory structure
    let testDirectory = "/tmp/static_file_test"
    let fileManager = FileManager.default

    try? fileManager.removeItem(atPath: testDirectory)
    try? fileManager.createDirectory(atPath: testDirectory, withIntermediateDirectories: true)

    // Create sample files
    let indexPath = "\(testDirectory)/index.html"
    let cssPath = "\(testDirectory)/style.css"
    let jsPath = "\(testDirectory)/app.js"
    let subDir = "\(testDirectory)/assets"
    try? fileManager.createDirectory(atPath: subDir, withIntermediateDirectories: true)

    try? "<html><body><h1>Test Page</h1></body></html>".write(toFile: indexPath, atomically: true, encoding: .utf8)
    try? "body { margin: 0; }".write(toFile: cssPath, atomically: true, encoding: .utf8)
    try? "console.log('Hello');".write(toFile: jsPath, atomically: true, encoding: .utf8)
    try? "Test".write(toFile: "\(subDir)/test.txt", atomically: true, encoding: .utf8)

    // 1. Basic file serving
    print("--- 1. Basic File Serving ---")
    var config = StaticFileConfig(rootDirectory: testDirectory)
    config.enableDirectoryBrowsing = true

    let server = StaticFileServer(config: config)

    // Test serving HTML file
    let response1 = server.serveFile(for: "/index.html")
    print("Served /index.html: \(response1.statusCode.rawValue)")
    if let contentType = response1.headers["Content-Type"] {
        print("  Content-Type: \(contentType)")
    }

    // Test serving CSS file
    let response2 = server.serveFile(for: "/style.css")
    print("Served /style.css: \(response2.statusCode.rawValue)")
    print("  Content-Type: \(response2.headers["Content-Type"] ?? "none")")

    // 2. Default file serving
    print("\n--- 2. Default File Serving ---")
    let response3 = server.serveFile(for: "/")
    print("Served / (default): \(response3.statusCode.rawValue)")

    // 3. Directory listing
    print("\n--- 3. Directory Listing ---")
    let response4 = server.serveFile(for: "/assets/")
    print("Served /assets/ (listing): \(response4.statusCode.rawValue)")

    // 4. 404 handling
    print("\n--- 4. 404 Handling ---")
    let response5 = server.serveFile(for: "/nonexistent.html")
    print("Served /nonexistent.html: \(response5.statusCode.rawValue)")

    // 5. Path traversal protection
    print("\n--- 5. Path Traversal Protection ---")
    let response6 = server.serveFile(for: "/../../../etc/passwd")
    print("Served ../../../etc/passwd: \(response6.statusCode.rawValue)")

    // 6. File type restriction
    print("\n--- 6. File Type Restriction ---")
    try? "test".write(toFile: "\(testDirectory)/test.exe", atomically: true, encoding: .utf8)
    let response7 = server.serveFile(for: "/test.exe")
    print("Served /test.exe: \(response7.statusCode.rawValue)")

    // 7. ETag generation
    print("\n--- 7. ETag Generation ---")
    config.enableETag = true
    let serverWithETag = StaticFileServer(config: config)
    let response8 = serverWithETag.serveFile(for: "/index.html")
    print("ETag: \(response8.headers["ETag"] ?? "none")")
    print("Cache-Control: \(response8.headers["Cache-Control"] ?? "none")")

    // Cleanup
    try? fileManager.removeItem(atPath: testDirectory)
    print("\nCleanup completed")

    print("\n=== Static File Serving Demo Completed ===")
}

// Run demonstration
demonstrateStaticFileServing()