mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-04-24 00:00:04 +08:00
Added support for gzip body encoding
This commit is contained in:
@@ -25,8 +25,116 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#import <zlib.h>
|
||||||
|
|
||||||
#import "GCDWebServerPrivate.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 () {
|
@interface GCDWebServerRequest () {
|
||||||
@private
|
@private
|
||||||
NSString* _method;
|
NSString* _method;
|
||||||
@@ -144,7 +252,12 @@
|
|||||||
_opened = YES;
|
_opened = YES;
|
||||||
|
|
||||||
_writer = self;
|
_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];
|
return [_writer open:error];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user