Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ios / net / crn_http_protocol_handler.mm
blob453b3b32f223217df2bf1a6f72b8dc9c7e9194dd
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.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/mac/bind_objc_block.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #import "ios/net/clients/crn_network_client_protocol.h"
17 #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h"
18 #import "ios/net/http_protocol_logging.h"
19 #include "ios/net/nsurlrequest_util.h"
20 #import "ios/net/protocol_handler_util.h"
21 #include "ios/net/request_tracker.h"
22 #include "net/base/auth.h"
23 #include "net/base/elements_upload_data_stream.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/load_flags.h"
26 #import "net/base/mac/url_conversions.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/base/upload_bytes_element_reader.h"
30 #include "net/http/http_request_headers.h"
31 #include "net/url_request/redirect_info.h"
32 #include "net/url_request/url_request.h"
33 #include "net/url_request/url_request_context.h"
34 #include "net/url_request/url_request_context_getter.h"
36 namespace net {
37 class HttpProtocolHandlerCore;
40 namespace {
42 // Size of the buffer used to read the net::URLRequest.
43 const int kIOBufferSize = 4096;
45 // Global instance of the HTTPProtocolHandlerDelegate.
46 net::HTTPProtocolHandlerDelegate* g_protocol_handler_delegate = nullptr;
48 // Empty callback.
49 void DoNothing(bool flag) {}
51 }  // namespace
53 // Bridge class to forward NSStream events to the HttpProtocolHandlerCore.
54 // Lives on the IO thread.
55 @interface CRWHTTPStreamDelegate : NSObject<NSStreamDelegate> {
56  @private
57   // The object is owned by |_core| and has a weak reference to it.
58   __weak net::HttpProtocolHandlerCore* _core;
60 - (instancetype)initWithHttpProtocolHandlerCore:
61     (net::HttpProtocolHandlerCore*)core;
62 // NSStreamDelegate method.
63 - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent;
64 @end
66 #pragma mark -
67 #pragma mark HttpProtocolHandlerCore
69 namespace net {
71 // static
72 void HTTPProtocolHandlerDelegate::SetInstance(
73     HTTPProtocolHandlerDelegate* delegate) {
74   g_protocol_handler_delegate = delegate;
77 // The HttpProtocolHandlerCore class is the bridge between the URLRequest
78 // and the NSURLProtocolClient.
79 // Threading and ownership details:
80 // - The HttpProtocolHandlerCore is owned by the HttpProtocolHandler
81 // - The HttpProtocolHandler is owned by the system and can be deleted anytime
82 // - All the methods of HttpProtocolHandlerCore must be called on the IO thread,
83 //   except its constructor that can be called from any thread.
85 // Implementation notes from Apple's "Read Me About CustomHttpProtocolHandler":
87 // An NSURLProtocol subclass is expected to call the various methods of the
88 // NSURLProtocolClient from the loading thread, including all of the following:
89 //  -URLProtocol:wasRedirectedToRequest:redirectResponse:
90 //  -URLProtocol:didReceiveResponse:cacheStoragePolicy:
91 //  -URLProtocol:didLoadData:
92 //  -URLProtocol:didFinishLoading:
93 //  -URLProtocol:didFailWithError:
94 //  -URLProtocol:didReceiveAuthenticationChallenge:
95 //  -URLProtocol:didCancelAuthenticationChallenge:
97 // The NSURLProtocol subclass must call the client callbacks in the expected
98 // order. This breaks down into three phases:
99 //  o pre-response -- In the initial phase the NSURLProtocol can make any number
100 //    of -URLProtocol:wasRedirectedToRequest:redirectResponse: and
101 //    -URLProtocol:didReceiveAuthenticationChallenge: callbacks.
102 //  o response -- It must then call
103 //    -URLProtocol:didReceiveResponse:cacheStoragePolicy: to indicate the
104 //    arrival of a definitive response.
105 //  o post-response -- After receive a response it may then make any number of
106 //    -URLProtocol:didLoadData: callbacks, followed by a
107 //    -URLProtocolDidFinishLoading: callback.
109 // The -URLProtocol:didFailWithError: callback can be made at any time
110 // (although keep in mind the following point).
112 // The NSProtocol subclass must only send one authentication challenge to the
113 // client at a time. After calling
114 // -URLProtocol:didReceiveAuthenticationChallenge:, it must wait for the client
115 // to resolve the challenge before calling any callbacks other than
116 // -URLProtocol:didCancelAuthenticationChallenge:. This means that, if the
117 // connection fails while there is an outstanding authentication challenge, the
118 // NSURLProtocol subclass must call
119 // -URLProtocol:didCancelAuthenticationChallenge: before calling
120 // -URLProtocol:didFailWithError:.
121 class HttpProtocolHandlerCore
122     : public base::RefCountedThreadSafe<HttpProtocolHandlerCore,
123                                         HttpProtocolHandlerCore>,
124       public URLRequest::Delegate {
125  public:
126   HttpProtocolHandlerCore(NSURLRequest* request);
127   // Starts the network request, and forwards the data downloaded from the
128   // network to |base_client|.
129   void Start(id<CRNNetworkClientProtocol> base_client);
130   // Cancels the request.
131   void Cancel();
132   // Called by NSStreamDelegate. Used for POST requests having a HTTPBodyStream.
133   void HandleStreamEvent(NSStream* stream, NSStreamEvent event);
135   // URLRequest::Delegate methods:
136   void OnReceivedRedirect(URLRequest* request,
137                           const RedirectInfo& new_url,
138                           bool* defer_redirect) override;
139   void OnAuthRequired(URLRequest* request,
140                       AuthChallengeInfo* auth_info) override;
141   void OnCertificateRequested(URLRequest* request,
142                               SSLCertRequestInfo* cert_request_info) override;
143   void OnSSLCertificateError(URLRequest* request,
144                              const SSLInfo& ssl_info,
145                              bool is_hsts_host) override;
146   void OnResponseStarted(URLRequest* request) override;
147   void OnReadCompleted(URLRequest* request, int bytes_read) override;
149  private:
150   friend class base::RefCountedThreadSafe<HttpProtocolHandlerCore,
151                                           HttpProtocolHandlerCore>;
152   friend class base::DeleteHelper<HttpProtocolHandlerCore>;
153   ~HttpProtocolHandlerCore() override;
155   // RefCountedThreadSafe traits implementation:
156   static void Destruct(const HttpProtocolHandlerCore* x);
158   void SetLoadFlags();
159   void StopNetRequest();
160   // Stop listening the delegate on the IO run loop.
161   void StopListeningStream(NSStream* stream);
162   NSInteger IOSErrorCode(int os_error);
163   void StopRequestWithError(NSInteger ns_error_code, int net_error_code);
164   // Pass an authentication result provided by a client down to the network
165   // request. |auth_ok| is true if the authentication was successful, false
166   // otherwise. |username| and |password| should be populated with the correct
167   // credentials if |auth_ok| is true.
168   void CompleteAuthentication(bool auth_ok,
169                               const base::string16& username,
170                               const base::string16& password);
171   void StripPostSpecificHeaders(NSMutableURLRequest* request);
172   void CancelAfterSSLError();
173   void ContinueAfterSSLError();
174   void SSLErrorCallback(bool carryOn);
175   void HostStateCallback(bool carryOn);
176   void StartReading();
177   // Pushes |client| at the end of the |clients_| array and sets it as the top
178   // level client.
179   void PushClient(id<CRNNetworkClientProtocol> client);
180   // Pushes all of the clients in |clients|, calling PushClient() on each one.
181   void PushClients(NSArray* clients);
183   base::ThreadChecker thread_checker_;
185   // Contains CRNNetworkClientProtocol objects. The first client is the original
186   // NSURLProtocol client, and the following clients are ordered such as the
187   // ith client is responsible for managing the (i-1)th client.
188   base::scoped_nsobject<NSMutableArray> clients_;
189   // Weak. This is the last client in |clients_|.
190   id<CRNNetworkClientProtocol> top_level_client_;
191   scoped_refptr<IOBuffer> buffer_;
192   base::scoped_nsobject<NSMutableURLRequest> request_;
193   // Stream delegate to read the HTTPBodyStream.
194   base::scoped_nsobject<CRWHTTPStreamDelegate> stream_delegate_;
195   // Vector of readers used to accumulate a POST data stream.
196   ScopedVector<UploadElementReader> post_data_readers_;
198   // This cannot be a scoped pointer because it must be deleted on the IO
199   // thread.
200   URLRequest* net_request_;
202   base::WeakPtr<RequestTracker> tracker_;
204   DISALLOW_COPY_AND_ASSIGN(HttpProtocolHandlerCore);
207 HttpProtocolHandlerCore::HttpProtocolHandlerCore(NSURLRequest* request)
208     : clients_([[NSMutableArray alloc] init]),
209       top_level_client_(nil),
210       buffer_(new IOBuffer(kIOBufferSize)),
211       net_request_(nullptr) {
212   // The request will be accessed from another thread. It is safer to make a
213   // copy to avoid conflicts.
214   // The copy is mutable, because that request will be given to the client in
215   // case of a redirect, but with a different URL. The URL must be created
216   // from the absoluteString of the original URL, because mutableCopy only
217   // shallowly copies the request, and just retains the non-threadsafe NSURL.
218   thread_checker_.DetachFromThread();
219   request_.reset([request mutableCopy]);
220   [request_ setURL:[NSURL URLWithString:[[request URL] absoluteString]]];
223 void HttpProtocolHandlerCore::HandleStreamEvent(NSStream* stream,
224                                                 NSStreamEvent event) {
225   DCHECK(thread_checker_.CalledOnValidThread());
226   DCHECK(stream_delegate_);
227   switch (event) {
228     case NSStreamEventErrorOccurred:
229       DLOG(ERROR)
230           << "Failed to read POST data: "
231           << base::SysNSStringToUTF8([[stream streamError] description]);
232       StopListeningStream(stream);
233       StopRequestWithError(NSURLErrorUnknown, ERR_UNEXPECTED);
234       break;
235     case NSStreamEventEndEncountered:
236       StopListeningStream(stream);
237       if (!post_data_readers_.empty()) {
238         // NOTE: This call will result in |post_data_readers_| being cleared,
239         // which is the desired behavior.
240         net_request_->set_upload(make_scoped_ptr(
241             new ElementsUploadDataStream(post_data_readers_.Pass(), 0)));
242         DCHECK(post_data_readers_.empty());
243       }
244       net_request_->Start();
245       if (tracker_)
246         tracker_->StartRequest(net_request_);
247       break;
248     case NSStreamEventHasBytesAvailable: {
249       NSUInteger length;
250       DCHECK([stream isKindOfClass:[NSInputStream class]]);
251       length = [(NSInputStream*)stream read:(unsigned char*)buffer_->data()
252                                   maxLength:kIOBufferSize];
253       if (length) {
254         std::vector<char> owned_data(buffer_->data(), buffer_->data() + length);
255         post_data_readers_.push_back(
256             new UploadOwnedBytesElementReader(&owned_data));
257       }
258       break;
259     }
260     case NSStreamEventNone:
261     case NSStreamEventOpenCompleted:
262     case NSStreamEventHasSpaceAvailable:
263       break;
264     default:
265       NOTREACHED() << "Unexpected stream event: " << event;
266       break;
267   }
270 #pragma mark URLRequest::Delegate methods
272 void HttpProtocolHandlerCore::OnReceivedRedirect(
273     URLRequest* request,
274     const RedirectInfo& redirect_info,
275     bool* /* defer_redirect */) {
276   DCHECK(thread_checker_.CalledOnValidThread());
278   // Cancel the request and notify UIWebView.
279   // If we did nothing, the network stack would follow the redirect
280   // automatically, however we do not want this because we need the UIWebView to
281   // be notified. The UIWebView will then issue a new request following the
282   // redirect.
283   DCHECK(request_);
284   GURL new_url = redirect_info.new_url;
286   if (!new_url.is_valid()) {
287     StopRequestWithError(NSURLErrorBadURL, ERR_INVALID_URL);
288     return;
289   }
291   DCHECK(new_url.is_valid());
292   NSURL* new_nsurl = NSURLWithGURL(new_url);
293   // Stash the original URL in case we need to report it in an error.
294   [request_ setURL:new_nsurl];
296   if (stream_delegate_.get())
297     StopListeningStream([request_ HTTPBodyStream]);
299   // TODO(droger): See if we can share some code with URLRequest::Redirect() in
300   // net/net_url_request/url_request.cc.
302   // For 303 redirects, all request methods except HEAD are converted to GET,
303   // as per the latest httpbis draft.  The draft also allows POST requests to
304   // be converted to GETs when following 301/302 redirects, for historical
305   // reasons. Most major browsers do this and so shall we.
306   // See:
307   // https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-17#section-7.3
308   const int http_status_code = request->GetResponseCode();
309   NSString* method = [request_ HTTPMethod];
310   const bool was_post = [method isEqualToString:@"POST"];
311   if ((http_status_code == 303 && ![method isEqualToString:@"HEAD"]) ||
312       ((http_status_code == 301 || http_status_code == 302) && was_post)) {
313     [request_ setHTTPMethod:@"GET"];
314     [request_ setHTTPBody:nil];
315     [request_ setHTTPBodyStream:nil];
316     if (was_post) {
317       // If being switched from POST to GET, must remove headers that were
318       // specific to the POST and don't have meaning in GET. For example
319       // the inclusion of a multipart Content-Type header in GET can cause
320       // problems with some servers:
321       // http://code.google.com/p/chromium/issues/detail?id=843
322       StripPostSpecificHeaders(request_.get());
323     }
324   }
326   NSURLResponse* response = GetNSURLResponseForRequest(request);
327 #if !defined(NDEBUG)
328   DVLOG(2) << "Redirect, to client:";
329   LogNSURLResponse(response);
330   DVLOG(2) << "Redirect, to client:";
331   LogNSURLRequest(request_);
332 #endif  // !defined(NDEBUG)
333   if (tracker_) {
334     tracker_->StopRedirectedRequest(request);
335     // Add clients from tracker that depend on redirect data.
336     PushClients(tracker_->ClientsHandlingRedirect(*request, new_url, response));
337   }
339   [top_level_client_ wasRedirectedToRequest:request_
340                               nativeRequest:request
341                            redirectResponse:response];
342   // Don't use |request_| or |response| anymore, as the client may be using them
343   // on another thread and they are not re-entrant. As |request_| is mutable, it
344   // is also important that it is not modified.
345   request_.reset(nil);
346   request->Cancel();
347   DCHECK_EQ(net_request_, request);
348   StopNetRequest();
351 void HttpProtocolHandlerCore::OnAuthRequired(URLRequest* request,
352                                              AuthChallengeInfo* auth_info) {
353   DCHECK(thread_checker_.CalledOnValidThread());
354   // A request with no tab ID should not hit HTTP authentication.
355   if (tracker_) {
356     // UIWebView does not handle authentication, so there is no point in calling
357     // the protocol method didReceiveAuthenticationChallenge.
358     // Instead, clients may handle proxy auth or display a UI to handle the
359     // challenge.
360     // Pass a weak reference, to avoid retain cycles.
361     network_client::AuthCallback callback =
362         base::Bind(&HttpProtocolHandlerCore::CompleteAuthentication,
363                    base::Unretained(this));
364     [top_level_client_ didRecieveAuthChallenge:auth_info
365                                  nativeRequest:*request
366                                       callback:callback];
367   } else if (net_request_ != nullptr) {
368     net_request_->CancelAuth();
369   }
372 void HttpProtocolHandlerCore::OnCertificateRequested(
373     URLRequest* request,
374     SSLCertRequestInfo* cert_request_info) {
375   DCHECK(thread_checker_.CalledOnValidThread());
377   // TODO(ios): The network stack does not support SSL client authentication
378   // on iOS yet. The request has to be canceled for now.
379   request->Cancel();
380   StopRequestWithError(NSURLErrorClientCertificateRequired,
381                        ERR_SSL_PROTOCOL_ERROR);
384 void HttpProtocolHandlerCore::ContinueAfterSSLError(void) {
385   DCHECK(thread_checker_.CalledOnValidThread());
386   if (net_request_ != nullptr) {
387     // Continue the request and load the data.
388     net_request_->ContinueDespiteLastError();
389   }
392 void HttpProtocolHandlerCore::CancelAfterSSLError(void) {
393   DCHECK(thread_checker_.CalledOnValidThread());
394   if (net_request_ != nullptr) {
395     // Cancel the request.
396     net_request_->Cancel();
398     // The request is signalled simply cancelled to the consumer, the
399     // presentation of the SSL error will be done via the tracker.
400     StopRequestWithError(NSURLErrorCancelled, ERR_BLOCKED_BY_CLIENT);
401   }
404 void HttpProtocolHandlerCore::SSLErrorCallback(bool carryOn) {
405   DCHECK(thread_checker_.CalledOnValidThread());
406   if (carryOn)
407     ContinueAfterSSLError();
408   else
409     CancelAfterSSLError();
412 void HttpProtocolHandlerCore::HostStateCallback(bool carryOn) {
413   DCHECK(thread_checker_.CalledOnValidThread());
414   if (carryOn)
415     StartReading();
416   else
417     CancelAfterSSLError();
420 void HttpProtocolHandlerCore::OnSSLCertificateError(URLRequest* request,
421                                                     const SSLInfo& ssl_info,
422                                                     bool is_hsts_host) {
423   DCHECK(thread_checker_.CalledOnValidThread());
425   if (is_hsts_host) {
426     if (tracker_) {
427       tracker_->OnSSLCertificateError(request, ssl_info, false,
428                                       base::Bind(&DoNothing));
429     }
430     CancelAfterSSLError();  // High security host do not tolerate any issue.
431   } else if (!tracker_) {
432     // No tracker, this is a request outside the context of a tab. There is no
433     // way to present anything to the user so only allow trivial errors.
434     // See ssl_cert_error_handler upstream.
435     if (IsCertStatusMinorError(ssl_info.cert_status))
436       ContinueAfterSSLError();
437     else
438       CancelAfterSSLError();
439   } else {
440     // The tracker will decide, eventually asking the user, and will invoke the
441     // callback.
442     RequestTracker::SSLCallback callback =
443         base::Bind(&HttpProtocolHandlerCore::SSLErrorCallback, this);
444     DCHECK(tracker_);
445     tracker_->OnSSLCertificateError(request, ssl_info, !is_hsts_host, callback);
446   }
449 void HttpProtocolHandlerCore::OnResponseStarted(URLRequest* request) {
450   DCHECK(thread_checker_.CalledOnValidThread());
452   if (net_request_ == nullptr)
453     return;
455   const URLRequestStatus& status = request->status();
456   if (!status.is_success()) {
457     int error = status.error();
458     StopRequestWithError(IOSErrorCode(error), error);
459     return;
460   }
462   if (tracker_ && IsCertStatusError(request->ssl_info().cert_status) &&
463       !request->context()->GetNetworkSessionParams()->
464           ignore_certificate_errors) {
465     // The certificate policy cache is captured here because SSL errors do not
466     // always trigger OnSSLCertificateError (this is the case when a page comes
467     // from the HTTP cache).
468     RequestTracker::SSLCallback callback =
469         base::Bind(&HttpProtocolHandlerCore::HostStateCallback, this);
470     tracker_->CaptureCertificatePolicyCache(request, callback);
471     return;
472   }
474   StartReading();
477 void HttpProtocolHandlerCore::StartReading() {
478   DCHECK(thread_checker_.CalledOnValidThread());
479   if (net_request_ == nullptr)
480     return;
482   NSURLResponse* response = GetNSURLResponseForRequest(net_request_);
483 #if !defined(NDEBUG)
484   DVLOG(2) << "To client:";
485   LogNSURLResponse(response);
486 #endif  // !defined(NDEBUG)
488   if (tracker_) {
489     tracker_->CaptureHeaders(net_request_);
490     long long expectedContentLength = [response expectedContentLength];
491     if (expectedContentLength > 0)
492       tracker_->CaptureExpectedLength(net_request_, expectedContentLength);
494     // Add clients from tracker.
495     PushClients(
496         tracker_->ClientsHandlingRequestAndResponse(*net_request_, response));
497   }
499   // Don't call any function on the response from now on, as the client may be
500   // using it and the object is not re-entrant.
501   [top_level_client_ didReceiveResponse:response];
503   int bytes_read = 0;
505   if (net_request_->Read(buffer_.get(), kIOBufferSize, &bytes_read)) {
506     OnReadCompleted(net_request_, bytes_read);
507   } else if (!net_request_->status().is_success()) {
508     int error = net_request_->status().error();
509     StopRequestWithError(IOSErrorCode(error), error);
510   }
513 void HttpProtocolHandlerCore::OnReadCompleted(URLRequest* request,
514                                               int bytes_read) {
515   DCHECK(thread_checker_.CalledOnValidThread());
517   if (net_request_ == nullptr)
518     return;
520   base::scoped_nsobject<NSMutableData> data([[NSMutableData alloc] init]);
522   // Read all we can from the socket and put it into data.
523   // TODO(droger): It may be possible to avoid some of the copies (using
524   // WrappedIOBuffer for example).
525   NSUInteger data_length;
526   bool loop = (bytes_read > 0);
527   bool io_pending = false;
528   uint64_t total_byte_read = loop ? bytes_read : 0;
529   while (loop) {
530     data_length = [data length];  // Assumes that getting the length is fast.
531     [data increaseLengthBy:bytes_read];
532     memcpy(reinterpret_cast<char*>([data mutableBytes]) + data_length,
533            buffer_->data(), bytes_read);
534     io_pending = !request->Read(buffer_.get(), kIOBufferSize, &bytes_read);
535     loop = !io_pending && (bytes_read > 0);
536     total_byte_read += bytes_read;
537   }
539   if (tracker_)
540     tracker_->CaptureReceivedBytes(request, total_byte_read);
542   // Notify the client.
543   const URLRequestStatus& status = request->status();
544   if (status.is_success()) {
545     if ([data length] > 0) {
546       // If the data is not encoded in UTF8, the NSString is nil.
547       DVLOG(3) << "To client:" << std::endl
548                << base::SysNSStringToUTF8([[[NSString alloc]
549                       initWithData:data
550                           encoding:NSUTF8StringEncoding] autorelease]);
551       [top_level_client_ didLoadData:data];
552     }
553     if (bytes_read == 0 && !io_pending) {
554       DCHECK_EQ(net_request_, request);
555       // There is nothing more to read.
556       StopNetRequest();
557       [top_level_client_ didFinishLoading];
558     }
559   } else {
560     // Request failed (not canceled).
561     int error = status.error();
562     StopRequestWithError(IOSErrorCode(error), error);
563   }
566 HttpProtocolHandlerCore::~HttpProtocolHandlerCore() {
567   DCHECK(thread_checker_.CalledOnValidThread());
568   [top_level_client_ cancelAuthRequest];
569   DCHECK(!net_request_);
570   DCHECK(!stream_delegate_);
573 // static
574 void HttpProtocolHandlerCore::Destruct(const HttpProtocolHandlerCore* x) {
575   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
576       g_protocol_handler_delegate->GetDefaultURLRequestContext()
577           ->GetNetworkTaskRunner();
578   if (task_runner->BelongsToCurrentThread())
579     delete x;
580   else
581     task_runner->DeleteSoon(FROM_HERE, x);
584 void HttpProtocolHandlerCore::SetLoadFlags() {
585   DCHECK(thread_checker_.CalledOnValidThread());
587   int load_flags = LOAD_VERIFY_EV_CERT;
588   // TODO(droger) Support MAIN_FRAME and SUB_FRAME flags.
589   if (![request_ HTTPShouldHandleCookies])
590     load_flags |= LOAD_DO_NOT_SEND_COOKIES | LOAD_DO_NOT_SAVE_COOKIES;
592   // Cache flags.
593   if (tracker_) {
594     RequestTracker::CacheMode cache_mode = tracker_->GetCacheMode();
595     switch (cache_mode) {
596       case RequestTracker::CACHE_RELOAD:
597         load_flags |= LOAD_VALIDATE_CACHE;
598         break;
599       case RequestTracker::CACHE_HISTORY:
600         load_flags |= LOAD_PREFERRING_CACHE;
601         break;
602       case RequestTracker::CACHE_BYPASS:
603         load_flags |= LOAD_DISABLE_CACHE | LOAD_BYPASS_CACHE;
604         break;
605       case RequestTracker::CACHE_ONLY:
606         load_flags |= LOAD_ONLY_FROM_CACHE;
607         break;
608       case RequestTracker::CACHE_NORMAL:
609         // Do nothing, normal load.
610         break;
611     }
612   } else {
613     switch ([request_ cachePolicy]) {
614       case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
615         load_flags |= LOAD_BYPASS_CACHE;
616       case NSURLRequestReloadIgnoringLocalCacheData:
617         load_flags |= LOAD_DISABLE_CACHE;
618         break;
619       case NSURLRequestReturnCacheDataElseLoad:
620         load_flags |= LOAD_PREFERRING_CACHE;
621         break;
622       case NSURLRequestReturnCacheDataDontLoad:
623         load_flags |= LOAD_ONLY_FROM_CACHE;
624         break;
625       case NSURLRequestReloadRevalidatingCacheData:
626         load_flags |= LOAD_VALIDATE_CACHE;
627         break;
628       default:
629         // For the NSURLRequestUseProtocolCachePolicy case.
630         break;
631     }
632   }
633   net_request_->SetLoadFlags(load_flags);
636 void HttpProtocolHandlerCore::Start(id<CRNNetworkClientProtocol> base_client) {
637   DCHECK(thread_checker_.CalledOnValidThread());
638   DCHECK(!top_level_client_);
639   DCHECK_EQ(0u, [clients_ count]);
640   DCHECK(base_client);
641   top_level_client_ = base_client;
642   [clients_ addObject:base_client];
643   GURL url = GURLWithNSURL([request_ URL]);
645   bool valid_tracker = RequestTracker::GetRequestTracker(request_, &tracker_);
646   if (!valid_tracker) {
647     // The request is associated with a tracker that does not exist, cancel it.
648     // NSURLErrorCancelled avoids triggering any error page.
649     [top_level_client_ didFailWithNSErrorCode:NSURLErrorCancelled
650                                  netErrorCode:ERR_ABORTED];
651     return;
652   }
654   if (tracker_) {
655     // Set up any clients that can operate regardless of the request
656     PushClients(tracker_->ClientsHandlingAnyRequest());
657   } else {
658     // There was no request_group_id, so the request was from something like a
659     // data: or file: URL.
660     // Attach any global clients to the request.
661     PushClients(RequestTracker::GlobalClientsHandlingAnyRequest());
662   }
664   // Now that all of the network clients are set up, if there was an error with
665   // the URL, it can be raised and all of the clients will have a chance to
666   // handle it.
667   if (!url.is_valid()) {
668     DLOG(ERROR) << "Trying to load an invalid URL: "
669                 << base::SysNSStringToUTF8([[request_ URL] absoluteString]);
670     [top_level_client_ didFailWithNSErrorCode:NSURLErrorBadURL
671                                  netErrorCode:ERR_INVALID_URL];
672     return;
673   }
675   const URLRequestContext* context =
676       tracker_ ? tracker_->GetRequestContext()
677                : g_protocol_handler_delegate->GetDefaultURLRequestContext()
678                      ->GetURLRequestContext();
679   DCHECK(context);
681   net_request_ =
682       context->CreateRequest(url, DEFAULT_PRIORITY, this).release();
683   net_request_->set_method(base::SysNSStringToUTF8([request_ HTTPMethod]));
685   net_request_->set_first_party_for_cookies(
686       GURLWithNSURL([request_ mainDocumentURL]));
688 #if !defined(NDEBUG)
689   DVLOG(2) << "From client:";
690   LogNSURLRequest(request_);
691 #endif  // !defined(NDEBUG)
693   CopyHttpHeaders(request_, net_request_);
695   // Add network clients.
696   if (tracker_)
697     PushClients(tracker_->ClientsHandlingRequest(*net_request_));
699   [top_level_client_ didCreateNativeRequest:net_request_];
700   SetLoadFlags();
702   if ([request_ HTTPBodyStream]) {
703     NSInputStream* input_stream = [request_ HTTPBodyStream];
704     stream_delegate_.reset(
705         [[CRWHTTPStreamDelegate alloc] initWithHttpProtocolHandlerCore:this]);
706     [input_stream setDelegate:stream_delegate_];
707     [input_stream scheduleInRunLoop:[NSRunLoop currentRunLoop]
708                             forMode:NSDefaultRunLoopMode];
709     [input_stream open];
710     // The request will be started when the stream is fully read.
711     return;
712   }
714   NSData* body = [request_ HTTPBody];
715   const NSUInteger body_length = [body length];
716   if (body_length > 0) {
717     const char* source_bytes = reinterpret_cast<const char*>([body bytes]);
718     std::vector<char> owned_data(source_bytes, source_bytes + body_length);
719     scoped_ptr<UploadElementReader> reader(
720         new UploadOwnedBytesElementReader(&owned_data));
721     net_request_->set_upload(
722         ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
723   }
725   net_request_->Start();
726   if (tracker_)
727     tracker_->StartRequest(net_request_);
730 void HttpProtocolHandlerCore::Cancel() {
731   DCHECK(thread_checker_.CalledOnValidThread());
732   if (net_request_ == nullptr)
733     return;
735   DVLOG(2) << "Client canceling request: " << net_request_->url().spec();
736   net_request_->Cancel();
737   StopNetRequest();
740 void HttpProtocolHandlerCore::StopNetRequest() {
741   DCHECK(thread_checker_.CalledOnValidThread());
742   if (tracker_)
743     tracker_->StopRequest(net_request_);
744   delete net_request_;
745   net_request_ = nullptr;
746   if (stream_delegate_.get())
747     StopListeningStream([request_ HTTPBodyStream]);
750 void HttpProtocolHandlerCore::StopListeningStream(NSStream* stream) {
751   DCHECK(thread_checker_.CalledOnValidThread());
752   DCHECK(stream);
753   DCHECK(stream_delegate_);
754   DCHECK([stream delegate] == stream_delegate_.get());
755   [stream setDelegate:nil];
756   [stream removeFromRunLoop:[NSRunLoop currentRunLoop]
757                     forMode:NSDefaultRunLoopMode];
758   stream_delegate_.reset(nil);
759   // Close the stream if needed.
760   switch ([stream streamStatus]) {
761     case NSStreamStatusOpening:
762     case NSStreamStatusOpen:
763     case NSStreamStatusReading:
764     case NSStreamStatusWriting:
765     case NSStreamStatusAtEnd:
766       [stream close];
767       break;
768     case NSStreamStatusNotOpen:
769     case NSStreamStatusClosed:
770     case NSStreamStatusError:
771       break;
772     default:
773       NOTREACHED() << "Unexpected stream status: " << [stream streamStatus];
774       break;
775   }
778 NSInteger HttpProtocolHandlerCore::IOSErrorCode(int os_error) {
779   DCHECK(thread_checker_.CalledOnValidThread());
780   switch (os_error) {
781     case ERR_SSL_PROTOCOL_ERROR:
782       return NSURLErrorClientCertificateRequired;
783     case ERR_CONNECTION_RESET:
784     case ERR_NETWORK_CHANGED:
785       return NSURLErrorNetworkConnectionLost;
786     case ERR_UNEXPECTED:
787       return NSURLErrorUnknown;
788     default:
789       return NSURLErrorCannotConnectToHost;
790   }
793 void HttpProtocolHandlerCore::StopRequestWithError(NSInteger ns_error_code,
794                                                    int net_error_code) {
795   DCHECK(net_request_ != nullptr);
796   DCHECK(thread_checker_.CalledOnValidThread());
798   // Don't show an error message on ERR_ABORTED because this is error is often
799   // fired when switching profiles (see RequestTracker::CancelRequests()).
800   DLOG_IF(ERROR, net_error_code != ERR_ABORTED)
801       << "HttpProtocolHandlerCore - Network error: "
802       << ErrorToString(net_error_code) << " (" << net_error_code << ")";
804   [top_level_client_ didFailWithNSErrorCode:ns_error_code
805                                netErrorCode:net_error_code];
806   StopNetRequest();
809 void HttpProtocolHandlerCore::CompleteAuthentication(
810     bool auth_ok,
811     const base::string16& username,
812     const base::string16& password) {
813   DCHECK(thread_checker_.CalledOnValidThread());
814   if (net_request_ == nullptr)
815     return;
816   if (auth_ok) {
817     net_request_->SetAuth(AuthCredentials(username, password));
818   } else {
819     net_request_->CancelAuth();
820   }
823 void HttpProtocolHandlerCore::StripPostSpecificHeaders(
824     NSMutableURLRequest* request) {
825   DCHECK(thread_checker_.CalledOnValidThread());
826   DCHECK(request);
827   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString(
828       HttpRequestHeaders::kContentLength)];
829   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString(
830       HttpRequestHeaders::kContentType)];
831   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString(
832       HttpRequestHeaders::kOrigin)];
835 void HttpProtocolHandlerCore::PushClient(id<CRNNetworkClientProtocol> client) {
836   DCHECK(thread_checker_.CalledOnValidThread());
837   [client setUnderlyingClient:top_level_client_];
838   [clients_ addObject:client];
839   top_level_client_ = client;
842 void HttpProtocolHandlerCore::PushClients(NSArray* clients) {
843   DCHECK(thread_checker_.CalledOnValidThread());
844   for (id<CRNNetworkClientProtocol> client in clients)
845     PushClient(client);
848 }  // namespace net
850 #pragma mark -
851 #pragma mark CRWHTTPStreamDelegate
853 @implementation CRWHTTPStreamDelegate
854 - (instancetype)initWithHttpProtocolHandlerCore:
855     (net::HttpProtocolHandlerCore*)core {
856   DCHECK(core);
857   self = [super init];
858   if (self)
859     _core = core;
860   return self;
863 - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent {
864   _core->HandleStreamEvent(theStream, streamEvent);
866 @end
868 #pragma mark -
869 #pragma mark HttpProtocolHandler
871 // The HttpProtocolHandler is called by the iOS system to handle the
872 // NSURLRequest.
873 @implementation CRNHTTPProtocolHandler {
874   scoped_refptr<net::HttpProtocolHandlerCore> _core;
875   base::scoped_nsprotocol<id<CRNHTTPProtocolHandlerProxy>> _protocolProxy;
876   BOOL _supportedURL;
879 #pragma mark NSURLProtocol methods
881 + (BOOL)canInitWithRequest:(NSURLRequest*)request {
882   DVLOG(5) << "canInitWithRequest " << net::FormatUrlRequestForLogging(request);
883   return g_protocol_handler_delegate->CanHandleRequest(request);
886 + (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request {
887   // TODO(droger): Is this used if we disable the cache of UIWebView? If it is,
888   // then we need a real implementation, even though Chrome network stack does
889   // not need it (GURLs are automatically canonized).
890   return request;
893 - (instancetype)initWithRequest:(NSURLRequest*)request
894                  cachedResponse:(NSCachedURLResponse*)cachedResponse
895                          client:(id<NSURLProtocolClient>)client {
896   DCHECK(!cachedResponse);
897   self = [super initWithRequest:request
898                  cachedResponse:cachedResponse
899                          client:client];
900   if (self) {
901     _supportedURL = g_protocol_handler_delegate->IsRequestSupported(request);
902     _core = new net::HttpProtocolHandlerCore(request);
903   }
904   return self;
907 #pragma mark NSURLProtocol overrides.
909 - (NSCachedURLResponse*)cachedResponse {
910   // We do not use the UIWebView cache.
911   // TODO(droger): Disable the UIWebView cache.
912   return nil;
915 - (void)startLoading {
916   // If the scheme is not valid, just return an error right away.
917   if (!_supportedURL) {
918     NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
920     // It is possible for URL to be nil, so check for that
921     // before creating the error object. See http://crbug/349051
922     NSURL* url = [[self request] URL];
923     if (url)
924       [dictionary setObject:url forKey:NSURLErrorKey];
926     NSError* error = [NSError errorWithDomain:NSURLErrorDomain
927                                          code:NSURLErrorUnsupportedURL
928                                      userInfo:dictionary];
929     [[self client] URLProtocol:self didFailWithError:error];
930     return;
931   }
933   _protocolProxy.reset([[CRNHTTPProtocolHandlerProxyWithClientThread alloc]
934       initWithProtocol:self
935           clientThread:[NSThread currentThread]
936            runLoopMode:[[NSRunLoop currentRunLoop] currentMode]]);
937   g_protocol_handler_delegate->GetDefaultURLRequestContext()
938       ->GetNetworkTaskRunner()
939       ->PostTask(FROM_HERE, base::Bind(&net::HttpProtocolHandlerCore::Start,
940                                        _core, _protocolProxy));
943 - (void)stopLoading {
944   g_protocol_handler_delegate->GetDefaultURLRequestContext()
945       ->GetNetworkTaskRunner()
946       ->PostTask(FROM_HERE,
947                  base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core));
948   [_protocolProxy invalidate];
949   _protocolProxy.reset();
952 @end