4

I am trying to make a subclass to NSURLProtocol. However, I am noticing that I seem to lose caching functionality when I register my subclass, even though it shouldn't do anything. For example, if I do [NSURLConnection sendSynchronousRequest...] multiple times to the same URL without registering the subclass, it only performs one actual http request. With this subclass registered, it performs a network request every time.

Here is my code:

@interface CustomURLProtocol : NSURLProtocol
@property (nonatomic, strong) NSURLConnection *connection;
@end

@implementation CustomURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    return [[[request URL] scheme] isEqualToString:@"http"] &&
           [NSURLProtocol propertyForKey:@"tagged" inRequest:request] == nil;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"tagged" inRequest:newRequest];
    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}

- (void)stopLoading {
    [self.connection cancel];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [[self client] URLProtocol:self didFailWithError:error];
    self.connection = nil;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:[[self request] cachePolicy]];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection  {
    [[self client] URLProtocolDidFinishLoading:self];
    self.connection = nil;
}
@end
ahb
  • 160
  • 2
  • 9

2 Answers2

1

The issue here was not what I expected. The real problem was that redirects were not being handled correctly, since the following code was not there:

- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response {
  [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
  return request;
}

Without this code, redirects were not being cached so requests so sequential requests to a redirected URL like wikipedia.com resulted in redirects every single time.

ahb
  • 160
  • 2
  • 9
1

I think the line you added is just to skip your custom NSURLProtocol. After adding this method, are those connectionDelegate methods still being called ?

Update: Finally figured this out. This is the reason. I made it work after adding this part into startLoading.

NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
    if (cachedResponse) {  
        [self connection:nil didReceiveResponse:[cachedResponse response]];
        [self connection:nil didReceiveData:[cachedResponse data]];
        [self connectionDidFinishLoading:nil];
    } else {
        _connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
jqyao
  • 170
  • 6
  • 1
    I think the possible reason is that the custom NSURLProtocol is not treated as http at all. Then the cache policy sent by the request is NSURLRequestUseProtocolCachePolicy by default and URL load system doesn't know how to cache it, so it just drops them all. – jqyao Jun 04 '14 at 21:39