OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #import "base/mac/scoped_nsobject.h" |
| 9 #include "base/time/time.h" |
| 10 #import "ios/net/protocol_handler_util.h" |
| 11 #include "net/base/auth.h" |
| 12 #include "net/url_request/url_request.h" |
| 13 |
| 14 // When the protocol is invalidated, no synchronization (lock) is needed: |
| 15 // - The actual calls to the protocol and its invalidation are all done on |
| 16 // clientThread_ and thus are serialized. |
| 17 // - When a proxy method is called, the protocol is compared to nil. There may |
| 18 // be a conflict at this point, in the case the protocol is being invalidated |
| 19 // during this comparison. However, in such a case, the actual value of the |
| 20 // pointer does not matter: an invalid pointer will behave as a valid one and |
| 21 // post a task on the clientThread_, and that task will be handled correctly, |
| 22 // as described by the item above. |
| 23 |
| 24 @interface CRNHTTPProtocolHandlerProxyWithClientThread () { |
| 25 __weak NSURLProtocol* _protocol; |
| 26 // Thread used to call the client back. |
| 27 // This thread does not have a base::MessageLoop, and thus does not work with |
| 28 // the usual task posting functions. |
| 29 __weak NSThread* _clientThread; |
| 30 // The run loop modes to use when posting tasks to |clientThread_|. |
| 31 base::scoped_nsobject<NSArray> _runLoopModes; |
| 32 // The request URL. |
| 33 base::scoped_nsobject<NSString> _url; |
| 34 // The creation time of the request. |
| 35 base::Time _creationTime; |
| 36 // |requestComplete_| is used in debug to check that the client is not called |
| 37 // after completion. |
| 38 BOOL _requestComplete; |
| 39 } |
| 40 |
| 41 // Performs the selector on |clientThread_| using |runLoopModes_|. |
| 42 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg; |
| 43 // These functions are just wrappers around the corresponding |
| 44 // NSURLProtocolClient methods, used for task posting. |
| 45 - (void)didFailWithErrorOnClientThread:(NSError*)error; |
| 46 - (void)didLoadDataOnClientThread:(NSData*)data; |
| 47 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response; |
| 48 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params; |
| 49 - (void)didFinishLoadingOnClientThread; |
| 50 @end |
| 51 |
| 52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread |
| 53 |
| 54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol |
| 55 clientThread:(NSThread*)clientThread |
| 56 runLoopMode:(NSString*)mode { |
| 57 DCHECK(protocol); |
| 58 DCHECK(clientThread); |
| 59 if ((self = [super init])) { |
| 60 _protocol = protocol; |
| 61 _url.reset([[[[protocol request] URL] absoluteString] copy]); |
| 62 _creationTime = base::Time::Now(); |
| 63 _clientThread = clientThread; |
| 64 // 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 |
| 66 // later on. |
| 67 if ([mode isEqualToString:NSRunLoopCommonModes]) |
| 68 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]); |
| 69 else |
| 70 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]); |
| 71 } |
| 72 return self; |
| 73 } |
| 74 |
| 75 - (void)invalidate { |
| 76 DCHECK([NSThread currentThread] == _clientThread); |
| 77 _protocol = nil; |
| 78 _requestComplete = YES; |
| 79 } |
| 80 |
| 81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg { |
| 82 [self performSelector:aSelector |
| 83 onThread:_clientThread |
| 84 withObject:arg |
| 85 waitUntilDone:NO |
| 86 modes:_runLoopModes]; |
| 87 } |
| 88 |
| 89 #pragma mark Proxy methods called from any thread. |
| 90 |
| 91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode |
| 92 netErrorCode:(int)netErrorCode { |
| 93 DCHECK(_clientThread); |
| 94 if (!_protocol) |
| 95 return; |
| 96 NSError* error = |
| 97 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime); |
| 98 [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:) |
| 99 withObject:error]; |
| 100 } |
| 101 |
| 102 - (void)didLoadData:(NSData*)data { |
| 103 DCHECK(_clientThread); |
| 104 if (!_protocol) |
| 105 return; |
| 106 [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:) |
| 107 withObject:data]; |
| 108 } |
| 109 |
| 110 - (void)didReceiveResponse:(NSURLResponse*)response { |
| 111 DCHECK(_clientThread); |
| 112 if (!_protocol) |
| 113 return; |
| 114 [self |
| 115 performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:) |
| 116 withObject:response]; |
| 117 } |
| 118 |
| 119 - (void)wasRedirectedToRequest:(NSURLRequest*)request |
| 120 nativeRequest:(net::URLRequest*)nativeRequest |
| 121 redirectResponse:(NSURLResponse*)redirectResponse { |
| 122 DCHECK(_clientThread); |
| 123 if (!_protocol) |
| 124 return; |
| 125 [self performSelectorOnClientThread:@selector( |
| 126 wasRedirectedToRequestOnClientThread:) |
| 127 withObject:@[ request, redirectResponse ]]; |
| 128 } |
| 129 |
| 130 - (void)didFinishLoading { |
| 131 DCHECK(_clientThread); |
| 132 if (!_protocol) |
| 133 return; |
| 134 [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread) |
| 135 withObject:nil]; |
| 136 } |
| 137 |
| 138 // Feature support methods that don't forward to the NSURLProtocolClient. |
| 139 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest { |
| 140 // no-op. |
| 141 } |
| 142 |
| 143 - (void)didRecieveAuthChallenge:(net::AuthChallengeInfo*)authInfo |
| 144 nativeRequest:(const net::URLRequest&)nativeRequest |
| 145 callback:(const network_client::AuthCallback&)callback { |
| 146 // If we get this far, authentication has failed. |
| 147 base::string16 empty; |
| 148 callback.Run(false, empty, empty); |
| 149 } |
| 150 |
| 151 - (void)cancelAuthRequest { |
| 152 // no-op. |
| 153 } |
| 154 |
| 155 - (void)setUnderlyingClient:(id<CRNNetworkClientProtocol>)underlyingClient { |
| 156 // This is the lowest level. |
| 157 DCHECK(!underlyingClient); |
| 158 } |
| 159 |
| 160 #pragma mark Proxy methods called from the client thread. |
| 161 |
| 162 - (void)didFailWithErrorOnClientThread:(NSError*)error { |
| 163 DCHECK([NSThread currentThread] == _clientThread); |
| 164 DCHECK(!_requestComplete || !_protocol); |
| 165 _requestComplete = YES; |
| 166 [[_protocol client] URLProtocol:_protocol didFailWithError:error]; |
| 167 } |
| 168 |
| 169 - (void)didLoadDataOnClientThread:(NSData*)data { |
| 170 DCHECK([NSThread currentThread] == _clientThread); |
| 171 DCHECK(!_requestComplete || !_protocol); |
| 172 [[_protocol client] URLProtocol:_protocol didLoadData:data]; |
| 173 } |
| 174 |
| 175 - (void)didReceiveResponseOnClientThread:(NSURLResponse*)response { |
| 176 DCHECK([NSThread currentThread] == _clientThread); |
| 177 DCHECK(!_requestComplete || !_protocol); |
| 178 [[_protocol client] URLProtocol:_protocol |
| 179 didReceiveResponse:response |
| 180 cacheStoragePolicy:NSURLCacheStorageNotAllowed]; |
| 181 } |
| 182 |
| 183 - (void)wasRedirectedToRequestOnClientThread:(NSArray*)params { |
| 184 DCHECK([NSThread currentThread] == _clientThread); |
| 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 |
| 190 wasRedirectedToRequest:params[0] |
| 191 redirectResponse:params[1]]; |
| 192 } |
| 193 |
| 194 - (void)didFinishLoadingOnClientThread { |
| 195 DCHECK([NSThread currentThread] == _clientThread); |
| 196 DCHECK(!_requestComplete || !_protocol); |
| 197 _requestComplete = YES; |
| 198 [[_protocol client] URLProtocolDidFinishLoading:_protocol]; |
| 199 } |
| 200 |
| 201 @end |
OLD | NEW |