| 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, ... NS_REQUIRES_NIL_TERMINATION; |
| 47 - (void)invokeOnClientThread:(NSInvocation*)invocation; |
| 48 // hese functions are just wrappers around the corresponding |
| 44 // NSURLProtocolClient methods, used for task posting. | 49 // NSURLProtocolClient methods, used for task posting. |
| 45 - (void)didFailWithErrorOnClientThread:(NSError*)error; | 50 - (void)didFailWithErrorOnClientThread:(NSError*)error; |
| 46 - (void)didLoadDataOnClientThread:(NSData*)data; | 51 - (void)didLoadDataOnClientThread:(NSData*)data; |
| 47 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; | 52 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; |
| 48 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params; | 53 - (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request |
| 54 redirectResponse:(NSURLResponse*)response; |
| 49 - (void)didFinishLoadingOnClientThread; | 55 - (void)didFinishLoadingOnClientThread; |
| 50 @end | 56 @end |
| 51 | 57 |
| 52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread | 58 @implementation CRNHTTPProtocolHandlerProxyWithClientThread |
| 53 | 59 |
| 54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol | 60 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol |
| 55 clientThread:(NSThread*)clientThread | 61 clientThread:(NSThread*)clientThread |
| 56 runLoopMode:(NSString*)mode { | 62 runLoopMode:(NSString*)mode { |
| 57 DCHECK(protocol); | 63 DCHECK(protocol); |
| 58 DCHECK(clientThread); | 64 DCHECK(clientThread); |
| 59 if ((self = [super init])) { | 65 if ((self = [super init])) { |
| 60 _protocol = protocol; | 66 _protocol = protocol; |
| 61 _url.reset([[[[protocol request] URL] absoluteString] copy]); | 67 _url.reset([[[[protocol request] URL] absoluteString] copy]); |
| 62 _creationTime = base::Time::Now(); | 68 _creationTime = base::Time::Now(); |
| 63 _clientThread = clientThread; | 69 _clientThread = clientThread; |
| 64 // Use the common run loop mode in addition to the client thread mode, in | 70 // 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 | 71 // hope that our tasks are executed even if the client thread changes mode |
| 66 // later on. | 72 // later on. |
| 67 if ([mode isEqualToString:NSRunLoopCommonModes]) | 73 if ([mode isEqualToString:NSRunLoopCommonModes]) |
| 68 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); | 74 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); |
| 69 else | 75 else |
| 70 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); | 76 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); |
| 77 _queuedInvocations.reset([[NSMutableArray alloc] init]); |
| 71 } | 78 } |
| 72 return self; | 79 return self; |
| 73 } | 80 } |
| 74 | 81 |
| 75 - (void)invalidate { | 82 - (void)invalidate { |
| 76 DCHECK([NSThread currentThread] == _clientThread); | 83 DCHECK([NSThread currentThread] == _clientThread); |
| 77 _protocol = nil; | 84 _protocol = nil; |
| 78 _requestComplete = YES; | 85 _requestComplete = YES; |
| 86 // Note that there may still be queued invocations here, if the chrome network |
| 87 // stack continues to emit events after the system network stack has paused |
| 88 // the request, and then the system network stack destroys the request. |
| 89 _queuedInvocations.reset(); |
| 79 } | 90 } |
| 80 | 91 |
| 81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg { | 92 - (void)runInvocationQueueOnClientThread { |
| 82 [self performSelector:aSelector | 93 DCHECK([NSThread currentThread] == _clientThread); |
| 94 DCHECK(!_requestComplete || !_protocol); |
| 95 // Each of the queued invocations may cause the system network stack to pause |
| 96 // this request, in which case |runInvocationQueueOnClientThread| should |
| 97 // immediately stop running further queued invocations. The queue will be |
| 98 // drained again the next time the system network stack calls |resume|. |
| 99 // |
| 100 // Specifically, the system stack can call back into |pause| with this |
| 101 // function still on the call stack. However, since new invocations are |
| 102 // enqueued on this thread via posted invocations, no new invocations can be |
| 103 // added while this function is running. |
| 104 while (!_paused && _queuedInvocations.get().count > 0) { |
| 105 NSInvocation* invocation = [_queuedInvocations objectAtIndex:0]; |
| 106 // Since |_queuedInvocations| owns the only reference to each queued |
| 107 // invocation, this function has to retain another reference before removing |
| 108 // the queued invocation from the array. |
| 109 [invocation invoke]; |
| 110 [_queuedInvocations removeObjectAtIndex:0]; |
| 111 } |
| 112 } |
| 113 |
| 114 - (void)postToClientThread:(SEL)aSelector, ... { |
| 115 // Build an NSInvocation representing an invocation of |aSelector| on |self| |
| 116 // with the supplied varargs passed as arguments to the invocation. |
| 117 NSMethodSignature* sig = [self methodSignatureForSelector:aSelector]; |
| 118 DCHECK(sig != nil); |
| 119 NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig]; |
| 120 [inv setTarget:self]; |
| 121 [inv setSelector:aSelector]; |
| 122 [inv retainArguments]; |
| 123 |
| 124 size_t arg_index = 2; |
| 125 va_list args; |
| 126 va_start(args, aSelector); |
| 127 NSObject* arg = va_arg(args, NSObject*); |
| 128 while (arg != nil) { |
| 129 [inv setArgument:&arg atIndex:arg_index]; |
| 130 arg = va_arg(args, NSObject*); |
| 131 arg_index++; |
| 132 } |
| 133 va_end(args); |
| 134 |
| 135 DCHECK(arg_index == sig.numberOfArguments); |
| 136 [self performSelector:@selector(invokeOnClientThread:) |
| 83 onThread:_clientThread | 137 onThread:_clientThread |
| 84 withObject:arg | 138 withObject:inv |
| 85 waitUntilDone:NO | 139 waitUntilDone:NO |
| 86 modes:_runLoopModes]; | 140 modes:_runLoopModes]; |
| 87 } | 141 } |
| 88 | 142 |
| 143 - (void)invokeOnClientThread:(NSInvocation*)invocation { |
| 144 DCHECK([NSThread currentThread] == _clientThread); |
| 145 DCHECK(!_requestComplete || !_protocol); |
| 146 if (!_paused) { |
| 147 [invocation invoke]; |
| 148 } else { |
| 149 [_queuedInvocations addObject:invocation]; |
| 150 } |
| 151 } |
| 152 |
| 89 #pragma mark Proxy methods called from any thread. | 153 #pragma mark Proxy methods called from any thread. |
| 90 | 154 |
| 91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode | 155 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode |
| 92 netErrorCode:(int)netErrorCode { | 156 netErrorCode:(int)netErrorCode { |
| 93 DCHECK(_clientThread); | 157 DCHECK(_clientThread); |
| 94 if (!_protocol) | 158 if (!_protocol) |
| 95 return; | 159 return; |
| 96 NSError* error = | 160 NSError* error = |
| 97 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime); | 161 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime); |
| 98 [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:) | 162 [self postToClientThread:@selector(didFailWithErrorOnClientThread:), error, |
| 99 withObject:error]; | 163 nil]; |
| 100 } | 164 } |
| 101 | 165 |
| 102 - (void)didLoadData:(NSData*)data { | 166 - (void)didLoadData:(NSData*)data { |
| 103 DCHECK(_clientThread); | 167 DCHECK(_clientThread); |
| 104 if (!_protocol) | 168 if (!_protocol) |
| 105 return; | 169 return; |
| 106 [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:) | 170 [self postToClientThread:@selector(didLoadDataOnClientThread:), data, nil]; |
| 107 withObject:data]; | |
| 108 } | 171 } |
| 109 | 172 |
| 110 - (void)didReceiveResponse:(NSURLResponse*)response { | 173 - (void)didReceiveResponse:(NSURLResponse*)response { |
| 111 DCHECK(_clientThread); | 174 DCHECK(_clientThread); |
| 112 if (!_protocol) | 175 if (!_protocol) |
| 113 return; | 176 return; |
| 114 [self | 177 [self postToClientThread:@selector(didReceiveResponseOnClientThread:), |
| 115 performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:) | 178 response, nil]; |
| 116 withObject:response]; | |
| 117 } | 179 } |
| 118 | 180 |
| 119 - (void)wasRedirectedToRequest:(NSURLRequest*)request | 181 - (void)wasRedirectedToRequest:(NSURLRequest*)request |
| 120 nativeRequest:(net::URLRequest*)nativeRequest | 182 nativeRequest:(net::URLRequest*)nativeRequest |
| 121 redirectResponse:(NSURLResponse*)redirectResponse { | 183 redirectResponse:(NSURLResponse*)redirectResponse { |
| 122 DCHECK(_clientThread); | 184 DCHECK(_clientThread); |
| 123 if (!_protocol) | 185 if (!_protocol) |
| 124 return; | 186 return; |
| 125 [self performSelectorOnClientThread:@selector( | 187 [self postToClientThread:@selector(wasRedirectedToRequestOnClientThread:), |
| 126 wasRedirectedToRequestOnClientThread:) | 188 request, redirectResponse, nil]; |
| 127 withObject:@[ request, redirectResponse ]]; | |
| 128 } | 189 } |
| 129 | 190 |
| 130 - (void)didFinishLoading { | 191 - (void)didFinishLoading { |
| 131 DCHECK(_clientThread); | 192 DCHECK(_clientThread); |
| 132 if (!_protocol) | 193 if (!_protocol) |
| 133 return; | 194 return; |
| 134 [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread) | 195 [self postToClientThread:@selector(didFinishLoadingOnClientThread), nil]; |
| 135 withObject: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 |