tags:

views:

147

answers:

1

I've got a desktop browser app which uses a WebView to host a Flash plugin. The Flash plugin makes regular requests to an external website for new data, which it then draws as fancy graphics.

I'd like to intercept these web requests and get at the data (so I can display it via Growl, instead of keeping a desktop window around). But best I can tell, requests made by Flash don't get picked up by the normal WebView delegates.

Is there another place I can set a hook? I tried installing a custom NSURLCache via [NSURLCache setSharedURLCache] but that never got called. I also tried method swizzling a few of the other classes (like NSCachedURLResponse) but couldn't find a way in. Any ideas? Many thanks!

A: 

Surprised no one answered this, it is actually pretty easy. Create a subclass of NSURLProtocol, and then call registerClass to start intercepting.

[NSURLProtocol registerClass:[MyCustomURLProtocol class]];

Here are the important bits of the subclass:

#define REQUEST_HEADER_TAG  @"x-mycustomurl-intercept"

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    // Check for the custom header on the request to break the 
    // infinite loop created by the [startLoading] below.
    if ([theRequest valueForHTTPHeaderField:REQUEST_HEADER_TAG]) {
        return NO;
    }

    if ([theRequest.URL.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame) {
        return YES;
    }
    return NO;
}

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

- (id)initWithRequest:(NSURLRequest*)theRequest
       cachedResponse:(NSCachedURLResponse*)cachedResponse
               client:(id<NSURLProtocolClient>)client
{
    // Add a custom header on the request to break the 
    // infinite loop created by the [startLoading] below.
    NSMutableURLRequest* newRequest = [theRequest mutableCopy];
    [newRequest setValue:@"" forHTTPHeaderField:REQUEST_HEADER_TAG];

    // Now continue the process with this "tagged" request
    self = [super initWithRequest:theRequest 
                   cachedResponse:cachedResponse 
                           client:client];
    if (self) {
       // capture the data received 
       [self setRequest:newRequest];
       receivedData = [[NSMutableData data] retain];
    }

    [newRequest release];
    return self;
}

- (void)dealloc
{
    [connection release];
    [request release];
    [receivedData release];
    [super dealloc];
}


- (void)startLoading
{
    // Load the data off the web as usual, but set myself up as the delegate 
    // so I can intercept the response data as it comes in.
    [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]];
}


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

#pragma mark NSURLConnection delegate implementation

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


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


- (void)connectionDidFinishLoading:(NSURLConnection*)conn
{
    [[self client] URLProtocolDidFinishLoading:self];
    [self setConnection:nil];
    if (requestTag != 0) {
        if (requestDelegate && 
            [requestDelegate respondsToSelector:
               @selector(finishedLoadingData:forURL:taggedWith:)]) {
            [requestDelegate finishedLoadingData:receivedData 
                                          forURL:[request URL] 
                                      taggedWith:requestTag];
        }
    }
}


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