1

I have the following code:

NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://some-example-domain.com/api"]
                                          cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
                                      timeoutInterval:30.0];

[NSURLConnection sendAsynchronousRequest:theRequest
                                   queue: [NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                           if (!error && data) {
                               // success!! - no errors and data returned
                               NSLog(@"success");
                           } else {
                               // error!! - something whent wrong
                               NSLog(@"failure: %@", [error localizedDescription]);
                           }

                       }
 ];

which works well - except for odd occasions when the server only sends part of the desired response (eg. half of a JSON response from an API) (it still is 'successful' according to my 'if' statement)

Is there some way using this block based method that I can check to see that the data received is complete??

I have tried looking into the NSURLResponse *response - but can't quite figure out how to use it (or, if it is indeed useful in this scenario). Any ideas on how to test for 'partially received' data returned by the block?

So Over It
  • 3,668
  • 3
  • 35
  • 46
  • I think partial data would be a problem on the server. – woz Apr 24 '13 at 12:07
  • @woz - yes, server problem - but I want to test for a partially received response from the dodgy server before trying to parse the data. – So Over It Apr 24 '13 at 12:08
  • Couldn't you do that right in `completionHandler`? – woz Apr 24 '13 at 12:10
  • @woz - my current test in completionHandler can be seen in the code - ie. checking no error response and checking data is returned. However, if only partial data is returned - there is no error and 'some' data - so it passes the check. Any ideas for testing for incomplete data in `completionHandler`? – So Over It Apr 24 '13 at 12:12

1 Answers1

1

There are potentially two different failure modes for this query that aren't handled, and you'll need to check for them separately:

  • "successful" HTTP connection, but an malformed response
  • "successful" HTTP connection, but status code indicates a problem on the server

In the case of NSURLConnection, the error is only set when the connection fails, not when a problem is reported from the server (for example: a 404 error or a 330 response).

Generally, when you are talking to an HTTP or HTTPS service, you'll need to check the -statusCode in the NSURLResponse, which in the case of these services will actually be an NSHTTPURLResponse. If there's an error on the server, such as 408 for request timed out on the server, you need to handle that separately from a connection failure (which will cause the error to be set).

Even if you get back a nice [response statusCode] == 200, you will likely still want to check for malformed responses, which you'll need to do when you parse the data that comes back. This is a less likely scenario, but if the server is flakey, you may get a partial response or an encoding failure.

gaige
  • 17,263
  • 6
  • 57
  • 68
  • Thanks for the info of NSHTTPURLResponse. `statusCode` is not available to `NSURLResponse`, but if I cast it then it's available `NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;`. As you suggested, the statusCode is still `==200` even for a partial response. I also noted the `expectedContentLength` method. I did have hopes of comparing this to the `[data length]`, however it only ever seems to give me `-1` as a response. Checking on the http response headers from the server, I can clearly see `Content-Length:2856`. Have you had any experience with `expectedContentLength`? – So Over It Apr 24 '13 at 14:26
  • I should have mentioned the cast explicitly. I did allude to it above. As for your server sending you an incomplete response with a `200` and no `expectedContentLength`, see the last paragraph about verifying your data in the parser even when the server sends back a `200`. The response doesn't have `expectedContentLength` because the server isn't setting the `Content-Length:` field in the response. Basically, you are going to have to build recognizing bad responses into the parser you use when you receive the data (which is a good idea in any case). – gaige Apr 24 '13 at 14:30
  • I have had success with `expectedContentLength`, but only when the server knows the length before the transaction starts streaming data, which is mostly when files are being sent. For scripts and json, it's a mixed bag based on how much of the content the `http` process knows about before it starts streaming data to the client. – gaige Apr 24 '13 at 14:32
  • It is in fact a JSON response that is coming from this flakey server. If I look at the response headers in Chrome Developer Tools, - I can see the header `Content-Length:2856`. Does this mean is should be available to `expectedContentLength`?? or - does `expectedContentLength` get pulled from somewhere else?? Even when the JSON does successfully load, `expectedContentLength` returns `-1`. – So Over It Apr 24 '13 at 14:39
  • In my experience, the two go hand-in-hand, the `Content-Length` header and the `expectedContentLength`. The only way to truly know is a network dump between your app and the server, as the failure modes and request headers might vary slightly between Chrome and `NSURLConnection`. Either way, even if you could depend on the `expectedContentLength`, are you sure you would want to? A flakey server might return invalid JSON in a valid response packet with valid length (such as an error message in plain text). I would suggest hardening the JSON parser and checking errors from it. – gaige Apr 24 '13 at 14:45
  • I totally agree about verifying valid JSON before sending it to the parser (`NSJSONSerialization` is particularly sensitive!!). I guess I was hoping to use this in a class more generally as another level of protection. That aside - do you have a URL which you have successfully used `expectedContentLength` so I can test it out and see if it is something I'm doing wrong on my end. Everything I've tried so far returns `-1` which makes me a little suspicious it could be me! – So Over It Apr 24 '13 at 14:53
  • Hmm. `expectedContentLength` does seem to work for images. I give up. I really appreciate your thoughts and input. I'll have a coffee and just beef up checking in other areas as you suggested. – So Over It Apr 24 '13 at 15:01