Memory issues with NSMutableURLRequest's setHTTPBody: method in iPhoneOS 2.1
October 16th, 2008
The iPhone developers at my work have been tearing their hair out over the last few days, trying to resolve the last few memory issues in our iPhone application before we send it off for approval by Apple. One of the problem areas they’ve noticed is when a photo is uploaded to our service via HTTP POST (both from the camera and otherwise).
Not sure who had the bright idea, but one of the developers decided to try passing in the HTTP body as an NSInputStream (via NSMutableURLRequest’s setHTTPBodyStream:), rather than as NSMutableData (via NSMutableURLRequest’s setHTTPBody:). Magically, this seems to have solved the memory leak issues.
Unfortunately, it created another issue.
You might remember that 3 months ago I wrote an article on how much of a pain in the arse it was to accept HTTP POST requests from user agents specifying a Transfer-Encoding HTTP header value of chunked (resulting in a POST request with no content length). In that article, I proposed a solution using Apache 2 and mod_proxy’s proxy-sendcl option to get things working again. This worked fine for our J2ME clients, but when our iPhone application started blowing chunks at us, our server crapped out with the dreaded 500 error I thought I’d fixed for good:
Chunked Transfer-Encoding is not supported
After whipping out Wireshark, we realised that there was a tiny difference between between what our J2ME client was doing and what the iPhone was doing.
This is the header the J2ME app was sending:
Transfer-Encoding: chunked
And this is the header the iPhone was sending:
Transfer-Encoding: Chunked
As much as I’d like to think the different casing of chunked and Chunked wouldn’t affect the behaviour of mod_proxy, it seems it does. Fortunately, we can work around this problem too by using Apache’s mod_headers module. This allows us to do the following:
RequestHeader edit Transfer-Encoding Chunked chunked early
When combined with the solution from my previous article, this leaves us with the following complete solution:
ProxyRequests Off
<Proxy http://localhost:81>
Order deny,allow
Allow from all
</Proxy>
Listen 80
<VirtualHost *:80>
RequestHeader edit Transfer-Encoding Chunked chunked early
SetEnv proxy-sendcl 1
ProxyPass / http://localhost:81/
ProxyPassReverse / http://localhost:81/
ProxyPreserveHost On
ProxyVia Full
<Directory proxy:*>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Listen 81
<VirtualHost *:81>
ServerName ooboontoo
DocumentRoot /path/to/my/rails/root/public
RailsEnv development
</VirtualHost>
Hopefully this saves someone a little bit of time :).
November 5th, 2008 at 01:09 AM
You really need all those “Allow from all”?
I was under the impression that the “allow from all” in the <proxy> should actually be in the <virtualhost>.
Either that or I need some securing in my Debian’s Apache…
November 15th, 2008 at 07:23 PM
Brilliant!
Thank you, Atnan!
Apparently not only iPhone sends “_C_hunked” encoding, but also os x 10.5.5, when sending big files via webdav.
November 28th, 2008 at 09:50 PM
@Luis Bruno: I’m pretty sure I could lock down my Apache configuration a little bit better than it is at present…I’ll look into removing those “allow from all” directives.
@Almi: Probably means I should file it as a bug in radar, since it’s much easier for Apple to roll out a change to CoreFoundation than it is for people to roll out upgrades to Apache’s mod_proxy.
February 11th, 2009 at 07:14 AM
@Nathan: Thanks for the solution, I had this exact problem today. Couldn’t find my leak anywhere and it was driving me nuts.
August 10th, 2009 at 10:59 AM
The problem I have is related to this – but I’m not able to solve it with the above methods. I have a DAV share up and fully accessible but Finder is the only client that can’t access it.
I keep getting an error such as “The Finder cannot complete the operation because some data in ”.....” could not be read or written. (Error code -36)” and leaves files 0 bytes in length in the server.
Any insights as to what might be the cause?
Thanks!
October 18th, 2009 at 11:30 PM
Thanks! This did solve a big headache for me while trying to post data. But I also have a web service method which I use to poll the server and find out how much of the data has been uploaded. The server gives me the number of bytes uploaded, but I have to know the length of the file and set it in the Content-Length header.
Any idea how I can do that without initializing an NSData object?
November 21st, 2009 at 06:28 AM
Has Apple fixed this?
I am trying to write my first Iphone networking app ever . . .