| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- #import "RoutingConnection.h"
- #import "RoutingHTTPServer.h"
- #import "HTTPMessage.h"
- #import "HTTPResponseProxy.h"
- #pragma clang diagnostic ignored "-Wdirect-ivar-access"
- #pragma clang diagnostic ignored "-Widiomatic-parentheses"
- #pragma clang diagnostic ignored "-Wundeclared-selector"
- @implementation RoutingConnection {
- __unsafe_unretained RoutingHTTPServer *http;
- NSDictionary *headers;
- }
- - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig {
- if (self = [super initWithAsyncSocket:newSocket configuration:aConfig]) {
- NSAssert([config.server isKindOfClass:[RoutingHTTPServer class]],
- @"A RoutingConnection is being used with a server that is not a %@",
- NSStringFromClass([RoutingHTTPServer class]));
-
- http = (RoutingHTTPServer *)config.server;
- }
- return self;
- }
- - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path {
-
- if ([http supportsMethod:method])
- return YES;
-
- return [super supportsMethod:method atPath:path];
- }
- - (BOOL)shouldHandleRequestForMethod:(NSString *)method atPath:(NSString *)path {
- // The default implementation is strict about the use of Content-Length. Either
- // a given method + path combination must *always* include data or *never*
- // include data. The routing connection is lenient, a POST that sometimes does
- // not include data or a GET that sometimes does is fine. It is up to the route
- // implementations to decide how to handle these situations.
- return YES;
- }
- - (void)processBodyData:(NSData *)postDataChunk {
- BOOL result = [request appendData:postDataChunk];
- if (!result) {
- // TODO: Log
- }
- }
- - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path {
- NSURL *url = [request url];
- NSString *query = nil;
- NSDictionary *params = [NSDictionary dictionary];
- headers = nil;
-
- if (url) {
- path = [url path]; // Strip the query string from the path
- query = [url query];
- if (query) {
- params = [self parseParams:query];
- }
- }
-
- RouteResponse *response = [http routeMethod:method withPath:path parameters:params request:request connection:self];
- if (response != nil) {
- headers = response.headers;
- return response.proxiedResponse;
- }
-
- // Set a MIME type for static files if possible
- NSObject<HTTPResponse> *staticResponse = [super httpResponseForMethod:method URI:path];
- if (staticResponse && [staticResponse respondsToSelector:@selector(filePath)]) {
- NSString *mimeType = [http mimeTypeForPath:[staticResponse performSelector:@selector(filePath)]];
- if (mimeType) {
- headers = [NSDictionary dictionaryWithObject:mimeType forKey:@"Content-Type"];
- }
- }
- return staticResponse;
- }
- - (void)responseHasAvailableData:(NSObject<HTTPResponse> *)sender {
- HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse;
- if (proxy.response == sender) {
- [super responseHasAvailableData:httpResponse];
- }
- }
- - (void)responseDidAbort:(NSObject<HTTPResponse> *)sender {
- HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse;
- if (proxy.response == sender) {
- [super responseDidAbort:httpResponse];
- }
- }
- - (void)setHeadersForResponse:(HTTPMessage *)response isError:(BOOL)isError {
- [http.defaultHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
- [response setHeaderField:field value:value];
- }];
-
- if (headers && !isError) {
- [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
- [response setHeaderField:field value:value];
- }];
- }
-
- // Set the connection header if not already specified
- NSString *connection = [response headerField:@"Connection"];
- if (!connection) {
- connection = [self shouldDie] ? @"close" : @"keep-alive";
- [response setHeaderField:@"Connection" value:connection];
- }
- }
- - (NSData *)preprocessResponse:(HTTPMessage *)response {
- [self setHeadersForResponse:response isError:NO];
- return [super preprocessResponse:response];
- }
- - (NSData *)preprocessErrorResponse:(HTTPMessage *)response {
- [self setHeadersForResponse:response isError:YES];
- return [super preprocessErrorResponse:response];
- }
- - (BOOL)shouldDie {
- __block BOOL shouldDie = [super shouldDie];
-
- // Allow custom headers to determine if the connection should be closed
- if (!shouldDie && headers) {
- [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
- if ([field caseInsensitiveCompare:@"connection"] == NSOrderedSame) {
- if ([value caseInsensitiveCompare:@"close"] == NSOrderedSame) {
- shouldDie = YES;
- }
- *stop = YES;
- }
- }];
- }
-
- return shouldDie;
- }
- @end
|