NSURLProtocol isn't asked to load after YES response to canInitWithRequest

I'm registering an implementation of NSURLProtocol to do some custom handling of certain URL requests (I tag these requests by setting properties on them). These URL requests are coming from a UIWebView's load method.

  • Create a UI web view (first load after launch)
  • Load content
  • Destroy web view
  • Create a new web view (subsequent loads)
  • Load content
  • I'm seeing significantly different behavior between steps 2 and 5. My NSURLProtocol implementation manages cached data and is designed to handle the requests. If I don't detect my property in a request, I do a second check for a specific URL (for debugging). In either case canInitWithRequest will return YES :

    First load after launch:

    2014-10-15 11:37:11.403 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ebbb40> https://example.com/ 
    2014-10-15 11:37:11.404 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15dc8da0> https://example.com/ 
    2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee5ef0> https://example.com/ 
    2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee6240> https://example.com/ 
    2014-10-15 11:37:11.410 MYApp[5813:60b] MYURLProtocol initWithRequest:cachedResponse:client: Request: https://example.com/
    2014-10-15 11:37:11.411 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee69d0> https://example.com/ 
    2014-10-15 11:37:11.415 MYApp[5813:9c07] MYURLProtocol startLoading Loading <0x15ee6240> https://example.com/ 
    ... A bunch of loading of assets occurs (cached responses) ...
    2014-10-15 11:37:12.497 MYApp[5813:60b] MyWebViewController webViewDidFinishLoad: Finished loading
    

    Others have pointed out that there are multiple calls to the protocol about the same asset, and this is not a concern, though it's interesting to note that each time it is called with a new object, and it is the 4th (of 4) object that gets passed to startLoading . Still, no real concerns here.

    Subsequent loads:

    2014-10-15 11:11:27.466 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x1727c310> https://example.com/ 
    2014-10-15 11:11:27.467 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x145b1d90> https://example.com/ 
    2014-10-15 11:11:27.488 MYApp[5782:560f] MYURLProtocol canInitWithRequest: Matched URL in request: <0x17266060> https://example.com/
    2014-10-15 11:11:27.669 MYApp[5782:60b] MYWebViewController webViewDidFinishLoad: Finished loading
    

    This is where the behavior, in my view, is unexpected. It appears that property has been stripped off of the request by the third time it is passed to canInitWithRequest , and then, even though we respond YES we never actually get inited -- the page is simply returned to the UIWebView in its entirety, with no subsequent requests for assets. Here is what the request looks like when it is created:

    NSURLRequest *request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    [self.webView loadRequest:request];
    

    Why, when I say that my protocol can handle the request, is it not being given the opportunity to do so? My guess is that the answer is in the implementation of UIWebView itself. Any thoughts on how to work around this if I really want my protocol to be the responsible entity for loading?


    I was able to workaround this issue by cache-busting the UIWebView cache, while not busting the NSURLCache.

  • Add a unique param to the query params of the original request. I chose 'key=000000' where the value is zero-led six digit random number.
  • In the protocol, strip the key in canonicalRequestForRequest: and in initWithRequest:cachedResponse:client
  • My stripping code looks like this (there might be a cleaner way to strip the param, but this works):

    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
    {
        NSURLRequest *canonicalRequest = request;
        BOOL myProtocolRequest = [[NSURLProtocol propertyForKey:kMYProtocolRequest inRequest:request] boolValue];
        if (myProtocolRequest)
        {
            NSMutableURLRequest *mutableRequest = [request mutableCopyWorkaround];
            NSString *originalURLString = mutableRequest.URL.absoluteString;
            NSString *regexString = [NSString stringWithFormat:@"(?:[?&])(key=[[:digit:]]{%d}&*)", kMYKeyLength];
    
            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:0];
            NSTextCheckingResult *result = [regex firstMatchInString:originalURLString options:0 range:NSMakeRange(0, originalURLString.length)];
            if (result.numberOfRanges > 1)
            {
                NSRange keyRange = [result rangeAtIndex:1];
                NSLog(@"Removing '%@' from request", [originalURLString substringWithRange:keyRange]);
                NSString *replacementURLString = [originalURLString stringByReplacingCharactersInRange:keyRange withString:@""];
                mutableRequest.URL = [NSURL URLWithString:replacementURLString];
                canonicalRequest = mutableRequest;
            }
        }
    
        return canonicalRequest;
    }
    

    My init code looks like this:

    - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
    {
        self = [super initWithRequest:[MYURLProtocol canonicalRequestForRequest:request] cachedResponse:cachedResponse client:client];
        return self;
    }
    

    I don't like that I have to do this, but I'm finally getting exactly the behavior I want. Hopefully it helps someone out there.


    *** Way to clear the cache of webview **********
    Follow this code:
    
    NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        for (NSHTTPCookie *cookie in [storage cookies]) {
            [storage deleteCookie:cookie];
        }
        [[NSUserDefaults standardUserDefaults] synchronize];
    
    链接地址: http://www.djcxy.com/p/34844.html

    上一篇: 嵌入在UIWebView中的MPMoviePlayerController中的视频流

    下一篇: 在对canInitWithRequest的YES响应之后,NSURLProtocol不会被要求加载