macOS Objective-C Web端功能示例

macOS Objective-C Web端功能示例,包括URL路由、中间件和静态文件服务

💻 URL路由 objectivec

🟡 intermediate ⭐⭐⭐⭐

解析和处理带有路径参数和查询字符串的URL路由

⏱️ 35 min 🏷️ objectivec, macos, web, routing
Prerequisites: Intermediate Objective-C, Foundation framework
// macOS Objective-C URL Routing Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. URL Parser

@interface URLParser : NSObject

+ (NSDictionary *)parseURL:(NSString *)urlString;
+ (NSString *)getPath:(NSString *)urlString;
+ (NSDictionary *)getQueryParameters:(NSString *)urlString;
+ (NSDictionary *)getPathComponents:(NSString *)urlString;

@end

@implementation URLParser

+ (NSDictionary *)parseURL:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return nil;
    }

    NSMutableDictionary *result = [NSMutableDictionary dictionary];
    result[@"scheme"] = url.scheme;
    result[@"host"] = url.host;
    result[@"port"] = url.port ?: @"default";
    result[@"path"] = url.path;
    result[@"query"] = url.query;
    result[@"fragment"] = url.fragment;

    NSLog(@"Parsed URL: %@", result);
    return [result copy];
}

+ (NSString *)getPath:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSString *path = url.path;
    NSLog(@"Path: %@", path);
    return path;
}

+ (NSDictionary *)getQueryParameters:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSString *query = url.query;

    if (!query) {
        return @{};
    }

    NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
    NSArray *pairs = [query componentsSeparatedByString:@"&"];

    for (NSString *pair in pairs) {
        NSArray *keyValue = [pair componentsSeparatedByString:@"="];

        if (keyValue.count == 2) {
            NSString *key = keyValue[0];
            NSString *value = [keyValue[1] stringByRemovingPercentEncoding];

            // Handle multiple values for same key
            if (parameters[key]) {
                // Convert to array if not already
                id existing = parameters[key];

                if ([existing isKindOfClass:[NSArray class]]) {
                    NSMutableArray *array = [existing mutableCopy];
                    [array addObject:value];
                    parameters[key] = array;
                } else {
                    parameters[key] = @[existing, value];
                }
            } else {
                parameters[key] = value;
            }
        }
    }

    NSLog(@"Query parameters: %@", parameters);
    return [parameters copy];
}

+ (NSDictionary *)getPathComponents:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSString *path = url.path;

    if (!path) {
        return @{};
    }

    // Remove leading slash and split
    NSString *trimmedPath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;
    NSArray *components = [trimmedPath componentsSeparatedByString:@"/"];

    // Filter out empty components
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self != ''"];
    NSArray *filteredComponents = [components filteredArrayUsingPredicate:predicate];

    NSMutableDictionary *result = [NSMutableDictionary dictionary];
    result[@"components"] = filteredComponents;
    result[@"count"] = @(filteredComponents.count);

    NSLog(@"Path components: %@", filteredComponents);
    return [result copy];
}

@end

// MARK: - 2. Route Definition

@interface Route : NSObject

@property (nonatomic, strong) NSString *pattern;
@property (nonatomic, strong) NSString *method;
@property (nonatomic, copy) void (^handler)(NSDictionary *params, NSDictionary *query);

- (instancetype)initWithPattern:(NSString *)pattern
                        method:(NSString *)method
                       handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;

- (BOOL)matchesPath:(NSString *)path
          parameters:(NSDictionary **)parameters;

@end

@implementation Route

- (instancetype)initWithPattern:(NSString *)pattern
                        method:(NSString *)method
                       handler:(void (^)(NSDictionary *, NSDictionary *))handler {

    self = [super init];

    if (self) {
        _pattern = pattern;
        _method = method;
        _handler = handler;
    }

    return self;
}

- (BOOL)matchesPath:(NSString *)path
          parameters:(NSDictionary **)parameters {

    // Convert pattern to regex
    // /users/:id -> /users/([^/]+)
    NSString *regexPattern = self.pattern;

    // Escape regex special characters except our parameter marker
    NSCharacterSet *specialChars = [NSCharacterSet characterSetWithCharactersInString:@".^$*+(){}[]|\\"];
    regexPattern = [regexPattern stringByTrimmingCharactersInSet:specialChars];

    // Replace :parameter with regex capture group
    regexPattern = [self.pattern stringByReplacingOccurrencesOfString:@":([^/]+)"
                                                       withString:@"([^/]+)"
                                                          options:NSRegularExpressionSearch
                                                            range:NSMakeRange(0, self.pattern.length)
                                                             error:nil];

    // Add anchors
    regexPattern = [NSString stringWithFormat:@"^%@$", regexPattern];

    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexPattern
                                                                           options:0
                                                                             error:&error];

    if (error) {
        NSLog(@"Invalid route pattern: %@", error.localizedDescription);
        return NO;
    }

    NSTextCheckingResult *match = [regex firstMatchInString:path
                                                    options:0
                                                      range:NSMakeRange(0, path.length)];

    if (match) {
        // Extract parameter names from pattern
        NSMutableArray *paramNames = [NSMutableArray array];
        NSRegularExpression *paramRegex = [NSRegularExpression regularExpressionWithPattern:@":([^/]+)"
                                                                                options:0
                                                                                  error:nil];

        [paramRegex enumerateMatchesInString:self.pattern
                                      options:0
                                        range:NSMakeRange(0, self.pattern.length)
                                   usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
            NSString *paramName = [self.pattern substringWithRange:[result rangeAtIndex:1]];
            [paramNames addObject:paramName];
        }];

        // Extract parameter values
        NSMutableDictionary *params = [NSMutableDictionary dictionary];

        for (NSInteger i = 0; i < paramNames.count && i < match.numberOfRanges - 1; i++) {
            NSRange valueRange = [match rangeAtIndex:i + 1];
            NSString *value = [path substringWithRange:valueRange];
            params[paramNames[i]] = value;
        }

        *parameters = [params copy];
        return YES;
    }

    return NO;
}

@end

// MARK: - 3. Router

@interface Router : NSObject

@property (nonatomic, strong) NSMutableArray<Route *> *routes;

- (void)addRoute:(Route *)route;
- (BOOL)handleRequest:(NSString *)method
                path:(NSString *)path
           query:(NSDictionary *)query;

- (void)GET:(NSString *)pattern
    handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;

- (void)POST:(NSString *)pattern
     handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;

- (void)PUT:(NSString *)pattern
    handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;

- (void)DELETE:(NSString *)pattern
       handler:(void (^)(NSDictionary *params, NSDictionary *query))handler;

@end

@implementation Router

- (instancetype)init {
    self = [super init];

    if (self) {
        _routes = [NSMutableArray array];
    }

    return self;
}

- (void)addRoute:(Route *)route {
    [self.routes addObject:route];
    NSLog(@"Added route: %@ %@", route.method, route.pattern);
}

- (BOOL)handleRequest:(NSString *)method
                path:(NSString *)path
           query:(NSDictionary *)query {

    for (Route *route in self.routes) {
        if ([route.method isEqualToString:method]) {
            NSDictionary *params = nil;

            if ([route matchesPath:path parameters:&params]) {
                NSLog(@"Route matched: %@ %@", method, route.pattern);
                route.handler(params, query);
                return YES;
            }
        }
    }

    NSLog(@"No route matched: %@ %@", method, path);
    return NO;
}

- (void)GET:(NSString *)pattern
    handler:(void (^)(NSDictionary *, NSDictionary *))handler {

    Route *route = [[Route alloc] initWithPattern:pattern
                                             method:@"GET"
                                            handler:handler];
    [self addRoute:route];
}

- (void)POST:(NSString *)pattern
     handler:(void (^)(NSDictionary *, NSDictionary *))handler {

    Route *route = [[Route alloc] initWithPattern:pattern
                                             method:@"POST"
                                            handler:handler];
    [self addRoute:route];
}

- (void)PUT:(NSString *)pattern
    handler:(void (^)(NSDictionary *, NSDictionary *))handler {

    Route *route = [[Route alloc] initWithPattern:pattern
                                             method:@"PUT"
                                            handler:handler];
    [self addRoute:route];
}

- (void)DELETE:(NSString *)pattern
       handler:(void (^)(NSDictionary *, NSDictionary *))handler {

    Route *route = [[Route alloc] initWithPattern:pattern
                                             method:@"DELETE"
                                            handler:handler];
    [self addRoute:route];
}

@end

// MARK: - 4. Request Handler

@interface RequestHandler : NSObject

+ (void)handleRequest:(NSString *)method
                  path:(NSString *)path
                  query:(NSDictionary *)query
               headers:(NSDictionary *)headers
                 body:(NSData *)body;

@end

@implementation RequestHandler

+ (void)handleRequest:(NSString *)method
                  path:(NSString *)path
                  query:(NSDictionary *)query
               headers:(NSDictionary *)headers
                 body:(NSData *)body {

    NSLog(@"\n=== Incoming Request ===");
    NSLog(@"Method: %@", method);
    NSLog(@"Path: %@", path);
    NSLog(@"Query: %@", query);
    NSLog(@"Headers: %@", headers);
    NSLog(@"Body Length: %lu bytes", (unsigned long)body.length);

    // Simulate response
    NSDictionary *response = @{
        @"status": @"200 OK",
        @"headers": @{
            @"Content-Type": @"application/json"
        },
        @"body": @{
            @"message": @"Request processed successfully",
            @"path": path,
            @"method": method
        }
    };

    NSLog(@"Response: %@", response);
}

@end

// MARK: - 5. Complete Routing System

@interface WebServer : NSObject

@property (nonatomic, strong) Router *router;

- (instancetype)init;
- (void)handleRequestString:(NSString *)requestString;

@end

@implementation WebServer

- (instancetype)init {
    self = [super init];

    if (self) {
        _router = [[Router alloc] init];
        [self setupRoutes];
    }

    return self;
}

- (void)setupRoutes {
    // Home page
    [self.router GET:@"/"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling home page");
        NSLog(@"Response: Welcome to the home page!");
    }];

    // User routes
    [self.router GET:@"/users"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling user list");
        NSLog(@"Response: List of all users");
    }];

    [self.router GET:@"/users/:id"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling user detail");
        NSLog(@"User ID: %@", params[@"id"]);
        NSLog(@"Response: User details for ID: %@", params[@"id"]);
    }];

    // Create user
    [self.router POST:@"/users"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling user creation");
        NSLog(@"Response: User created");
    }];

    // Update user
    [self.router PUT:@"/users/:id"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling user update");
        NSLog(@"User ID: %@", params[@"id"]);
        NSLog(@"Response: User updated");
    }];

    // Delete user
    [self.router DELETE:@"/users/:id"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling user deletion");
        NSLog(@"User ID: %@", params[@"id"]);
        NSLog(@"Response: User deleted");
    }];

    // Products with category filter
    [self.router GET:@"/products/:category"
        handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handling product list by category");
        NSLog(@"Category: %@", params[@"category"]);
        NSLog(@"Query: %@", query);
        NSLog(@"Response: Products in category: %@", params[@"category"]);
    }];
}

- (void)handleRequestString:(NSString *)requestString {
    // Parse request: "GET /users/123?sort=name HTTP/1.1"
    NSArray *components = [requestString componentsSeparatedByString:@" "];

    if (components.count < 2) {
        NSLog(@"Invalid request: %@", requestString);
        return;
    }

    NSString *method = components[0];
    NSString *fullPath = components[1];

    // Split path and query
    NSArray *pathComponents = [fullPath componentsSeparatedByString:@"?"];
    NSString *path = pathComponents[0];
    NSDictionary *query = pathComponents.count > 1 ? [URLParser getQueryParameters:fullPath] : @{};

    // Handle through router
    BOOL handled = [self.router handleRequest:method
                                             path:path
                                            query:query];

    if (!handled) {
        NSLog(@"404 Not Found: %@", path);
    }
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C URL Routing Examples ===\n");

        // 1. URL parsing
        NSLog(@"--- 1. URL Parsing ---");

        NSString *testURL1 = @"https://api.example.com/users/123?sort=name&order=asc";
        [URLParser parseURL:testURL1];
        [URLParser getPath:testURL1];
        [URLParser getQueryParameters:testURL1];
        [URLParser getPathComponents:testURL1];

        // 2. Route matching
        NSLog(@"\n--- 2. Route Matching ---");

        Route *userRoute = [[Route alloc] initWithPattern:@"/users/:id"
                                                      method:@"GET"
                                                     handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"Handler called with params: %@", params);
    }];

        NSDictionary *params = nil;
        BOOL matches = [userRoute matchesPath:@"/users/123" parameters:&params];
        NSLog(@"Matches /users/123: %@ (params: %@)", matches ? @"YES" : @"NO", params);

        // 3. Router setup
        NSLog(@"\n--- 3. Router Setup ---");

        Router *router = [[Router alloc] init];

        [router GET:@"/posts"
          handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"GET /posts - Blog posts list");
    }];

        [router GET:@"/posts/:id"
          handler:^(NSDictionary *params, NSDictionary *query) {
        NSLog(@"GET /posts/%@ - Single post", params[@"id"]);
    }];

        // 4. Request handling
        NSLog(@"\n--- 4. Request Handling ---");

        [router handleRequest:@"GET"
                        path:@"/posts"
                       query:@{@"page": @"1", @"limit": @"10"}];

        [router handleRequest:@"GET"
                        path:@"/posts/42"
                       query:@{}];

        // 5. Complete web server
        NSLog(@"\n--- 5. Complete Web Server ---");

        WebServer *server = [[WebServer alloc] init];

        // Simulate requests
        NSLog(@"\nSimulating HTTP requests:");
        NSLog(@"\nGET /");
        [server handleRequestString:@"GET / HTTP/1.1"];

        NSLog(@"\nGET /users");
        [server handleRequestString:@"GET /users HTTP/1.1"];

        NSLog(@"\nGET /users/123");
        [server handleRequestString:@"GET /users/123 HTTP/1.1"];

        NSLog(@"\nGET /products/electronics?sort=price");
        [server handleRequestString:@"GET /products/electronics?sort=price HTTP/1.1"];

        NSLog(@"\nPOST /users");
        [server handleRequestString:@"POST /users HTTP/1.1"];

        NSLog(@"\nPUT /users/456");
        [server handleRequestString:@"PUT /users/456 HTTP/1.1"];

        NSLog(@"\nDELETE /users/789");
        [server handleRequestString:@"DELETE /users/789 HTTP/1.1"];

        NSLog(@"\nGET /nonexistent");
        [server handleRequestString:@"GET /nonexistent HTTP/1.1"];

        NSLog(@"\n=== URL Routing Examples Completed ===");
    }

    return 0;
}

💻 静态文件 objectivec

🟡 intermediate ⭐⭐⭐

提供静态文件服务,支持MIME类型检测和缓存

⏱️ 30 min 🏷️ objectivec, macos, web, static files
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C Static Files Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. MIME Type Detector

@interface MIMETypeDetector : NSObject

+ (instancetype)sharedDetector;
+ (NSString *)MIMETypeForExtension:(NSString *)extension;
+ (NSString *)MIMETypeForFile:(NSString *)filePath;

@end

@implementation MIMETypeDetector

+ (instancetype)sharedDetector {
    static MIMETypeDetector *instance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        instance = [[MIMETypeDetector alloc] init];
    });

    return instance;
}

+ (NSString *)MIMETypeForExtension:(NSString *)extension {
    NSDictionary *mimeTypes = @{
        // Text
        @"html": @"text/html",
        @"htm": @"text/html",
        @"css": @"text/css",
        @"js": @"application/javascript",
        @"json": @"application/json",
        @"xml": @"application/xml",
        @"txt": @"text/plain",
        @"md": @"text/markdown",

        // 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
        @"ttf": @"font/ttf",
        @"otf": @"font/otf",
        @"woff": @"font/woff",
        @"woff2": @"font/woff2",
        @"eot": @"application/vnd.ms-fontobject",

        // Documents
        @"pdf": @"application/pdf",
        @"doc": @"application/msword",
        @"docx": @"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        @"xls": @"application/vnd.ms-excel",
        @"xlsx": @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",

        // Audio
        @"mp3": @"audio/mpeg",
        @"wav": @"audio/wav",
        @"ogg": @"audio/ogg",

        // Video
        @"mp4": @"video/mp4",
        @"mov": @"video/quicktime",
        @"avi": @"video/x-msvideo",
        @"webm": @"video/webm",

        // Archives
        @"zip": @"application/zip",
        @"tar": @"application/x-tar",
        @"gz": @"application/gzip",

        // Default
        @"bin": @"application/octet-stream"
    };

    NSString *lowerExt = [extension lowercaseString];
    NSString *mimeType = mimeTypes[lowerExt];

    if (!mimeType) {
        return @"application/octet-stream";
    }

    return mimeType;
}

+ (NSString *)MIMETypeForFile:(NSString *)filePath {
    NSString *extension = [filePath pathExtension];
    return [self MIMETypeForExtension:extension];
}

@end

// MARK: - 2. Static File Handler

@interface StaticFileHandler : NSObject

@property (nonatomic, strong) NSString *documentRoot;
@property (nonatomic, strong) NSString *indexFile;

- (instancetype)initWithDocumentRoot:(NSString *)root;

- (NSData *)serveFileAtPath:(NSString *)path
                   mimeType:(NSString **)mimeType
                    statusCode:(NSInteger *)statusCode;

- (NSDictionary *)fileResponseForPath:(NSString *)path;

@end

@implementation StaticFileHandler

- (instancetype)initWithDocumentRoot:(NSString *)root {
    self = [super init];

    if (self) {
        _documentRoot = root;
        _indexFile = @"index.html";
    }

    return self;
}

- (NSData *)serveFileAtPath:(NSString *)path
                   mimeType:(NSString **)mimeType
                statusCode:(NSInteger *)statusCode {

    // Remove leading slash
    NSString *relativePath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;

    // If directory, append index file
    if ([relativePath hasSuffix:@"/"] || relativePath.length == 0) {
        relativePath = [relativePath stringByAppendingString:self.indexFile];
    }

    // Build full path
    NSString *fullPath = [self.documentRoot stringByAppendingPathComponent:relativePath];

    // Security: Check path doesn't escape document root
    NSString *canonicalFullPath = [fullPath stringByStandardizingPath];
    NSString *canonicalRoot = [self.documentRoot stringByStandardizingPath];

    if (![canonicalFullPath hasPrefix:canonicalRoot]) {
        NSLog(@"Attempted path traversal attack: %@", path);
        *statusCode = 403;
        *mimeType = @"text/plain";
        return [@"Forbidden: Path traversal detected" dataUsingEncoding:NSUTF8StringEncoding];
    }

    // Check if file exists
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:fullPath]) {
        NSLog(@"File not found: %@", fullPath);
        *statusCode = 404;
        *mimeType = @"text/plain";
        return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
    }

    // Check if it's a directory
    BOOL isDirectory = NO;
    [fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory];

    if (isDirectory) {
        // Try index file
        fullPath = [fullPath stringByAppendingPathComponent:self.indexFile];

        if (![fileManager fileExistsAtPath:fullPath]) {
            *statusCode = 404;
            *mimeType = @"text/plain";
            return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
        }
    }

    // Read file
    NSError *error = nil;
    NSData *fileData = [NSData dataWithContentsOfFile:fullPath options:0 error:&error];

    if (error) {
        NSLog(@"Error reading file: %@", error.localizedDescription);
        *statusCode = 500;
        *mimeType = @"text/plain";
        return [@"500 Internal Server Error" dataUsingEncoding:NSUTF8StringEncoding];
    }

    // Determine MIME type
    *mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForFile:fullPath];
    *statusCode = 200;

    NSLog(@"Serving file: %@ (%@)", fullPath, *mimeType);
    return fileData;
}

- (NSDictionary *)fileResponseForPath:(NSString *)path {
    NSString *mimeType = nil;
    NSInteger statusCode = 0;

    NSData *body = [self serveFileAtPath:path
                                mimeType:&mimeType
                             statusCode:&statusCode];

    return @{
        @"statusCode": @(statusCode),
        @"headers": @{
            @"Content-Type": mimeType
        },
        @"body": body ?: [NSData data]
    };
}

@end

// MARK: - 3. Cached Static File Handler

@interface CachedStaticFileHandler : StaticFileHandler

@property (nonatomic, strong) NSCache<NSString *, NSData *> *fileCache;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *fileSizes;

- (instancetype)initWithDocumentRoot:(NSString *)root cacheSize:(NSUInteger)count;

@end

@implementation CachedStaticFileHandler

- (instancetype)initWithDocumentRoot:(NSString *)root cacheSize:(NSUInteger)count {
    self = [super initWithDocumentRoot:root];

    if (self) {
        _fileCache = [[NSCache alloc] init];
        _fileCache.countLimit = count;
        _fileSizes = [NSMutableDictionary dictionary];
    }

    return self;
}

- (NSData *)serveFileAtPath:(NSString *)path
                   mimeType:(NSString **)mimeType
                statusCode:(NSInteger *)statusCode {

    // Check cache first
    NSData *cachedData = [self.fileCache objectForKey:path];

    if (cachedData) {
        NSLog(@"Cache hit: %@", path);

        *mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForExtension:[path pathExtension]];
        *statusCode = 200;
        return cachedData;
    }

    NSLog(@"Cache miss: %@", path);

    // Load from disk
    NSData *fileData = [super serveFileAtPath:path
                                   mimeType:mimeType
                                statusCode:statusCode];

    if (*statusCode == 200) {
        [self.fileCache setObject:fileData forKey:path];
        self.fileSizes[path] = @(fileData.length);
    }

    return fileData;
}

- (void)clearCache {
    [self.fileCache removeAllObjects];
    NSLog(@"File cache cleared");
}

- (NSUInteger)getCacheSize {
    return self.fileCache.countLimit;
}

@end

// MARK: - 4. File Server with Range Support

@interface RangeAwareFileHandler : StaticFileHandler

- (NSData *)serveFileAtPath:(NSString *)path
                   range:(NSRange)range
                statusCode:(NSInteger *)statusCode
                headers:(NSDictionary **)headers;

@end

@implementation RangeAwareFileHandler

- (NSData *)serveFileAtPath:(NSString *)path
                   range:(NSRange)range
                statusCode:(NSInteger *)statusCode
                headers:(NSDictionary **)headers {

    // Remove leading slash
    NSString *relativePath = [path hasPrefix:@"/"] ? [path substringFromIndex:1] : path;
    NSString *fullPath = [self.documentRoot stringByAppendingPathComponent:relativePath];

    // Get file size
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDictionary *attributes = [fileManager attributesOfItemAtPath:fullPath error:nil];

    if (!attributes) {
        *statusCode = 404;
        return nil;
    }

    NSUInteger fileSize = [attributes[NSFileSize] unsignedIntegerValue];

    // Validate range
    if (range.location >= fileSize) {
        *statusCode = 416; // Range Not Satisfiable
        *headers = @{
            @"Content-Range": [NSString stringWithFormat:@"bytes */%lu", (unsigned long)fileSize]
        };
        return nil;
    }

    // Adjust range length if needed
    if (range.location + range.length > fileSize) {
        range.length = fileSize - range.location;
    }

    // Read file range
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fullPath];
    [fileHandle seekToFileOffset:range.location];
    NSData *rangeData = [fileHandle readDataOfLength:range.length];
    [fileHandle closeFile];

    *statusCode = 206; // Partial Content
    *headers = @{
        @"Content-Range": [NSString stringWithFormat:@"bytes %lu-%lu/%lu",
                          (unsigned long)range.location,
                          (unsigned long)(range.location + range.length - 1),
                          (unsigned long)fileSize],
        @"Content-Length": @(rangeData.length)
    };

    NSLog(@"Serving range: %lu-%lu of %@",
          (unsigned long)range.location,
          (unsigned long)(range.location + range.length - 1),
          path);

    return rangeData;
}

@end

// MARK: - 5. Virtual File System

@interface VirtualFileSystem : NSObject

@property (nonatomic, strong) NSMutableDictionary<NSString *, NSData *> *files;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *mimeTypes;

- (void)addVirtualFile:(NSString *)path
                content:(NSString *)content
              mimeType:(NSString *)mimeType;

- (NSData *)getFileAtPath:(NSString *)path
                mimeType:(NSString **)mimeType
             statusCode:(NSInteger *)statusCode;

@end

@implementation VirtualFileSystem

- (instancetype)init {
    self = [super init];

    if (self) {
        _files = [NSMutableDictionary dictionary];
        _mimeTypes = [NSMutableDictionary dictionary];

        // Add default files
        [self addVirtualFile:@"/index.html"
                    content:@"<html><body><h1>Welcome</h1></body></html>"
                  mimeType:@"text/html"];

        [self addVirtualFile:@"/api/status.json"
                    content:@"{\"status\": \"ok\"}"
                  mimeType:@"application/json"];
    }

    return self;
}

- (void)addVirtualFile:(NSString *)path
                content:(NSString *)content
              mimeType:(NSString *)mimeType {

    NSData *fileData = [content dataUsingEncoding:NSUTF8StringEncoding];
    self.files[path] = fileData;
    self.mimeTypes[path] = mimeType;

    NSLog(@"Added virtual file: %@", path);
}

- (NSData *)getFileAtPath:(NSString *)path
                mimeType:(NSString **)mimeType
             statusCode:(NSInteger *)statusCode {

    // Try exact match first
    NSData *data = self.files[path];

    if (data) {
        *mimeType = self.mimeTypes[path];
        *statusCode = 200;
        return data;
    }

    // Try with leading slash
    NSString *key = [path hasPrefix:@"/"] ? path : [@"/" stringByAppendingString:path];
    data = self.files[key];

    if (data) {
        *mimeType = self.mimeTypes[key];
        *statusCode = 200;
        return data;
    }

    // 404
    *statusCode = 404;
    *mimeType = @"text/plain";
    return [@"404 Not Found" dataUsingEncoding:NSUTF8StringEncoding];
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Static Files Examples ===\n");

        // 1. MIME type detection
        NSLog(@"--- 1. MIME Type Detection ---");

        NSArray *testFiles = @[@"index.html", @"styles.css", @"app.js", @"image.png",
                              @"document.pdf", @"data.json", @"video.mp4"];

        for (NSString *file in testFiles) {
            NSString *mimeType = [[MIMETypeDetector sharedDetector] MIMETypeForExtension:[file pathExtension]];
            NSLog(@"%@ -> %@", file, mimeType);
        }

        // 2. Basic static file serving
        NSLog(@"\n--- 2. Static File Serving ---");

        StaticFileHandler *fileHandler = [[StaticFileHandler alloc] initWithDocumentRoot:@"/tmp/static"];

        // Create test directory and files
        NSFileManager *fileManager = [NSFileManager defaultManager];
        [fileManager createDirectoryAtPath:@"/tmp/static" withIntermediateDirectories:YES attributes:nil error:nil];

        NSString *htmlContent = @"<html><head><title>Test</title></head><body><h1>Hello World</h1></body></html>";
        [htmlContent writeToFile:@"/tmp/static/index.html"
                        atomically:YES
                          encoding:NSUTF8StringEncoding
                             error:nil];

        NSString *jsonContent = @"{\"message\": \"Hello from static file\"}";
        [jsonContent writeToFile:@"/tmp/static/api/data.json"
                        atomically:YES
                          encoding:NSUTF8StringEncoding
                             error:nil];

        // Serve files
        NSString *mimeType = nil;
        NSInteger statusCode = 0;

        NSData *htmlData = [fileHandler serveFileAtPath:@"/index.html"
                                               mimeType:&mimeType
                                            statusCode:&statusCode];
        NSLog(@"Served /index.html: status %ld, %lu bytes, mime: %@",
              (long)statusCode, (unsigned long)htmlData.length, mimeType);

        NSData *jsonData = [fileHandler serveFileAtPath:@"/api/data.json"
                                               mimeType:&mimeType
                                            statusCode:&statusCode];
        NSLog(@"Served /api/data.json: status %ld, %lu bytes, mime: %@",
              (long)statusCode, (unsigned long)jsonData.length, mimeType);

        // 404 case
        NSData *notFoundData = [fileHandler serveFileAtPath:@"/nonexistent.html"
                                                      mimeType:&mimeType
                                                   statusCode:&statusCode];
        NSLog(@"Served /nonexistent.html: status %ld, %lu bytes",
              (long)statusCode, (unsigned long)notFoundData.length);

        // 3. Cached static file serving
        NSLog(@"\n--- 3. Cached Static File Serving ---");

        CachedStaticFileHandler *cachedHandler = [[CachedStaticFileHandler alloc]
            initWithDocumentRoot:@"/tmp/static"
                     cacheSize:100];

        // First request (cache miss)
        [cachedHandler serveFileAtPath:@"/index.html"
                             mimeType:&mimeType
                          statusCode:&statusCode];

        // Second request (cache hit)
        [cachedHandler serveFileAtPath:@"/index.html"
                             mimeType:&mimeType
                          statusCode:&statusCode];

        NSLog(@"Cache size: %lu files", (unsigned long)[cachedHandler getCacheSize]);

        // 4. Range requests
        NSLog(@"\n--- 4. Range Requests ---");

        RangeAwareFileHandler *rangeHandler = [[RangeAwareFileHandler alloc]
            initWithDocumentRoot:@"/tmp/static"];

        // Create a larger file for range testing
        NSMutableData *largeFile = [NSMutableData data];
        for (int i = 0; i < 1000; i++) {
            NSString *line = [NSString stringWithFormat:@"Line %d\n", i];
            [largeFile appendData:[line dataUsingEncoding:NSUTF8StringEncoding]];
        }
        [largeFile writeToFile:@"/tmp/static/large.txt" atomically:YES];

        NSDictionary *headers = nil;
        NSData *rangeData = [rangeHandler serveFileAtPath:@"/large.txt"
                                                  range:NSMakeRange(100, 200)
                                               statusCode:&statusCode
                                                headers:&headers];

        NSLog(@"Range request: status %ld, %lu bytes, headers: %@",
              (long)statusCode, (unsigned long)rangeData.length, headers);

        // 5. Virtual file system
        NSLog(@"\n--- 5. Virtual File System ---");

        VirtualFileSystem *vfs = [[VirtualFileSystem alloc] init];

        [vfs addVirtualFile:@"/about.html"
                   content:@"<html><body><h1>About</h1></body></html>"
                 mimeType:@"text/html"];

        [vfs addVirtualFile:@"/config.json"
                   content:@"{\"env\": \"production\", \"debug\": false}"
                 mimeType:@"application/json"];

        NSData *vfsData = [vfs getFileAtPath:@"/about.html"
                                  mimeType:&mimeType
                               statusCode:&statusCode];
        NSLog(@"Virtual file /about.html: status %ld, %lu bytes",
              (long)statusCode, (unsigned long)vfsData.length);

        // Cleanup
        [fileManager removeItemAtPath:@"/tmp/static" error:nil];

        NSLog(@"\n=== Static Files Examples Completed ===");
        NSLog(@"\nKey features demonstrated:");
        NSLog(@"  - MIME type detection for common file types");
        NSLog(@"  - Static file serving from document root");
        NSLog(@"  - In-memory caching for performance");
        NSLog(@"  - HTTP Range request support");
        NSLog(@"  - Virtual file system for testing");
    }

    return 0;
}

💻 中间件 objectivec

🔴 complex ⭐⭐⭐⭐

实现用于日志记录、身份验证和错误处理的请求处理中间件管道

⏱️ 40 min 🏷️ objectivec, macos, web, middleware
Prerequisites: Advanced Objective-C, Foundation framework
// macOS Objective-C Middleware Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Request and Response Models

@interface HTTPRequest : NSObject

@property (nonatomic, strong) NSString *method;
@property (nonatomic, strong) NSString *path;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *headers;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *query;
@property (nonatomic, strong) NSData *body;
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *userData;

- (instancetype)initWithMethod:(NSString *)method
                         path:(NSString *)path;

- (void)addHeader:(NSString *)value forKey:(NSString *)key;

@end

@implementation HTTPRequest

- (instancetype)initWithMethod:(NSString *)method
                         path:(NSString *)path {
    self = [super init];

    if (self) {
        _method = method;
        _path = path;
        _headers = [NSMutableDictionary dictionary];
        _query = [NSMutableDictionary dictionary];
        _userData = [NSMutableDictionary dictionary];
    }

    return self;
}

- (void)addHeader:(NSString *)value forKey:(NSString *)key {
    [(NSMutableDictionary *)self.headers setObject:value forKey:key];
}

@end

@interface HTTPResponse : NSObject

@property (nonatomic, assign) NSInteger statusCode;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *headers;
@property (nonatomic, strong) NSData *body;
@property (nonatomic, strong) NSString *statusText;

- (instancetype)initWithStatusCode:(NSInteger)statusCode;

- (void)setHeader:(NSString *)value forKey:(NSString *)key;
- (NSString *)description;

@end

@implementation HTTPResponse

- (instancetype)initWithStatusCode:(NSInteger)statusCode {
    self = [super init];

    if (self) {
        _statusCode = statusCode;
        _headers = [NSMutableDictionary dictionary];
        _statusText = [self statusTextForCode:statusCode];
    }

    return self;
}

- (void)setHeader:(NSString *)value forKey:(NSString *)key {
    [(NSMutableDictionary *)self.headers setObject:value forKey:key];
}

- (NSString *)statusTextForCode:(NSInteger)code {
    switch (code) {
        case 200: return @"OK";
        case 201: return @"Created";
        case 204: return @"No Content";
        case 400: return @"Bad Request";
        case 401: return @"Unauthorized";
        case 403: return @"Forbidden";
        case 404: return @"Not Found";
        case 500: return @"Internal Server Error";
        default: return @"Unknown";
    }
}

- (NSString *)description {
    return [NSString stringWithFormat:@"HTTPResponse(%@ - %ld)", self.statusText, (long)self.statusCode];
}

@end

// MARK: - 2. Middleware Protocol

@protocol Middleware <NSObject>

@required
- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next;

@end

// MARK: - 3. Logging Middleware

@interface LoggingMiddleware : NSObject <Middleware>

+ (instancetype)sharedMiddleware;

@end

@implementation LoggingMiddleware

+ (instancetype)sharedMiddleware {
    static LoggingMiddleware *instance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        instance = [[LoggingMiddleware alloc] init];
    });

    return instance;
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next {

    NSLog(@"\n=== Request ===");
    NSLog(@"Method: %@", request.method);
    NSLog(@"Path: %@", request.path);
    NSLog(@"Headers: %@", request.headers);
    NSLog(@"Query: %@", request.query);

    NSDate *startTime = [NSDate date];

    // Call next middleware
    if (next) {
        next();
    }

    NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:startTime];

    NSLog(@"\n=== Response ===");
    NSLog(@"Status: %ld %@", (long)response.statusCode, response.statusText);
    NSLog(@"Duration: %.0f ms", duration * 1000);
    NSLog(@"Headers: %@", response.headers);
}

@end

// MARK: - 4. Authentication Middleware

@interface AuthenticationMiddleware : NSObject <Middleware>

@property (nonatomic, strong) NSSet<NSString *> *publicPaths;
+ (instancetype)middlewareWithValidTokens:(NSSet<NSString *> *)tokens;

- (instancetype)initWithValidTokens:(NSSet<NSString *> *)tokens;

@end

@implementation AuthenticationMiddleware

{
    NSSet<NSString *> *_validTokens;
}

+ (instancetype)middlewareWithValidTokens:(NSSet<NSString *> *)tokens {
    return [[self alloc] initWithValidTokens:tokens];
}

- (instancetype)initWithValidTokens:(NSSet<NSString *> *)tokens {
    self = [super init];

    if (self) {
        _validTokens = tokens;
        _publicPaths = [NSSet setWithObjects:@"/", @"/health", @"/ping", nil];
    }

    return self;
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next {

    // Check if path is public
    if ([self.publicPaths containsObject:request.path]) {
        NSLog(@"Public path, skipping authentication");
        if (next) next();
        return;
    }

    // Get authorization header
    NSString *authHeader = request.headers[@"Authorization"];

    if (!authHeader) {
        NSLog(@"Missing authorization header");
        response.statusCode = 401;
        [response setHeader:@"application/json" forKey:@"Content-Type"];
        return;
    }

    // Check Bearer token
    if ([authHeader hasPrefix:@"Bearer "]) {
        NSString *token = [authHeader substringFromIndex:7];

        if ([_validTokens containsObject:token]) {
            NSLog(@"Authentication successful");
            request.userData[@"authenticatedUser"] = @"user_from_token";
            if (next) next();
        } else {
            NSLog(@"Invalid token");
            response.statusCode = 401;
            NSString *errorBody = @"{\"error\": \"Invalid token\"}";
            response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
            [response setHeader:@"application/json" forKey:@"Content-Type"];
        }
    } else {
        NSLog(@"Invalid authorization format");
        response.statusCode = 401;
        [response setHeader:@"application/json" forKey:@"Content-Type"];
    }
}

@end

// MARK: - 5. CORS Middleware

@interface CORSMiddleware : NSObject <Middleware>

@property (nonatomic, strong) NSString *allowedOrigin;
@property (nonatomic, strong) NSSet<NSString *> *allowedMethods;
@property (nonatomic, strong) NSSet<NSString *> *allowedHeaders;

+ (instancetype)defaultMiddleware;
- (instancetype)initWithAllowedOrigin:(NSString *)origin;

@end

@implementation CORSMiddleware

+ (instancetype)defaultMiddleware {
    return [[self alloc] initWithAllowedOrigin:@"*"];
}

- (instancetype)initWithAllowedOrigin:(NSString *)origin {
    self = [super init];

    if (self) {
        _allowedOrigin = origin;
        _allowedMethods = [NSSet setWithObjects:@"GET", @"POST", @"PUT", @"DELETE", @"OPTIONS", nil];
        _allowedHeaders = [NSSet setWithObjects:@"Content-Type", @"Authorization", nil];
    }

    return self;
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next {

    // Add CORS headers
    [response setHeader:self.allowedOrigin forKey:@"Access-Control-Allow-Origin"];

    // Handle preflight request
    if ([request.method isEqualToString:@"OPTIONS"]) {
        [response setHeader:[self.allowedMethods allObjects].componentsJoinedByString:@", "]
                   forKey:@"Access-Control-Allow-Methods"];

        [response setHeader:[self.allowedHeaders allObjects].componentsJoinedByString:@", "]
                   forKey:@"Access-Control-Allow-Headers"];

        [response setHeader:@"3600" forKey:@"Access-Control-Max-Age"];

        response.statusCode = 204;
        NSLog(@"CORS preflight request handled");
        return;
    }

    // Process request
    if (next) {
        next();
    }

    // Add CORS headers to response
    [response setHeader:self.allowedOrigin forKey:@"Access-Control-Allow-Origin"];
    [response setHeader:@"true" forKey:@"Access-Control-Allow-Credentials"];
}

@end

// MARK: - 6. Error Handling Middleware

@interface ErrorHandlingMiddleware : NSObject <Middleware>

+ (instancetype)sharedMiddleware;

@end

@implementation ErrorHandlingMiddleware

+ (instancetype)sharedMiddleware {
    static ErrorHandlingMiddleware *instance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        instance = [[ErrorHandlingMiddleware alloc] init];
    });

    return instance;
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next {

    @try {
        if (next) {
            next();
        }

        // Check if response has error status
        if (response.statusCode >= 400) {
            NSLog(@"Error response: %ld", (long)response.statusCode);

            // Ensure error response has body
            if (!response.body) {
                NSString *errorBody = [NSString stringWithFormat:@"{\"error\": \"%@\", \"status\": %ld}",
                                        response.statusText, (long)response.statusCode];
                response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
                [response setHeader:@"application/json" forKey:@"Content-Type"];
            }
        }
    }
    @catch (NSException *exception) {
        NSLog(@"Exception in middleware chain: %@", exception.reason);

        response.statusCode = 500;
        NSString *errorBody = [NSString stringWithFormat:@"{\"error\": \"Internal Server Error\", \"message\": \"%@\"}",
                                exception.reason];
        response.body = [errorBody dataUsingEncoding:NSUTF8StringEncoding];
        [response setHeader:@"application/json" forKey:@"Content-Type"];
    }
}

@end

// MARK: - 7. Content Type Middleware

@interface ContentTypeMiddleware : NSObject <Middleware>

+ (instancetype)sharedMiddleware;

@end

@implementation ContentTypeMiddleware

+ (instancetype)sharedMiddleware {
    static ContentTypeMiddleware *instance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        instance = [[ContentTypeMiddleware alloc] init];
    });

    return instance;
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              next:(void (^)(void))next {

    // Validate content type for POST/PUT requests
    if ([request.method isEqualToString:@"POST"] ||
        [request.method isEqualToString:@"PUT"]) {

        NSString *contentType = request.headers[@"Content-Type"];

        if (!contentType) {
            NSLog(@"Missing Content-Type header");
            response.statusCode = 400;
            return;
        }

        // Store content type in user data for later use
        request.userData[@"contentType"] = contentType;
    }

    // Set default content type for response if not set
    if (next) {
        next();
    }

    if (!response.headers[@"Content-Type"]) {
        [response setHeader:@"application/json" forKey:@"Content-Type"];
    }
}

@end

// MARK: - 8. Middleware Pipeline

@interface MiddlewarePipeline : NSObject

@property (nonatomic, strong) NSMutableArray<id<Middleware>> *middlewares;

- (instancetype)initWithMiddlewares:(NSArray<id<Middleware>> *)middlewares;
- (void)addMiddleware:(id<Middleware>)middleware;
- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              handler:(void (^)(HTTPRequest *, HTTPResponse *))handler;

@end

@implementation MiddlewarePipeline

- (instancetype)initWithMiddlewares:(NSArray<id<Middleware>> *)middlewares {
    self = [super init];

    if (self) {
        _middlewares = [middlewares mutableCopy];
    }

    return self;
}

- (instancetype)init {
    return [self initWithMiddlewares:@[]];
}

- (void)addMiddleware:(id<Middleware>)middleware {
    [self.middlewares addObject:middleware];
}

- (void)processRequest:(HTTPRequest *)request
             response:(HTTPResponse *)response
              handler:(void (^)(HTTPRequest *, HTTPResponse *))handler {

    // Create middleware chain
    __block NSInteger index = 0;

    void (^next)(void) = ^{
        if (index < self.middlewares.count) {
            id<Middleware> middleware = self.middlewares[index];
            index++;
            [middleware processRequest:request response:response next:next];
        } else if (handler) {
            // All middleware processed, call final handler
            handler(request, response);
        }
    };

    // Start middleware chain
    next();
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Middleware Examples ===\n");

        // Create middleware pipeline
        NSLog(@"--- Setting up middleware pipeline ---\n");

        // Create authentication middleware with valid tokens
        NSSet *validTokens = [NSSet setWithObjects:@"secret-token-123", @"admin-token-456", nil];
        AuthenticationMiddleware *authMiddleware = [AuthenticationMiddleware middlewareWithValidTokens:validTokens];

        // Create middleware pipeline
        MiddlewarePipeline *pipeline = [[MiddlewarePipeline alloc] init];
        [pipeline addMiddleware:[LoggingMiddleware sharedMiddleware]];
        [pipeline addMiddleware:[ContentTypeMiddleware sharedMiddleware]];
        [pipeline addMiddleware:authMiddleware];
        [pipeline addMiddleware:[CORSMiddleware defaultMiddleware]];
        [pipeline addMiddleware:[ErrorHandlingMiddleware sharedMiddleware]];

        NSLog(@"Middleware pipeline configured with:");
        NSLog(@"  1. Logging Middleware");
        NSLog(@"  2. Content Type Middleware");
        NSLog(@"  3. Authentication Middleware");
        NSLog(@"  4. CORS Middleware");
        NSLog(@"  5. Error Handling Middleware\n");

        // Test request 1: Public path
        NSLog(@"\n=== Test 1: Public path (no auth required) ===");

        HTTPRequest *request1 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/health"];
        [request1 addHeader:@"application/json" forKey:@"Accept"];

        HTTPResponse *response1 = [[HTTPResponse alloc] initWithStatusCode:200];

        [pipeline processRequest:request1
                     response:response1
                      handler:^(HTTPRequest *req, HTTPResponse *resp) {
            NSLog(@"Handler called");
            resp.statusCode = 200;
            NSString *body = @"{\"status\": \"healthy\"}";
            resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
        }];

        // Test request 2: Protected path with valid token
        NSLog(@"\n=== Test 2: Protected path with valid token ===");

        HTTPRequest *request2 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/api/users"];
        [request2 addHeader:@"Bearer secret-token-123" forKey:@"Authorization"];
        [request2 addHeader:@"application/json" forKey:@"Accept"];

        HTTPResponse *response2 = [[HTTPResponse alloc] initWithStatusCode:200];

        [pipeline processRequest:request2
                     response:response2
                      handler:^(HTTPRequest *req, HTTPResponse *resp) {
            NSLog(@"Handler called");
            resp.statusCode = 200;
            NSString *body = @"{\"users\": [\"user1\", \"user2\"]}";
            resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
        }];

        // Test request 3: Protected path without token
        NSLog(@"\n=== Test 3: Protected path without token ===");

        HTTPRequest *request3 = [[HTTPRequest alloc] initWithMethod:@"GET" path:@"/api/users"];
        [request3 addHeader:@"application/json" forKey:@"Accept"];

        HTTPResponse *response3 = [[HTTPResponse alloc] initWithStatusCode:200];

        [pipeline processRequest:request3
                     response:response3
                      handler:^(HTTPRequest *req, HTTPResponse *resp) {
            NSLog(@"Handler called (should not reach here)");
            resp.statusCode = 200;
        }];

        // Test request 4: OPTIONS preflight
        NSLog(@"\n=== Test 4: CORS preflight request ===");

        HTTPRequest *request4 = [[HTTPRequest alloc] initWithMethod:@"OPTIONS" path:@"/api/users"];
        [request4 addHeader:@"*" forKey:@"Origin"];
        [request4 addHeader:@"POST" forKey:@"Access-Control-Request-Method"];

        HTTPResponse *response4 = [[HTTPResponse alloc] initWithStatusCode:200];

        [pipeline processRequest:request4
                     response:response4
                      handler:^(HTTPRequest *req, HTTPResponse *resp) {
            NSLog(@"Handler should not be called for OPTIONS");
        }];

        // Test request 5: POST with content type
        NSLog(@"\n=== Test 5: POST with content type ===");

        HTTPRequest *request5 = [[HTTPRequest alloc] initWithMethod:@"POST" path:@"/api/users"];
        [request5 addHeader:@"Bearer secret-token-123" forKey:@"Authorization"];
        [request5 addHeader:@"application/json" forKey:@"Content-Type"];

        NSString *jsonBody = @"{\"name\": \"John\", \"email\": \"[email protected]\"}";
        request5.body = [jsonBody dataUsingEncoding:NSUTF8StringEncoding];

        HTTPResponse *response5 = [[HTTPResponse alloc] initWithStatusCode:201];

        [pipeline processRequest:request5
                     response:response5
                      handler:^(HTTPRequest *req, HTTPResponse *resp) {
            NSLog(@"Handler called - creating user");
            resp.statusCode = 201;
            NSString *body = @"{\"id\": \"123\", \"name\": \"John\"}";
            resp.body = [body dataUsingEncoding:NSUTF8StringEncoding];
        }];

        NSLog(@"\n=== Middleware Examples Completed ===");
        NSLog(@"\nMiddleware execution order:");
        NSLog(@"  1. Request -> Logging Middleware");
        NSLog(@"  2.         -> Content Type Middleware");
        NSLog(@"  3.         -> Authentication Middleware");
        NSLog(@"  4.         -> CORS Middleware");
        NSLog(@"  5.         -> Error Handling Middleware");
        NSLog(@"  6.         -> Handler");
        NSLog(@"  7.         -> Response back through middleware chain");
    }

    return 0;
}