NSURLConnection - Get HTTP status code on request failure - objective-c

Is there any way I can get HTTP status code after NSURLConnection has failed to request? According to the documentation, I can get the HTTP status code from -connection:didReceiveResponse:. However, in case the request fails, only -connection:didFailedWithError: is called, and by far I haven't found any method to get status code inside this method.
p/s: I'm working on a VERY old, complicated project, and it's very error-prone, so I cannot use AFNetworking or NSURLSession.

As mentioned in the Apple docs for NSURLConnectionDataDelegate Protocol the connection:didReceiveResponse: is called only when sufficient data has been received to create the NSURLResponse object.
While the NSURLResponse object may not be available until later, it is possible for a connection to receive partial data by using the connection:didReceiveData: method. Note that this seems to exclude the HTTP headers and only contain the "data" component of the response. There may be a way to trigger the didReceiveResponse: early if the server knows ahead of time how much data will be transmitted although I am no familiar enough with this to confirm.
Be aware that the response data typically comes in blocks of approximately 1KiB of data each, depending on various network settings such as MTU and how often the server "flush" its output pipe.
Good luck!

Related

How to perform non-asynchronous AFHTTPRequestOperation with AFNetworking?

Having followed the Ray Wenderlich Parse + Core Data + AFNetworking tutorial, it seems that he pulls JSON from Parse by creating an AFHTTPRequestOperation object using an NSMutableURLRequest and then adds that operation to a queue via enqueueBatchOfHTTPRequestOperations:(NSArray *)operations progressBlock:(void (^__strong)(NSUInteger, NSUInteger))progressBlock completionBlock:(void (^__strong)(NSArray *__strong))completionBlock.
My question is: Is it possible to process an AFHTTPRequestOperation immediately (rather than adding it to queue) so that the subsequent line of code processes once the request has been fully processed? Is this bad form?
What I am actually trying to do: If a requested object does not exist locally, then attempt to download it from Parse. If that request fails, assume (for now) that the object does not exist on Parse. If that request succeeds, then the user can now start using that object locally.
Advanced apologies if this is a dumb question as I am new to AFNetworking and any kind of data synchronization...also the few iOS developer friends I have have never used AFNetworking / Parse / any kind of data synchronization.
Most people consider it bad form, although I understand wanting to try it when you are getting started. I would advise never doing it in code you are submitting to the App Store. Some discussion about the same thing here: Synchronous AFNetworking calls

Objective-C iOS NSURLConnection statusCode 400 No body returned

not really sure where to start here other then to dive into CF (I REALLY don't want to do that) but....
I have an NSURLConnection signing OAuth2 requests to an ASP.NET WebAPI Resource Server, this resource server returns JSON body AND statusCode 400. I have yet to find a way to parse the data from the response when it returns code 400.
Does anyone here have any ideas? I would prefer to keep using NSURLConnection as this is only an OAuth2 consumer class. My other code is using restkit, but I do not want the OAuth2 end to require said library.
The process to parse data from a request which returns status 400 should be identical to that of a request returning status 200.
Simply note the status code in -connection:didReceiveResponse: and allow the request to continue; you will receive any additional data that the server sends in -connection:didReceiveData: as usual. Finally, you'll get a -connectionDidFinishLoading: call when all data has been received, and you can parse the JSON there.
Does your HTTP request Accept header specify "application/json"? If so, then this is probably an IIS bug and not iOS.
Interestingly enough, MVC4 ActionResult is broken in the RTM. Switching it over to Pure WebApi and fine tuning the response, I was able to finesse it into returning the proper data, it was likely serializing the json improperly which other languages weren't catching.

How to get a complete request from a NSURLRequest?

Is there any way to get the actual data that will be sent when a NSURLConnection sends a NSURLRequest? Right now I'm mainly interested in looking at HTTP and HTTPS requests, but since NSURLRequest works for many protocols, it seems like there ought to be a general way to see the corresponding data for any type of request.
Am I missing something, or do I need to construct the request myself by combining the headers, body, etc?
Just to be clear, I'd like to do this programmatically, not by watching what shows up at the server end.
Update: At this point I've written code that effectively constructs a request using the data in a NSURLRequest, so I'm not asking how to go about that. However, I'd still like to know if there's a way to access the request stream that the URL loading system would generate. In other words, can I get access to the exact bytes that would be sent if a NSURLConnection were to send my NSURLRequest?
According your last question:
Check cookiesCenter.
Check credentialsStorage.
Log all headers for example on the first urlConnection's delegate method didReceiveResponse:.
Make this connection with local webservice and try to catch all headers, params, etc.
Also, the most simple way, is making request yourself.
Am I missing something, or do I need to construct the request myself
by combining the headers, body, etc?
It turns out that this is exactly what I needed to do, and it makes sense. It's the protocol handler (NSURLProtocol subclass), not NSURLRequest, that knows how to format the request for its particular protocol. Luckily, it's very easy to render an instance of NSURLRequest into a valid HTTP request. You can get the method (GET, POST, etc.), requested resource, and host from the request, and you can also get all the HTTP headers using the -allHTTPHeaderFields method, which returns a dictionary. You can then loop over the keys in the dictionary and add lines to the request like:
for (NSString *h in headers) {
[renderedRequest appendFormat:#"%#: %#\r\n", h, [headers objectForKey:h]];
}

Retrieve file size from web server

Looking for a way to retrieve a file size from a web server using cocoa/foundation. I know one can use NSURLconnection which will return NSURLResponse that contains the file size. Is there any other way to get the size. I'm looking for a synchronous way of doing it so when i do [myClass getsize] the size is returned.
Thanks
You can use a NSMutableURLRequest to send a HTTP HEAD request (there’s a method called setHTTPMethod). You’ll get the same response headers as with GET, but you won’t have to download the whole resource body. And if you want to get the data synchronously, use the sendSynchronousRequest… method of NSURLConnection.

Atomic, asynchronous HTTP file POST with sensible feedback?

I've just started development on Macs and have found Cocoa to be a useful and thoughtful framework, but its HTTP functionality has me puzzled.
I have an NSURLConnection object to download a file from my webserver using the HTTP GET method. NSURLConnect's asynchronous connection is great, I get plenty of feedback, I get each chunk received as a new NSData object that I can use to atomically rebuild the file on the client end and, importantly, provide the user with a progress report: [myData length].
Uploads, however, are nowhere near as neat. You can either stick a synchronous request in its own thread or call an asynchronous request (which I believe spawns its own thread), but neither provide you with any useful feedback. There's no delegates to request data or even let me know when data is being sent. Presumably this limits me to files smaller than available memory.
My question is, therefore, is there a simple and elegant solution to HTTP POST file uploads using Cocoa that provides a good deal of feedback and the ability to read files part-by-part, rather than all at once? Or should I write my own class from low-level networking functionality?
Thanks!
You may want to look at the ASIHTTPRequest framework. I haven't used it for uploading but it looks like it has more feedback and the usage is pretty straightforward.
I decided to go with CFNetwork functions instead of NSURLConnection. There appears to be a bit more flexibility in async notifications and in specific features (authentication for instance). Unfortunately it's a bit more complicated (run loops for instance blow my mind) so I recommend you read the CFNetwork reference guide if you go this route:
http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Introduction/Introduction.html
Here's a snippet of code from my POST routine, FWIW:
// Create our URL
CFStringRef url = CFSTR("Submit");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, baseUrl);
// Create the message request (POST)
CFStringRef requestMethod = CFSTR("POST");
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL, kCFHTTPVersion1_1);
// Connect the read socket to the HTTP request stream
CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, myRequest, readStream);
// TODO: why does this have to be done?
succ &= CFReadStreamSetClient(myReadStream,
kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
(CFReadStreamClientCallBack) &MyReadCallBack, &myClientContext);
CFReadStreamScheduleWithRunLoop(myReadStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
succ &= CFReadStreamOpen(myReadStream);
ASIHTTPRequest was originally designed just for this purpose (tracking POST progress), since in the 2.x API, this isn't possible with NSURLConnection. It will definitely be easier to integrate than rolling your own with CFNetwork, and you get lots of other stuff for free (e.g. progress tracking across multiple requests, resuming downloads etc). :)
If the files you are uploading are large, be sure to look at the options for streaming directly from disk, so you don't have to hold the data in memory.
Unfortunately, you're quite correct that NSURLConnection is weak here. The most flexible approach that I would recommend is CocoaAsyncSocket. It means rolling your own HTTP, which is unfortunate, but in most cases not that difficult. CocoaHTTPServer demonstrates how to build a full HTTP server on top of CocoaAsyncSocket, and may have a lot of useful code for your problem. I've found both of these very useful.
Another approach that may be worth investigating is WebKit. Create an invisible WebView, and loadRequest: a POST. I haven't dug into whether the estimatedChange notification system includes the time to upload or only the time to download, but it's worth a try.
You can take a look at the HTTPMessage section of my toolkit repository on github for a simple ObjC wrapper around CFHTTPMessageRef; among other things it'll hand you an NSInputStream object, which saves you thinking about plain-C callback functions.
Depending on what you're reading, you may want to take a look at the StreamingXMLParser section of the same repository for an XML (and HTML) parser which will parse data directly from said NSInputStream on your behalf.