Skip to content
Browse files

* Fix an issue with ASIDataCompressor that would have caused it to fa…

…il to deflate files larger than 256KB

* Move requestFinished: back onto the request thread as running on the main thread caused breakage in subclasses (including S3)
* Tweaks to ASIDataDecompressor to make it match the new behaviour of ASIDataCompressor
* Improve tests for gzip
  • Loading branch information...
1 parent 307716a commit 65912e26cf17963c042dba8494abb074aec8a3f2 @pokeb committed Dec 13, 2010
View
3 Classes/ASIDataCompressor.h
@@ -22,7 +22,8 @@
+ (id)compressor;
// Compress the passed chunk of data
-- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err;
+// Passing YES for shouldFinish will finalize the deflated data - you must pass YES when you are on the last chunk of data
+- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish;
// Convenience method - pass it some data, and you'll get deflated data back
+ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err;
View
33 Classes/ASIDataCompressor.m
@@ -66,7 +66,7 @@ - (NSError *)closeStream
return nil;
}
-- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err
+- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish
{
if (length == 0) return nil;
@@ -91,17 +91,14 @@ - (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSErro
zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready;
zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready));
-
- status = deflate(&zStream, Z_FINISH);
+ status = deflate(&zStream, shouldFinish ? Z_FINISH : Z_NO_FLUSH);
if (status == Z_STREAM_END) {
- theError = [self closeStream];
break;
} else if (status != Z_OK) {
if (err) {
*err = [[self class] deflateErrorWithCode:status];
}
- [self closeStream];
return NO;
}
}
@@ -122,7 +119,7 @@ - (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSErro
+ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err
{
NSError *theError = nil;
- NSData *outputData = [[ASIDataCompressor compressor] compressBytes:(Bytef *)[uncompressedData bytes] length:[uncompressedData length] error:&theError];
+ NSData *outputData = [[ASIDataCompressor compressor] compressBytes:(Bytef *)[uncompressedData bytes] length:[uncompressedData length] error:&theError shouldFinish:YES];
if (theError) {
if (err) {
*err = theError;
@@ -170,23 +167,27 @@ + (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinati
// Read some data from the file
readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE];
-
// Make sure nothing went wrong
if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
- [compressor closeStream];
if (err) {
*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]];
}
+ [compressor closeStream];
return NO;
}
+ // Have we reached the end of the input data?
+ if (!readLength) {
+ break;
+ }
- // Attempt to inflate the chunk of data
- outputData = [compressor compressBytes:inputData length:readLength error:&theError];
+ // Attempt to deflate the chunk of data
+ outputData = [compressor compressBytes:inputData length:readLength error:&theError shouldFinish:readLength < DATA_CHUNK_SIZE ];
if (theError) {
if (err) {
*err = theError;
}
+ [compressor closeStream];
return NO;
}
@@ -195,17 +196,25 @@ + (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinati
// Make sure nothing went wrong
if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
- [compressor closeStream];
if (err) {
*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]];
}
+ [compressor closeStream];
return NO;
}
}
-
[inputStream close];
[outputStream close];
+
+ NSError *error = [compressor closeStream];
+ if (error) {
+ if (err) {
+ *err = error;
+ }
+ return NO;
+ }
+
return YES;
}
View
22 Classes/ASIDataDecompressor.m
@@ -89,16 +89,14 @@ - (NSData *)uncompressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSEr
zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready;
zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready));
- status = inflate(&zStream, Z_SYNC_FLUSH);
+ status = inflate(&zStream, Z_NO_FLUSH);
if (status == Z_STREAM_END) {
- theError = [self closeStream];
break;
} else if (status != Z_OK) {
if (err) {
*err = [[self class] inflateErrorWithCode:status];
}
- [self closeStream];
return nil;
}
}
@@ -169,19 +167,24 @@ + (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destina
// Make sure nothing went wrong
if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
- [decompressor closeStream];
if (err) {
*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]];
}
+ [decompressor closeStream];
return NO;
}
+ // Have we reached the end of the input data?
+ if (!readLength) {
+ break;
+ }
// Attempt to inflate the chunk of data
outputData = [decompressor uncompressBytes:inputData length:readLength error:&theError];
if (theError) {
if (err) {
*err = theError;
}
+ [decompressor closeStream];
return NO;
}
@@ -190,17 +193,26 @@ + (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destina
// Make sure nothing went wrong
if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
- [decompressor closeStream];
if (err) {
*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]];
}
+ [decompressor closeStream];
return NO;
}
}
[inputStream close];
[outputStream close];
+
+ NSError *error = [decompressor closeStream];
+ if (error) {
+ if (err) {
+ *err = error;
+ }
+ return NO;
+ }
+
return YES;
}
View
16 Classes/ASIHTTPRequest.m
@@ -24,7 +24,7 @@
#import "ASIDataCompressor.h"
// Automatically set on build
-NSString *ASIHTTPRequestVersion = @"v1.8-21 2010-12-04";
+NSString *ASIHTTPRequestVersion = @"v1.8-22 2010-12-13";
NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
@@ -1930,7 +1930,6 @@ - (void)requestWillRedirectToURL:(NSURL *)newURL
}
}
-/* ALWAYS CALLED ON MAIN THREAD! */
// Subclasses might override this method to process the result in the same thread
// If you do this, don't forget to call [super requestFinished] to let the queue / delegate know we're done
- (void)requestFinished
@@ -1941,18 +1940,23 @@ - (void)requestFinished
if ([self error] || [self mainRequest]) {
return;
}
+ [self performSelectorOnMainThread:@selector(reportFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
+}
+/* ALWAYS CALLED ON MAIN THREAD! */
+- (void)reportFinished
+{
if (delegate && [delegate respondsToSelector:didFinishSelector]) {
[delegate performSelector:didFinishSelector withObject:self];
}
if (queue && [queue respondsToSelector:@selector(requestFinished:)]) {
[queue performSelector:@selector(requestFinished:) withObject:self];
}
- #if NS_BLOCKS_AVAILABLE
+#if NS_BLOCKS_AVAILABLE
if(completionBlock){
completionBlock();
}
- #endif
+#endif
}
/* ALWAYS CALLED ON MAIN THREAD! */
@@ -3245,7 +3249,7 @@ - (void)handleStreamComplete
if (fileError) {
[self failWithError:fileError];
} else {
- [self performSelectorOnMainThread:@selector(requestFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ [self requestFinished];
}
[self markAsFinished];
@@ -3344,7 +3348,7 @@ - (void)useDataFromCache
}
[theRequest updateProgressIndicators];
- [theRequest performSelectorOnMainThread:@selector(requestFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
+ [theRequest requestFinished];
[theRequest markAsFinished];
if ([self mainRequest]) {
[self markAsFinished];
View
35 Classes/Tests/ASIDataCompressorTests.m
@@ -14,17 +14,25 @@
@implementation ASIDataCompressorTests
+- (void)setUp
+{
+ NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
+
+ // Download a 1.7MB text file
+ NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"];
+ if (![fileManager fileExistsAtPath:filePath] || [[[fileManager attributesOfItemAtPath:filePath error:NULL] objectForKey:NSFileSize] unsignedLongLongValue] < 1693961) {
+ ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_hound_of_the_baskervilles.text"]];
+ [request setDownloadDestinationPath:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"]];
+ [request startSynchronous];
+ }
+}
- (void)testInflateData
{
- // Test in-memory inflate using uncompressData:error:
- NSUInteger i;
-
- NSString *originalString = [NSString string];
- for (i=0; i<1000; i++) {
- originalString = [originalString stringByAppendingFormat:@"This is line %i\r\n",i];
- }
+
+ NSString *originalString = [NSString stringWithContentsOfFile:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"] encoding:NSUTF8StringEncoding error:NULL];
+ // Test in-memory inflate using uncompressData:error:
NSError *error = nil;
NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"];
NSString *gzippedFilePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt.gz"];
@@ -79,13 +87,10 @@ - (void)testInflateData
- (void)testDeflateData
{
- // Test in-memory deflate using compressData:error:
- NSUInteger i;
+
+ NSString *originalString = [NSString stringWithContentsOfFile:[[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"story.txt"] encoding:NSUTF8StringEncoding error:NULL];
- NSString *originalString = [NSString string];
- for (i=0; i<1000; i++) {
- originalString = [originalString stringByAppendingFormat:@"This is line %i\r\n",i];
- }
+ // Test in-memory deflate using compressData:error:
NSError *error = nil;
NSData *deflatedData = [ASIDataCompressor compressData:[originalString dataUsingEncoding:NSUTF8StringEncoding] error:&error];
if (error) {
@@ -104,6 +109,10 @@ - (void)testDeflateData
}
NSString *filePath = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"uncompressed_file.txt"];
+ [ASIHTTPRequest removeFileAtPath:filePath error:&error];
+ if (error) {
+ GHFail(@"Failed to remove old file, cannot proceed with test");
+ }
NSTask *task = [[[NSTask alloc] init] autorelease];
[task setLaunchPath:@"/usr/bin/gzip"];

0 comments on commit 65912e2

Please sign in to comment.
Something went wrong with that request. Please try again.