NSURLConnection gzip magic

Sun, 28. Jun 2009

Categories: en development Tags: Cocoa compression gzip htaccess iPhone NSURLConnection Objective C wget

For quite some time I ranted about not being able to use compressed network communcation out-of-the-box on the iPhone.

Despite being undocumented (or I just overlooked the hint), NSURLConnection does gzip decompression transparently!

That’s how to use it:

  1. ensure the webserver sends gzipped content, use e.g. wget to verify:

    1$ wget --header='Accept-Encoding: gzip' \
    2--server-response http://example.com/demo.xmlz

    It should (in case of a gzipped xml document) look like

    1HTTP/1.1 200 OK
    2...
    3Content-Type: text/xml
    4Content-Encoding: gzip
    5...
  2. If your webserver doesn’t support transparent compression, you can still upload gzipped content and tell the server to send the correct response headers by setting up a .htaccess file:

 1...
 2AddType text/xml .xml .xmlz
 3AddEncoding gzip .gz .xmlz
 4
 5# inspired by
 6# http://betterexplained.com/articles/
 7#  how-to-optimize-your-site-with-gzip-compression/
 8# compress all text & html:
 9AddOutputFilterByType DEFLATE text/html text/plain text/xml
10...
  1. to verify from within your app, log the response header in the NSURLConnection callbacks:
 1- (void)connection:(NSURLConnection *)connection
 2  didReceiveResponse:(NSURLResponse *)response
 3{
 4    NSLog(@"didReceiveResponse %@: %@", [response URL],
 5      [(NSHTTPURLResponse*)response allHeaderFields]);
 6    buffer = [[NSMutableData dataWithCapacity:1024*1024] retain];
 7}
 8
 9- (void)connection:(NSURLConnection *)connection
10  didReceiveData:(NSData *)dat
11{
12    [buffer appendData:dat];
13}
14
15- (void)connectionDidFinishLoading:(NSURLConnection *)connection
16{
17    NSLog(@"connectionDidFinishLoading %d bytes", [buffer length]);
18    [buffer release];
19}

It should look like

 12009-06-28 14:31:09.722 DemoApp[3981:20b] \
 2  didReceiveResponse http://example.com/demo.xmlz: {
 3...
 4"Content-Type" = "text/xml";
 5"Content-Encoding" = gzip;
 6"Content-Length" = 123042;
 7}
 8...
 92009-06-28 14:31:11.619 DemoApp[3981:20b] \
10  connectionDidFinishLoading 602979 bytes

As you can see we received way more bytes than went over the wire.

P.S.: It seems not to be necessary setting the request header yourself:

1NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
2    cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
3    timeoutInterval:60.0];
4// set explicitly:
5[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];