Added support for gzip body encoding

This commit is contained in:
Pierre-Olivier Latour
2014-04-06 18:03:24 -07:00
parent 63a66ff331
commit 06630d3245

View File

@@ -25,8 +25,116 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <zlib.h>
#import "GCDWebServerPrivate.h"
#define kZlibErrorDomain @"ZlibErrorDomain"
#define kGZipInitialBufferSize (256 * 1024)
@interface GCDWebServerBodyDecoder : NSObject <GCDWebServerBodyWriter>
- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id<GCDWebServerBodyWriter>)writer;
@end
@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder
@end
@interface GCDWebServerBodyDecoder () {
@private
GCDWebServerRequest* __unsafe_unretained _request;
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
}
@end
@implementation GCDWebServerBodyDecoder
- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id<GCDWebServerBodyWriter>)writer {
if ((self = [super init])) {
_request = request;
_writer = writer;
}
return self;
}
- (BOOL)open:(NSError**)error {
return [_writer open:error];
}
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
return [_writer writeData:data error:error];
}
- (BOOL)close:(NSError**)error {
return [_writer close:error];
}
@end
@interface GCDWebServerGZipDecoder () {
@private
z_stream _stream;
BOOL _finished;
}
@end
@implementation GCDWebServerGZipDecoder
- (BOOL)open:(NSError**)error {
int result = inflateInit2(&_stream, 15 + 16);
if (result != Z_OK) {
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
return NO;
}
if (![super open:error]) {
deflateEnd(&_stream);
return NO;
}
return YES;
}
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
DCHECK(!_finished);
_stream.next_in = (Bytef*)data.bytes;
_stream.avail_in = (uInt)data.length;
NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:1024]; // kGZipInitialBufferSize
if (decodedData == nil) {
DNOT_REACHED();
return NO;
}
NSUInteger length = 0;
while (1) {
NSUInteger maxLength = decodedData.length - length;
_stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length);
_stream.avail_out = (uInt)maxLength;
int result = inflate(&_stream, Z_NO_FLUSH);
if ((result != Z_OK) && (result != Z_STREAM_END)) {
ARC_RELEASE(decodedData);
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
return nil;
}
length += maxLength - _stream.avail_out;
if (_stream.avail_out > 0) {
if (result == Z_STREAM_END) {
_finished = YES;
}
break;
}
decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available
}
decodedData.length = length;
BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet
ARC_RELEASE(decodedData);
return success;
}
- (BOOL)close:(NSError**)error {
DCHECK(_finished);
inflateEnd(&_stream);
return [super close:error];
}
@end
@interface GCDWebServerRequest () {
@private
NSString* _method;
@@ -144,7 +252,12 @@
_opened = YES;
_writer = self;
// TODO: Inject decoders
if ([[[self.headers objectForKey:@"Content-Encoding"] lowercaseString] isEqualToString:@"gzip"]) {
GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer];
[_decoders addObject:decoder];
ARC_RELEASE(decoder);
_writer = decoder;
}
return [_writer open:error];
}