Elysia Tools
Navigation
Objective-C
macOS Web Features Objective-C Samples
macOS Objective-C web features examples including URL routing, middleware, and static file serving
Samples
Entries inside this sample collection
URL Routing
Parse and handle URL routes with path parameters and query strings
Difficulty
7/10
Estimated time
35 min
Tags
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:¶ms]) {
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:¶ms];
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;
}Middleware
Implement request processing middleware pipeline for logging, authentication, and error handling
Difficulty
8/10
Estimated time
40 min
Tags
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;
}Static Files
Serve static files with MIME type detection and caching support
Difficulty
6/10
Estimated time
30 min
Tags
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];
[fileManager createDirectoryAtPath:@"/tmp/static/api" withIntermediateDirectories:YES attributes:nil 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;
}Tools
Tools frequently paired with this sample
Related