SEGNetworkManager.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FirebaseSegmentation/Sources/SEGNetworkManager.h"
  15. #import "FirebaseCore/Sources/Private/FIRAppInternal.h"
  16. #import "FirebaseCore/Sources/Private/FIRLogger.h"
  17. #import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
  18. // TODO(dmandar): define in build file.
  19. #define SEG_ALPHA_SERVER
  20. static NSString *const kServerURLDomain = @"https://firebasesegmentation.googleapis.com";
  21. #ifdef SEG_ALPHA_SERVER
  22. static NSString *const kServerURLVersion = @"/v1alpha";
  23. #else
  24. static NSString *const kServerURLVersion = @"/v1";
  25. #endif
  26. static NSString *const kServerURLStringProjects = @"/projects/";
  27. static NSString *const kServerURLStringInstallations = @"/installations/";
  28. static NSString *const kServerURLStringCustomSegmentationData = @"/customSegmentationData";
  29. static NSString *const kHTTPMethodPatch = @"PATCH";
  30. static NSString *const kRequestHeaderAuthorizationValueString = @"FIREBASE_INSTALLATIONS_AUTH";
  31. static NSString *const kRequestDataCustomInstallationIdString = @"custom_installation_id";
  32. // HTTP header names.
  33. static NSString *const kHeaderNameAPIKey = @"x-goog-api-key";
  34. static NSString *const kHeaderNameFirebaseAuthorizationToken = @"Authorization";
  35. static NSString *const kHeaderNameContentType = @"Content-Type";
  36. static NSString *const kHeaderNameContentEncoding = @"Content-Encoding";
  37. static NSString *const kHeaderNameAcceptEncoding = @"Accept-Encoding";
  38. // Sends the bundle ID. Refer to b/130301479 for details.
  39. static NSString *const kiOSBundleIdentifierHeaderName =
  40. @"X-Ios-Bundle-Identifier"; ///< HTTP Header Field Name
  41. /// Config HTTP request content type JSON
  42. static NSString *const kContentTypeValueJSON = @"application/json";
  43. // TODO: Handle error codes.
  44. /// HTTP status codes. Ref: https://cloud.google.com/apis/design/errors#error_retries
  45. static NSInteger const kSEGResponseHTTPStatusCodeOK = 200;
  46. // static NSInteger const kSEGResponseHTTPStatusCodeConflict = 409;
  47. // static NSInteger const kSEGResponseHTTPStatusTooManyRequests = 429;
  48. // static NSInteger const kSEGResponseHTTPStatusCodeInternalError = 500;
  49. // static NSInteger const kSEGResponseHTTPStatusCodeServiceUnavailable = 503;
  50. // static NSInteger const kSEGResponseHTTPStatusCodeGatewayTimeout = 504;
  51. // HTTP default timeout.
  52. static NSTimeInterval const kSEGHTTPRequestTimeout = 60;
  53. /// Completion handler invoked by URLSession completion handler.
  54. typedef void (^URLSessionCompletion)(NSData *data, NSURLResponse *response, NSError *error);
  55. @implementation SEGNetworkManager {
  56. FIROptions *_firebaseAppOptions;
  57. NSURLSession *_URLSession;
  58. }
  59. - (instancetype)initWithOptions:(FIROptions *)options {
  60. self = [super init];
  61. if (self) {
  62. _firebaseAppOptions = options;
  63. _URLSession = [self newURLSession];
  64. }
  65. return self;
  66. }
  67. - (void)dealloc {
  68. [_URLSession invalidateAndCancel];
  69. }
  70. - (void)makeAssociationRequestToBackendWithData:
  71. (NSDictionary<NSString *, NSString *> *)associationData
  72. token:(NSString *)token
  73. completion:(SEGRequestCompletion)completionHandler {
  74. // Construct the server URL.
  75. NSString *URL = [self constructServerURLWithAssociationData:associationData];
  76. if (!URL) {
  77. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000020", @"Could not construct backend URL.");
  78. completionHandler(NO, @{kSEGErrorDescription : @"Could not construct backend URL"});
  79. }
  80. FIRLogDebug(kFIRLoggerSegmentation, @"I-SEG000019", @"%@",
  81. [NSString stringWithFormat:@"Making config request: %@", URL]);
  82. // Construct the request data.
  83. NSString *customInstallationIdentifier =
  84. [associationData objectForKey:kSEGCustomInstallationIdentifierKey];
  85. // TODO: Add tests for nil.
  86. NSDictionary<NSString *, NSString *> *requestDataDictionary =
  87. @{kRequestDataCustomInstallationIdString : customInstallationIdentifier};
  88. NSError *error = nil;
  89. NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestDataDictionary
  90. options:0
  91. error:nil];
  92. if (!requestData || error) {
  93. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000021", @"Could not create request data. %@",
  94. error.localizedDescription);
  95. completionHandler(NO,
  96. @{kSEGErrorDescription : @"Could not serialize JSON data for network call."});
  97. }
  98. // Handle NSURLSession completion.
  99. __weak SEGNetworkManager *weakSelf = self;
  100. [self URLSessionDataTaskWithURL:URL
  101. content:requestData
  102. token:token
  103. completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  104. SEGNetworkManager *strongSelf = weakSelf;
  105. if (!strongSelf) {
  106. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000022",
  107. @"Internal error making network request.");
  108. completionHandler(
  109. NO, @{kSEGErrorDescription : @"Internal error making network request."});
  110. return;
  111. }
  112. NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode];
  113. if (!error && (statusCode == kSEGResponseHTTPStatusCodeOK)) {
  114. FIRLogDebug(kFIRLoggerSegmentation, @"I-SEG000017",
  115. @"SEGNetworkManager: Network request successful.");
  116. completionHandler(YES, nil);
  117. } else {
  118. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000018",
  119. @"SEGNetworkManager: Network request failed with status code:%lu",
  120. (long)statusCode);
  121. completionHandler(NO, @{
  122. kSEGErrorDescription :
  123. [NSString stringWithFormat:@"Network Error: %lu", (long)statusCode]
  124. });
  125. };
  126. }];
  127. }
  128. #pragma mark Private
  129. - (NSURLSession *)newURLSession {
  130. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  131. config.timeoutIntervalForRequest = kSEGHTTPRequestTimeout;
  132. config.timeoutIntervalForResource = kSEGHTTPRequestTimeout;
  133. NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
  134. return session;
  135. }
  136. - (NSString *)constructServerURLWithAssociationData:
  137. (NSDictionary<NSString *, NSString *> *)associationData {
  138. NSString *serverURLStr = [[NSString alloc] initWithString:kServerURLDomain];
  139. serverURLStr = [serverURLStr stringByAppendingString:kServerURLVersion];
  140. serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringProjects];
  141. if (_firebaseAppOptions.projectID) {
  142. serverURLStr = [serverURLStr stringByAppendingString:_firebaseAppOptions.projectID];
  143. } else {
  144. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000070",
  145. @"Missing `projectID` from `FirebaseOptions`, please ensure the configured "
  146. @"`FirebaseApp` is configured with `FirebaseOptions` that contains a `projectID`.");
  147. return nil;
  148. }
  149. serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringInstallations];
  150. // Get the FID.
  151. NSString *firebaseInstallationIdentifier =
  152. [associationData objectForKey:kSEGFirebaseInstallationIdentifierKey];
  153. if (!firebaseInstallationIdentifier) {
  154. FIRLogError(kFIRLoggerSegmentation, @"I-SEG000071",
  155. @"Missing Firebase installation identifier");
  156. return nil;
  157. }
  158. serverURLStr = [serverURLStr stringByAppendingString:firebaseInstallationIdentifier];
  159. serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringCustomSegmentationData];
  160. return serverURLStr;
  161. }
  162. - (void)URLSessionDataTaskWithURL:(NSString *)stringURL
  163. content:(NSData *)content
  164. token:(NSString *)token
  165. completionHandler:(URLSessionCompletion)completionHandler {
  166. NSTimeInterval timeoutInterval = kSEGHTTPRequestTimeout;
  167. NSURL *URL = [NSURL URLWithString:stringURL];
  168. NSMutableURLRequest *URLRequest =
  169. [[NSMutableURLRequest alloc] initWithURL:URL
  170. cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
  171. timeoutInterval:timeoutInterval];
  172. URLRequest.HTTPMethod = kHTTPMethodPatch;
  173. // Setup headers.
  174. [URLRequest setValue:_firebaseAppOptions.APIKey forHTTPHeaderField:kHeaderNameAPIKey];
  175. NSString *authorizationTokenHeaderValue =
  176. [NSString stringWithFormat:@"%@ %@", kRequestHeaderAuthorizationValueString, token];
  177. [URLRequest setValue:authorizationTokenHeaderValue
  178. forHTTPHeaderField:kHeaderNameFirebaseAuthorizationToken];
  179. // TODO: Check if we accept gzip.
  180. // [URLRequest setValue:@"gzip" forHTTPHeaderField:kHeaderNameContentEncoding];
  181. // [URLRequest setValue:@"gzip" forHTTPHeaderField:kHeaderNameAcceptEncoding];
  182. // Send the bundleID for API Key restrictions.
  183. [URLRequest setValue:[[NSBundle mainBundle] bundleIdentifier]
  184. forHTTPHeaderField:kiOSBundleIdentifierHeaderName];
  185. [URLRequest setHTTPBody:content];
  186. NSURLSessionDataTask *task = [_URLSession dataTaskWithRequest:URLRequest
  187. completionHandler:completionHandler];
  188. [task resume];
  189. }
  190. @end