Skip to content

Commit

Permalink
Merge pull request #588 from segmentio/dev
Browse files Browse the repository at this point in the history
Sprint 14
  • Loading branch information
f2prateek authored Aug 5, 2016
2 parents 3b5cf0a + 34b7269 commit 2b02144
Show file tree
Hide file tree
Showing 96 changed files with 3,465 additions and 1,129 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
//


#import "NSData+GZIP.h"
#import "NSData+SEGGZIP.h"
#import <zlib.h>
#import <dlfcn.h>

Expand All @@ -56,13 +56,13 @@ - (NSData *)seg_gzippedDataWithCompressionLevel:(float)level
if (self.length == 0 || [self seg_isGzippedData]) {
return self;
}

void *libz = seg_libzOpen();
int (*deflateInit2_)(z_streamp, int, int, int, int, int, const char *, int) =
(int (*)(z_streamp, int, int, int, int, int, const char *, int))dlsym(libz, "deflateInit2_");
(int (*)(z_streamp, int, int, int, int, int, const char *, int))dlsym(libz, "deflateInit2_");
int (*deflate)(z_streamp, int) = (int (*)(z_streamp, int))dlsym(libz, "deflate");
int (*deflateEnd)(z_streamp) = (int (*)(z_streamp))dlsym(libz, "deflateEnd");

z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
Expand All @@ -71,9 +71,9 @@ - (NSData *)seg_gzippedDataWithCompressionLevel:(float)level
stream.next_in = (Bytef *)(void *)self.bytes;
stream.total_out = 0;
stream.avail_out = 0;

static const NSUInteger ChunkSize = 16384;

NSMutableData *output = nil;
int compression = (level < 0.0f) ? Z_DEFAULT_COMPRESSION : (int)(roundf(level * 9));
if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
Expand All @@ -89,7 +89,7 @@ - (NSData *)seg_gzippedDataWithCompressionLevel:(float)level
deflateEnd(&stream);
output.length = stream.total_out;
}

return output;
}

Expand Down
18 changes: 0 additions & 18 deletions Analytics/Classes/Internal/SEGAnalyticsRequest.h

This file was deleted.

120 changes: 0 additions & 120 deletions Analytics/Classes/Internal/SEGAnalyticsRequest.m

This file was deleted.

1 change: 1 addition & 0 deletions Analytics/Classes/Internal/SEGAnalyticsUtils.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import <Foundation/Foundation.h>

NSURL *SEGAnalyticsURLForFilename(NSString *filename);
NSString *GenerateUUIDString();

// Date Utils
NSString *iso8601FormattedString(NSDate *date);
Expand Down
8 changes: 8 additions & 0 deletions Analytics/Classes/Internal/SEGAnalyticsUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
return [[NSURL alloc] initFileURLWithPath:[supportPath stringByAppendingPathComponent:filename]];
}

NSString *GenerateUUIDString()
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
NSString *UUIDString = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return UUIDString;
}

// Date Utils
NSString *iso8601FormattedString(NSDate *date)
{
Expand Down
23 changes: 23 additions & 0 deletions Analytics/Classes/Internal/SEGHTTPClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#import <Foundation/Foundation.h>
#import "SEGAnalytics.h"


@interface SEGHTTPClient : NSObject

@property (nonatomic, strong) SEGRequestFactory requestFactory;

+ (SEGRequestFactory)defaultRequestFactory;

- (instancetype)initWithRequestFactory:(SEGRequestFactory)requestFactory;

/**
* Upload dictionary formatted as per https://segment.com/docs/sources/server/http/#batch.
* This method will convert the dictionary to json, gzip it and upload the data.
* It will respond with retry = YES if the batch should be reuploaded at a later time.
* It will ask to retry for json errors and 3xx/5xx codes, and not retry for 2xx/4xx response codes.
*/
- (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler;

- (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL success, NSDictionary *settings))completionHandler;

@end
141 changes: 141 additions & 0 deletions Analytics/Classes/Internal/SEGHTTPClient.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#import "SEGHTTPClient.h"
#import "NSData+SEGGZIP.h"
#import "SEGAnalyticsUtils.h"


@implementation SEGHTTPClient

+ (NSMutableURLRequest * (^)(NSURL *))defaultRequestFactory
{
return ^(NSURL *url) {
return [NSMutableURLRequest requestWithURL:url];
};
}

- (instancetype)initWithRequestFactory:(SEGRequestFactory)requestFactory
{
if (self = [self init]) {
if (requestFactory == nil) {
self.requestFactory = [SEGHTTPClient defaultRequestFactory];
} else {
self.requestFactory = requestFactory;
}
}
return self;
}

- (NSString *)authorizationHeader:(NSString *)writeKey
{
NSString *rawHeader = [writeKey stringByAppendingString:@":"];
NSData *userPasswordData = [rawHeader dataUsingEncoding:NSUTF8StringEncoding];
return [userPasswordData base64EncodedStringWithOptions:0];
}

- (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler
{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{
@"Accept-Encoding" : @"gzip",
@"Content-Encoding" : @"gzip",
@"Content-Type" : @"application/json",
@"Authorization" : [@"Basic " stringByAppendingString:[self authorizationHeader:writeKey]],
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

NSURL *url = [NSURL URLWithString:@"https://api.segment.io/v1/batch"];
NSMutableURLRequest *request = self.requestFactory(url);
[request setHTTPMethod:@"POST"];

NSError *error = nil;
NSException *exception = nil;
NSData *payload = nil;
@try {
payload = [NSJSONSerialization dataWithJSONObject:batch options:0 error:&error];
}
@catch (NSException *exc) {
exception = exc;
}
if (error || exception) {
SEGLog(@"%@ Error serializing JSON for batch upload %@", self, error);
completionHandler(NO); // Don't retry this batch.
return nil;
}
NSData *gzippedPayload = [payload seg_gzippedData];

NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:gzippedPayload completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
if (error) {
SEGLog(@"Error uploading request %@.", error);
completionHandler(YES);
return;
}

NSInteger code = ((NSHTTPURLResponse *)response).statusCode;
if (code < 300) {
// 2xx response codes.
completionHandler(NO);
return;
}
if (code < 400) {
// 3xx response codes.
SEGLog(@"Server responded with unexpected HTTP code %d.", code);
completionHandler(YES);
return;
}
if (code < 500) {
// 4xx response codes.
SEGLog(@"Server rejected payload with HTTP code %d.", code);
completionHandler(NO);
return;
}

// 5xx response codes.
SEGLog(@"Server error with HTTP code %d.", code);
completionHandler(YES);
}];
[task resume];
return task;
}

- (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL success, NSDictionary *settings))completionHandler
{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{
@"Accept-Encoding" : @"gzip"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

NSString *rawURL = [NSString stringWithFormat:@"https://cdn.segment.com/v1/projects/%@/settings", writeKey];
NSURL *url = [NSURL URLWithString:rawURL];
NSMutableURLRequest *request = self.requestFactory(url);
[request setHTTPMethod:@"GET"];

NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
if (error != nil) {
SEGLog(@"Error fetching settings %@.", error);
completionHandler(NO, nil);
return;
}

NSInteger code = ((NSHTTPURLResponse *)response).statusCode;
if (code > 300) {
SEGLog(@"Server responded with unexpected HTTP code %d.", code);
completionHandler(NO, nil);
return;
}

NSError *jsonError = nil;
id responseJson = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError != nil) {
SEGLog(@"Error deserializing response body %@.", jsonError);
completionHandler(NO, nil);
return;
}

// 2xx response codes.
completionHandler(YES, responseJson);
}];
[task resume];
return task;
}

@end
3 changes: 3 additions & 0 deletions Analytics/Classes/Internal/SEGReachability.m
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ + (SEGReachability *)reachabilityWithHostname:(NSString *)hostname
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
if (ref) {
id reachability = [[self alloc] initWithReachabilityRef:ref];
CFRelease(ref);

#if __has_feature(objc_arc)
return reachability;
Expand All @@ -129,6 +130,7 @@ + (SEGReachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAdd
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
if (ref) {
id reachability = [[self alloc] initWithReachabilityRef:ref];
CFRelease(ref);

#if __has_feature(objc_arc)
return reachability;
Expand Down Expand Up @@ -171,6 +173,7 @@ - (SEGReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
if (self != nil) {
self.reachableOnWWAN = YES;
self.reachabilityRef = ref;
CFRetain(self.reachabilityRef);
}

return self;
Expand Down
Loading

0 comments on commit 2b02144

Please sign in to comment.