Search code examples
objective-ccocoacfnetwork

Cocoa: Handling 407 http response cfnetwork


I am creating downloader application. I am facing a problem with proxy authentication. I am getting 407 response code i.e proxy authentication required. I have valid proxy authentication details.

Following is Code Flow:

 1. Create Http request using CFHTTPMessageCreateRequest
 2. Set necessary header field values like Cache-Control, Accept-Ranges, Range & User-Agent  using CFHTTPMessageSetHeaderFieldValue
 3. Create read stream using CFReadStreamCreateForHTTPRequest
 4. Set proxy server URL & port properties on read stream using CFReadStreamSetProperty
 5. Set kCFStreamPropertyHTTPShouldAutoredirect to kCFBooleanTrue using CFReadStreamSetProperty
 6. open read stream using CFReadStreamOpen
 7. In a loop wait for stream to get opened

while (1)
 {
     if (kCFStreamStatusOpen == CFReadStreamGetStatus)
     {
           if (CFReadStreamHasBytesAvailable)
           {
                 Get Http response header using CFReadStreamCopyProperty
                 Get response code using CFHTTPMessageGetResponseStatusCode
                 if (200 || 206 is response code)
                 SUCCESS
                 else check if response code is 407.
           }
      }
 }

I tried using following code

if (407 == nsiStatusCode)
{
CFStreamError err;
cfAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, cfHttpResponse);
if ((cfAuthentication) && (CFHTTPAuthenticationIsValid(cfAuthentication, &err)))
{
if (CFHTTPAuthenticationRequiresUserNameAndPassword(cfAuthentication))
{
CFHTTPMessageApplyCredentials(cfHttpRequest, cfAuthentication, (CFStringRef)pnsUserName, (CFStringRef)pnsPassword, &err);
}
}
}

but unable to make it work. How do I handle 407 status code so as to communicate with authenticating HTTP server?

Thanks in advance. Vaibhav.


Solution

  • Build a CFHTTPMessageRef

    -(CFHTTPMessageRef)buildMessage
    {
       NSURL *myURL = [NSURL URLWithString:@"http://myurl.com"];
       NSData *dataToPost = [[NSString stringWithString:@"POST Data It Doesn't Matter What It Is"] dataUsingEncoding:NSUTF8StringEncoding];
    
    
       //Create with the default allocator (NULL), a post request,
       //the URL, and pick either
       //kCFHTTPVersion1_0 or kCFHTTPVersion1_1
       CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CSTR("POST"), (CFURLRef)myURL, kCFHTTPVersion1_1);
    
    
       CFHTTPMessageSetBody(request, (CFDataRef)dataToPost);
    
    
       //Unfortunately, this isn't smart enough to set reasonable headers for you
       CFHTTPMessageSetHeaderFieldValue(request, CFSTR("HOST"), (CFStringRef)[myURL host]);
       CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat:"%d", [dataToPost length]);
       CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("charset=utf-8"));
    
    
       return [NSMakeCollectable(request) autorelease];
    }
    

    Send it to the server and read back the response

    -(CFHTTPMessageRef)performHTTPRequest:(CFHTTPMessageRef)request
    {
       CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(NULL, request);
       CFReadStreamOpen(requestStream);
    
    
       NSMutableData *responseBytes = [NSMutableData data];
    
    
       CFIndex numBytesRead = 0 ;
       do
       {
          UInt8 buf[1024];
          numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf));
    
    
          if(numBytesRead > 0)
             [responseBytes appendBytes:buf length:numBytesRead];
    
    
       } while(numBytesRead > 0);
    
    
       CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader);
       CFHTTPMessageSetBody(response, (CFDataRef)responseBytes);
    
    
       CFReadStreamClose(requestStream);
    CFRelease(requestStream);
    
    
       return [NSMakeCollectable(response) autorelease];
    }  
    

    Adding Authentication to an HTTP Request

    -(void)addAuthenticationToRequest:(CFHTTPMessageRef)request withResponse:(CFHTTPMessageRef)response
    {
       CFHTTPAuthenticationRef authentication = CFHTTPAuthenticationCreateFromResponse(NULL, response);
       [NSMakeCollectable(authentication) autorelease];
    
    
       CFStreamError err;
       Boolean success = CFHTTPMessageApplyCredentials(request, authentication, CFSTR("username"), CFSTR("password"), &err);
    }  
    

    Putting It All Together

    -(void)magicHappens
    {
       CFHTTPMessageRef request = [self buildMessage];
       CFHTTPMessageRef response = [self performHTTPRequest: request];
    
    
       UInt32 statusCode;
       statusCode = CFHTTPMessageGetResponseStatusCode(response);
    
    
       //An HTTP status code of 401 or 407 indicates that authentication is  
       //required I use an auth count to make sure we don't get stuck in an    
       //infinite loop if our credentials are bad. Sometimes, making the    
       //request more than once lets it go through.
       //I admit I don't know why.
    
    
       int authCount = 0;
       while((statusCode == 401 || statusCode == 407) && authCount < 3)
       {
          request = [self buildMessage];
          [self addAuthenticationToRequest:request withResponse:response];
    
    
          response = [self performHTTPRequest: request];
          statusCode = CFHTTPMessageGetResponseStatusCode;
          authCount++;
       }
    
    
        NSData *responseBodyData = [(NSData*)CFHTTPMessageCopyBody(response) autorelease];
       NSString *responseBody = [[[NSString alloc] initWithData:responseBodyData encoding:NSUTF8StringEncoding] autorelease];
    
    
       NSLog(responseBody);
    }  
    

    Refer this link.