FBRoute.m 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. id<FBResponsePayload> (*requestMsgSend)(id, SEL, FBRouteRequest *) = ((id<FBResponsePayload>(*)(id, SEL, FBRouteRequest *))objc_msgSend);
  32. id<FBResponsePayload> payload = requestMsgSend(self.target, self.action, request);
  33. [payload dispatchWithResponse:response];
  34. }
  35. @end
  36. @interface FBRoute_Sync : FBRoute
  37. @property (nonatomic, copy, readwrite) FBRouteSyncHandler handler;
  38. @end
  39. @implementation FBRoute_Sync
  40. - (void)mountRequest:(FBRouteRequest *)request intoResponse:(RouteResponse *)response
  41. {
  42. [self decorateRequest:request];
  43. id<FBResponsePayload> payload = self.handler(request);
  44. [payload dispatchWithResponse:response];
  45. }
  46. @end
  47. @implementation FBRoute
  48. + (instancetype)withVerb:(NSString *)verb path:(NSString *)pathPattern requiresSession:(BOOL)requiresSession
  49. {
  50. FBRoute *route = [self new];
  51. route.verb = verb;
  52. route.path = [FBRoute pathPatternWithSession:pathPattern requiresSession:requiresSession];
  53. route.requiresSession = requiresSession;
  54. return route;
  55. }
  56. + (instancetype)OPTIONS:(NSString *)pathPattern
  57. {
  58. return [self withVerb:@"OPTIONS" path:pathPattern requiresSession:NO];
  59. }
  60. + (instancetype)GET:(NSString *)pathPattern
  61. {
  62. return [self withVerb:@"GET" path:pathPattern requiresSession:YES];
  63. }
  64. + (instancetype)POST:(NSString *)pathPattern
  65. {
  66. return [self withVerb:@"POST" path:pathPattern requiresSession:YES];
  67. }
  68. + (instancetype)PUT:(NSString *)pathPattern
  69. {
  70. return [self withVerb:@"PUT" path:pathPattern requiresSession:YES];
  71. }
  72. + (instancetype)DELETE:(NSString *)pathPattern
  73. {
  74. return [self withVerb:@"DELETE" path:pathPattern requiresSession:YES];
  75. }
  76. + (NSString *)pathPatternWithSession:(NSString *)pathPattern requiresSession:(BOOL)requiresSession
  77. {
  78. NSRange range = [pathPattern rangeOfString:FBRouteSessionPrefix];
  79. if (requiresSession) {
  80. if (range.location != 0) {
  81. pathPattern = [FBRouteSessionPrefix stringByAppendingPathComponent:pathPattern];
  82. }
  83. } else {
  84. if (range.location == 0) {
  85. pathPattern = [pathPattern stringByReplacingCharactersInRange:range withString:@""];
  86. }
  87. }
  88. if (pathPattern.length == 0) {
  89. pathPattern = @"/";
  90. }
  91. return pathPattern;
  92. }
  93. - (instancetype)withoutSession
  94. {
  95. self.requiresSession = NO;
  96. return self;
  97. }
  98. - (instancetype)respondWithBlock:(FBRouteSyncHandler)handler
  99. {
  100. FBRoute_Sync *route = [FBRoute_Sync withVerb:self.verb path:self.path requiresSession:self.requiresSession];
  101. route.handler = handler;
  102. return route;
  103. }
  104. - (instancetype)respondWithTarget:(id)target action:(SEL)action
  105. {
  106. FBRoute_TargetAction *route = [FBRoute_TargetAction withVerb:self.verb path:self.path requiresSession:self.requiresSession];
  107. route.target = target;
  108. route.action = action;
  109. return route;
  110. }
  111. - (void)decorateRequest:(FBRouteRequest *)request
  112. {
  113. if (!self.requiresSession) {
  114. return;
  115. }
  116. NSString *sessionID = request.parameters[@"sessionID"];
  117. if (!sessionID) {
  118. [self raiseNoSessionException];
  119. return;
  120. }
  121. FBSession *session = [FBSession sessionWithIdentifier:sessionID];
  122. if (!session) {
  123. [self raiseNoSessionException];
  124. return;
  125. }
  126. request.session = session;
  127. }
  128. - (void)raiseNoSessionException
  129. {
  130. [[NSException exceptionWithName:FBSessionDoesNotExistException reason:@"Session does not exist" userInfo:nil] raise];
  131. }
  132. - (void)mountRequest:(FBRouteRequest *)request intoResponse:(RouteResponse *)response
  133. {
  134. id<FBResponsePayload> payload = FBResponseWithStatus([FBCommandStatus unknownCommandErrorWithMessage:@"Unhandled route"
  135. traceback:[NSString stringWithFormat:@"%@", NSThread.callStackSymbols]]);
  136. [payload dispatchWithResponse:response];
  137. }
  138. @end