How to set statusCode in NSURLResponse
Asked Answered
L

3

12

I override NSURLProtocol and need to return HTTP response with specific statusCode. NSHTTPURLResponse does not have statusCode setter, so I tried to override it with:

@interface MyHTTPURLResponse : NSHTTPURLResponse {} 

@implementation MyHTTPURLResponse

    - (NSInteger)statusCode {
        return 200; //stub code
    }
@end

Overridden startLoading method of NSURLProtocol looks like this:

-(void)startLoading
{   
   NSString *url = [[[self request] URL] absoluteString];
   if([url isEqualToString:SPECIFIC_URL]){
       MyURLResponse *response = [[MyURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://fakeUrl"]
       MIMEType:@"text/plain"
       expectedContentLength:0  textEncodingName:nil];

       [[self client] URLProtocol:self     
            didReceiveResponse:response 
            cacheStoragePolicy:NSURLCacheStorageNotAllowed];

       [[self client] URLProtocol:self didLoadData:[@"Fake response string"
            dataUsingEncoding:NSASCIIStringEncoding]];

       [[self client] URLProtocolDidFinishLoading:self];                

       [response release];

    }
    else{   
        [NSURLConnection connectionWithRequest:[self request] delegate:self];   
    }
}

But this approach does not work, the response created in NSURLProtocol is always with statusCode = 0 on web page. At the same time responses that are returned from network by NSURLConnection have normal expected statusCodes.

Can anyone please help with ideas how to set statusCode explicitly for created NSURLResponse? Thanx.

Linseylinseywoolsey answered 8/12, 2010 at 2:47 Comment(1)
Did you find a solution to this problem?Sequestration
V
4

I've implemented custom init method using following code:

    NSInteger statusCode = 200;
    id headerFields = nil;
    double requestTime = 1;

    SEL selector = NSSelectorFromString(@"initWithURL:statusCode:headerFields:requestTime:");
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];

    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature];
    [inv setTarget:self];
    [inv setSelector:selector];
    [inv setArgument:&URL atIndex:2];
    [inv setArgument:&statusCode atIndex:3];
    [inv setArgument:&headerFields atIndex:4];
    [inv setArgument:&requestTime atIndex:5];

    [inv invoke];
Vimen answered 8/8, 2011 at 7:41 Comment(2)
Thank you for reply. I've tried your solution and got statusCode 200. This solves my problem, I needed exactly status 200. But I failed to set arbitrary status: whatever statusCode is set in invocation by NSInvocation statusCode in ready NSHTTPURLResponse is 200. Have you tried other statusCodes? Anyway I mark this as an answer, thnx.Linseylinseywoolsey
Just tried - I can set any status code (I set 215). And it's the same on web page - 215.Vimen
T
12

Here is a better solution.

On iOS 5.0 and up you don't have to do anything crazy with private APIs or overloading NSHTTPURLResponse anymore.

To create an NSHTTPUTLResponse with your own status code and headers, you can now simply use:

initWithURL:statusCode:HTTPVersion:headerFields:

Which is not documented in the generated documentation but is actually present in the NSURLResponse.h header file and also marked as a public API that is available on OS X 10.7 and iOS 5.0.

Also note that if you are using the NSURLProtocol trick to make XMLHTTPRequest calls from a UIWebView, that you will need to set the Access-Control-Allow-Origin header appropriately. Otherwise the XMLHTTPRequest security kicks in and even though your NSURLProtocol will receive and handle the request, you will not be able to send a response back.

Trunnion answered 2/3, 2012 at 18:50 Comment(0)
V
4

I've implemented custom init method using following code:

    NSInteger statusCode = 200;
    id headerFields = nil;
    double requestTime = 1;

    SEL selector = NSSelectorFromString(@"initWithURL:statusCode:headerFields:requestTime:");
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];

    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:signature];
    [inv setTarget:self];
    [inv setSelector:selector];
    [inv setArgument:&URL atIndex:2];
    [inv setArgument:&statusCode atIndex:3];
    [inv setArgument:&headerFields atIndex:4];
    [inv setArgument:&requestTime atIndex:5];

    [inv invoke];
Vimen answered 8/8, 2011 at 7:41 Comment(2)
Thank you for reply. I've tried your solution and got statusCode 200. This solves my problem, I needed exactly status 200. But I failed to set arbitrary status: whatever statusCode is set in invocation by NSInvocation statusCode in ready NSHTTPURLResponse is 200. Have you tried other statusCodes? Anyway I mark this as an answer, thnx.Linseylinseywoolsey
Just tried - I can set any status code (I set 215). And it's the same on web page - 215.Vimen
W
3

U get the status code from the URLResponse only. No need to set it explicitly:-

    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&requestError];  

    NSString *responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
    NSLog(@"ResponseString:%@",responseString);

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    int statusCode = [httpResponse statusCode];
    NSLog(@"%d",statusCode);
Winkelman answered 8/12, 2010 at 6:49 Comment(1)
Thank you for reply. But I do not need to get statusCode of http response from network. I create NSURLResponse myself to return it inside overridden NSURLProtocol and need to set statusCode of the response.Linseylinseywoolsey

© 2022 - 2024 — McMap. All rights reserved.