Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Unified Diff: ios/net/crn_http_protocol_handler.mm

Issue 1142383006: CrNet: add pauseable NSURLProtocol and switch to using it (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: main.m -> main.mm & format Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ios/net/crn_http_protocol_handler.h ('k') | ios/net/crn_http_protocol_handler_proxy.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/net/crn_http_protocol_handler.mm
diff --git a/ios/net/crn_http_protocol_handler.mm b/ios/net/crn_http_protocol_handler.mm
index 453b3b32f223217df2bf1a6f72b8dc9c7e9194dd..12b035a93835fbe9b251786278114de681d6e29c 100644
--- a/ios/net/crn_http_protocol_handler.mm
+++ b/ios/net/crn_http_protocol_handler.mm
@@ -866,13 +866,58 @@ void HttpProtocolHandlerCore::PushClients(NSArray* clients) {
@end
#pragma mark -
+#pragma mark DeferredCancellation
+
+// An object of class |DeferredCancellation| represents a deferred cancellation
+// of a request. In principle this is a block posted to a thread's runloop, but
+// since there is no performBlock:onThread:, this class wraps the desired
+// behavior in an object.
+@interface DeferredCancellation : NSObject
+
+- (instancetype)initWithCore:(scoped_refptr<net::HttpProtocolHandlerCore>)core;
+- (void)cancel;
+
+@end
+
+@implementation DeferredCancellation {
+ scoped_refptr<net::HttpProtocolHandlerCore> _core;
+}
+
+- (instancetype)initWithCore:(scoped_refptr<net::HttpProtocolHandlerCore>)core {
+ if ((self = [super init])) {
+ _core = core;
+ }
+ return self;
+}
+
+- (void)cancel {
+ g_protocol_handler_delegate->GetDefaultURLRequestContext()
+ ->GetNetworkTaskRunner()
+ ->PostTask(FROM_HERE,
+ base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core));
+}
+
+@end
+
+#pragma mark -
#pragma mark HttpProtocolHandler
+@interface CRNHTTPProtocolHandler (Private)
+
+- (id<CRNHTTPProtocolHandlerProxy>)getProtocolHandlerProxy;
+- (scoped_refptr<net::HttpProtocolHandlerCore>)getCore;
+- (NSThread*)getClientThread;
+- (void)cancelRequest;
+
+@end
+
// The HttpProtocolHandler is called by the iOS system to handle the
// NSURLRequest.
@implementation CRNHTTPProtocolHandler {
scoped_refptr<net::HttpProtocolHandlerCore> _core;
base::scoped_nsprotocol<id<CRNHTTPProtocolHandlerProxy>> _protocolProxy;
+ NSThread* _clientThread;
+ NSString* _clientRunLoopMode;
BOOL _supportedURL;
}
@@ -930,23 +975,119 @@ void HttpProtocolHandlerCore::PushClients(NSArray* clients) {
return;
}
- _protocolProxy.reset([[CRNHTTPProtocolHandlerProxyWithClientThread alloc]
- initWithProtocol:self
- clientThread:[NSThread currentThread]
- runLoopMode:[[NSRunLoop currentRunLoop] currentMode]]);
+ _clientThread = [NSThread currentThread];
+
g_protocol_handler_delegate->GetDefaultURLRequestContext()
->GetNetworkTaskRunner()
->PostTask(FROM_HERE, base::Bind(&net::HttpProtocolHandlerCore::Start,
- _core, _protocolProxy));
+ _core, [self getProtocolHandlerProxy]));
}
-- (void)stopLoading {
+- (id<CRNHTTPProtocolHandlerProxy>)getProtocolHandlerProxy {
+ DCHECK_EQ([NSThread currentThread], _clientThread);
+ if (!_protocolProxy.get()) {
+ _protocolProxy.reset([[CRNHTTPProtocolHandlerProxyWithClientThread alloc]
+ initWithProtocol:self
+ clientThread:_clientThread
+ runLoopMode:[[NSRunLoop currentRunLoop] currentMode]]);
+ }
+ return _protocolProxy.get();
+}
+
+- (scoped_refptr<net::HttpProtocolHandlerCore>)getCore {
+ return _core;
+}
+
+- (NSThread*)getClientThread {
+ return _clientThread;
+}
+
+- (void)cancelRequest {
g_protocol_handler_delegate->GetDefaultURLRequestContext()
->GetNetworkTaskRunner()
->PostTask(FROM_HERE,
base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core));
[_protocolProxy invalidate];
+}
+
+- (void)stopLoading {
+ [self cancelRequest];
_protocolProxy.reset();
}
@end
+
+#pragma mark -
+#pragma mark PauseableHttpProtocolHandler
+
+// The HttpProtocolHandler is called by the iOS system to handle the
+// NSURLRequest. This HttpProtocolHandler conforms to the observed semantics of
+// NSURLProtocol when used with NSURLSession on iOS 8 - i.e., |-startLoading|
+// means "start or resume request" and |-stopLoading| means "pause request".
+// Since there is no way to actually pause a request in the network stack, this
+// is implemented using a subclass of CRNHTTPProtocolHandlerProxy that knows how
+// to defer callbacks.
+//
+// Note that this class conforms to somewhat complex threading rules:
+// 1) |initWithRequest:cachedResponse:client:| and |dealloc| can be called on
+// any thread.
+// 2) |startLoading| and |stopLoading| are always called on the client thread.
+// 3) |stopLoading| is called before |dealloc| is called.
+//
+// The main wrinkle is that |dealloc|, which may be called on any thread, needs
+// to clean up a running network request. To do this, |dealloc| needs to run
+// |cancelRequest|, which needs to be run on the client thread. Since it is
+// guaranteed that |startLoading| is called before |dealloc| is called, the
+// |startLoading| method stores a pointer to the client thread, then |dealloc|
+// asks that client thread to perform the |cancelRequest| selector via
+// |scheduleCancelRequest|.
+//
+// Some of the above logic is implemented in the parent class
+// (CRNHTTPProtocolHandler) because it is convenient.
+@implementation CRNPauseableHTTPProtocolHandler {
+ BOOL _started;
+ dispatch_queue_t _queue;
+}
+
+#pragma mark NSURLProtocol methods
+
+- (void)dealloc {
+ [self scheduleCancelRequest];
+ [super dealloc];
+}
+
+#pragma mark NSURLProtocol overrides.
+
+- (void)startLoading {
+ if (_started) {
+ [[self getProtocolHandlerProxy] resume];
+ return;
+ }
+
+ _started = YES;
+ [super startLoading];
+}
+
+- (void)stopLoading {
+ [[self getProtocolHandlerProxy] pause];
+}
+
+// This method has unusual concurrency properties. It can be called on any
+// thread, but it must be called from |-dealloc|, which guarantees that no other
+// method of this object is running concurrently (since |-dealloc| is only
+// called when the last reference to the object drops).
+//
+// This method takes a reference to _core to ensure that _core lives long enough
+// to have the request cleanly cancelled.
+- (void)scheduleCancelRequest {
+ DeferredCancellation* cancellation =
+ [[DeferredCancellation alloc] initWithCore:[self getCore]];
+ NSArray* modes = @[ [[NSRunLoop currentRunLoop] currentMode] ];
+ [cancellation performSelector:@selector(cancel)
+ onThread:[self getClientThread]
+ withObject:nil
+ waitUntilDone:NO
+ modes:modes];
+}
+
+@end
« no previous file with comments | « ios/net/crn_http_protocol_handler.h ('k') | ios/net/crn_http_protocol_handler_proxy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698