mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
208 lines
8.9 KiB
Objective-C
208 lines
8.9 KiB
Objective-C
/*
|
|
* Copyright 2018 Google
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#import <TargetConditionals.h>
|
|
#if TARGET_OS_IOS || TARGET_OS_TV || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|
|
|
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
|
|
|
#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutHttpRequestSender.h"
|
|
#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"
|
|
#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
|
|
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClientInfoFetcher.h"
|
|
#import "FirebaseInAppMessaging/Sources/Private/Util/FIRIAMTimeFetcher.h"
|
|
|
|
@interface FIRIAMClearcutHttpRequestSender ()
|
|
@property(readonly, copy, nonatomic) NSString *serverHostName;
|
|
|
|
@property(readwrite, nonatomic) id<FIRIAMTimeFetcher> timeFetcher;
|
|
@property(readonly, copy, nonatomic) NSString *osMajorVersion;
|
|
@end
|
|
|
|
@implementation FIRIAMClearcutHttpRequestSender
|
|
|
|
- (instancetype)initWithClearcutHost:(NSString *)serverHost
|
|
usingTimeFetcher:(id<FIRIAMTimeFetcher>)timeFetcher
|
|
withOSMajorVersion:(NSString *)osMajorVersion {
|
|
if (self = [super init]) {
|
|
_serverHostName = [serverHost copy];
|
|
_timeFetcher = timeFetcher;
|
|
_osMajorVersion = [osMajorVersion copy];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)updateRequestBodyWithClearcutEnvelopeFields:(NSMutableDictionary *)bodyDict {
|
|
bodyDict[@"client_info"] = @{
|
|
@"client_type" : @15, // 15 is the enum value for IOS_FIREBASE client
|
|
@"ios_client_info" : @{@"os_major_version" : self.osMajorVersion ?: @""}
|
|
};
|
|
bodyDict[@"log_source"] = @"FIREBASE_INAPPMESSAGING";
|
|
|
|
NSTimeInterval nowInMs = [self.timeFetcher currentTimestampInSeconds] * 1000;
|
|
bodyDict[@"request_time_ms"] = @((long)nowInMs);
|
|
}
|
|
|
|
- (NSArray<NSDictionary *> *)constructLogEventsArrayLogRecords:
|
|
(NSArray<FIRIAMClearcutLogRecord *> *)logRecords {
|
|
NSMutableArray<NSDictionary *> *logEvents = [[NSMutableArray alloc] init];
|
|
for (id next in logRecords) {
|
|
FIRIAMClearcutLogRecord *logRecord = (FIRIAMClearcutLogRecord *)next;
|
|
[logEvents addObject:@{
|
|
@"event_time_ms" : @((long)logRecord.eventTimestampInSeconds * 1000),
|
|
@"source_extension_json" : logRecord.eventExtensionJsonString ?: @""
|
|
}];
|
|
}
|
|
|
|
return [logEvents copy];
|
|
}
|
|
|
|
// @return nil if error happened in constructing the body
|
|
- (NSDictionary *)constructRequestBodyWithRetryRecords:
|
|
(NSArray<FIRIAMClearcutLogRecord *> *)logRecords {
|
|
NSMutableDictionary *body = [[NSMutableDictionary alloc] init];
|
|
[self updateRequestBodyWithClearcutEnvelopeFields:body];
|
|
body[@"log_event"] = [self constructLogEventsArrayLogRecords:logRecords];
|
|
return [body copy];
|
|
}
|
|
|
|
// a helper method for dealing with the response received from
|
|
// executing NSURLSessionDataTask. Triggers the completion callback accordingly
|
|
- (void)handleClearcutAPICallResponseWithData:(NSData *)data
|
|
response:(NSURLResponse *)response
|
|
error:(NSError *)error
|
|
completion:
|
|
(nonnull void (^)(BOOL success,
|
|
BOOL shouldRetryLogs,
|
|
int64_t waitTimeInMills))completion {
|
|
if (error) {
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250003",
|
|
@"Internal error: encountered error in uploading clearcut message"
|
|
":%@",
|
|
error);
|
|
completion(NO, YES, 0);
|
|
return;
|
|
}
|
|
|
|
if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250008",
|
|
@"Received non http response from sending "
|
|
"clearcut requests %@",
|
|
response);
|
|
completion(NO, YES, 0);
|
|
return;
|
|
}
|
|
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
|
if (httpResponse.statusCode == 200) {
|
|
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM250004",
|
|
@"Sending clearcut logging request was successful");
|
|
|
|
NSError *errorJson = nil;
|
|
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data
|
|
options:kNilOptions
|
|
error:&errorJson];
|
|
|
|
int64_t waitTimeFromClearcutServer = 0;
|
|
if (!errorJson && responseDict[@"next_request_wait_millis"]) {
|
|
waitTimeFromClearcutServer = [responseDict[@"next_request_wait_millis"] longLongValue];
|
|
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM250007",
|
|
@"Wait time from clearcut server response is %d seconds",
|
|
(int)waitTimeFromClearcutServer / 1000);
|
|
}
|
|
completion(YES, NO, waitTimeFromClearcutServer);
|
|
} else if (httpResponse.statusCode == 400) {
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250012",
|
|
@"Seeing 400 status code in response and we are discarding this log"
|
|
@"record");
|
|
// 400 means bad request data and it won't be successful with retries. So
|
|
// we give up on these log records
|
|
completion(NO, NO, 0);
|
|
} else {
|
|
// May need to handle 401 errors if we do authentication in the future
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250005",
|
|
@"Other http status code seen in clearcut request response %d",
|
|
(int)httpResponse.statusCode);
|
|
// can be retried
|
|
completion(NO, YES, 0);
|
|
}
|
|
}
|
|
|
|
- (void)sendClearcutHttpRequestForLogs:(NSArray<FIRIAMClearcutLogRecord *> *)logs
|
|
withCompletion:(nonnull void (^)(BOOL success,
|
|
BOOL shouldRetryLogs,
|
|
int64_t waitTimeInMills))completion {
|
|
NSDictionary *requestBody = [self constructRequestBodyWithRetryRecords:logs];
|
|
|
|
if (!requestBody) {
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250014",
|
|
@"Not able to construct request body for clearcut request, giving up");
|
|
completion(NO, NO, 0);
|
|
} else {
|
|
// sending the log via a http request
|
|
NSURLSession *URLSession = [NSURLSession sharedSession];
|
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
|
[request setHTTPMethod:@"POST"];
|
|
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
|
[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
|
|
|
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM250001",
|
|
@"Request body dictionary is %@ for clearcut logging request", requestBody);
|
|
|
|
NSError *error;
|
|
NSData *requestBodyData = [NSJSONSerialization dataWithJSONObject:requestBody
|
|
options:0
|
|
error:&error];
|
|
|
|
if (error) {
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250011",
|
|
@"Error in creating request body json for clearcut requests:%@", error);
|
|
completion(NO, NO, 0);
|
|
return;
|
|
}
|
|
|
|
NSString *requestURLString =
|
|
[NSString stringWithFormat:@"https://%@/log?format=json_proto", self.serverHostName];
|
|
[request setURL:[NSURL URLWithString:requestURLString]];
|
|
[request setHTTPBody:requestBodyData];
|
|
|
|
NSURLSessionDataTask *clearCutLogDataTask =
|
|
[URLSession dataTaskWithRequest:request
|
|
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
[self handleClearcutAPICallResponseWithData:data
|
|
response:response
|
|
error:error
|
|
completion:completion];
|
|
}];
|
|
|
|
if (clearCutLogDataTask == nil) {
|
|
NSString *errorDesc = @"Internal error: NSURLSessionDataTask failed to be created due to "
|
|
"possibly incorrect parameters";
|
|
FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM250005", @"%@", errorDesc);
|
|
completion(NO, NO, 0);
|
|
} else {
|
|
[clearCutLogDataTask resume];
|
|
FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM250002",
|
|
@"Making a restful api for sending clearcut logging data with "
|
|
"a NSURLSessionDataTask request as %@",
|
|
clearCutLogDataTask.currentRequest);
|
|
}
|
|
}
|
|
}
|
|
@end
|
|
|
|
#endif // TARGET_OS_IOS || TARGET_OS_TV || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|