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.
5 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h"
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"
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.
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;
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
38 BOOL _requestComplete;
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;
52 @implementation CRNHTTPProtocolHandlerProxyWithClientThread
54 - (instancetype)initWithProtocol:(NSURLProtocol*)protocol
55 clientThread:(NSThread*)clientThread
56 runLoopMode:(NSString*)mode {
59 if ((self = [super init])) {
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
67 if ([mode isEqualToString:NSRunLoopCommonModes])
68 _runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]);
70 _runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]);
76 DCHECK([NSThread currentThread] == _clientThread);
78 _requestComplete = YES;
81 - (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg {
82 [self performSelector:aSelector
83 onThread:_clientThread
89 #pragma mark Proxy methods called from any thread.
91 - (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
92 netErrorCode:(int)netErrorCode {
93 DCHECK(_clientThread);
97 net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime);
98 [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:)
102 - (void)didLoadData:(NSData*)data {
103 DCHECK(_clientThread);
106 [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:)
110 - (void)didReceiveResponse:(NSURLResponse*)response {
111 DCHECK(_clientThread);
115 performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:)
116 withObject:response];
119 - (void)wasRedirectedToRequest:(NSURLRequest*)request
120 nativeRequest:(net::URLRequest*)nativeRequest
121 redirectResponse:(NSURLResponse*)redirectResponse {
122 DCHECK(_clientThread);
125 [self performSelectorOnClientThread:@selector(
126 wasRedirectedToRequestOnClientThread:)
127 withObject:@[ request, redirectResponse ]];
130 - (void)didFinishLoading {
131 DCHECK(_clientThread);
134 [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread)
138 // Feature support methods that don't forward to the NSURLProtocolClient.
139 - (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
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);
151 - (void)cancelAuthRequest {
155 - (void)setUnderlyingClient:(id<CRNNetworkClientProtocol>)underlyingClient {
156 // This is the lowest level.
157 DCHECK(!underlyingClient);
160 #pragma mark Proxy methods called from the client thread.
162 - (void)didFailWithErrorOnClientThread:(NSError*)error {
163 DCHECK([NSThread currentThread] == _clientThread);
164 DCHECK(!_requestComplete || !_protocol);
165 _requestComplete = YES;
166 [[_protocol client] URLProtocol:_protocol didFailWithError:error];
169 - (void)didLoadDataOnClientThread:(NSData*)data {
170 DCHECK([NSThread currentThread] == _clientThread);
171 DCHECK(!_requestComplete || !_protocol);
172 [[_protocol client] URLProtocol:_protocol didLoadData:data];
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];
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]];
194 - (void)didFinishLoadingOnClientThread {
195 DCHECK([NSThread currentThread] == _clientThread);
196 DCHECK(!_requestComplete || !_protocol);
197 _requestComplete = YES;
198 [[_protocol client] URLProtocolDidFinishLoading:_protocol];