RoutingConnection.m 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #import "RoutingConnection.h"
  2. #import "RoutingHTTPServer.h"
  3. #import "HTTPMessage.h"
  4. #import "HTTPResponseProxy.h"
  5. #pragma clang diagnostic ignored "-Wdirect-ivar-access"
  6. #pragma clang diagnostic ignored "-Widiomatic-parentheses"
  7. #pragma clang diagnostic ignored "-Wundeclared-selector"
  8. @implementation RoutingConnection {
  9. __unsafe_unretained RoutingHTTPServer *http;
  10. NSDictionary *headers;
  11. }
  12. - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig {
  13. if (self = [super initWithAsyncSocket:newSocket configuration:aConfig]) {
  14. NSAssert([config.server isKindOfClass:[RoutingHTTPServer class]],
  15. @"A RoutingConnection is being used with a server that is not a %@",
  16. NSStringFromClass([RoutingHTTPServer class]));
  17. http = (RoutingHTTPServer *)config.server;
  18. }
  19. return self;
  20. }
  21. - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path {
  22. if ([http supportsMethod:method])
  23. return YES;
  24. return [super supportsMethod:method atPath:path];
  25. }
  26. - (BOOL)shouldHandleRequestForMethod:(NSString *)method atPath:(NSString *)path {
  27. // The default implementation is strict about the use of Content-Length. Either
  28. // a given method + path combination must *always* include data or *never*
  29. // include data. The routing connection is lenient, a POST that sometimes does
  30. // not include data or a GET that sometimes does is fine. It is up to the route
  31. // implementations to decide how to handle these situations.
  32. return YES;
  33. }
  34. - (void)processBodyData:(NSData *)postDataChunk {
  35. BOOL result = [request appendData:postDataChunk];
  36. if (!result) {
  37. // TODO: Log
  38. }
  39. }
  40. - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path {
  41. NSURL *url = [request url];
  42. NSString *query = nil;
  43. NSDictionary *params = [NSDictionary dictionary];
  44. headers = nil;
  45. if (url) {
  46. path = [url path]; // Strip the query string from the path
  47. query = [url query];
  48. if (query) {
  49. params = [self parseParams:query];
  50. }
  51. }
  52. RouteResponse *response = [http routeMethod:method withPath:path parameters:params request:request connection:self];
  53. if (response != nil) {
  54. headers = response.headers;
  55. return response.proxiedResponse;
  56. }
  57. // Set a MIME type for static files if possible
  58. NSObject<HTTPResponse> *staticResponse = [super httpResponseForMethod:method URI:path];
  59. if (staticResponse && [staticResponse respondsToSelector:@selector(filePath)]) {
  60. NSString *mimeType = [http mimeTypeForPath:[staticResponse performSelector:@selector(filePath)]];
  61. if (mimeType) {
  62. headers = [NSDictionary dictionaryWithObject:mimeType forKey:@"Content-Type"];
  63. }
  64. }
  65. return staticResponse;
  66. }
  67. - (void)responseHasAvailableData:(NSObject<HTTPResponse> *)sender {
  68. HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse;
  69. if (proxy.response == sender) {
  70. [super responseHasAvailableData:httpResponse];
  71. }
  72. }
  73. - (void)responseDidAbort:(NSObject<HTTPResponse> *)sender {
  74. HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse;
  75. if (proxy.response == sender) {
  76. [super responseDidAbort:httpResponse];
  77. }
  78. }
  79. - (void)setHeadersForResponse:(HTTPMessage *)response isError:(BOOL)isError {
  80. [http.defaultHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
  81. [response setHeaderField:field value:value];
  82. }];
  83. if (headers && !isError) {
  84. [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
  85. [response setHeaderField:field value:value];
  86. }];
  87. }
  88. // Set the connection header if not already specified
  89. NSString *connection = [response headerField:@"Connection"];
  90. if (!connection) {
  91. connection = [self shouldDie] ? @"close" : @"keep-alive";
  92. [response setHeaderField:@"Connection" value:connection];
  93. }
  94. }
  95. - (NSData *)preprocessResponse:(HTTPMessage *)response {
  96. [self setHeadersForResponse:response isError:NO];
  97. return [super preprocessResponse:response];
  98. }
  99. - (NSData *)preprocessErrorResponse:(HTTPMessage *)response {
  100. [self setHeadersForResponse:response isError:YES];
  101. return [super preprocessErrorResponse:response];
  102. }
  103. - (BOOL)shouldDie {
  104. __block BOOL shouldDie = [super shouldDie];
  105. // Allow custom headers to determine if the connection should be closed
  106. if (!shouldDie && headers) {
  107. [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) {
  108. if ([field caseInsensitiveCompare:@"connection"] == NSOrderedSame) {
  109. if ([value caseInsensitiveCompare:@"close"] == NSOrderedSame) {
  110. shouldDie = YES;
  111. }
  112. *stop = YES;
  113. }
  114. }];
  115. }
  116. return shouldDie;
  117. }
  118. @end