123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // Copyright 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h"
- #include <stddef.h>
- #include "base/check.h"
- #include "base/time/time.h"
- #import "ios/net/protocol_handler_util.h"
- #include "net/base/auth.h"
- #include "net/url_request/url_request.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- // When the protocol is invalidated, no synchronization (lock) is needed:
- // - The actual calls to the protocol and its invalidation are all done on
- // clientThread_ and thus are serialized.
- // - When a proxy method is called, the protocol is compared to nil. There may
- // be a conflict at this point, in the case the protocol is being invalidated
- // during this comparison. However, in such a case, the actual value of the
- // pointer does not matter: an invalid pointer will behave as a valid one and
- // post a task on the clientThread_, and that task will be handled correctly,
- // as described by the item above.
- @interface CRNHTTPProtocolHandlerProxyWithClientThread () {
- __weak NSURLProtocol* _protocol;
- // Thread used to call the client back.
- // This thread does not have a base::MessageLoop, and thus does not work with
- // the usual task posting functions.
- __weak NSThread* _clientThread;
- // The run loop modes to use when posting tasks to |clientThread_|.
- NSArray* _runLoopModes;
- // The request URL.
- NSString* _url;
- // The creation time of the request.
- base::Time _creationTime;
- // |requestComplete_| is used in debug to check that the client is not called
- // after completion.
- BOOL _requestComplete;
- BOOL _paused;
- // Contains code blocks to execute when the connection transitions from paused
- // to resumed state.
- NSMutableArray<void (^)()>* _queuedBlocks;
- }
- // Performs queued blocks on |clientThread_| using |runLoopModes_|.
- - (void)runQueuedBlocksOnClientThread;
- // These functions are just wrappers around the corresponding
- // NSURLProtocolClient methods, used for task posting.
- - (void)didFailWithErrorOnClientThread:(NSError*)error;
- - (void)didLoadDataOnClientThread:(NSData*)data;
- - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response;
- - (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request
- redirectResponse:(NSURLResponse*)response;
- - (void)didFinishLoadingOnClientThread;
- @end
- @implementation CRNHTTPProtocolHandlerProxyWithClientThread
- - (instancetype)initWithProtocol:(NSURLProtocol*)protocol
- clientThread:(NSThread*)clientThread
- runLoopMode:(NSString*)mode {
- DCHECK(protocol);
- DCHECK(clientThread);
- if ((self = [super init])) {
- _protocol = protocol;
- _url = [[[[protocol request] URL] absoluteString] copy];
- _creationTime = base::Time::Now();
- _clientThread = clientThread;
- // Use the common run loop mode in addition to the client thread mode, in
- // hope that our tasks are executed even if the client thread changes mode
- // later on.
- if ([mode isEqualToString:NSRunLoopCommonModes])
- _runLoopModes = @[ NSRunLoopCommonModes ];
- else
- _runLoopModes = @[ mode, NSRunLoopCommonModes ];
- _queuedBlocks = [[NSMutableArray alloc] init];
- }
- return self;
- }
- - (void)invalidate {
- DCHECK([NSThread currentThread] == _clientThread);
- _protocol = nil;
- _requestComplete = YES;
- // Note that there may still be queued blocks here, if the chrome network
- // stack continues to emit events after the system network stack has paused
- // the request, and then the system network stack destroys the request.
- _queuedBlocks = nil;
- }
- - (void)runQueuedBlocksOnClientThread {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
- // Each of the queued blocks may cause the system network stack to pause
- // this request, in which case |runQueuedBlocksOnClientThread| should
- // immediately stop running further queued invocations. The queue will be
- // drained again the next time the system network stack calls |resume|.
- //
- // Specifically, the system stack can call back into |pause| with this
- // function still on the call stack. However, since new blocks are
- // enqueued on this thread via posted invocations, no new blocks can be
- // added while this function is running.
- while (!_paused && _queuedBlocks.count > 0) {
- void (^block)() = _queuedBlocks[0];
- // Since |_queuedBlocks| owns the only reference to each queued
- // block, this function has to retain another reference before removing
- // the queued block from the array.
- block();
- [_queuedBlocks removeObjectAtIndex:0];
- }
- }
- - (void)postBlockToClientThread:(dispatch_block_t)block {
- DCHECK(block);
- [self performSelector:@selector(performBlockOnClientThread:)
- onThread:_clientThread
- withObject:[block copy]
- waitUntilDone:NO
- modes:_runLoopModes];
- }
- - (void)performBlockOnClientThread:(dispatch_block_t)block {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
- DCHECK(block);
- if (!_paused) {
- block();
- } else {
- [_queuedBlocks addObject:block];
- }
- }
- #pragma mark Proxy methods called from any thread.
- - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
- netErrorCode:(int)netErrorCode {
- DCHECK(_clientThread);
- if (!_protocol)
- return;
- NSError* error =
- net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime);
- [self postBlockToClientThread:^{
- [self didFailWithErrorOnClientThread:error];
- }];
- }
- - (void)didLoadData:(NSData*)data {
- DCHECK(_clientThread);
- if (!_protocol)
- return;
- [self postBlockToClientThread:^{
- [self didLoadDataOnClientThread:data];
- }];
- }
- - (void)didReceiveResponse:(NSURLResponse*)response {
- DCHECK(_clientThread);
- if (!_protocol)
- return;
- [self postBlockToClientThread:^{
- [self didReceiveResponseOnClientThread:response];
- }];
- }
- - (void)wasRedirectedToRequest:(NSURLRequest*)request
- nativeRequest:(net::URLRequest*)nativeRequest
- redirectResponse:(NSURLResponse*)redirectResponse {
- DCHECK(_clientThread);
- if (!_protocol)
- return;
- [self postBlockToClientThread:^{
- [self wasRedirectedToRequestOnClientThread:request
- redirectResponse:redirectResponse];
- }];
- }
- - (void)didFinishLoading {
- DCHECK(_clientThread);
- if (!_protocol)
- return;
- [self postBlockToClientThread:^{
- [self didFinishLoadingOnClientThread];
- }];
- }
- // Feature support methods that don't forward to the NSURLProtocolClient.
- - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
- // no-op.
- }
- #pragma mark Proxy methods called from the client thread.
- - (void)didFailWithErrorOnClientThread:(NSError*)error {
- _requestComplete = YES;
- [[_protocol client] URLProtocol:_protocol didFailWithError:error];
- }
- - (void)didLoadDataOnClientThread:(NSData*)data {
- [[_protocol client] URLProtocol:_protocol didLoadData:data];
- }
- - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response {
- [[_protocol client] URLProtocol:_protocol
- didReceiveResponse:response
- cacheStoragePolicy:NSURLCacheStorageNotAllowed];
- }
- - (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request
- redirectResponse:(NSURLResponse*)redirectResponse {
- [[_protocol client] URLProtocol:_protocol
- wasRedirectedToRequest:request
- redirectResponse:redirectResponse];
- }
- - (void)didFinishLoadingOnClientThread {
- _requestComplete = YES;
- [[_protocol client] URLProtocolDidFinishLoading:_protocol];
- }
- - (void)pause {
- DCHECK([NSThread currentThread] == _clientThread);
- // It's legal (in fact, required) for |pause| to be called after the request
- // has already finished, so the usual invalidation DCHECK is missing here.
- _paused = YES;
- }
- - (void)resume {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
- _paused = NO;
- [self runQueuedBlocksOnClientThread];
- }
- @end
|