Ben Copsey

Added gzip for request bodies

Comments / code tweaks
@@ -44,12 +44,15 @@ extern NSString* const NetworkRequestErrorDomain; @@ -44,12 +44,15 @@ extern NSString* const NetworkRequestErrorDomain;
44 // A queue delegate that should *ALSO* be notified of delegate message (used by ASINetworkQueue) 44 // A queue delegate that should *ALSO* be notified of delegate message (used by ASINetworkQueue)
45 id queue; 45 id queue;
46 46
47 - // HTTP method to use (GET / POST / PUT / DELETE). Defaults to GET 47 + // HTTP method to use (GET / POST / PUT / DELETE / HEAD). Defaults to GET
48 NSString *requestMethod; 48 NSString *requestMethod;
49 49
50 // Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false) 50 // Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false)
51 NSMutableData *postBody; 51 NSMutableData *postBody;
52 52
  53 + // gzipped request body used when shouldCompressRequestBody is YES
  54 + NSData *compressedPostBody;
  55 +
53 // When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads) 56 // When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads)
54 // Automatically set to true in ASIFormDataRequests when using setFile:forKey: 57 // Automatically set to true in ASIFormDataRequests when using setFile:forKey:
55 BOOL shouldStreamPostDataFromDisk; 58 BOOL shouldStreamPostDataFromDisk;
@@ -58,6 +61,9 @@ extern NSString* const NetworkRequestErrorDomain; @@ -58,6 +61,9 @@ extern NSString* const NetworkRequestErrorDomain;
58 // You can set this yourself - useful if you want to PUT a file from local disk 61 // You can set this yourself - useful if you want to PUT a file from local disk
59 NSString *postBodyFilePath; 62 NSString *postBodyFilePath;
60 63
  64 + // Path to a temporary file used to store a deflated post body (when shouldCompressPostBody is YES)
  65 + NSString *compressedPostBodyFilePath;
  66 +
61 // Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request) 67 // Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request)
62 BOOL didCreateTemporaryPostDataFile; 68 BOOL didCreateTemporaryPostDataFile;
63 69
@@ -91,6 +97,10 @@ extern NSString* const NetworkRequestErrorDomain; @@ -91,6 +97,10 @@ extern NSString* const NetworkRequestErrorDomain;
91 // If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true. 97 // If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true.
92 BOOL allowCompressedResponse; 98 BOOL allowCompressedResponse;
93 99
  100 + // If shouldCompressRequestBody is true, the request body will be gzipped. Default is false.
  101 + // You will probably need to enable this feature on your webserver to make this work. Tested with apache only.
  102 + BOOL shouldCompressRequestBody;
  103 +
94 // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location 104 // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location
95 // If downloadDestinationPath is not set, download data will be stored in memory 105 // If downloadDestinationPath is not set, download data will be stored in memory
96 NSString *downloadDestinationPath; 106 NSString *downloadDestinationPath;
@@ -203,11 +213,18 @@ extern NSString* const NetworkRequestErrorDomain; @@ -203,11 +213,18 @@ extern NSString* const NetworkRequestErrorDomain;
203 // Prevents the body of the post being built more than once (largely for subclasses) 213 // Prevents the body of the post being built more than once (largely for subclasses)
204 BOOL haveBuiltPostBody; 214 BOOL haveBuiltPostBody;
205 215
  216 + // Used internally, may reflect the size of the internal used by CFNetwork
  217 + // POST / PUT operations with body sizes greater than uploadBufferSize will not timeout unless more than uploadBufferSize bytes have been sent
  218 + // Likely to be 32KB on iPhone 3.0, 128KB on Mac OS X Leopard and iPhone 2.2.x
206 unsigned long long uploadBufferSize; 219 unsigned long long uploadBufferSize;
207 220
  221 + // Text encoding for responses that do not send a Content-Type with a charset value. Defaults to NSISOLatin1StringEncoding
208 NSStringEncoding defaultResponseEncoding; 222 NSStringEncoding defaultResponseEncoding;
  223 +
  224 + // The text encoding of the response, will be defaultResponseEncoding if the server didn't specify. Can't be set.
209 NSStringEncoding responseEncoding; 225 NSStringEncoding responseEncoding;
210 226
  227 + // Tells ASIHTTPRequest not to delete partial downloads, and allows it to use an existing file to resume a download. Defaults to NO.
211 BOOL allowResumeForFileDownloads; 228 BOOL allowResumeForFileDownloads;
212 229
213 // Custom user information assosiated with the request 230 // Custom user information assosiated with the request
@@ -360,7 +377,7 @@ extern NSString* const NetworkRequestErrorDomain; @@ -360,7 +377,7 @@ extern NSString* const NetworkRequestErrorDomain;
360 // Dump all session data (authentication and cookies) 377 // Dump all session data (authentication and cookies)
361 + (void)clearSession; 378 + (void)clearSession;
362 379
363 -#pragma mark gzip compression 380 +#pragma mark gzip decompression
364 381
365 // Uncompress gzipped data with zlib 382 // Uncompress gzipped data with zlib
366 + (NSData *)uncompressZippedData:(NSData*)compressedData; 383 + (NSData *)uncompressZippedData:(NSData*)compressedData;
@@ -369,6 +386,15 @@ extern NSString* const NetworkRequestErrorDomain; @@ -369,6 +386,15 @@ extern NSString* const NetworkRequestErrorDomain;
369 + (int)uncompressZippedDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath; 386 + (int)uncompressZippedDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath;
370 + (int)uncompressZippedDataFromSource:(FILE *)source toDestination:(FILE *)dest; 387 + (int)uncompressZippedDataFromSource:(FILE *)source toDestination:(FILE *)dest;
371 388
  389 +#pragma mark gzip compression
  390 +
  391 +// Compress data with gzip using zlib
  392 ++ (NSData *)compressData:(NSData*)uncompressedData;
  393 +
  394 +// gzip compress data from a file, saving to another file, used for uploading when shouldCompressRequestBody is true
  395 ++ (int)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath;
  396 ++ (int)compressDataFromSource:(FILE *)source toDestination:(FILE *)dest;
  397 +
372 @property (retain) NSString *username; 398 @property (retain) NSString *username;
373 @property (retain) NSString *password; 399 @property (retain) NSString *password;
374 @property (retain) NSString *domain; 400 @property (retain) NSString *domain;
@@ -417,4 +443,5 @@ extern NSString* const NetworkRequestErrorDomain; @@ -417,4 +443,5 @@ extern NSString* const NetworkRequestErrorDomain;
417 @property (assign, readonly) unsigned long long partialDownloadSize; 443 @property (assign, readonly) unsigned long long partialDownloadSize;
418 @property (assign) BOOL shouldRedirect; 444 @property (assign) BOOL shouldRedirect;
419 @property (assign) BOOL validatesSecureCertificate; 445 @property (assign) BOOL validatesSecureCertificate;
  446 +@property (assign) BOOL shouldCompressRequestBody;
420 @end 447 @end
This diff is collapsed. Click to expand it.
@@ -37,5 +37,6 @@ @@ -37,5 +37,6 @@
37 - (void)testTooMuchRedirection; 37 - (void)testTooMuchRedirection;
38 - (void)testRedirectToNewDomain; 38 - (void)testRedirectToNewDomain;
39 - (void)test303Redirect; 39 - (void)test303Redirect;
  40 +- (void)testCompression;
40 41
41 @end 42 @end
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
10 #import "ASIHTTPRequest.h" 10 #import "ASIHTTPRequest.h"
11 #import "ASINSStringAdditions.h" 11 #import "ASINSStringAdditions.h"
12 #import "ASINetworkQueue.h" 12 #import "ASINetworkQueue.h"
13 - 13 +#import "ASIFormDataRequest.h"
14 14
15 @implementation ASIHTTPRequestTests 15 @implementation ASIHTTPRequestTests
16 16
@@ -680,6 +680,58 @@ @@ -680,6 +680,58 @@
680 GHAssertTrue(success,@"Failed to use GET on new URL"); 680 GHAssertTrue(success,@"Failed to use GET on new URL");
681 } 681 }
682 682
  683 +- (void)testCompression
  684 +{
  685 + NSString *content = @"This is the test content. This is the test content. This is the test content. This is the test content.";
  686 +
  687 + // Test in memory compression / decompression
  688 + NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
  689 + NSData *compressedData = [ASIHTTPRequest compressData:data];
  690 + NSData *uncompressedData = [ASIHTTPRequest uncompressZippedData:compressedData];
  691 + NSString *newContent = [[[NSString alloc] initWithBytes:[uncompressedData bytes] length:[uncompressedData length] encoding:NSUTF8StringEncoding] autorelease];
  692 +
  693 + BOOL success = [newContent isEqualToString:content];
  694 + GHAssertTrue(success,@"Failed compress or decompress the correct data");
  695 +
  696 + // Test file to file compression / decompression
  697 + NSString *basePath = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];
  698 + NSString *sourcePath = [basePath stringByAppendingPathComponent:@"text.txt"];
  699 + NSString *destPath = [basePath stringByAppendingPathComponent:@"text.txt.compressed"];
  700 + NSString *newPath = [basePath stringByAppendingPathComponent:@"text2.txt"];
  701 +
  702 + [content writeToFile:sourcePath atomically:NO encoding:NSUTF8StringEncoding error:NULL];
  703 + [ASIHTTPRequest compressDataFromFile:sourcePath toFile:destPath];
  704 + [ASIHTTPRequest uncompressZippedDataFromFile:destPath toFile:newPath];
  705 + success = [[NSString stringWithContentsOfFile:newPath encoding:NSUTF8StringEncoding error:NULL] isEqualToString:content];
  706 + GHAssertTrue(success,@"Failed compress or decompress the correct data");
  707 +
  708 + // Test compressed body
  709 + // Body is deflated by ASIHTTPRequest, sent, inflated by the server, printed, deflated by mod_deflate, response is inflated by ASIHTTPRequest
  710 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://asi/ASIHTTPRequest/tests/compressed_post_body"]];
  711 + [request setRequestMethod:@"PUT"];
  712 + [request setShouldCompressRequestBody:YES];
  713 + [request appendPostData:data];
  714 + [request start];
  715 +
  716 + success = [[request responseString] isEqualToString:content];
  717 + GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it");
  718 +
  719 +
  720 + request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/compressed_post_body"]];
  721 + [request setRequestMethod:@"PUT"];
  722 + [request setShouldCompressRequestBody:YES];
  723 + [request setShouldStreamPostDataFromDisk:YES];
  724 + [request setUploadProgressDelegate:self];
  725 + [request setPostBodyFilePath:sourcePath];
  726 +
  727 + [request start];
  728 +
  729 + success = [[request responseString] isEqualToString:content];
  730 + GHAssertTrue(success,@"Failed to compress the body, or server failed to decompress it");
  731 +
  732 +
  733 +
  734 +}
683 735
684 736
685 @end 737 @end