Showing
11 changed files
with
674 additions
and
73 deletions
Classes/ASIDataCompressor.h
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataCompressor.h | ||
| 3 | +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +// This is a helper class used by ASIHTTPRequest to handle deflating (compressing) data in memory and on disk | ||
| 10 | +// You may also find it helpful if you need to deflate data and files yourself - see the class methods below | ||
| 11 | +// Most of the zlib stuff is based on the sample code by Mark Adler available at http://zlib.net | ||
| 12 | + | ||
| 13 | +#import <Foundation/Foundation.h> | ||
| 14 | +#import <zlib.h> | ||
| 15 | + | ||
| 16 | +@interface ASIDataCompressor : NSObject { | ||
| 17 | + BOOL streamReady; | ||
| 18 | + z_stream zStream; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// Convenience constructor will call setupStream for you | ||
| 22 | ++ (id)compressor; | ||
| 23 | + | ||
| 24 | +// Compress the passed chunk of data | ||
| 25 | +- (NSData *)compressBytes:(Bytef *)bytes length:(NSInteger)length error:(NSError **)err; | ||
| 26 | + | ||
| 27 | +// Convenience method - pass it some data, and you'll get deflated data back | ||
| 28 | ++ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err; | ||
| 29 | + | ||
| 30 | +// Convenience method - pass it a file containing the data to compress in sourcePath, and it will write deflated data to destinationPath | ||
| 31 | ++ (void)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; | ||
| 32 | + | ||
| 33 | +// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'compressor' | ||
| 34 | +- (NSError *)setupStream; | ||
| 35 | + | ||
| 36 | +// Tells zlib to clean up. You need to call this if you need to cancel deflating part way through | ||
| 37 | +// If deflating finishes or fails, this method will be called automatically | ||
| 38 | +- (NSError *)closeStream; | ||
| 39 | + | ||
| 40 | +@property (assign, readonly) BOOL streamReady; | ||
| 41 | +@end |
Classes/ASIDataCompressor.m
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataCompressor.m | ||
| 3 | +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "ASIDataCompressor.h" | ||
| 10 | +#import "ASIHTTPRequest.h" | ||
| 11 | + | ||
| 12 | +#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks | ||
| 13 | +#define COMPRESSION_AMOUNT Z_DEFAULT_COMPRESSION | ||
| 14 | + | ||
| 15 | +@interface ASIDataCompressor () | ||
| 16 | ++ (NSError *)deflateErrorWithCode:(int)code; | ||
| 17 | +@end | ||
| 18 | + | ||
| 19 | +@implementation ASIDataCompressor | ||
| 20 | + | ||
| 21 | ++ (id)compressor | ||
| 22 | +{ | ||
| 23 | + ASIDataCompressor *compressor = [[[self alloc] init] autorelease]; | ||
| 24 | + [compressor setupStream]; | ||
| 25 | + return compressor; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +- (void)dealloc | ||
| 29 | +{ | ||
| 30 | + if (streamReady) { | ||
| 31 | + [self closeStream]; | ||
| 32 | + } | ||
| 33 | + [super dealloc]; | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +- (NSError *)setupStream | ||
| 37 | +{ | ||
| 38 | + if (streamReady) { | ||
| 39 | + return nil; | ||
| 40 | + } | ||
| 41 | + // Setup the inflate stream | ||
| 42 | + zStream.zalloc = Z_NULL; | ||
| 43 | + zStream.zfree = Z_NULL; | ||
| 44 | + zStream.opaque = Z_NULL; | ||
| 45 | + zStream.avail_in = 0; | ||
| 46 | + zStream.next_in = 0; | ||
| 47 | + int status = deflateInit2(&zStream, COMPRESSION_AMOUNT, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY); | ||
| 48 | + if (status != Z_OK) { | ||
| 49 | + return [[self class] deflateErrorWithCode:status]; | ||
| 50 | + } | ||
| 51 | + streamReady = YES; | ||
| 52 | + return nil; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +- (NSError *)closeStream | ||
| 56 | +{ | ||
| 57 | + if (!streamReady) { | ||
| 58 | + return nil; | ||
| 59 | + } | ||
| 60 | + // Close the deflate stream | ||
| 61 | + streamReady = NO; | ||
| 62 | + int status = deflateEnd(&zStream); | ||
| 63 | + if (status != Z_OK) { | ||
| 64 | + return [[self class] deflateErrorWithCode:status]; | ||
| 65 | + } | ||
| 66 | + return nil; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +- (NSData *)compressBytes:(Bytef *)bytes length:(NSInteger)length error:(NSError **)err | ||
| 70 | +{ | ||
| 71 | + if (length == 0) return nil; | ||
| 72 | + | ||
| 73 | + NSUInteger halfLength = length/2; | ||
| 74 | + | ||
| 75 | + // We'll take a guess that the compressed data will fit in half the size of the original (ie the max to compress at once is half DATA_CHUNK_SIZE), if not, we'll increase it below | ||
| 76 | + NSMutableData *outputData = [NSMutableData dataWithLength:length/2]; | ||
| 77 | + | ||
| 78 | + int status; | ||
| 79 | + | ||
| 80 | + zStream.next_in = bytes; | ||
| 81 | + zStream.avail_in = length; | ||
| 82 | + | ||
| 83 | + NSInteger bytesProcessedAlready = zStream.total_out; | ||
| 84 | + while (zStream.avail_out == 0) { | ||
| 85 | + | ||
| 86 | + if (zStream.total_out-bytesProcessedAlready >= [outputData length]) { | ||
| 87 | + [outputData increaseLengthBy:halfLength]; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready; | ||
| 91 | + zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready)); | ||
| 92 | + | ||
| 93 | + status = deflate(&zStream, Z_FINISH); | ||
| 94 | + | ||
| 95 | + if (status == Z_STREAM_END) { | ||
| 96 | + [self closeStream]; | ||
| 97 | + } else if (status != Z_OK) { | ||
| 98 | + *err = [[self class] deflateErrorWithCode:status]; | ||
| 99 | + [self closeStream]; | ||
| 100 | + return NO; | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + // Set real length | ||
| 105 | + [outputData setLength: zStream.total_out-bytesProcessedAlready]; | ||
| 106 | + return outputData; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | + | ||
| 110 | ++ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err | ||
| 111 | +{ | ||
| 112 | + NSData *outputData = [[ASIDataCompressor compressor] compressBytes:(Bytef *)[uncompressedData bytes] length:[uncompressedData length] error:err]; | ||
| 113 | + if (*err) { | ||
| 114 | + return nil; | ||
| 115 | + } | ||
| 116 | + return outputData; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | + | ||
| 120 | + | ||
| 121 | ++ (void)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err | ||
| 122 | +{ | ||
| 123 | + // Create an empty file at the destination path | ||
| 124 | + if (![[NSFileManager defaultManager] createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) { | ||
| 125 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]]; | ||
| 126 | + return; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + // Ensure the source file exists | ||
| 130 | + if (![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) { | ||
| 131 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]]; | ||
| 132 | + return; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + UInt8 inputData[DATA_CHUNK_SIZE]; | ||
| 136 | + NSData *outputData; | ||
| 137 | + int readLength; | ||
| 138 | + | ||
| 139 | + ASIDataCompressor *compressor = [ASIDataCompressor compressor]; | ||
| 140 | + | ||
| 141 | + NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath]; | ||
| 142 | + [inputStream open]; | ||
| 143 | + NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]; | ||
| 144 | + [outputStream open]; | ||
| 145 | + | ||
| 146 | + while ([compressor streamReady]) { | ||
| 147 | + | ||
| 148 | + // Read some data from the file | ||
| 149 | + readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE]; | ||
| 150 | + | ||
| 151 | + // Make sure nothing went wrong | ||
| 152 | + if ([inputStream streamError]) { | ||
| 153 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]]; | ||
| 154 | + [compressor closeStream]; | ||
| 155 | + return; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + // Attempt to inflate the chunk of data | ||
| 159 | + outputData = [compressor compressBytes:inputData length:readLength error:err]; | ||
| 160 | + if (*err) { | ||
| 161 | + return; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + // Write the deflated data out to the destination file | ||
| 165 | + [outputStream write:[outputData bytes] maxLength:[outputData length]]; | ||
| 166 | + | ||
| 167 | + // Make sure nothing went wrong | ||
| 168 | + if ([outputStream streamError]) { | ||
| 169 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to write to the destination data file at &@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]]; | ||
| 170 | + [compressor closeStream]; | ||
| 171 | + return; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + [inputStream close]; | ||
| 177 | + [outputStream close]; | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | ++ (NSError *)deflateErrorWithCode:(int)code | ||
| 181 | +{ | ||
| 182 | + return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]]; | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +@synthesize streamReady; | ||
| 186 | +@end |
Classes/ASIDataDecompressor.h
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataDecompressor.h | ||
| 3 | +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +// This is a helper class used by ASIHTTPRequest to handle inflating (decompressing) data in memory and on disk | ||
| 10 | +// You may also find it helpful if you need to inflate data and files yourself - see the class methods below | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +#import <Foundation/Foundation.h> | ||
| 14 | +#import <zlib.h> | ||
| 15 | + | ||
| 16 | +@interface ASIDataDecompressor : NSObject { | ||
| 17 | + BOOL streamReady; | ||
| 18 | + z_stream zStream; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// Convenience constructor will call setupStream for you | ||
| 22 | ++ (id)decompressor; | ||
| 23 | + | ||
| 24 | +// Uncompress the passed chunk of data | ||
| 25 | +- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSInteger)length error:(NSError **)err; | ||
| 26 | + | ||
| 27 | +// Convenience method - pass it some deflated data, and you'll get inflated data back | ||
| 28 | ++ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err; | ||
| 29 | + | ||
| 30 | +// Convenience method - pass it a file containing deflated data in sourcePath, and it will write inflated data to destinationPath | ||
| 31 | ++ (void)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; | ||
| 32 | + | ||
| 33 | +// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'decompressor' | ||
| 34 | +- (NSError *)setupStream; | ||
| 35 | + | ||
| 36 | +// Tells zlib to clean up. You need to call this if you need to cancel inflating part way through | ||
| 37 | +// If inflating finishes or fails, this method will be called automatically | ||
| 38 | +- (NSError *)closeStream; | ||
| 39 | + | ||
| 40 | +@property (assign, readonly) BOOL streamReady; | ||
| 41 | +@end |
Classes/ASIDataDecompressor.m
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataDecompressor.m | ||
| 3 | +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "ASIDataDecompressor.h" | ||
| 10 | +#import "ASIHTTPRequest.h" | ||
| 11 | + | ||
| 12 | +#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks | ||
| 13 | + | ||
| 14 | +@interface ASIDataDecompressor () | ||
| 15 | ++ (NSError *)inflateErrorWithCode:(int)code; | ||
| 16 | +@end; | ||
| 17 | + | ||
| 18 | +@implementation ASIDataDecompressor | ||
| 19 | + | ||
| 20 | ++ (id)decompressor | ||
| 21 | +{ | ||
| 22 | + ASIDataDecompressor *decompressor = [[[self alloc] init] autorelease]; | ||
| 23 | + [decompressor setupStream]; | ||
| 24 | + return decompressor; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +- (void)dealloc | ||
| 28 | +{ | ||
| 29 | + if (streamReady) { | ||
| 30 | + [self closeStream]; | ||
| 31 | + } | ||
| 32 | + [super dealloc]; | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +- (NSError *)setupStream | ||
| 36 | +{ | ||
| 37 | + if (streamReady) { | ||
| 38 | + return nil; | ||
| 39 | + } | ||
| 40 | + // Setup the inflate stream | ||
| 41 | + zStream.zalloc = Z_NULL; | ||
| 42 | + zStream.zfree = Z_NULL; | ||
| 43 | + zStream.opaque = Z_NULL; | ||
| 44 | + zStream.avail_in = 0; | ||
| 45 | + zStream.next_in = 0; | ||
| 46 | + int status = inflateInit2(&zStream, (15+32)); | ||
| 47 | + if (status != Z_OK) { | ||
| 48 | + return [[self class] inflateErrorWithCode:status]; | ||
| 49 | + } | ||
| 50 | + streamReady = YES; | ||
| 51 | + return nil; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +- (NSError *)closeStream | ||
| 55 | +{ | ||
| 56 | + if (!streamReady) { | ||
| 57 | + return nil; | ||
| 58 | + } | ||
| 59 | + // Close the inflate stream | ||
| 60 | + streamReady = NO; | ||
| 61 | + int status = inflateEnd(&zStream); | ||
| 62 | + if (status != Z_OK) { | ||
| 63 | + return [[self class] inflateErrorWithCode:status]; | ||
| 64 | + } | ||
| 65 | + return nil; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSInteger)length error:(NSError **)err | ||
| 69 | +{ | ||
| 70 | + if (length == 0) return nil; | ||
| 71 | + | ||
| 72 | + NSUInteger halfLength = length/2; | ||
| 73 | + NSMutableData *outputData = [NSMutableData dataWithLength:length+halfLength]; | ||
| 74 | + | ||
| 75 | + int status; | ||
| 76 | + | ||
| 77 | + zStream.next_in = bytes; | ||
| 78 | + zStream.avail_in = length; | ||
| 79 | + | ||
| 80 | + NSInteger bytesProcessedAlready = zStream.total_out; | ||
| 81 | + while (zStream.avail_out == 0) { | ||
| 82 | + | ||
| 83 | + if (zStream.total_out-bytesProcessedAlready >= [outputData length]) { | ||
| 84 | + [outputData increaseLengthBy:halfLength]; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready; | ||
| 88 | + zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready)); | ||
| 89 | + | ||
| 90 | + status = inflate(&zStream, Z_SYNC_FLUSH); | ||
| 91 | + | ||
| 92 | + if (status == Z_STREAM_END) { | ||
| 93 | + *err = [self closeStream]; | ||
| 94 | + if (*err) { | ||
| 95 | + return nil; | ||
| 96 | + } | ||
| 97 | + } else if (status != Z_OK) { | ||
| 98 | + *err = [[self class] inflateErrorWithCode:status]; | ||
| 99 | + [self closeStream]; | ||
| 100 | + return nil; | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + // Set real length | ||
| 105 | + [outputData setLength: zStream.total_out-bytesProcessedAlready]; | ||
| 106 | + return outputData; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | + | ||
| 110 | ++ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err | ||
| 111 | +{ | ||
| 112 | + NSData *outputData = [[ASIDataDecompressor decompressor] uncompressBytes:(Bytef *)[compressedData bytes] length:[compressedData length] error:err]; | ||
| 113 | + if (*err) { | ||
| 114 | + return nil; | ||
| 115 | + } | ||
| 116 | + return outputData; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | ++ (void)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err | ||
| 120 | +{ | ||
| 121 | + // Create an empty file at the destination path | ||
| 122 | + if (![[NSFileManager defaultManager] createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) { | ||
| 123 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]]; | ||
| 124 | + return; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + // Ensure the source file exists | ||
| 128 | + if (![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) { | ||
| 129 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]]; | ||
| 130 | + return; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + UInt8 inputData[DATA_CHUNK_SIZE]; | ||
| 134 | + NSData *outputData; | ||
| 135 | + int readLength; | ||
| 136 | + | ||
| 137 | + ASIDataDecompressor *decompressor = [ASIDataDecompressor decompressor]; | ||
| 138 | + | ||
| 139 | + NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath]; | ||
| 140 | + [inputStream open]; | ||
| 141 | + NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]; | ||
| 142 | + [outputStream open]; | ||
| 143 | + | ||
| 144 | + while ([decompressor streamReady]) { | ||
| 145 | + | ||
| 146 | + // Read some data from the file | ||
| 147 | + readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE]; | ||
| 148 | + | ||
| 149 | + // Make sure nothing went wrong | ||
| 150 | + if ([inputStream streamError]) { | ||
| 151 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]]; | ||
| 152 | + [decompressor closeStream]; | ||
| 153 | + return; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + // Attempt to inflate the chunk of data | ||
| 157 | + outputData = [decompressor uncompressBytes:inputData length:readLength error:err]; | ||
| 158 | + if (*err) { | ||
| 159 | + return; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // Write the inflated data out to the destination file | ||
| 163 | + [outputStream write:[outputData bytes] maxLength:[outputData length]]; | ||
| 164 | + | ||
| 165 | + // Make sure nothing went wrong | ||
| 166 | + if ([outputStream streamError]) { | ||
| 167 | + *err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to write to the destination data file at &@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]]; | ||
| 168 | + [decompressor closeStream]; | ||
| 169 | + return; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + [inputStream close]; | ||
| 175 | + [outputStream close]; | ||
| 176 | +} | ||
| 177 | + | ||
| 178 | + | ||
| 179 | ++ (NSError *)inflateErrorWithCode:(int)code | ||
| 180 | +{ | ||
| 181 | + return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]]; | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +@synthesize streamReady; | ||
| 185 | +@end |
| @@ -14,13 +14,15 @@ | @@ -14,13 +14,15 @@ | ||
| 14 | #if TARGET_OS_IPHONE | 14 | #if TARGET_OS_IPHONE |
| 15 | #import <CFNetwork/CFNetwork.h> | 15 | #import <CFNetwork/CFNetwork.h> |
| 16 | #endif | 16 | #endif |
| 17 | -#import <zlib.h> | 17 | + |
| 18 | #import <stdio.h> | 18 | #import <stdio.h> |
| 19 | #import "ASIHTTPRequestConfig.h" | 19 | #import "ASIHTTPRequestConfig.h" |
| 20 | #import "ASIHTTPRequestDelegate.h" | 20 | #import "ASIHTTPRequestDelegate.h" |
| 21 | #import "ASIProgressDelegate.h" | 21 | #import "ASIProgressDelegate.h" |
| 22 | #import "ASICacheDelegate.h" | 22 | #import "ASICacheDelegate.h" |
| 23 | 23 | ||
| 24 | +@class ASIDataDecompressor; | ||
| 25 | + | ||
| 24 | extern NSString *ASIHTTPRequestVersion; | 26 | extern NSString *ASIHTTPRequestVersion; |
| 25 | 27 | ||
| 26 | // Make targeting different platforms more reliable | 28 | // Make targeting different platforms more reliable |
| @@ -54,7 +56,8 @@ typedef enum _ASINetworkErrorType { | @@ -54,7 +56,8 @@ typedef enum _ASINetworkErrorType { | ||
| 54 | ASIInternalErrorWhileApplyingCredentialsType = 7, | 56 | ASIInternalErrorWhileApplyingCredentialsType = 7, |
| 55 | ASIFileManagementError = 8, | 57 | ASIFileManagementError = 8, |
| 56 | ASITooMuchRedirectionErrorType = 9, | 58 | ASITooMuchRedirectionErrorType = 9, |
| 57 | - ASIUnhandledExceptionError = 10 | 59 | + ASIUnhandledExceptionError = 10, |
| 60 | + ASICompressionError = 11 | ||
| 58 | 61 | ||
| 59 | } ASINetworkErrorType; | 62 | } ASINetworkErrorType; |
| 60 | 63 | ||
| @@ -149,7 +152,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -149,7 +152,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
| 149 | // The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath | 152 | // The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath |
| 150 | NSString *temporaryFileDownloadPath; | 153 | NSString *temporaryFileDownloadPath; |
| 151 | 154 | ||
| 152 | - // If | 155 | + // If the response is gzipped and shouldWaitToInflateCompressedResponses is NO, a file will be created at this path containing the inflated response as it comes in |
| 153 | NSString *temporaryUncompressedDataDownloadPath; | 156 | NSString *temporaryUncompressedDataDownloadPath; |
| 154 | 157 | ||
| 155 | // Used for writing data to a file when downloadDestinationPath is set | 158 | // Used for writing data to a file when downloadDestinationPath is set |
| @@ -430,12 +433,24 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -430,12 +433,24 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
| 430 | // Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache | 433 | // Set secondsToCache to use a custom time interval for expiring the response when it is stored in a cache |
| 431 | NSTimeInterval secondsToCache; | 434 | NSTimeInterval secondsToCache; |
| 432 | 435 | ||
| 433 | - z_stream zStream; | 436 | + // When downloading a gzipped response, the request will use this helper object to inflate the response |
| 434 | - NSMutableData *uncompressedResponseData; | 437 | + ASIDataDecompressor *dataDecompressor; |
| 435 | - int lastLen; | 438 | + |
| 436 | - | 439 | + // Controls how responses with a gzipped encoding are inflated (decompressed) |
| 440 | + // When set to YES (This is the default): | ||
| 441 | + // * gzipped responses for requests without a downloadDestinationPath will be inflated only when [request responseData] / [request responseString] is called | ||
| 442 | + // * gzipped responses for requests with a downloadDestinationPath set will be inflated only when the request completes | ||
| 443 | + // | ||
| 444 | + // When set to NO | ||
| 445 | + // All requests will inflate the response as it comes in | ||
| 446 | + // * If the request has no downloadDestinationPath set, the raw (compressed) response is disgarded and rawResponseData will contain the decompressed response | ||
| 447 | + // * If the request has a downloadDestinationPath, the raw response will be stored in temporaryFileDownloadPath as normal, the inflated response will be stored in temporaryUncompressedDataDownloadPath | ||
| 448 | + // Once the request completes suceessfully, the contents of temporaryUncompressedDataDownloadPath are moved into downloadDestinationPath | ||
| 449 | + // | ||
| 450 | + // Setting this to NO may be especially useful for users using ASIHTTPRequest in conjunction with a streaming parser, as it will allow partial gzipped responses to be inflated and passed on to the parser while the request is still running | ||
| 451 | + BOOL shouldWaitToInflateCompressedResponses; | ||
| 452 | + | ||
| 437 | } | 453 | } |
| 438 | -- (NSData *)uncompressBytes:(void *)bytes length:(NSInteger)length error:(NSError **)err; | ||
| 439 | 454 | ||
| 440 | #pragma mark init / dealloc | 455 | #pragma mark init / dealloc |
| 441 | 456 | ||
| @@ -664,24 +679,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -664,24 +679,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
| 664 | // Dump all session data (authentication and cookies) | 679 | // Dump all session data (authentication and cookies) |
| 665 | + (void)clearSession; | 680 | + (void)clearSession; |
| 666 | 681 | ||
| 667 | -#pragma mark gzip decompression | ||
| 668 | - | ||
| 669 | -// Uncompress gzipped data with zlib | ||
| 670 | -+ (NSData *)uncompressZippedData:(NSData*)compressedData error:(NSError **)err; | ||
| 671 | - | ||
| 672 | -// Uncompress gzipped data from a file into another file, used when downloading to a file | ||
| 673 | -+ (BOOL)uncompressZippedDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; | ||
| 674 | -+ (int)uncompressZippedDataFromSource:(FILE *)source toDestination:(FILE *)dest; | ||
| 675 | - | ||
| 676 | -#pragma mark gzip compression | ||
| 677 | - | ||
| 678 | -// Compress data with gzip using zlib | ||
| 679 | -+ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err; | ||
| 680 | - | ||
| 681 | -// gzip compress data from a file, saving to another file, used for uploading when shouldCompressRequestBody is true | ||
| 682 | -+ (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err; | ||
| 683 | -+ (int)compressDataFromSource:(FILE *)source toDestination:(FILE *)dest; | ||
| 684 | - | ||
| 685 | #pragma mark get user agent | 682 | #pragma mark get user agent |
| 686 | 683 | ||
| 687 | // Will be used as a user agent if requests do not specify a custom user agent | 684 | // Will be used as a user agent if requests do not specify a custom user agent |
| @@ -865,4 +862,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -865,4 +862,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
| 865 | @property (assign) ASICacheStoragePolicy cacheStoragePolicy; | 862 | @property (assign) ASICacheStoragePolicy cacheStoragePolicy; |
| 866 | @property (assign, readonly) BOOL didUseCachedResponse; | 863 | @property (assign, readonly) BOOL didUseCachedResponse; |
| 867 | @property (assign) NSTimeInterval secondsToCache; | 864 | @property (assign) NSTimeInterval secondsToCache; |
| 865 | +@property (retain) ASIDataDecompressor *dataDecompressor; | ||
| 866 | +@property (assign) BOOL shouldWaitToInflateCompressedResponses; | ||
| 868 | @end | 867 | @end |
This diff is collapsed. Click to expand it.
Classes/Tests/ASIDataCompressorTests.h
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataCompressorTests.h | ||
| 3 | +// Mac | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <Foundation/Foundation.h> | ||
| 10 | +#import "ASITestCase.h" | ||
| 11 | + | ||
| 12 | +@interface ASIDataCompressorTests : ASITestCase { | ||
| 13 | + | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +@end |
Classes/Tests/ASIDataCompressorTests.m
0 → 100644
| 1 | +// | ||
| 2 | +// ASIDataCompressorTests.m | ||
| 3 | +// Mac | ||
| 4 | +// | ||
| 5 | +// Created by Ben Copsey on 17/08/2010. | ||
| 6 | +// Copyright 2010 All-Seeing Interactive. All rights reserved. | ||
| 7 | +// | ||
| 8 | +// Sadly these tests only work on Mac because of the dependency on NSTask, but I'm fairly sure this class should behave in the same way on iOS | ||
| 9 | + | ||
| 10 | +#import "ASIDataCompressorTests.h" | ||
| 11 | +#import "ASIDataCompressor.h" | ||
| 12 | +#import "ASIDataDecompressor.h" | ||
| 13 | +#import "ASIHTTPRequest.h" | ||
| 14 | + | ||
| 15 | +@implementation ASIDataCompressorTests | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +- (void)testInflateData | ||
| 19 | +{ | ||
| 20 | + // Test in-memory inflate using uncompressData:error: | ||
| 21 | + NSUInteger i; | ||
| 22 | + | ||
| 23 | + NSString *originalString = [NSString string]; | ||
| 24 | + for (i=0; i<1000; i++) { | ||
| 25 | + originalString = [originalString stringByAppendingFormat:@"This is line %i\r\n",i]; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + NSError *error = nil; | ||
| 29 | + NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"]; | ||
| 30 | + NSString *gzippedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt.gz"]; | ||
| 31 | + [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; | ||
| 32 | + if (error) { | ||
| 33 | + GHFail(@"Failed to remove old file, cannot proceed with test"); | ||
| 34 | + } | ||
| 35 | + [originalString writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:&error]; | ||
| 36 | + if (error) { | ||
| 37 | + GHFail(@"Failed to write string, cannot proceed with test"); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + NSTask *task = [[[NSTask alloc] init] autorelease]; | ||
| 41 | + [task setLaunchPath:@"/usr/bin/gzip"]; | ||
| 42 | + [task setArguments:[NSArray arrayWithObject:filePath]]; | ||
| 43 | + [task launch]; | ||
| 44 | + [task waitUntilExit]; | ||
| 45 | + | ||
| 46 | + NSData *deflatedData = [NSData dataWithContentsOfFile:gzippedFilePath]; | ||
| 47 | + | ||
| 48 | + NSData *inflatedData = [ASIDataDecompressor uncompressData:deflatedData error:&error]; | ||
| 49 | + if (error) { | ||
| 50 | + GHFail(@"Inflate failed because %@",error); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + NSString *inflatedString = [[[NSString alloc] initWithBytes:[inflatedData bytes] length:[inflatedData length] encoding:NSUTF8StringEncoding] autorelease]; | ||
| 54 | + | ||
| 55 | + | ||
| 56 | + BOOL success = [inflatedString isEqualToString:originalString]; | ||
| 57 | + GHAssertTrue(success,@"inflated data is not the same as original"); | ||
| 58 | + | ||
| 59 | + // Test file to file inflate | ||
| 60 | + NSString *inflatedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"inflated_file.txt"]; | ||
| 61 | + [ASIHTTPRequest removeFileAtPath:inflatedFilePath error:&error]; | ||
| 62 | + if (error) { | ||
| 63 | + GHFail(@"Failed to remove old file, cannot proceed with test"); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + [ASIDataDecompressor uncompressDataFromFile:gzippedFilePath toFile:inflatedFilePath error:&error]; | ||
| 67 | + if (error) { | ||
| 68 | + GHFail(@"Inflate failed because %@",error); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + originalString = [NSString stringWithContentsOfFile:inflatedFilePath encoding:NSUTF8StringEncoding error:&error]; | ||
| 72 | + if (error) { | ||
| 73 | + GHFail(@"Failed to read the inflated data, cannot proceed with test"); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + success = [inflatedString isEqualToString:originalString]; | ||
| 77 | + GHAssertTrue(success,@"inflated data is not the same as original"); | ||
| 78 | + | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +- (void)testDeflateData | ||
| 82 | +{ | ||
| 83 | + // Test in-memory deflate using compressData:error: | ||
| 84 | + NSUInteger i; | ||
| 85 | + | ||
| 86 | + NSString *originalString = [NSString string]; | ||
| 87 | + for (i=0; i<1000; i++) { | ||
| 88 | + originalString = [originalString stringByAppendingFormat:@"This is line %i\r\n",i]; | ||
| 89 | + } | ||
| 90 | + NSError *error = nil; | ||
| 91 | + NSData *deflatedData = [ASIDataCompressor compressData:[originalString dataUsingEncoding:NSUTF8StringEncoding] error:&error]; | ||
| 92 | + if (error) { | ||
| 93 | + GHFail(@"Failed to deflate the data"); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + NSString *gzippedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt.gz"]; | ||
| 97 | + [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; | ||
| 98 | + if (error) { | ||
| 99 | + GHFail(@"Failed to remove old file, cannot proceed with test"); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + [deflatedData writeToFile:gzippedFilePath options:0 error:&error]; | ||
| 103 | + if (error) { | ||
| 104 | + GHFail(@"Failed to write data, cannot proceed with test"); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"]; | ||
| 108 | + | ||
| 109 | + NSTask *task = [[[NSTask alloc] init] autorelease]; | ||
| 110 | + [task setLaunchPath:@"/usr/bin/gzip"]; | ||
| 111 | + [task setArguments:[NSArray arrayWithObjects:@"-d",gzippedFilePath,nil]]; | ||
| 112 | + [task launch]; | ||
| 113 | + [task waitUntilExit]; | ||
| 114 | + | ||
| 115 | + NSString *inflatedString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; | ||
| 116 | + if (error) { | ||
| 117 | + GHFail(@"Failed to read the inflated data, cannot proceed with test"); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + BOOL success = [inflatedString isEqualToString:originalString]; | ||
| 121 | + GHAssertTrue(success,@"inflated data is not the same as original"); | ||
| 122 | + | ||
| 123 | + | ||
| 124 | + // Test file to file deflate | ||
| 125 | + [ASIHTTPRequest removeFileAtPath:gzippedFilePath error:&error]; | ||
| 126 | + | ||
| 127 | + [ASIDataCompressor compressDataFromFile:filePath toFile:gzippedFilePath error:&error]; | ||
| 128 | + if (error) { | ||
| 129 | + GHFail(@"Deflate failed because %@",error); | ||
| 130 | + } | ||
| 131 | + [ASIHTTPRequest removeFileAtPath:filePath error:&error]; | ||
| 132 | + | ||
| 133 | + task = [[[NSTask alloc] init] autorelease]; | ||
| 134 | + [task setLaunchPath:@"/usr/bin/gzip"]; | ||
| 135 | + [task setArguments:[NSArray arrayWithObjects:@"-d",gzippedFilePath,nil]]; | ||
| 136 | + [task launch]; | ||
| 137 | + [task waitUntilExit]; | ||
| 138 | + | ||
| 139 | + inflatedString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; | ||
| 140 | + | ||
| 141 | + success = ([inflatedString isEqualToString:originalString]); | ||
| 142 | + GHAssertTrue(success,@"deflate data is not the same as that generated by gzip"); | ||
| 143 | + | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +@end |
| @@ -47,7 +47,6 @@ | @@ -47,7 +47,6 @@ | ||
| 47 | - (void)testTooMuchRedirection; | 47 | - (void)testTooMuchRedirection; |
| 48 | - (void)testRedirectToNewDomain; | 48 | - (void)testRedirectToNewDomain; |
| 49 | - (void)test303Redirect; | 49 | - (void)test303Redirect; |
| 50 | -- (void)testCompression; | ||
| 51 | - (void)testSubclass; | 50 | - (void)testSubclass; |
| 52 | - (void)testTimeOutWithoutDownloadDelegate; | 51 | - (void)testTimeOutWithoutDownloadDelegate; |
| 53 | - (void)testThrottlingDownloadBandwidth; | 52 | - (void)testThrottlingDownloadBandwidth; |
| @@ -636,21 +636,30 @@ | @@ -636,21 +636,30 @@ | ||
| 636 | - (void)testCompressedResponseDownloadToFile | 636 | - (void)testCompressedResponseDownloadToFile |
| 637 | { | 637 | { |
| 638 | NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile"]; | 638 | NSString *path = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"testfile"]; |
| 639 | - | 639 | + |
| 640 | NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; | 640 | NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; |
| 641 | ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | 641 | ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; |
| 642 | [request setDownloadDestinationPath:path]; | 642 | [request setDownloadDestinationPath:path]; |
| 643 | [request startSynchronous]; | 643 | [request startSynchronous]; |
| 644 | - | 644 | + |
| 645 | NSString *tempPath = [request temporaryFileDownloadPath]; | 645 | NSString *tempPath = [request temporaryFileDownloadPath]; |
| 646 | GHAssertNil(tempPath,@"Failed to clean up temporary download file"); | 646 | GHAssertNil(tempPath,@"Failed to clean up temporary download file"); |
| 647 | - | 647 | + |
| 648 | - //BOOL success = (![[NSFileManager defaultManager] fileExistsAtPath:tempPath]); | 648 | + BOOL success = [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL] isEqualToString:@"This is the expected content for the first string"]; |
| 649 | - //GHAssertTrue(success,@"Failed to remove file from temporary location"); | 649 | + GHAssertTrue(success,@"Failed to download data to a file"); |
| 650 | - | 650 | + |
| 651 | - BOOL success = [[NSString stringWithContentsOfURL:[NSURL fileURLWithPath:path] encoding:NSUTF8StringEncoding error:NULL] isEqualToString:@"This is the expected content for the first string"]; | 651 | + // Now test with inflating the response on the fly |
| 652 | + [ASIHTTPRequest removeFileAtPath:path error:NULL]; | ||
| 653 | + request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | ||
| 654 | + [request setDownloadDestinationPath:path]; | ||
| 655 | + [request setShouldWaitToInflateCompressedResponses:NO]; | ||
| 656 | + [request startSynchronous]; | ||
| 657 | + | ||
| 658 | + tempPath = [request temporaryFileDownloadPath]; | ||
| 659 | + GHAssertNil(tempPath,@"Failed to clean up temporary download file"); | ||
| 660 | + | ||
| 661 | + success = [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL] isEqualToString:@"This is the expected content for the first string"]; | ||
| 652 | GHAssertTrue(success,@"Failed to download data to a file"); | 662 | GHAssertTrue(success,@"Failed to download data to a file"); |
| 653 | - | ||
| 654 | } | 663 | } |
| 655 | 664 | ||
| 656 | 665 | ||
| @@ -1171,18 +1180,25 @@ | @@ -1171,18 +1180,25 @@ | ||
| 1171 | NSString *encoding = [[request responseHeaders] objectForKey:@"Content-Encoding"]; | 1180 | NSString *encoding = [[request responseHeaders] objectForKey:@"Content-Encoding"]; |
| 1172 | BOOL success = (!encoding || [encoding rangeOfString:@"gzip"].location != NSNotFound); | 1181 | BOOL success = (!encoding || [encoding rangeOfString:@"gzip"].location != NSNotFound); |
| 1173 | GHAssertTrue(success,@"Got incorrect request headers from server"); | 1182 | GHAssertTrue(success,@"Got incorrect request headers from server"); |
| 1174 | - | 1183 | + |
| 1175 | success = ([request rawResponseData] == [request responseData]); | 1184 | success = ([request rawResponseData] == [request responseData]); |
| 1176 | GHAssertTrue(success,@"Attempted to uncompress data that was not compressed"); | 1185 | GHAssertTrue(success,@"Attempted to uncompress data that was not compressed"); |
| 1177 | - | 1186 | + |
| 1178 | url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; | 1187 | url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; |
| 1179 | request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | 1188 | request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; |
| 1180 | [request startSynchronous]; | 1189 | [request startSynchronous]; |
| 1181 | success = ([request rawResponseData] != [request responseData]); | 1190 | success = ([request rawResponseData] != [request responseData]); |
| 1182 | GHAssertTrue(success,@"Uncompressed data is the same as compressed data"); | 1191 | GHAssertTrue(success,@"Uncompressed data is the same as compressed data"); |
| 1183 | - | 1192 | + |
| 1184 | success = [[request responseString] isEqualToString:@"This is the expected content for the first string"]; | 1193 | success = [[request responseString] isEqualToString:@"This is the expected content for the first string"]; |
| 1185 | GHAssertTrue(success,@"Failed to decompress data correctly?"); | 1194 | GHAssertTrue(success,@"Failed to decompress data correctly?"); |
| 1195 | + | ||
| 1196 | + url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/first"] autorelease]; | ||
| 1197 | + request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | ||
| 1198 | + [request setShouldWaitToInflateCompressedResponses:NO]; | ||
| 1199 | + [request startSynchronous]; | ||
| 1200 | + success = ([request rawResponseData] == [request responseData]); | ||
| 1201 | + GHAssertTrue(success,@"Failed to populate rawResponseData with the inflated data"); | ||
| 1186 | } | 1202 | } |
| 1187 | 1203 | ||
| 1188 | 1204 | ||
| @@ -1284,54 +1300,26 @@ | @@ -1284,54 +1300,26 @@ | ||
| 1284 | GHAssertTrue(success,@"Failed to use GET on new URL"); | 1300 | GHAssertTrue(success,@"Failed to use GET on new URL"); |
| 1285 | } | 1301 | } |
| 1286 | 1302 | ||
| 1287 | -- (void)testCompression | 1303 | +- (void)testCompressedBody |
| 1288 | { | 1304 | { |
| 1305 | + | ||
| 1289 | NSString *content = @"This is the test content. This is the test content. This is the test content. This is the test content."; | 1306 | NSString *content = @"This is the test content. This is the test content. This is the test content. This is the test content."; |
| 1290 | 1307 | ||
| 1291 | // Test in memory compression / decompression | 1308 | // Test in memory compression / decompression |
| 1292 | NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding]; | 1309 | NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding]; |
| 1293 | - NSData *compressedData = [ASIHTTPRequest compressData:data]; | 1310 | + |
| 1294 | - NSData *uncompressedData = [ASIHTTPRequest uncompressZippedData:compressedData]; | ||
| 1295 | - NSString *newContent = [[[NSString alloc] initWithBytes:[uncompressedData bytes] length:[uncompressedData length] encoding:NSUTF8StringEncoding] autorelease]; | ||
| 1296 | - | ||
| 1297 | - BOOL success = [newContent isEqualToString:content]; | ||
| 1298 | - GHAssertTrue(success,@"Failed compress or decompress the correct data"); | ||
| 1299 | - | ||
| 1300 | - // Test file to file compression / decompression | ||
| 1301 | - | ||
| 1302 | - NSString *basePath = [self filePathForTemporaryTestFiles]; | ||
| 1303 | - NSString *sourcePath = [basePath stringByAppendingPathComponent:@"text.txt"]; | ||
| 1304 | - NSString *destPath = [basePath stringByAppendingPathComponent:@"text.txt.compressed"]; | ||
| 1305 | - NSString *newPath = [basePath stringByAppendingPathComponent:@"text2.txt"]; | ||
| 1306 | - | ||
| 1307 | - [content writeToFile:sourcePath atomically:NO encoding:NSUTF8StringEncoding error:NULL]; | ||
| 1308 | - [ASIHTTPRequest compressDataFromFile:sourcePath toFile:destPath]; | ||
| 1309 | - [ASIHTTPRequest uncompressZippedDataFromFile:destPath toFile:newPath]; | ||
| 1310 | - success = [[NSString stringWithContentsOfFile:newPath encoding:NSUTF8StringEncoding error:NULL] isEqualToString:content]; | ||
| 1311 | - GHAssertTrue(success,@"Failed compress or decompress the correct data"); | ||
| 1312 | 1311 | ||
| 1313 | - // Test compressed body | ||
| 1314 | - // Body is deflated by ASIHTTPRequest, sent, inflated by the server, printed, deflated by mod_deflate, response is inflated by ASIHTTPRequest | ||
| 1315 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/compressed_post_body"]]; | 1312 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/compressed_post_body"]]; |
| 1316 | [request setRequestMethod:@"PUT"]; | 1313 | [request setRequestMethod:@"PUT"]; |
| 1317 | [request setShouldCompressRequestBody:YES]; | 1314 | [request setShouldCompressRequestBody:YES]; |
| 1318 | - [request appendPostData:data]; | ||
| 1319 | - [request startSynchronous]; | ||
| 1320 | - | ||
| 1321 | - success = [[request responseString] isEqualToString:content]; | ||
| 1322 | - GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it"); | ||
| 1323 | - | ||
| 1324 | - request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/compressed_post_body"]]; | ||
| 1325 | - [request setRequestMethod:@"PUT"]; | ||
| 1326 | - [request setShouldCompressRequestBody:YES]; | ||
| 1327 | [request setShouldStreamPostDataFromDisk:YES]; | 1315 | [request setShouldStreamPostDataFromDisk:YES]; |
| 1328 | [request setUploadProgressDelegate:self]; | 1316 | [request setUploadProgressDelegate:self]; |
| 1329 | - [request setPostBodyFilePath:sourcePath]; | 1317 | + [request appendPostData:data]; |
| 1330 | [request startSynchronous]; | 1318 | [request startSynchronous]; |
| 1331 | 1319 | ||
| 1332 | - success = [[request responseString] isEqualToString:content]; | 1320 | + BOOL success = ([[request responseString] isEqualToString:content]); |
| 1333 | - GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it"); | 1321 | + GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it"); |
| 1334 | - | 1322 | + |
| 1335 | } | 1323 | } |
| 1336 | 1324 | ||
| 1337 | 1325 |
This diff was suppressed by a .gitattributes entry.
-
Please register or login to post a comment