Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" | 5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "base/mac/scoped_nsobject.h" | 8 #import "base/mac/scoped_nsobject.h" |
| 9 #include "base/time/time.h" | 9 #include "base/time/time.h" |
| 10 #import "ios/net/protocol_handler_util.h" | 10 #import "ios/net/protocol_handler_util.h" |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 __weak NSThread* _clientThread; | 29 __weak NSThread* _clientThread; |
| 30 // The run loop modes to use when posting tasks to |clientThread_|. | 30 // The run loop modes to use when posting tasks to |clientThread_|. |
| 31 base::scoped_nsobject<NSArray> _runLoopModes; | 31 base::scoped_nsobject<NSArray> _runLoopModes; |
| 32 // The request URL. | 32 // The request URL. |
| 33 base::scoped_nsobject<NSString> _url; | 33 base::scoped_nsobject<NSString> _url; |
| 34 // The creation time of the request. | 34 // The creation time of the request. |
| 35 base::Time _creationTime; | 35 base::Time _creationTime; |
| 36 // |requestComplete_| is used in debug to check that the client is not called | 36 // |requestComplete_| is used in debug to check that the client is not called |
| 37 // after completion. | 37 // after completion. |
| 38 BOOL _requestComplete; | 38 BOOL _requestComplete; |
| 39 BOOL _paused; | |
| 40 | |
| 41 base::scoped_nsobject<NSMutableArray> _queuedInvocations; | |
| 39 } | 42 } |
| 40 | 43 |
| 41 // Performs the selector on |clientThread_| using |runLoopModes_|. | 44 // Performs the selector on |clientThread_| using |runLoopModes_|. |
| 42 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg; | 45 - (void)runInvocationQueueOnClientThread; |
| 43 // These functions are just wrappers around the corresponding | 46 - (void)postToClientThread:(SEL)aSelector, ... |
| 47 NS_REQUIRES_NIL_TERMINATION; | |
| 48 - (void)invokeOnClientThread:(NSInvocation*)invocation; | |
| 49 // hese functions are just wrappers around the corresponding | |
| 44 // NSURLProtocolClient methods, used for task posting. | 50 // NSURLProtocolClient methods, used for task posting. |
| 45 - (void)didFailWithErrorOnClientThread:(NSError*)error; | 51 - (void)didFailWithErrorOnClientThread:(NSError*)error; |
| 46 - (void)didLoadDataOnClientThread:(NSData*)data; | 52 - (void)didLoadDataOnClientThread:(NSData*)data; |
| 47 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; | 53 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; |
| 48 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params; | 54 - (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request |
| 55 redirectResponse:(NSURLResponse*)response; | |
| 49 - (void)didFinishLoadingOnClientThread; | 56 - (void)didFinishLoadingOnClientThread; |
| 50 @end | 57 @end |
| 51 | 58 |
| 52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread | 59 @implementation CRNHTTPProtocolHandlerProxyWithClientThread |
| 53 | 60 |
| 54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol | 61 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol |
| 55 clientThread:(NSThread*)clientThread | 62 clientThread:(NSThread*)clientThread |
| 56 runLoopMode:(NSString*)mode { | 63 runLoopMode:(NSString*)mode { |
| 57 DCHECK(protocol); | 64 DCHECK(protocol); |
| 58 DCHECK(clientThread); | 65 DCHECK(clientThread); |
| 59 if ((self = [super init])) { | 66 if ((self = [super init])) { |
| 60 _protocol = protocol; | 67 _protocol = protocol; |
| 61 _url.reset([[[[protocol request] URL] absoluteString] copy]); | 68 _url.reset([[[[protocol request] URL] absoluteString] copy]); |
| 62 _creationTime = base::Time::Now(); | 69 _creationTime = base::Time::Now(); |
| 63 _clientThread = clientThread; | 70 _clientThread = clientThread; |
| 64 // Use the common run loop mode in addition to the client thread mode, in | 71 // Use the common run loop mode in addition to the client thread mode, in |
| 65 // hope that our tasks are executed even if the client thread changes mode | 72 // hope that our tasks are executed even if the client thread changes mode |
| 66 // later on. | 73 // later on. |
| 67 if ([mode isEqualToString:NSRunLoopCommonModes]) | 74 if ([mode isEqualToString:NSRunLoopCommonModes]) |
| 68 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); | 75 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); |
| 69 else | 76 else |
| 70 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); | 77 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); |
| 78 _queuedInvocations.reset([[NSMutableArray alloc] init]); | |
| 71 } | 79 } |
| 72 return self; | 80 return self; |
| 73 } | 81 } |
| 74 | 82 |
| 75 - (void)invalidate { | 83 - (void)invalidate { |
| 76 DCHECK([NSThread currentThread] == _clientThread); | 84 DCHECK([NSThread currentThread] == _clientThread); |
| 77 _protocol = nil; | 85 _protocol = nil; |
| 78 _requestComplete = YES; | 86 _requestComplete = YES; |
| 87 // TODO(ellyjones): This DCHECK fails sometimes. What this implies is that a | |
| 88 // callback is coming from the chrome network stack, being enqueued, and then | |
| 89 // this proxy is being invalidated before that callback has ever been | |
| 90 // delivered. Disturbingly, in practice these seem to be didLoadData and | |
| 91 // similar... | |
| 92 DCHECK(_queuedInvocations.get().count == 0); | |
|
droger
2015/06/11 08:56:19
I think it's expected that the queue may not be em
Elly Fong-Jones
2015/06/11 13:49:33
Done.
| |
| 79 } | 93 } |
| 80 | 94 |
| 81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg { | 95 - (void)runInvocationQueueOnClientThread { |
| 82 [self performSelector:aSelector | 96 DCHECK([NSThread currentThread] == _clientThread); |
| 97 DCHECK(!_requestComplete || !_protocol); | |
| 98 while (!_paused && _queuedInvocations.get().count > 0) { | |
|
droger
2015/06/11 08:56:20
I don't understand this. Do you expect _paused or
Elly Fong-Jones
2015/06/11 13:49:33
Yes, that is the crux of it. A callback can lead t
| |
| 99 NSInvocation* inv = [_queuedInvocations objectAtIndex:0]; | |
| 100 // Since |_queuedInvocations| owns the only reference to each queued | |
| 101 // invocation, this function has to retain another reference before removing | |
| 102 // the queued invocation from the array. | |
| 103 [inv retain]; | |
| 104 [_queuedInvocations removeObjectAtIndex:0]; | |
| 105 [inv invoke]; | |
| 106 [inv release]; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 - (void)postToClientThread:(SEL)aSelector, ... { | |
| 111 // Build an NSInvocation representing an invocation of |aSelector| on |self| | |
| 112 // with the supplied varargs passed as arguments to the invocation. | |
| 113 NSMethodSignature* sig = [self methodSignatureForSelector:aSelector]; | |
| 114 DCHECK(sig != nil); | |
| 115 NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig]; | |
| 116 [inv setTarget:self]; | |
| 117 [inv setSelector:aSelector]; | |
| 118 [inv retainArguments]; | |
| 119 | |
| 120 size_t arg_index = 2; | |
| 121 va_list args; | |
| 122 va_start(args, aSelector); | |
| 123 NSObject* arg = va_arg(args, NSObject*); | |
| 124 while (arg != nil) { | |
| 125 [inv setArgument:&arg atIndex:arg_index]; | |
| 126 arg = va_arg(args, NSObject*); | |
| 127 arg_index++; | |
| 128 } | |
| 129 va_end(args); | |
| 130 | |
| 131 DCHECK(arg_index == sig.numberOfArguments); | |
| 132 [inv retain]; | |
|
droger
2015/06/11 08:56:20
Can we remove this retain and the corresponding re
Elly Fong-Jones
2015/06/11 13:49:33
Done.
| |
| 133 [self performSelector:@selector(invokeOnClientThread:) | |
| 83 onThread:_clientThread | 134 onThread:_clientThread |
| 84 withObject:arg | 135 withObject:inv |
| 85 waitUntilDone:NO | 136 waitUntilDone:NO |
| 86 modes:_runLoopModes]; | 137 modes:_runLoopModes]; |
| 87 } | 138 } |
| 88 | 139 |
| 140 - (void)invokeOnClientThread:(NSInvocation*)invocation { | |
| 141 DCHECK([NSThread currentThread] == _clientThread); | |
| 142 DCHECK(!_requestComplete || !_protocol); | |
| 143 if (!_paused) { | |
| 144 [invocation invoke]; | |
| 145 } else { | |
| 146 [_queuedInvocations addObject:invocation]; | |
| 147 } | |
| 148 [invocation release]; | |
| 149 } | |
| 150 | |
| 89 #pragma mark Proxy methods called from any thread. | 151 #pragma mark Proxy methods called from any thread. |
| 90 | 152 |
| 91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode | 153 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode |
| 92 netErrorCode:(int)netErrorCode { | 154 netErrorCode:(int)netErrorCode { |
| 93 DCHECK(_clientThread); | 155 DCHECK(_clientThread); |
| 94 if (!_protocol) | 156 if (!_protocol) |
|
droger
2015/06/11 08:56:19
Optional:
I realize that this check is not trivia
| |
| 95 return; | 157 return; |
| 96 NSError* error = | 158 NSError* error = |
| 97 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime); | 159 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime); |
| 98 [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:) | 160 [self postToClientThread:@selector(didFailWithErrorOnClientThread:), |
| 99 withObject:error]; | 161 error, nil]; |
| 100 } | 162 } |
| 101 | 163 |
| 102 - (void)didLoadData:(NSData*)data { | 164 - (void)didLoadData:(NSData*)data { |
| 103 DCHECK(_clientThread); | 165 DCHECK(_clientThread); |
| 104 if (!_protocol) | 166 if (!_protocol) |
| 105 return; | 167 return; |
| 106 [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:) | 168 [self postToClientThread:@selector(didLoadDataOnClientThread:), |
| 107 withObject:data]; | 169 data, nil]; |
| 108 } | 170 } |
| 109 | 171 |
| 110 - (void)didReceiveResponse:(NSURLResponse*)response { | 172 - (void)didReceiveResponse:(NSURLResponse*)response { |
| 111 DCHECK(_clientThread); | 173 DCHECK(_clientThread); |
| 112 if (!_protocol) | 174 if (!_protocol) |
| 113 return; | 175 return; |
| 114 [self | 176 [self postToClientThread:@selector(didReceiveResponseOnClientThread:), |
| 115 performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:) | 177 response, nil]; |
| 116 withObject:response]; | |
| 117 } | 178 } |
| 118 | 179 |
| 119 - (void)wasRedirectedToRequest:(NSURLRequest*)request | 180 - (void)wasRedirectedToRequest:(NSURLRequest*)request |
| 120 nativeRequest:(net::URLRequest*)nativeRequest | 181 nativeRequest:(net::URLRequest*)nativeRequest |
| 121 redirectResponse:(NSURLResponse*)redirectResponse { | 182 redirectResponse:(NSURLResponse*)redirectResponse { |
| 122 DCHECK(_clientThread); | 183 DCHECK(_clientThread); |
| 123 if (!_protocol) | 184 if (!_protocol) |
| 124 return; | 185 return; |
| 125 [self performSelectorOnClientThread:@selector( | 186 [self postToClientThread:@selector(wasRedirectedToRequestOnClientThread:), |
| 126 wasRedirectedToRequestOnClientThread:) | 187 request, redirectResponse, nil]; |
| 127 withObject:@[ request, redirectResponse ]]; | |
| 128 } | 188 } |
| 129 | 189 |
| 130 - (void)didFinishLoading { | 190 - (void)didFinishLoading { |
| 131 DCHECK(_clientThread); | 191 DCHECK(_clientThread); |
| 132 if (!_protocol) | 192 if (!_protocol) |
| 133 return; | 193 return; |
| 134 [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread) | 194 [self postToClientThread:@selector(didFinishLoadingOnClientThread), |
| 135 withObject:nil]; | 195 nil]; |
| 136 } | 196 } |
| 137 | 197 |
| 138 // Feature support methods that don't forward to the NSURLProtocolClient. | 198 // Feature support methods that don't forward to the NSURLProtocolClient. |
| 139 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest { | 199 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest { |
| 140 // no-op. | 200 // no-op. |
| 141 } | 201 } |
| 142 | 202 |
| 143 - (void)didRecieveAuthChallenge:(net::AuthChallengeInfo*)authInfo | 203 - (void)didRecieveAuthChallenge:(net::AuthChallengeInfo*)authInfo |
| 144 nativeRequest:(const net::URLRequest&)nativeRequest | 204 nativeRequest:(const net::URLRequest&)nativeRequest |
| 145 callback:(const network_client::AuthCallback&)callback { | 205 callback:(const network_client::AuthCallback&)callback { |
| 146 // If we get this far, authentication has failed. | 206 // If we get this far, authentication has failed. |
| 147 base::string16 empty; | 207 base::string16 empty; |
| 148 callback.Run(false, empty, empty); | 208 callback.Run(false, empty, empty); |
| 149 } | 209 } |
| 150 | 210 |
| 151 - (void)cancelAuthRequest { | 211 - (void)cancelAuthRequest { |
| 152 // no-op. | 212 // no-op. |
| 153 } | 213 } |
| 154 | 214 |
| 155 - (void)setUnderlyingClient:(id<CRNNetworkClientProtocol>)underlyingClient { | 215 - (void)setUnderlyingClient:(id<CRNNetworkClientProtocol>)underlyingClient { |
| 156 // This is the lowest level. | 216 // This is the lowest level. |
| 157 DCHECK(!underlyingClient); | 217 DCHECK(!underlyingClient); |
| 158 } | 218 } |
| 159 | 219 |
| 160 #pragma mark Proxy methods called from the client thread. | 220 #pragma mark Proxy methods called from the client thread. |
| 161 | 221 |
| 162 - (void)didFailWithErrorOnClientThread:(NSError*)error { | 222 - (void)didFailWithErrorOnClientThread:(NSError*)error { |
| 163 DCHECK([NSThread currentThread] == _clientThread); | |
| 164 DCHECK(!_requestComplete || !_protocol); | |
| 165 _requestComplete = YES; | 223 _requestComplete = YES; |
| 166 [[_protocol client] URLProtocol:_protocol didFailWithError:error]; | 224 [[_protocol client] URLProtocol:_protocol didFailWithError:error]; |
| 167 } | 225 } |
| 168 | 226 |
| 169 - (void)didLoadDataOnClientThread:(NSData*)data { | 227 - (void)didLoadDataOnClientThread:(NSData*)data { |
| 170 DCHECK([NSThread currentThread] == _clientThread); | |
| 171 DCHECK(!_requestComplete || !_protocol); | |
| 172 [[_protocol client] URLProtocol:_protocol didLoadData:data]; | 228 [[_protocol client] URLProtocol:_protocol didLoadData:data]; |
| 173 } | 229 } |
| 174 | 230 |
| 175 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response { | 231 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response { |
| 176 DCHECK([NSThread currentThread] == _clientThread); | |
| 177 DCHECK(!_requestComplete || !_protocol); | |
| 178 [[_protocol client] URLProtocol:_protocol | 232 [[_protocol client] URLProtocol:_protocol |
| 179 didReceiveResponse:response | 233 didReceiveResponse:response |
| 180 cacheStoragePolicy:NSURLCacheStorageNotAllowed]; | 234 cacheStoragePolicy:NSURLCacheStorageNotAllowed]; |
| 181 } | 235 } |
| 182 | 236 |
| 183 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params { | 237 - (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request |
| 184 DCHECK([NSThread currentThread] == _clientThread); | 238 redirectResponse:(NSURLResponse*)redirectResponse { |
| 185 DCHECK_EQ(2u, [params count]); | |
| 186 DCHECK([params[0] isKindOfClass:[NSURLRequest class]]); | |
| 187 DCHECK([params[1] isKindOfClass:[NSURLResponse class]]); | |
| 188 DCHECK(!_requestComplete || !_protocol); | |
| 189 [[_protocol client] URLProtocol:_protocol | 239 [[_protocol client] URLProtocol:_protocol |
| 190 wasRedirectedToRequest:params[0] | 240 wasRedirectedToRequest:request |
| 191 redirectResponse:params[1]]; | 241 redirectResponse:redirectResponse]; |
| 192 } | 242 } |
| 193 | 243 |
| 194 - (void)didFinishLoadingOnClientThread { | 244 - (void)didFinishLoadingOnClientThread { |
| 195 DCHECK([NSThread currentThread] == _clientThread); | |
| 196 DCHECK(!_requestComplete || !_protocol); | |
| 197 _requestComplete = YES; | 245 _requestComplete = YES; |
| 198 [[_protocol client] URLProtocolDidFinishLoading:_protocol]; | 246 [[_protocol client] URLProtocolDidFinishLoading:_protocol]; |
| 199 } | 247 } |
| 200 | 248 |
| 249 - (void)pause { | |
| 250 DCHECK([NSThread currentThread] == _clientThread); | |
| 251 // It's legal (in fact, required) for |pause| to be called after the request | |
| 252 // has already finished, so the usual invalidation DCHECK is missing here. | |
| 253 _paused = YES; | |
| 254 } | |
| 255 | |
| 256 - (void)resume { | |
| 257 DCHECK([NSThread currentThread] == _clientThread); | |
| 258 DCHECK(!_requestComplete || !_protocol); | |
| 259 _paused = NO; | |
| 260 [self runInvocationQueueOnClientThread]; | |
| 261 } | |
| 262 | |
| 201 @end | 263 @end |
| OLD | NEW |