5

I'm using NSURLConnection to make an async HTTPS POST to our webservice to upload a jpg. It works fine most of the time.

But when the post body is between 196609 and 196868 bytes (inclusive), the connection hangs after the file has been uploaded 100% (didSendBodyData tells me that it's 100% sent). Then after 5 minutes, the connection times out with the message "The network connection was lost." Larger and smaller filesizes work. I've tested this by capping the filesize of the file that I add to the post body.

The content length is getting set correctly. See the code below.

The upload looks fine when I upload to netcat via HTTP. The entire body of the message gets through. But I suspect something's going wrong because of the SSL encryption, like it's buffering an SSL encrypted block frame.

Is there a bug in the iPhone's SSL code that makes it not upload the entire post body even though it's reporting that it is, or something else?

Here's the section where I set the content length and append the file to the multi-part POST body. You can see i'm capping the filesize manually, to test.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nsurl];
[request setHTTPMethod:@"POST"];
[request setTimeoutInterval:1];
[request addValue:@"close" forHTTPHeaderField: @"Connection"];

// Add headers and POST information
NSLog( @"Posting file." );
NSString *stringBoundary = [NSString stringWithString:@"0xKhTmLbOuNdArY"];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",stringBoundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];
NSMutableData *postBody = [NSMutableData data];

...

[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithString:@"Content-Disposition: form-data; name=\"uploadFile\"; filename=\"upload.jpg\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithString:@"Content-Type: image/jpg\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
NSLog( @"file size...%d", [uploadFile length] );
//[postBody appendData:uploadFile];
[postBody appendData:[NSData dataWithBytes: [uploadFile bytes] length: 196099]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
[request setValue:[NSString stringWithFormat:@"%d", [postBody length]] forHTTPHeaderField:@"Content-Length"];
NSLog(@"Uploaded POST size: %d", [postBody length] );
tom
  • 81
  • 1
  • 5

3 Answers3

3

Ah, I've found what the issue is. Connection: Keep-alive is being set, even though I set connection: close. How do I override this?? It looks like you can't!?

tom
  • 81
  • 1
  • 5
1

I have following code that is tested for 30-40 MB of files, and it never hangs. It looks almost similar to your code, however are you sure that its not your server side scrip that may be waiting for something?

url = [NSURL URLWithString:u];

NSData* d = [self data];

request = [NSMutableURLRequest requestWithURL:url];
[request setTimeoutInterval: 2000 ];
[request setHTTPMethod:@"POST"];
[request setValue:
 [NSString stringWithFormat:@"multipart/form-data; boundary=%@", BOUNDRY]
forHTTPHeaderField:@"Content-Type"];

NSMutableData *postData =
[NSMutableData dataWithCapacity:[d length] + 512];
[postData appendData:
 [[NSString stringWithFormat:@"--%@\r\n", BOUNDRY] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:
 [[NSString stringWithFormat:
   @"Content-Disposition: form-data; name=\"%@\"; filename=\"file.bin\"\r\n\r\n", FORM_FLE_INPUT]
  dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:d];
[postData appendData:
 [[NSString stringWithFormat:@"\r\n--%@--\r\n", BOUNDRY] dataUsingEncoding:NSUTF8StringEncoding]];

[request setHTTPBody:postData];
Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • I thought my code never hanged either. Most photos work. But then i took one photo that got stuck. I found it never got a response back when the photo was that size. So then i tested the size up and down, and found those limits. i haven't found any other region that causes this issue. Yes, it sounds like our server is waiting for the full request before giving anything back, and thinks it's not getting the full request. It's really strange. – tom Dec 08 '10 at 07:24
  • Well one condition is that the Boundry including dashes (--) and new line characters, must be unique and should not appear inside the data. – Akash Kava Dec 08 '10 at 08:34
1

I think you can try to remove this line of codes.

[request addValue:@"close" forHTTPHeaderField: @"Connection"];

It will tell the http server to close this connection. By the RFC 2616 (HTTP/1.1), servers and clients should maintain persistent connection.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118
  • 2
    Actually, it turns out that's the exact problem. the iPhone is ignoring that close header, and setting it to keep-alive. with keep-alive, it breaks our server. with close, it works. how can i override the nsurlconnection to accept my close param? – tom Dec 08 '10 at 08:27
  • I am reading the rfc2616 recently. And the document said, this headerfield "Connection" with "close" will close the connect. Maybe you can send a request only with this "close" header when you finish uploading. – AechoLiu Dec 08 '10 at 12:24
  • I remember some posts in SO said the setHTTPBody will modify some connection parameters. For example, after setHTTPBody, the timeout will become 75 seconds. Maybe you can set this headerField after you call setHTTPBody. – AechoLiu Dec 08 '10 at 12:26
  • No luck, it looks like you can't override this. What thu. – tom Dec 09 '10 at 06:27