Created
May 10, 2012 03:35
-
-
Save Nub/2650888 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HTTPRouter.h | |
// DynamicServer | |
// | |
// Created by Zachry Thayer on 5/9/12. | |
// Copyright (c) 2012 Zachry Thayer. All rights reserved. | |
// | |
#import "HTTPConnection.h" | |
#import "HTTPMessage.h" | |
@interface HTTPMessage (HTTPRouterMessage) | |
@property (nonatomic, copy) NSMutableDictionary* params; | |
@end | |
typedef id (^HTTPRouterBlock)(HTTPMessage* message); | |
@interface HTTPRouter : HTTPConnection | |
// :var = keyed variable | |
+ (void)handleRoute:(NSString*)route withBlock:(HTTPRouterBlock)routeBlock; | |
+ (void)handleRoute:(NSString*)route withRouter:(HTTPRouter*)router; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HTTPRouter.m | |
// CocoaHTTPServer | |
// | |
// Created by Zachry Thayer on 5/9/12. | |
// Copyright (c) 2012 Zachry Thayer. All rights reserved. | |
// | |
#import "HTTPRouter.h" | |
#import "HTTPDataResponse.h" | |
#import "HTTPLogging.h" | |
#import <objc/runtime.h> | |
// Patch HTTPMessage to have params for the query and route parameters | |
static char * const HTTPRouterMessageKey = "HTTPRouterMessageKey"; | |
@implementation HTTPMessage (HTTPRouterMessage) | |
- (void)setParams:(NSMutableDictionary *)newParams | |
{ | |
objc_setAssociatedObject(self, HTTPRouterMessageKey, newParams, OBJC_ASSOCIATION_COPY); | |
} | |
- (NSMutableDictionary*)params | |
{ | |
return objc_getAssociatedObject(self, HTTPRouterMessageKey); | |
} | |
@end | |
//Eeek! Globals! | |
NSMutableDictionary* HTTPRouterRoutes; | |
//BOOL HTTPRouterInitialized = NO; | |
@interface HTTPRouter () | |
//Magic alias! | |
@property (nonatomic, strong)NSMutableDictionary* routes; | |
//Helpers | |
+ (void)prepareRoute:(NSString*)route; | |
- (HTTPDataResponse*)processResponse:(id)response; | |
@end | |
@implementation HTTPRouter | |
@synthesize routes; | |
#pragma mark method overides | |
- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path | |
{ | |
// Find best matched route to request | |
NSString* basePath = [[path componentsSeparatedByString:@"?"] objectAtIndex:0]; | |
//NSLog(@"Searching for path match to %@", basePath); | |
for (NSString* key in self.routes) | |
{ | |
NSString* predicatePath = [key stringByAppendingString:@".predicate"]; | |
NSPredicate *uriMatcher = [NSPredicate predicateWithFormat:[self.routes valueForKeyPath:predicatePath]]; | |
BOOL uriMatch = [uriMatcher evaluateWithObject:basePath]; | |
if (uriMatch) // Handle Match | |
{ | |
// NSLog(@"Matched URI %@ with route %@", basePath, key); | |
NSString* blockPath = [key stringByAppendingString:@".block"]; | |
HTTPRouterBlock block = [self.routes valueForKeyPath:blockPath]; | |
request.params = [self parseRouteParams:key withURI:basePath]; | |
id response = block(request); | |
//NSLog(@"Response:%@", response); | |
return [self processResponse:response]; | |
} | |
} | |
// Default response | |
return [super httpResponseForMethod:method URI:path]; | |
} | |
- (NSMutableDictionary *)parseRouteParams:(NSString*)route withURI:(NSString*)path | |
{ | |
NSMutableDictionary* paramDictionary = [NSMutableDictionary dictionaryWithDictionary:[self parseGetParams]]; | |
NSMutableArray* components = [NSMutableArray arrayWithArray:[[path substringFromIndex:1] componentsSeparatedByString:@"/"]]; | |
NSMutableDictionary* routeDictionary = [self.routes valueForKey:route]; | |
NSDictionary* paramIndices = [routeDictionary valueForKey:@"paramIndices"]; | |
for (NSString* key in paramIndices) | |
{ | |
NSString* value = [components objectAtIndex:[[paramIndices valueForKey:key] integerValue]]; | |
[paramDictionary setValue:value forKey:key]; | |
} | |
return paramDictionary; | |
} | |
#pragma mark route management | |
+ (void)handleRoute:(NSString*)route withBlock:(HTTPRouterBlock)routeBlock | |
{ | |
if (!HTTPRouterRoutes) | |
{ | |
HTTPRouterRoutes = [NSMutableDictionary dictionary]; | |
} | |
//Add route | |
NSMutableDictionary* routeDictionary = [NSMutableDictionary dictionary]; | |
//[routeDictionary setValue:(__bridge_transfer id)Block_copy((__bridge void *)routeBlock) forKey:@"block"]; | |
[routeDictionary setValue:[routeBlock copy] forKey:@"block"]; | |
[HTTPRouterRoutes setValue:routeDictionary forKey:route]; | |
[self prepareRoute:route]; | |
} | |
+ (void)handleRoute:(NSString*)route withRouter:(HTTPRouter*)router | |
{ | |
if (!HTTPRouterRoutes) | |
{ | |
HTTPRouterRoutes = [NSMutableDictionary dictionary]; | |
} | |
//Add route | |
NSMutableDictionary* routeDictionary = [NSMutableDictionary dictionary]; | |
[routeDictionary setValue:router forKey:@"router"]; | |
[HTTPRouterRoutes setValue:routeDictionary forKey:route]; | |
[self prepareRoute:route]; | |
} | |
#pragma mark Accessor | |
- (NSMutableDictionary*)routes | |
{ | |
if (!HTTPRouterRoutes) | |
{ | |
HTTPRouterRoutes = [NSMutableDictionary dictionary]; | |
} | |
return HTTPRouterRoutes; | |
} | |
#pragma mark Helpers | |
+ (void)prepareRoute:(NSString*)route | |
{ | |
NSMutableDictionary* routeDictionary = [HTTPRouterRoutes valueForKey:route]; | |
NSMutableArray* components = [NSMutableArray arrayWithArray:[route componentsSeparatedByString:@"/"]]; | |
NSMutableString *regex = [NSMutableString stringWithFormat:@""]; | |
NSInteger index = 0; | |
for (NSString *component in components) | |
{ | |
if ([component characterAtIndex:0] == ':')//Found parameter | |
{ | |
//Prepare components to become a regex string | |
//[components replaceObjectAtIndex:index withObject:@".*?\/"]; | |
[regex appendFormat:@".*\/?"]; | |
//Get parameter dictionary or create and add | |
NSMutableDictionary* parameters = [routeDictionary valueForKey:@"paramIndices"]; | |
if (!parameters) | |
{ | |
parameters = [NSMutableDictionary dictionary]; | |
[routeDictionary setValue:parameters forKey:@"paramIndices"]; | |
} | |
//Insert the route component index for later easy fetching | |
NSNumber* paramIndex = [NSNumber numberWithInteger:index]; | |
NSString* paramKey = [component substringFromIndex:1];//Skip ':' | |
[parameters setValue:paramIndex forKey:paramKey]; | |
} | |
else | |
{ | |
[regex appendFormat:@"\/%@", component]; | |
} | |
index++; | |
} | |
//NSLog(@"Predicate Regex: %@", regex); | |
NSString* predicate = [NSString stringWithFormat:@"SELF MATCHES '%@\/?'", regex];//skip first / | |
[routeDictionary setValue:predicate forKey:@"predicate"]; | |
} | |
- (HTTPDataResponse*)processResponse:(id)response | |
{ | |
HTTPDataResponse *dataResponse = nil; | |
if ([response isKindOfClass:[NSString class]]) | |
{ | |
dataResponse = [[HTTPDataResponse alloc] initWithData:[response dataUsingEncoding:NSUTF8StringEncoding]]; | |
} | |
if ([response isKindOfClass:[NSData class]]) | |
{ | |
dataResponse = [[HTTPDataResponse alloc] initWithData:response]; | |
} | |
if ([response isKindOfClass:[NSDictionary class]] || [response isKindOfClass:[NSArray class]]) | |
{ | |
dataResponse = [[HTTPDataResponse alloc] initWithData:[NSJSONSerialization dataWithJSONObject:response options:0 error:nil]]; | |
} | |
return dataResponse; | |
} | |
@end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment