浏览代码

First pass at adding body encoders

Pierre-Olivier Latour 11 年之前
父节点
当前提交
81638ad086

+ 28 - 26
CGDWebServer/GCDWebServerConnection.m

@@ -30,7 +30,6 @@
 #import "GCDWebServerPrivate.h"
 
 #define kHeadersReadBuffer 1024
-#define kBodyWriteBufferSize (32 * 1024)
 
 typedef void (^ReadBufferCompletionBlock)(dispatch_data_t buffer);
 typedef void (^ReadDataCompletionBlock)(NSData* data);
@@ -234,27 +233,25 @@ static dispatch_queue_t _formatterQueue = NULL;
 
 - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
   DCHECK([_response hasBody]);
-  void* buffer = malloc(kBodyWriteBufferSize);
-  NSInteger result = [_response read:buffer maxLength:kBodyWriteBufferSize];
-  if (result > 0) {
-    dispatch_data_t wrapper = dispatch_data_create(buffer, result, NULL, DISPATCH_DATA_DESTRUCTOR_FREE);
-    [self _writeBuffer:wrapper withCompletionBlock:^(BOOL success) {
-      
-      if (success) {
-        [self _writeBodyWithCompletionBlock:block];
-      } else {
-        block(NO);
-      }
-      
-    }];
-    ARC_DISPATCH_RELEASE(wrapper);
-  } else if (result < 0) {
-    LOG_ERROR(@"Failed reading response body on socket %i (error %i)", _socket, (int)result);
-    block(NO);
-    free(buffer);
+  NSError* error = nil;
+  NSData* data = [_response performReadData:&error];
+  if (data) {
+    if (data.length) {
+      [self _writeData:data withCompletionBlock:^(BOOL success) {
+        
+        if (success) {
+          [self _writeBodyWithCompletionBlock:block];
+        } else {
+          block(NO);
+        }
+        
+      }];
+    } else {
+      block(YES);
+    }
   } else {
-    block(YES);
-    free(buffer);
+    LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
+    block(NO);
   }
 }
 
@@ -318,8 +315,13 @@ static dispatch_queue_t _formatterQueue = NULL;
   DCHECK(_responseMessage == NULL);
   
   GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
-  if (![response hasBody] || [response open]) {
-    _response = ARC_RETAIN(response);
+  if (response) {
+    NSError* error = nil;
+    if ([response hasBody] && ![response performOpen:&error]) {
+      LOG_WARNING(@"Failed opening response body for socket %i: %@", _socket, error);
+    } else {
+      _response = ARC_RETAIN(response);
+    }
   }
   
   if (_response) {
@@ -344,12 +346,13 @@ static dispatch_queue_t _formatterQueue = NULL;
         if ([_response hasBody]) {
           [self _writeBodyWithCompletionBlock:^(BOOL successInner) {
             
-            [_response close];  // Can't do anything with result anyway
+            [_response performClose];
+            LOG_VERBOSE(@"%@ | %@ \"%@ %@\" %i %lu", self.localAddressString, self.remoteAddressString, _request.method, _request.path, (int)_response.statusCode, (unsigned long)_bytesWritten);
             
           }];
         }
       } else if ([_response hasBody]) {
-        [_response close];  // Can't do anything with result anyway
+        [_response performClose];
       }
       
     }];
@@ -537,7 +540,6 @@ static NSString* _StringFromAddressData(NSData* data) {
   GCDWebServerResponse* response = nil;
   @try {
     response = block(request);
-    LOG_VERBOSE(@"%@ | %@ \"%@ %@\" %i %lu", self.localAddressString, self.remoteAddressString, _request.method, _request.path, (int)response.statusCode, (unsigned long)(response.contentLength != NSNotFound ? response.contentLength : 0));
   }
   @catch (NSException* exception) {
     LOG_EXCEPTION(exception);

+ 7 - 0
CGDWebServer/GCDWebServerPrivate.h

@@ -104,3 +104,10 @@ extern void GCDLogMessage(long level, NSString* format, ...) NS_FORMAT_FUNCTION(
 @property(nonatomic, readonly) GCDWebServerProcessBlock processBlock;
 - (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
 @end
+
+@interface GCDWebServerResponse ()
+@property(nonatomic, readonly) NSDictionary* additionalHeaders;
+- (BOOL)performOpen:(NSError**)error;
+- (NSData*)performReadData:(NSError**)error;
+- (void)performClose;
+@end

+ 13 - 12
CGDWebServer/GCDWebServerResponse.h

@@ -27,26 +27,27 @@
 
 #import <Foundation/Foundation.h>
 
-typedef NSData* (^GCDWebServerChunkBlock)();
+typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
 
-@interface GCDWebServerResponse : NSObject
+@protocol GCDWebServerBodyReader <NSObject>
+- (BOOL)open:(NSError**)error;
+- (NSData*)readData:(NSError**)error;  // Return nil on error or empty NSData if at end
+- (void)close;
+@end
+
+@interface GCDWebServerResponse : NSObject <GCDWebServerBodyReader>
 @property(nonatomic, copy) NSString* contentType;  // Default is nil i.e. no body
 @property(nonatomic) NSUInteger contentLength;  // Default is NSNotFound i.e. undefined
 @property(nonatomic) NSInteger statusCode;  // Default is 200
 @property(nonatomic) NSUInteger cacheControlMaxAge;  // Default is 0 seconds i.e. "no-cache"
-@property(nonatomic, readonly) NSDictionary* additionalHeaders;
+@property(nonatomic) BOOL gzipContentEncoding;
+@property(nonatomic) BOOL chunkedTransferEncoding;
 + (GCDWebServerResponse*) response;
 - (id)init;
 - (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header;
 - (BOOL)hasBody;  // Convenience method
 @end
 
-@interface GCDWebServerResponse (Subclassing)
-- (BOOL)open;  // Implementation required
-- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length;  // Implementation required
-- (BOOL)close;  // Implementation required
-@end
-
 @interface GCDWebServerResponse (Extensions)
 + (GCDWebServerResponse*)responseWithStatusCode:(NSInteger)statusCode;
 + (GCDWebServerResponse*)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
@@ -83,7 +84,7 @@ typedef NSData* (^GCDWebServerChunkBlock)();
 - (id)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
 @end
 
-@interface GCDWebServerChunkedResponse : GCDWebServerResponse  // Use chunked transfer encoding
-+ (GCDWebServerChunkedResponse*)responseWithContentType:(NSString*)type chunkBlock:(GCDWebServerChunkBlock)block;
-- (id)initWithContentType:(NSString*)type chunkBlock:(GCDWebServerChunkBlock)block;  // Return nil when done
+@interface GCDWebServerStreamResponse : GCDWebServerResponse  // Forces chunked transfer encoding
++ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
+- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;  // Block must return empty NSData when done or nil on error
 @end

+ 277 - 113
CGDWebServer/GCDWebServerResponse.m

@@ -26,47 +26,208 @@
  */
 
 #import <sys/stat.h>
+#import <zlib.h>
 
 #import "GCDWebServerPrivate.h"
 
-@interface GCDWebServerResponse () {
+#define kZlibErrorDomain @"ZlibErrorDomain"
+#define kGZipInitialBufferSize (256 * 1024)
+#define kFileReadBufferSize (32 * 1024)
+
+@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
+- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader;
+@end
+
+@interface GCDWebServerChunkEncoder : GCDWebServerBodyEncoder
+@end
+
+@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder
+@end
+
+@interface GCDWebServerBodyEncoder () {
 @private
-  NSString* _type;
-  NSUInteger _length;
-  NSInteger _status;
-  NSUInteger _maxAge;
-  NSMutableDictionary* _headers;
+  GCDWebServerResponse* __unsafe_unretained _response;
+  id<GCDWebServerBodyReader> __unsafe_unretained _reader;
 }
 @end
 
-@interface GCDWebServerDataResponse () {
+@implementation GCDWebServerBodyEncoder
+
+- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
+  if ((self = [super init])) {
+    _response = response;
+    _reader = reader;
+  }
+  return self;
+}
+
+- (BOOL)open:(NSError**)error {
+  return [_reader open:error];
+}
+
+- (NSData*)readData:(NSError**)error {
+  return [_reader readData:error];
+}
+
+- (void)close {
+  [_reader close];
+}
+
+@end
+
+@interface GCDWebServerChunkEncoder () {
 @private
-  NSData* _data;
-  NSInteger _offset;
+  BOOL _finished;
 }
 @end
 
-@interface GCDWebServerFileResponse () {
+@implementation GCDWebServerChunkEncoder
+
+- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
+  if ((self = [super initWithResponse:response reader:reader])) {
+    response.contentLength = NSNotFound;  // Make sure "Content-Length" header is not set
+    [response setValue:@"chunked" forAdditionalHeader:@"Transfer-Encoding"];
+  }
+  return self;
+}
+
+- (NSData*)readData:(NSError**)error {
+  NSData* chunk;
+  if (_finished) {
+    chunk = [[NSData alloc] init];
+  } else {
+    NSData* data = [super readData:error];
+    if (data == nil) {
+      return nil;
+    }
+    if (data.length) {
+      const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
+      size_t hexLength = strlen(hexString);
+      chunk = [[NSMutableData alloc] initWithLength:(hexLength + 2 + data.length + 2)];
+      if (chunk == nil) {
+        DNOT_REACHED();
+        return nil;
+      }
+      char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
+      bcopy(hexString, ptr, hexLength);
+      ptr += hexLength;
+      *ptr++ = '\r';
+      *ptr++ = '\n';
+      bcopy(data.bytes, ptr, data.length);
+      ptr += data.length;
+      *ptr++ = '\r';
+      *ptr = '\n';
+    } else {
+      chunk = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
+      DCHECK(chunk);
+      _finished = YES;
+    }
+  }
+  return ARC_AUTORELEASE(chunk);
+}
+
+@end
+
+@interface GCDWebServerGZipEncoder () {
 @private
-  NSString* _path;
-  NSUInteger _offset;
-  NSUInteger _size;
-  int _file;
+  z_stream _stream;
+  BOOL _finished;
 }
 @end
 
-@interface GCDWebServerChunkedResponse () {
+@implementation GCDWebServerGZipEncoder
+
+- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
+  if ((self = [super initWithResponse:response reader:reader])) {
+    response.contentLength = NSNotFound;  // Make sure "Content-Length" header is not set
+    [response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"];
+  }
+  return self;
+}
+
+- (BOOL)open:(NSError**)error {
+  int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
+  if (result != Z_OK) {
+    *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
+    return NO;
+  }
+  if (![super open:error]) {
+    deflateEnd(&_stream);
+    return NO;
+  }
+  return YES;
+}
+
+- (NSData*)readData:(NSError**)error {
+  NSMutableData* gzipData;
+  if (_finished) {
+    gzipData = [[NSMutableData alloc] init];
+  } else {
+    gzipData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize];
+    if (gzipData == nil) {
+      DNOT_REACHED();
+      return nil;
+    }
+    NSUInteger length = 0;
+    do {
+      NSData* data = [super readData:error];
+      if (data == nil) {
+        return nil;
+      }
+      _stream.next_in = (Bytef*)data.bytes;
+      _stream.avail_in = (uInt)data.length;
+      while (1) {
+        NSUInteger maxLength = gzipData.length - length;
+        _stream.next_out = (Bytef*)((char*)gzipData.mutableBytes + length);
+        _stream.avail_out = (uInt)maxLength;
+        int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH);
+        if (result == Z_STREAM_END) {
+          _finished = YES;
+        } else if (result != Z_OK) {
+          ARC_RELEASE(gzipData);
+          *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
+          return nil;
+        }
+        length += maxLength - _stream.avail_out;
+        if (_stream.avail_out > 0) {
+          break;
+        }
+        gzipData.length = 2 * gzipData.length;  // zlib has used all the output buffer so resize it and try again in case more data is available
+      }
+      DCHECK(_stream.avail_in == 0);
+    } while (length == 0);  // Make sure we don't return an empty NSData if not in finished state
+    gzipData.length = length;
+  }
+  return ARC_AUTORELEASE(gzipData);
+}
+
+- (void)close {
+  deflateEnd(&_stream);
+  [super close];
+}
+
+@end
+
+@interface GCDWebServerResponse () {
 @private
-  GCDWebServerChunkBlock _block;
-  NSData* _chunk;
-  NSUInteger _offset;
-  BOOL _terminated;
+  NSString* _type;
+  NSUInteger _length;
+  NSInteger _status;
+  NSUInteger _maxAge;
+  NSMutableDictionary* _headers;
+  BOOL _gzipped;
+  BOOL _chunked;
+  
+  BOOL _opened;
+  NSMutableArray* _encoders;
+  id<GCDWebServerBodyReader> __unsafe_unretained _reader;
 }
 @end
 
 @implementation GCDWebServerResponse
 
-@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, additionalHeaders=_headers;
+@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge,
+            gzipContentEncoding=_gzipped, chunkedTransferEncoding=_chunked, additionalHeaders=_headers;
 
 + (GCDWebServerResponse*)response {
   return ARC_AUTORELEASE([[[self class] alloc] init]);
@@ -79,6 +240,7 @@
     _status = 200;
     _maxAge = 0;
     _headers = [[NSMutableDictionary alloc] init];
+    _encoders = [[NSMutableArray alloc] init];
   }
   return self;
 }
@@ -86,6 +248,7 @@
 - (void)dealloc {
   ARC_RELEASE(_type);
   ARC_RELEASE(_headers);
+  ARC_RELEASE(_encoders);
   
   ARC_DEALLOC(super);
 }
@@ -98,23 +261,47 @@
   return _type ? YES : NO;
 }
 
-@end
+- (BOOL)open:(NSError**)error {
+  return YES;
+}
 
-@implementation GCDWebServerResponse (Subclassing)
+- (NSData*)readData:(NSError**)error {
+  return nil;
+}
 
-- (BOOL)open {
-  [self doesNotRecognizeSelector:_cmd];
-  return NO;
+- (void)close {
+  ;
 }
 
-- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
-  [self doesNotRecognizeSelector:_cmd];
-  return -1;
+- (BOOL)performOpen:(NSError**)error {
+  if (_opened) {
+    DNOT_REACHED();
+    return NO;
+  }
+  _opened = YES;
+  
+  _reader = self;
+  if (_gzipped) {
+    GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
+    [_encoders addObject:encoder];
+    ARC_RELEASE(encoder);
+    _reader = encoder;
+  }
+  if (_chunked) {
+    GCDWebServerChunkEncoder* encoder = [[GCDWebServerChunkEncoder alloc] initWithResponse:self reader:_reader];
+    [_encoders addObject:encoder];
+    ARC_RELEASE(encoder);
+    _reader = encoder;
+  }
+  return [_reader open:error];
+}
+
+- (NSData*)performReadData:(NSError**)error {
+  return [_reader readData:error];
 }
 
-- (BOOL)close {
-  [self doesNotRecognizeSelector:_cmd];
-  return NO;
+- (void)performClose {
+  [_reader close];
 }
 
 @end
@@ -146,6 +333,13 @@
 
 @end
 
+@interface GCDWebServerDataResponse () {
+@private
+  NSData* _data;
+  BOOL _done;
+}
+@end
+
 @implementation GCDWebServerDataResponse
 
 + (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type {
@@ -161,7 +355,6 @@
   
   if ((self = [super init])) {
     _data = ARC_RETAIN(data);
-    _offset = -1;
     
     self.contentType = type;
     self.contentLength = data.length;
@@ -170,33 +363,20 @@
 }
 
 - (void)dealloc {
-  DCHECK(_offset < 0);
   ARC_RELEASE(_data);
   
   ARC_DEALLOC(super);
 }
 
-- (BOOL)open {
-  DCHECK(_offset < 0);
-  _offset = 0;
-  return YES;
-}
-
-- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
-  DCHECK(_offset >= 0);
-  NSInteger size = 0;
-  if (_offset < (NSInteger)_data.length) {
-    size = MIN(_data.length - _offset, length);
-    bcopy((char*)_data.bytes + _offset, buffer, size);
-    _offset += size;
+- (NSData*)readData:(NSError**)error {
+  NSData* data;
+  if (_done) {
+    data = [NSData data];
+  } else {
+    data = _data;
+    _done = YES;
   }
-  return size;
-}
-
-- (BOOL)close {
-  DCHECK(_offset >= 0);
-  _offset = -1;
-  return YES;
+  return data;
 }
 
 @end
@@ -268,6 +448,15 @@
 
 @end
 
+@interface GCDWebServerFileResponse () {
+@private
+  NSString* _path;
+  NSUInteger _offset;
+  NSUInteger _size;
+  int _file;
+}
+@end
+
 @implementation GCDWebServerFileResponse
 
 + (GCDWebServerFileResponse*)responseWithFile:(NSString*)path {
@@ -356,13 +545,19 @@
   ARC_DEALLOC(super);
 }
 
-- (BOOL)open {
+static inline NSError* _MakePosixError(int code) {
+  return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%s", strerror(code)]}];
+}
+
+- (BOOL)open:(NSError**)error {
   DCHECK(_file <= 0);
   _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
   if (_file <= 0) {
+    *error = _MakePosixError(errno);
     return NO;
   }
   if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
+    *error = _MakePosixError(errno);
     close(_file);
     _file = 0;
     return NO;
@@ -370,91 +565,60 @@
   return YES;
 }
 
-- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
+- (NSData*)readData:(NSError**)error {
   DCHECK(_file > 0);
-  ssize_t result = read(_file, buffer, MIN(length, _size));
+  size_t length = MIN((NSUInteger)kFileReadBufferSize, _size);
+  NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
+  ssize_t result = read(_file, data.mutableBytes, length);
+  if (result < 0) {
+    *error = _MakePosixError(errno);
+    return nil;
+  }
   if (result > 0) {
+    [data setLength:result];
     _size -= result;
   }
-  return result;
+  return ARC_AUTORELEASE(data);
 }
 
-- (BOOL)close {
+- (void)close {
   DCHECK(_file > 0);
-  int result = close(_file);
+  close(_file);
   _file = 0;
-  return (result == 0 ? YES : NO);
 }
 
 @end
 
-@implementation GCDWebServerChunkedResponse
+@interface GCDWebServerStreamResponse () {
+@private
+  GCDWebServerStreamBlock _block;
+}
+@end
+
+@implementation GCDWebServerStreamResponse
 
-+ (GCDWebServerChunkedResponse*)responseWithContentType:(NSString*)type chunkBlock:(GCDWebServerChunkBlock)block {
-  return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type chunkBlock:block]);
++ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
+  return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
 }
 
-- (id)initWithContentType:(NSString*)type chunkBlock:(GCDWebServerChunkBlock)block {
+- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
   if ((self = [super init])) {
     _block = [block copy];
     
     self.contentType = type;
-    [self setValue:@"chunked" forAdditionalHeader:@"Transfer-Encoding"];
+    self.chunkedTransferEncoding = YES;
   }
   return self;
 }
 
-- (BOOL)open {
-  DCHECK(_chunk == nil);
-  return YES;
-}
-
-- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
-  if (_offset >= _chunk.length) {
-    ARC_RELEASE(_chunk);
-    _chunk = nil;
-  }
-  if (_chunk == nil) {
-    if (_terminated) {
-      return 0;
-    }
-    NSData* data = _block();
-    if (data.length > 0) {
-      const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
-      size_t hexLength = strlen(hexString);
-      _chunk = [[NSMutableData alloc] initWithLength:(hexLength + 2 + data.length + 2)];
-      char* ptr = (char*)_chunk.bytes;
-      bcopy(hexString, ptr, hexLength);
-      ptr += hexLength;
-      *ptr++ = '\r';
-      *ptr++ = '\n';
-      bcopy(data.bytes, ptr, data.length);
-      ptr += data.length;
-      *ptr++ = '\r';
-      *ptr = '\n';
-    } else {
-      _chunk = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
-      _terminated = YES;
-    }
-    _offset = 0;
-  }
-  NSInteger size = MIN(_chunk.length - _offset, length);
-  bcopy((char*)_chunk.bytes + _offset, buffer, size);
-  _offset += size;
-  return size;
-}
-
-- (BOOL)close {
-  ARC_RELEASE(_chunk);
-  _chunk = nil;
-  return YES;
-}
-
 - (void)dealloc {
-  DCHECK(_chunk == nil);
-  ARC_RELEASE(_chunk);
+  ARC_RELEASE(_block);
   
   ARC_DEALLOC(super);
 }
 
+- (NSData*)readData:(NSError**)error {
+  return _block(error);
+}
+
 @end

+ 10 - 1
GCDWebServer.xcodeproj/project.pbxproj

@@ -38,6 +38,8 @@
 		E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
 		E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; };
 		E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
+		E2B0D4A718F13495009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A618F13495009A7927 /* libz.dylib */; };
+		E2B0D4A918F134A8009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A818F134A8009A7927 /* libz.dylib */; };
 		E2BE850A18E77ECA0061360B /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */; };
 		E2BE850B18E77ECA0061360B /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = E2BE850918E77ECA0061360B /* GCDWebUploader.m */; };
 		E2BE850C18E785940061360B /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = E2BE850918E77ECA0061360B /* GCDWebUploader.m */; };
@@ -97,6 +99,8 @@
 		E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
 		E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
 		E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
+		E2B0D4A618F13495009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+		E2B0D4A818F134A8009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 		E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GCDWebUploader.bundle; sourceTree = "<group>"; };
 		E2BE850818E77ECA0061360B /* GCDWebUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebUploader.h; sourceTree = "<group>"; };
 		E2BE850918E77ECA0061360B /* GCDWebUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebUploader.m; sourceTree = "<group>"; };
@@ -111,6 +115,7 @@
 				E2BE851118E79DAF0061360B /* SystemConfiguration.framework in Frameworks */,
 				E208D1B3167BB17E00500836 /* CoreServices.framework in Frameworks */,
 				E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */,
+				E2B0D4A718F13495009A7927 /* libz.dylib in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -121,6 +126,7 @@
 				E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */,
 				E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */,
 				E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */,
+				E2B0D4A918F134A8009A7927 /* libz.dylib in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -191,6 +197,7 @@
 				E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */,
 				E221129A1690B7B10048D2B2 /* UIKit.framework */,
 				E22112981690B7AA0048D2B2 /* CFNetwork.framework */,
+				E2B0D4A818F134A8009A7927 /* libz.dylib */,
 			);
 			name = "iOS Frameworks and Libraries";
 			sourceTree = "<group>";
@@ -201,6 +208,7 @@
 				E2BE851018E79DAF0061360B /* SystemConfiguration.framework */,
 				E208D1B2167BB17E00500836 /* CoreServices.framework */,
 				E208D148167B76B700500836 /* CFNetwork.framework */,
+				E2B0D4A618F13495009A7927 /* libz.dylib */,
 			);
 			name = "Mac Frameworks and Libraries";
 			sourceTree = "<group>";
@@ -366,9 +374,10 @@
 				ONLY_ACTIVE_ARCH = YES;
 				WARNING_CFLAGS = (
 					"-Wall",
-					"-Wshadow",
 					"-Weverything",
+					"-Wshadow",
 					"-Wshorten-64-to-32",
+					"-Wno-explicit-ownership-type",
 					"-Wno-gnu-statement-expression",
 					"-Wno-direct-ivar-access",
 					"-Wno-implicit-retain-self",