views:

39

answers:

2

I'm posting some data to a http authenticated url with ASIFormDataRequest.

When the authentication fails and the authentication dialog delegate is invoked the upload progress seems to still proceed fully.

So in these cases:

1) The user's credentials are not yet stored in the keychain 2) The user's credentials which are stored in the keychain fail authentication (expired etc.)

I see this behavior:

  • I see the request come in to my server and the 401 denied error returned to the client
  • The uploadFailed delegate is not called.
  • Progress bar delegate slowly fills as the file appears to still be pushed out on the network connection. It completes in a time consistent with the amount of time to fully upload
  • The built in authentication dialog modal appears
  • User enters correct credentials
  • Progress bar delegate resets
  • Upload begins again - progress bar fills as post data is received on server
  • Finished delegate method is called as expected.
  • Everything has uploaded just fine with this second attempt

Here's where I setup my operation:

[self setRequest:[ASIFormDataRequest requestWithURL:uploadURL]];

[request setDelegate:self];
[request setDidFailSelector:@selector(uploadFailed:)];
[request setDidFinishSelector:@selector(uploadFinished:)];

[request setUseKeychainPersistence:TRUE];
[request setShouldPresentAuthenticationDialog:TRUE];
[request setShouldPresentCredentialsBeforeChallenge:TRUE];

[request setPostValue:captionTextField.text forKey:@"caption"];
[request setPostValue:[siteID stringValue] forKey:@"site_id"];
[request setFile:fileToUpload forKey:@"site_photo"];

[request setUploadProgressDelegate:progressView];
[request startAsynchronous];

I am thinking I need to issue a [request cancel] upon the authentication failing but I'm not sure where I should be doing this.

Is it expected behavior that the POST will still chug away even after the server has returned a 401?

Appreciate any guidance or pointers to existing questions that address this.

+1  A: 

A 401 "error" is an HTTP status code, not a request failure. Your request went through okay and you got a response, which happens to be an authentication error notice. You are responsible for handling the response, whatever it might be.

There are many possible status codes you can get from a successful request, other than 401. As an aside, you may want to think about how to handle those kinds of responses as well, depending on what the end user is doing and what responses are appropriate.

The method -uploadFinished: should not generally be waiting until the data is fully uploaded before you see any NSLog statements or other notification of the request finishing.

So one thing to do is change the -uploadFailed: and -uploadFinished: method names to -requestFailed: and -requestFinished: to more accurately reflect what is happening in the logic of your application.

In your delegate's -requestFinished: method, check the responseStatusCode property of the request and issue the appropriate commands:

- (void) requestFinished:(ASIHTTPRequest *)request {
    if ([request responseStatusCode] == 401) {
        //
        // cancel the current request and/or trigger a new, 
        // separate authentication request, passing along a 
        // reference to the request's body data, etc. 
        //
    }
}
Alex Reynolds
Point taken on request vs. upload naming and errors vs. status codes. Generally, I find people usually call anything in the 4xx range an "error" but you are correct and I appreciate the feedback.I do look for this status code in the finished delegate but the delegate doesn't get called until all the data is transmitted - I guess I was mistakenly thinking the 401 response would be returned and the delegate invoked before all the data in the multipart form was sent. I guess I need to validate I'm authenticated before attempting to POST or see if I get the 401 earlier with didRecieveData.
Nick
A: 

This is fairly common behaviour for HTTP clients - they do not attempt to read the reply from the server till they have fully sent the request, including the attached file.

It's common behaviour for a client to pre-emptively send the authentication if it has already has a request from the same server rejected with a 401 with in the same session - I am unsure if ASIHTTPRequest does this, but if it does one solution would be to make a GET request to the server before you do the POST. If the GET is successfully authenticated then the cached credentials should be sent for the post and hence there won't be a 401 error.

The only other option I can think of would be to move to cookie based authentication instead, if you are in control of the server, or use authentication in a custom http header. But I think my suggestion of doing a GET request first may be the best approach.

JosephH
Thank you - I have moved to doing an initial GET prior to the POST. In other situations I've used a cookie based authentication mechanism but for a variety of reasons HTTP basic is needed with this particular situation.
Nick