FBRoute.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "FBRoute.h"
  10. #import "FBRouteRequest-Private.h"
  11. #import <objc/message.h>
  12. #import "FBExceptionHandler.h"
  13. #import "FBExceptions.h"
  14. #import "FBResponsePayload.h"
  15. #import "FBSession.h"
  16. @interface FBRoute ()
  17. @property (nonatomic, assign, readwrite) BOOL requiresSession;
  18. @property (nonatomic, copy, readwrite) NSString *verb;
  19. @property (nonatomic, copy, readwrite) NSString *path;
  20. - (void)decorateRequest:(FBRouteRequest *)request;
  21. @end
  22. static NSString *const FBRouteSessionPrefix = @"/session/:sessionID";
  23. @interface FBRoute_TargetAction : FBRoute
  24. @property (nonatomic, strong, readwrite) id target;
  25. @property (nonatomic, assign, readwrite) SEL action;
  26. @end
  27. @implementation FBRoute_TargetAction
  28. - (void)mountRequest:(FBRouteRequest *)request intoResponse:(RouteResponse *)response
  29. {
  30. [self decorateRequest:request];
  31. #pragma clang diagnostic push
  32. #pragma clang diagnostic ignored "-Wcast-function-type-strict"
  33. id<FBResponsePayload> (*requestMsgSend)(id, SEL, FBRouteRequest *) = ((id<FBResponsePayload>(*)(id, SEL, FBRouteRequest *))objc_msgSend);
  34. #pragma clang diagnostic pop
  35. id<FBResponsePayload> payload = requestMsgSend(self.target, self.action, request);
  36. [payload dispatchWithResponse:response];
  37. }
  38. @end
  39. @interface FBRoute_Sync : FBRoute
  40. @property (nonatomic, copy, readwrite) FBRouteSyncHandler handler;
  41. @end
  42. @implementation FBRoute_Sync
  43. - (void)mountRequest:(FBRouteRequest *)request intoResponse:(RouteResponse *)response
  44. {
  45. [self decorateRequest:request];
  46. id<FBResponsePayload> payload = self.handler(request);
  47. [payload dispatchWithResponse:response];
  48. }
  49. @end
  50. @implementation FBRoute
  51. + (instancetype)withVerb:(NSString *)verb path:(NSString *)pathPattern requiresSession:(BOOL)requiresSession
  52. {
  53. FBRoute *route = [self new];
  54. route.verb = verb;
  55. route.path = [FBRoute pathPatternWithSession:pathPattern requiresSession:requiresSession];
  56. route.requiresSession = requiresSession;
  57. return route;
  58. }
  59. + (instancetype)OPTIONS:(NSString *)pathPattern
  60. {
  61. return [self withVerb:@"OPTIONS" path:pathPattern requiresSession:NO];
  62. }
  63. + (instancetype)GET:(NSString *)pathPattern
  64. {
  65. return [self withVerb:@"GET" path:pathPattern requiresSession:YES];
  66. }
  67. + (instancetype)POST:(NSString *)pathPattern
  68. {
  69. return [self withVerb:@"POST" path:pathPattern requiresSession:YES];
  70. }
  71. + (instancetype)PUT:(NSString *)pathPattern
  72. {
  73. return [self withVerb:@"PUT" path:pathPattern requiresSession:YES];
  74. }
  75. + (instancetype)DELETE:(NSString *)pathPattern
  76. {
  77. return [self withVerb:@"DELETE" path:pathPattern requiresSession:YES];
  78. }
  79. + (NSString *)pathPatternWithSession:(NSString *)pathPattern requiresSession:(BOOL)requiresSession
  80. {
  81. NSRange range = [pathPattern rangeOfString:FBRouteSessionPrefix];
  82. if (requiresSession) {
  83. if (range.location != 0) {
  84. pathPattern = [FBRouteSessionPrefix stringByAppendingPathComponent:pathPattern];
  85. }
  86. } else {
  87. if (range.location == 0) {
  88. pathPattern = [pathPattern stringByReplacingCharactersInRange:range withString:@""];
  89. }
  90. }
  91. if (pathPattern.length == 0) {
  92. pathPattern = @"/";
  93. }
  94. return pathPattern;
  95. }
  96. - (instancetype)withoutSession
  97. {
  98. self.requiresSession = NO;
  99. return self;
  100. }
  101. - (instancetype)respondWithBlock:(FBRouteSyncHandler)handler
  102. {
  103. FBRoute_Sync *route = [FBRoute_Sync withVerb:self.verb path:self.path requiresSession:self.requiresSession];
  104. route.handler = handler;
  105. return route;
  106. }
  107. - (instancetype)respondWithTarget:(id)target action:(SEL)action
  108. {
  109. FBRoute_TargetAction *route = [FBRoute_TargetAction withVerb:self.verb path:self.path requiresSession:self.requiresSession];
  110. route.target = target;
  111. route.action = action;
  112. return route;
  113. }
  114. - (void)decorateRequest:(FBRouteRequest *)request
  115. {
  116. if (!self.requiresSession) {
  117. return;
  118. }
  119. NSString *sessionID = request.parameters[@"sessionID"];
  120. if (!sessionID) {
  121. [self raiseNoSessionException];
  122. return;
  123. }
  124. FBSession *session = [FBSession sessionWithIdentifier:sessionID];
  125. if (!session) {
  126. [self raiseNoSessionException];
  127. return;
  128. }
  129. request.session = session;
  130. }
  131. - (void)raiseNoSessionException
  132. {
  133. [[NSException exceptionWithName:FBSessionDoesNotExistException reason:@"Session does not exist" userInfo:nil] raise];
  134. }
  135. - (void)mountRequest:(FBRouteRequest *)request intoResponse:(RouteResponse *)response
  136. {
  137. id<FBResponsePayload> payload = FBResponseWithStatus([FBCommandStatus unknownCommandErrorWithMessage:@"Unhandled route"
  138. traceback:[NSString stringWithFormat:@"%@", NSThread.callStackSymbols]]);
  139. [payload dispatchWithResponse:response];
  140. }
  141. @end