| // Copyright 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #import "ios/net/crn_http_protocol_handler.h" | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <memory> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/command_line.h" | 
 | #include "base/logging.h" | 
 | #include "base/mac/foundation_util.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/sys_string_conversions.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "ios/net/chunked_data_stream_uploader.h" | 
 | #import "ios/net/clients/crn_network_client_protocol.h" | 
 | #import "ios/net/crn_http_protocol_handler_proxy_with_client_thread.h" | 
 | #import "ios/net/http_protocol_logging.h" | 
 | #include "ios/net/nsurlrequest_util.h" | 
 | #import "ios/net/protocol_handler_util.h" | 
 | #include "ios/net/request_tracker.h" | 
 | #include "net/base/auth.h" | 
 | #include "net/base/elements_upload_data_stream.h" | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/load_flags.h" | 
 | #import "net/base/mac/url_conversions.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/upload_bytes_element_reader.h" | 
 | #include "net/http/http_request_headers.h" | 
 | #include "net/url_request/redirect_info.h" | 
 | #include "net/url_request/url_request.h" | 
 | #include "net/url_request/url_request_context.h" | 
 | #include "net/url_request/url_request_context_getter.h" | 
 |  | 
 | #if !defined(__has_feature) || !__has_feature(objc_arc) | 
 | #error "This file requires ARC support." | 
 | #endif | 
 |  | 
 | namespace net { | 
 | class HttpProtocolHandlerCore; | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // Minimum size of the buffer used to read the net::URLRequest. | 
 | const int kIOBufferMinSize = 64 * 1024; | 
 |  | 
 | // Maximum size of the buffer used to read the net::URLRequest. | 
 | const int kIOBufferMaxSize = 16 * kIOBufferMinSize;  // 1MB | 
 |  | 
 | // Global instance of the HTTPProtocolHandlerDelegate. | 
 | net::HTTPProtocolHandlerDelegate* g_protocol_handler_delegate = nullptr; | 
 |  | 
 | // Global instance of the MetricsDelegate. | 
 | net::MetricsDelegate* g_metrics_delegate = nullptr; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Bridge class to forward NSStream events to the HttpProtocolHandlerCore. | 
 | // Lives on the IO thread. | 
 | @interface CRWHTTPStreamDelegate : NSObject<NSStreamDelegate> { | 
 |  @private | 
 |   // The object is owned by |_core| and has a weak reference to it. | 
 |   net::HttpProtocolHandlerCore* _core;  // weak | 
 | } | 
 | - (instancetype)initWithHttpProtocolHandlerCore: | 
 |     (net::HttpProtocolHandlerCore*)core; | 
 | // NSStreamDelegate method. | 
 | - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent; | 
 | @end | 
 |  | 
 | #pragma mark - | 
 | #pragma mark HttpProtocolHandlerCore | 
 |  | 
 | namespace net { | 
 |  | 
 | MetricsDelegate::Metrics::Metrics() = default; | 
 | MetricsDelegate::Metrics::~Metrics() = default; | 
 |  | 
 | // static | 
 | void HTTPProtocolHandlerDelegate::SetInstance( | 
 |     HTTPProtocolHandlerDelegate* delegate) { | 
 |   g_protocol_handler_delegate = delegate; | 
 | } | 
 |  | 
 | // static | 
 | void MetricsDelegate::SetInstance(MetricsDelegate* delegate) { | 
 |   g_metrics_delegate = delegate; | 
 | } | 
 |  | 
 | // The HttpProtocolHandlerCore class is the bridge between the URLRequest | 
 | // and the NSURLProtocolClient. | 
 | // Threading and ownership details: | 
 | // - The HttpProtocolHandlerCore is owned by the HttpProtocolHandler | 
 | // - The HttpProtocolHandler is owned by the system and can be deleted anytime | 
 | // - All the methods of HttpProtocolHandlerCore must be called on the IO thread, | 
 | //   except its constructor that can be called from any thread. | 
 |  | 
 | // Implementation notes from Apple's "Read Me About CustomHttpProtocolHandler": | 
 | // | 
 | // An NSURLProtocol subclass is expected to call the various methods of the | 
 | // NSURLProtocolClient from the loading thread, including all of the following: | 
 | //  -URLProtocol:wasRedirectedToRequest:redirectResponse: | 
 | //  -URLProtocol:didReceiveResponse:cacheStoragePolicy: | 
 | //  -URLProtocol:didLoadData: | 
 | //  -URLProtocol:didFinishLoading: | 
 | //  -URLProtocol:didFailWithError: | 
 | //  -URLProtocol:didReceiveAuthenticationChallenge: | 
 | //  -URLProtocol:didCancelAuthenticationChallenge: | 
 | // | 
 | // The NSURLProtocol subclass must call the client callbacks in the expected | 
 | // order. This breaks down into three phases: | 
 | //  o pre-response -- In the initial phase the NSURLProtocol can make any number | 
 | //    of -URLProtocol:wasRedirectedToRequest:redirectResponse: and | 
 | //    -URLProtocol:didReceiveAuthenticationChallenge: callbacks. | 
 | //  o response -- It must then call | 
 | //    -URLProtocol:didReceiveResponse:cacheStoragePolicy: to indicate the | 
 | //    arrival of a definitive response. | 
 | //  o post-response -- After receive a response it may then make any number of | 
 | //    -URLProtocol:didLoadData: callbacks, followed by a | 
 | //    -URLProtocolDidFinishLoading: callback. | 
 | // | 
 | // The -URLProtocol:didFailWithError: callback can be made at any time | 
 | // (although keep in mind the following point). | 
 | // | 
 | // The NSProtocol subclass must only send one authentication challenge to the | 
 | // client at a time. After calling | 
 | // -URLProtocol:didReceiveAuthenticationChallenge:, it must wait for the client | 
 | // to resolve the challenge before calling any callbacks other than | 
 | // -URLProtocol:didCancelAuthenticationChallenge:. This means that, if the | 
 | // connection fails while there is an outstanding authentication challenge, the | 
 | // NSURLProtocol subclass must call | 
 | // -URLProtocol:didCancelAuthenticationChallenge: before calling | 
 | // -URLProtocol:didFailWithError:. | 
 | class HttpProtocolHandlerCore | 
 |     : public base::RefCountedThreadSafe<HttpProtocolHandlerCore, | 
 |                                         HttpProtocolHandlerCore>, | 
 |       public URLRequest::Delegate, | 
 |       public ChunkedDataStreamUploader::Delegate { | 
 |  public: | 
 |   explicit HttpProtocolHandlerCore(NSURLRequest* request); | 
 |   explicit HttpProtocolHandlerCore(NSURLSessionTask* task); | 
 |   // Starts the network request, and forwards the data downloaded from the | 
 |   // network to |base_client|. | 
 |   void Start(id<CRNNetworkClientProtocol> base_client); | 
 |   // Cancels the request. | 
 |   void Cancel(); | 
 |   // Called by NSStreamDelegate. Used for POST requests having a HTTPBodyStream. | 
 |   void HandleStreamEvent(NSStream* stream, NSStreamEvent event); | 
 |  | 
 |   // URLRequest::Delegate methods: | 
 |   void OnReceivedRedirect(URLRequest* request, | 
 |                           const RedirectInfo& new_url, | 
 |                           bool* defer_redirect) override; | 
 |   void OnAuthRequired(URLRequest* request, | 
 |                       AuthChallengeInfo* auth_info) override; | 
 |   void OnCertificateRequested(URLRequest* request, | 
 |                               SSLCertRequestInfo* cert_request_info) override; | 
 |   void OnSSLCertificateError(URLRequest* request, | 
 |                              const SSLInfo& ssl_info, | 
 |                              bool fatal) override; | 
 |   void OnResponseStarted(URLRequest* request, int net_error) override; | 
 |   void OnReadCompleted(URLRequest* request, int bytes_read) override; | 
 |  | 
 |   // ChunkedDataStreamUploader::Delegate method: | 
 |   int OnRead(char* buffer, int buffer_length) override; | 
 |  | 
 |  private: | 
 |   friend class base::RefCountedThreadSafe<HttpProtocolHandlerCore, | 
 |                                           HttpProtocolHandlerCore>; | 
 |   friend class base::DeleteHelper<HttpProtocolHandlerCore>; | 
 |   ~HttpProtocolHandlerCore() override; | 
 |  | 
 |   // RefCountedThreadSafe traits implementation: | 
 |   static void Destruct(const HttpProtocolHandlerCore* x); | 
 |  | 
 |   void SetLoadFlags(); | 
 |   void StopNetRequest(); | 
 |   // Stop listening the delegate on the IO run loop. | 
 |   void StopListeningStream(NSStream* stream); | 
 |   NSInteger IOSErrorCode(int os_error); | 
 |   void StopRequestWithError(NSInteger ns_error_code, int net_error_code); | 
 |   // Pass an authentication result provided by a client down to the network | 
 |   // request. |auth_ok| is true if the authentication was successful, false | 
 |   // otherwise. |username| and |password| should be populated with the correct | 
 |   // credentials if |auth_ok| is true. | 
 |   void CompleteAuthentication(bool auth_ok, | 
 |                               const base::string16& username, | 
 |                               const base::string16& password); | 
 |   void StripPostSpecificHeaders(NSMutableURLRequest* request); | 
 |   void CancelAfterSSLError(); | 
 |   void ContinueAfterSSLError(); | 
 |   void SSLErrorCallback(bool carryOn); | 
 |   void HostStateCallback(bool carryOn); | 
 |   void StartReading(); | 
 |   void AllocateReadBuffer(int last_read_data_size); | 
 |  | 
 |   base::ThreadChecker thread_checker_; | 
 |  | 
 |   // The NSURLProtocol client. | 
 |   id<CRNNetworkClientProtocol> client_; | 
 |   std::unique_ptr<char, base::FreeDeleter> read_buffer_; | 
 |   int read_buffer_size_; | 
 |   scoped_refptr<WrappedIOBuffer> read_buffer_wrapper_; | 
 |   NSMutableURLRequest* request_; | 
 |   NSURLSessionTask* task_; | 
 |   // Stream delegate to read the HTTPBodyStream. | 
 |   CRWHTTPStreamDelegate* stream_delegate_; | 
 |   // Vector of readers used to accumulate a POST data stream. | 
 |   std::vector<std::unique_ptr<UploadElementReader>> post_data_readers_; | 
 |  | 
 |   // This cannot be a scoped pointer because it must be deleted on the IO | 
 |   // thread. | 
 |   URLRequest* net_request_; | 
 |  | 
 |   // It is a weak pointer because the owner of the uploader is the URLRequest. | 
 |   base::WeakPtr<ChunkedDataStreamUploader> chunked_uploader_; | 
 |  | 
 |   // The stream has data to upload. | 
 |   NSInputStream* pending_stream_; | 
 |  | 
 |   base::WeakPtr<RequestTracker> tracker_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(HttpProtocolHandlerCore); | 
 | }; | 
 |  | 
 | HttpProtocolHandlerCore::HttpProtocolHandlerCore(NSURLRequest* request) | 
 |     : client_(nil), | 
 |       read_buffer_size_(kIOBufferMinSize), | 
 |       read_buffer_wrapper_(nullptr), | 
 |       net_request_(nullptr), | 
 |       pending_stream_(nil) { | 
 |   // The request will be accessed from another thread. It is safer to make a | 
 |   // copy to avoid conflicts. | 
 |   // The copy is mutable, because that request will be given to the client in | 
 |   // case of a redirect, but with a different URL. The URL must be created | 
 |   // from the absoluteString of the original URL, because mutableCopy only | 
 |   // shallowly copies the request, and just retains the non-threadsafe NSURL. | 
 |   thread_checker_.DetachFromThread(); | 
 |   task_ = nil; | 
 |   request_ = [request mutableCopy]; | 
 |   // Will allocate read buffer with size |kIOBufferMinSize|. | 
 |   AllocateReadBuffer(0); | 
 |   [request_ setURL:request.URL]; | 
 | } | 
 |  | 
 | HttpProtocolHandlerCore::HttpProtocolHandlerCore(NSURLSessionTask* task) | 
 |     : HttpProtocolHandlerCore([task currentRequest]) { | 
 |   task_ = task; | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::HandleStreamEvent(NSStream* stream, | 
 |                                                 NSStreamEvent event) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   DCHECK(stream_delegate_); | 
 |   switch (event) { | 
 |     case NSStreamEventErrorOccurred: | 
 |       DLOG(ERROR) | 
 |           << "Failed to read POST data: " | 
 |           << base::SysNSStringToUTF8([[stream streamError] description]); | 
 |       StopListeningStream(stream); | 
 |       StopRequestWithError(NSURLErrorUnknown, ERR_UNEXPECTED); | 
 |       break; | 
 |     case NSStreamEventEndEncountered: | 
 |       StopListeningStream(stream); | 
 |       if (chunked_uploader_) { | 
 |         chunked_uploader_->UploadWhenReady(true); | 
 |         break; | 
 |       } | 
 |  | 
 |       if (!post_data_readers_.empty()) { | 
 |         // NOTE: This call will result in |post_data_readers_| being cleared, | 
 |         // which is the desired behavior. | 
 |         net_request_->set_upload(std::make_unique<ElementsUploadDataStream>( | 
 |             std::move(post_data_readers_), 0)); | 
 |         DCHECK(post_data_readers_.empty()); | 
 |       } | 
 |       net_request_->Start(); | 
 |       if (tracker_) | 
 |         tracker_->StartRequest(net_request_); | 
 |       break; | 
 |     case NSStreamEventHasBytesAvailable: { | 
 |       if (chunked_uploader_) { | 
 |         DCHECK(pending_stream_ == nil || pending_stream_ == stream); | 
 |         pending_stream_ = base::mac::ObjCCastStrict<NSInputStream>(stream); | 
 |         chunked_uploader_->UploadWhenReady(false); | 
 |         break; | 
 |       } | 
 |  | 
 |       NSInteger length; | 
 |       // TODO(crbug.com/738025): Dynamically change the size of the read buffer | 
 |       // to improve the read (POST) performance, see AllocateReadBuffer(), & | 
 |       // avoid unnecessary data copy. | 
 |       length = [base::mac::ObjCCastStrict<NSInputStream>(stream) | 
 |                read:reinterpret_cast<unsigned char*>(read_buffer_.get()) | 
 |           maxLength:read_buffer_size_]; | 
 |       if (length > 0) { | 
 |         std::vector<char> owned_data(read_buffer_.get(), | 
 |                                      read_buffer_.get() + length); | 
 |         post_data_readers_.push_back( | 
 |             std::make_unique<UploadOwnedBytesElementReader>(&owned_data)); | 
 |       } else if (length < 0) {  // Error | 
 |         StopRequestWithError(stream.streamError.code, ERR_FAILED); | 
 |       } | 
 |       break; | 
 |     } | 
 |     case NSStreamEventNone: | 
 |     case NSStreamEventOpenCompleted: | 
 |     case NSStreamEventHasSpaceAvailable: | 
 |       break; | 
 |     default: | 
 |       NOTREACHED() << "Unexpected stream event: " << event; | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | #pragma mark URLRequest::Delegate methods | 
 |  | 
 | void HttpProtocolHandlerCore::OnReceivedRedirect( | 
 |     URLRequest* request, | 
 |     const RedirectInfo& redirect_info, | 
 |     bool* /* defer_redirect */) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |  | 
 |   // Cancel the request and notify UIWebView. | 
 |   // If we did nothing, the network stack would follow the redirect | 
 |   // automatically, however we do not want this because we need the UIWebView to | 
 |   // be notified. The UIWebView will then issue a new request following the | 
 |   // redirect. | 
 |   DCHECK(request_); | 
 |   GURL new_url = redirect_info.new_url; | 
 |  | 
 |   if (!new_url.is_valid()) { | 
 |     StopRequestWithError(NSURLErrorBadURL, ERR_INVALID_URL); | 
 |     return; | 
 |   } | 
 |  | 
 |   DCHECK(new_url.is_valid()); | 
 |   NSURL* new_nsurl = NSURLWithGURL(new_url); | 
 |   // Stash the original URL in case we need to report it in an error. | 
 |   [request_ setURL:new_nsurl]; | 
 |  | 
 |   if (stream_delegate_) | 
 |     StopListeningStream([request_ HTTPBodyStream]); | 
 |  | 
 |   // TODO(droger): See if we can share some code with URLRequest::Redirect() in | 
 |   // net/net_url_request/url_request.cc. | 
 |  | 
 |   // For 303 redirects, all request methods except HEAD are converted to GET, | 
 |   // as per the latest httpbis draft.  The draft also allows POST requests to | 
 |   // be converted to GETs when following 301/302 redirects, for historical | 
 |   // reasons. Most major browsers do this and so shall we. | 
 |   // See: | 
 |   // https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-17#section-7.3 | 
 |   const int http_status_code = request->GetResponseCode(); | 
 |   NSString* method = [request_ HTTPMethod]; | 
 |   const bool was_post = [method isEqualToString:@"POST"]; | 
 |   if ((http_status_code == 303 && ![method isEqualToString:@"HEAD"]) || | 
 |       ((http_status_code == 301 || http_status_code == 302) && was_post)) { | 
 |     [request_ setHTTPMethod:@"GET"]; | 
 |     [request_ setHTTPBody:nil]; | 
 |     [request_ setHTTPBodyStream:nil]; | 
 |     if (was_post) { | 
 |       // If being switched from POST to GET, must remove headers that were | 
 |       // specific to the POST and don't have meaning in GET. For example | 
 |       // the inclusion of a multipart Content-Type header in GET can cause | 
 |       // problems with some servers: | 
 |       // http://code.google.com/p/chromium/issues/detail?id=843 | 
 |       StripPostSpecificHeaders(request_); | 
 |     } | 
 |   } | 
 |  | 
 |   NSURLResponse* response = GetNSURLResponseForRequest(request); | 
 | #if !defined(NDEBUG) | 
 |   DVLOG(2) << "Redirect, to client:"; | 
 |   LogNSURLResponse(response); | 
 |   DVLOG(2) << "Redirect, to client:"; | 
 |   LogNSURLRequest(request_); | 
 | #endif  // !defined(NDEBUG) | 
 |   if (tracker_) | 
 |     tracker_->StopRedirectedRequest(request); | 
 |  | 
 |   [client_ wasRedirectedToRequest:request_ | 
 |                     nativeRequest:request | 
 |                  redirectResponse:response]; | 
 |   // Don't use |request_| or |response| anymore, as the client may be using them | 
 |   // on another thread and they are not re-entrant. As |request_| is mutable, it | 
 |   // is also important that it is not modified. | 
 |   request_ = nil; | 
 |   request->Cancel(); | 
 |   DCHECK_EQ(net_request_, request); | 
 |   StopNetRequest(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::OnAuthRequired(URLRequest* request, | 
 |                                              AuthChallengeInfo* auth_info) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   // A request with no tab ID should not hit HTTP authentication. | 
 |   if (tracker_) { | 
 |     // UIWebView does not handle authentication, so there is no point in calling | 
 |     // the protocol method didReceiveAuthenticationChallenge. | 
 |     // Instead, clients may handle proxy auth or display a UI to handle the | 
 |     // challenge. | 
 |     // Pass a weak reference, to avoid retain cycles. | 
 |     network_client::AuthCallback callback = | 
 |         base::Bind(&HttpProtocolHandlerCore::CompleteAuthentication, | 
 |                    base::Unretained(this)); | 
 |     [client_ didRecieveAuthChallenge:auth_info | 
 |                        nativeRequest:*request | 
 |                             callback:callback]; | 
 |   } else if (net_request_ != nullptr) { | 
 |     net_request_->CancelAuth(); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::OnCertificateRequested( | 
 |     URLRequest* request, | 
 |     SSLCertRequestInfo* cert_request_info) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |  | 
 |   // TODO(ios): The network stack does not support SSL client authentication | 
 |   // on iOS yet. The request has to be canceled for now. | 
 |   request->Cancel(); | 
 |   StopRequestWithError(NSURLErrorClientCertificateRequired, | 
 |                        ERR_SSL_PROTOCOL_ERROR); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::ContinueAfterSSLError(void) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (net_request_ != nullptr) { | 
 |     // Continue the request and load the data. | 
 |     net_request_->ContinueDespiteLastError(); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::CancelAfterSSLError(void) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (net_request_ != nullptr) { | 
 |     // Cancel the request. | 
 |     net_request_->Cancel(); | 
 |  | 
 |     // The request is signalled simply cancelled to the consumer, the | 
 |     // presentation of the SSL error will be done via the tracker. | 
 |     StopRequestWithError(NSURLErrorCancelled, ERR_BLOCKED_BY_CLIENT); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::SSLErrorCallback(bool carryOn) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (carryOn) | 
 |     ContinueAfterSSLError(); | 
 |   else | 
 |     CancelAfterSSLError(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::HostStateCallback(bool carryOn) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (carryOn) | 
 |     StartReading(); | 
 |   else | 
 |     CancelAfterSSLError(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::OnSSLCertificateError(URLRequest* request, | 
 |                                                     const SSLInfo& ssl_info, | 
 |                                                     bool fatal) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |  | 
 |   if (fatal) { | 
 |     if (tracker_) { | 
 |       tracker_->OnSSLCertificateError(request, ssl_info, false, | 
 |                                       base::DoNothing()); | 
 |     } | 
 |     CancelAfterSSLError();  // High security host do not tolerate any issue. | 
 |   } else if (!tracker_) { | 
 |     // No tracker, this is a request outside the context of a tab. There is no | 
 |     // way to present anything to the user so only allow trivial errors. | 
 |     // See ssl_cert_error_handler upstream. | 
 |     if (IsCertStatusMinorError(ssl_info.cert_status)) | 
 |       ContinueAfterSSLError(); | 
 |     else | 
 |       CancelAfterSSLError(); | 
 |   } else { | 
 |     // The tracker will decide, eventually asking the user, and will invoke the | 
 |     // callback. | 
 |     RequestTracker::SSLCallback callback = | 
 |         base::Bind(&HttpProtocolHandlerCore::SSLErrorCallback, this); | 
 |     DCHECK(tracker_); | 
 |     tracker_->OnSSLCertificateError(request, ssl_info, !fatal, callback); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::OnResponseStarted(URLRequest* request, | 
 |                                                 int net_error) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   DCHECK_NE(net::ERR_IO_PENDING, net_error); | 
 |  | 
 |   if (net_request_ == nullptr) | 
 |     return; | 
 |  | 
 |   if (net_error != net::OK) { | 
 |     StopRequestWithError(IOSErrorCode(net_error), net_error); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (tracker_ && IsCertStatusError(request->ssl_info().cert_status) && | 
 |       !request->context()->GetNetworkSessionParams()-> | 
 |           ignore_certificate_errors) { | 
 |     // The certificate policy cache is captured here because SSL errors do not | 
 |     // always trigger OnSSLCertificateError (this is the case when a page comes | 
 |     // from the HTTP cache). | 
 |     RequestTracker::SSLCallback callback = | 
 |         base::Bind(&HttpProtocolHandlerCore::HostStateCallback, this); | 
 |     tracker_->CaptureCertificatePolicyCache(request, callback); | 
 |     return; | 
 |   } | 
 |  | 
 |   StartReading(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::StartReading() { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (net_request_ == nullptr) | 
 |     return; | 
 |  | 
 |   NSURLResponse* response = GetNSURLResponseForRequest(net_request_); | 
 | #if !defined(NDEBUG) | 
 |   DVLOG(2) << "To client:"; | 
 |   LogNSURLResponse(response); | 
 | #endif  // !defined(NDEBUG) | 
 |  | 
 |   if (tracker_) { | 
 |     long long expectedContentLength = [response expectedContentLength]; | 
 |     if (expectedContentLength > 0) | 
 |       tracker_->CaptureExpectedLength(net_request_, expectedContentLength); | 
 |   } | 
 |  | 
 |   // Don't call any function on the response from now on, as the client may be | 
 |   // using it and the object is not re-entrant. | 
 |   [client_ didReceiveResponse:response]; | 
 |  | 
 |   int bytes_read = | 
 |       net_request_->Read(read_buffer_wrapper_.get(), read_buffer_size_); | 
 |   if (bytes_read == net::ERR_IO_PENDING) | 
 |     return; | 
 |  | 
 |   if (bytes_read >= 0) { | 
 |     OnReadCompleted(net_request_, bytes_read); | 
 |   } else { | 
 |     int error = bytes_read; | 
 |     StopRequestWithError(IOSErrorCode(error), error); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::OnReadCompleted(URLRequest* request, | 
 |                                               int bytes_read) { | 
 |   DCHECK_NE(net::ERR_IO_PENDING, bytes_read); | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |  | 
 |   if (net_request_ == nullptr) | 
 |     return; | 
 |  | 
 |   DCHECK_EQ(net_request_, request); | 
 |  | 
 |   // Read data from the socket until no bytes left to read. | 
 |   uint64_t total_bytes_read = 0; | 
 |   while (bytes_read > 0) { | 
 |     total_bytes_read += bytes_read; | 
 |     // The NSData will take the ownership of |read_buffer_|. | 
 |     NSData* data = | 
 |         [NSData dataWithBytesNoCopy:read_buffer_.release() length:bytes_read]; | 
 |     // If the data is not encoded in UTF8, the NSString is nil. | 
 |     DVLOG(3) << "To client:" << std::endl | 
 |              << base::SysNSStringToUTF8([[NSString alloc] | 
 |                     initWithData:data | 
 |                         encoding:NSUTF8StringEncoding]); | 
 |     // Pass the read data to the client. | 
 |     [client_ didLoadData:data]; | 
 |  | 
 |     // Allocate a new buffer and continue reading from the socket. | 
 |     AllocateReadBuffer(bytes_read); | 
 |     bytes_read = request->Read(read_buffer_wrapper_.get(), read_buffer_size_); | 
 |   } | 
 |  | 
 |   if (tracker_) | 
 |     tracker_->CaptureReceivedBytes(request, total_bytes_read); | 
 |  | 
 |   if (bytes_read == net::OK) { | 
 |     // If there is nothing more to read. | 
 |     StopNetRequest(); | 
 |     [client_ didFinishLoading]; | 
 |   } else if (bytes_read != net::ERR_IO_PENDING) { | 
 |     // If there was an error (not canceled). | 
 |     int error = bytes_read; | 
 |     StopRequestWithError(IOSErrorCode(error), error); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::AllocateReadBuffer(int last_read_data_size) { | 
 |   if (last_read_data_size == read_buffer_size_) { | 
 |     // If the whole buffer was filled with data then increase the buffer size | 
 |     // for the next read but don't exceed |kIOBufferMaxSize|. | 
 |     read_buffer_size_ = std::min(read_buffer_size_ * 2, kIOBufferMaxSize); | 
 |   } else if (read_buffer_size_ / 2 >= last_read_data_size) { | 
 |     // If only a half or less of the buffer was filled with data then reduce | 
 |     // the buffer size for the next read but not make it smaller than | 
 |     // |kIOBufferMinSize|. | 
 |     read_buffer_size_ = std::max(read_buffer_size_ / 2, kIOBufferMinSize); | 
 |   } | 
 |   read_buffer_.reset(static_cast<char*>(malloc(read_buffer_size_))); | 
 |   read_buffer_wrapper_ = base::MakeRefCounted<WrappedIOBuffer>( | 
 |       static_cast<const char*>(read_buffer_.get())); | 
 | } | 
 |  | 
 | HttpProtocolHandlerCore::~HttpProtocolHandlerCore() { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   [client_ cancelAuthRequest]; | 
 |   DCHECK(!net_request_); | 
 |   DCHECK(!stream_delegate_); | 
 | } | 
 |  | 
 | // static | 
 | void HttpProtocolHandlerCore::Destruct(const HttpProtocolHandlerCore* x) { | 
 |   scoped_refptr<base::SingleThreadTaskRunner> task_runner = | 
 |       g_protocol_handler_delegate->GetDefaultURLRequestContext() | 
 |           ->GetNetworkTaskRunner(); | 
 |   if (task_runner->BelongsToCurrentThread()) | 
 |     delete x; | 
 |   else | 
 |     task_runner->DeleteSoon(FROM_HERE, x); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::SetLoadFlags() { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   int load_flags = LOAD_NORMAL; | 
 |  | 
 |   if (![request_ HTTPShouldHandleCookies]) | 
 |     load_flags |= LOAD_DO_NOT_SEND_COOKIES | LOAD_DO_NOT_SAVE_COOKIES; | 
 |  | 
 |   // If a RequestTracker is defined, always use normal load, otherwise | 
 |   // respect the cache policy from the request. | 
 |   if (!tracker_) { | 
 |     switch ([request_ cachePolicy]) { | 
 |       case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: | 
 |         load_flags |= LOAD_BYPASS_CACHE; | 
 |         FALLTHROUGH; | 
 |       case NSURLRequestReloadIgnoringLocalCacheData: | 
 |         load_flags |= LOAD_DISABLE_CACHE; | 
 |         break; | 
 |       case NSURLRequestReturnCacheDataElseLoad: | 
 |         load_flags |= LOAD_SKIP_CACHE_VALIDATION; | 
 |         break; | 
 |       case NSURLRequestReturnCacheDataDontLoad: | 
 |         load_flags |= LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION; | 
 |         break; | 
 |       case NSURLRequestReloadRevalidatingCacheData: | 
 |         load_flags |= LOAD_VALIDATE_CACHE; | 
 |         break; | 
 |       case NSURLRequestUseProtocolCachePolicy: | 
 |         // Do nothing, normal load. | 
 |         break; | 
 |     } | 
 |   } | 
 |   net_request_->SetLoadFlags(load_flags); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::Start(id<CRNNetworkClientProtocol> base_client) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   DCHECK(!client_); | 
 |   DCHECK(base_client); | 
 |   client_ = base_client; | 
 |   GURL url = GURLWithNSURL([request_ URL]); | 
 |  | 
 |   bool valid_tracker = RequestTracker::GetRequestTracker(request_, &tracker_); | 
 |   if (!valid_tracker) { | 
 |     // The request is associated with a tracker that does not exist, cancel it. | 
 |     // NSURLErrorCancelled avoids triggering any error page. | 
 |     [client_ didFailWithNSErrorCode:NSURLErrorCancelled | 
 |                        netErrorCode:ERR_ABORTED]; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Now that all of the network clients are set up, if there was an error with | 
 |   // the URL, it can be raised and all of the clients will have a chance to | 
 |   // handle it. | 
 |   if (!url.is_valid()) { | 
 |     DLOG(ERROR) << "Trying to load an invalid URL: " | 
 |                 << base::SysNSStringToUTF8([[request_ URL] absoluteString]); | 
 |     [client_ didFailWithNSErrorCode:NSURLErrorBadURL | 
 |                        netErrorCode:ERR_INVALID_URL]; | 
 |     return; | 
 |   } | 
 |  | 
 |   const URLRequestContext* context = | 
 |       tracker_ ? tracker_->GetRequestContext() | 
 |                : g_protocol_handler_delegate->GetDefaultURLRequestContext() | 
 |                      ->GetURLRequestContext(); | 
 |   DCHECK(context); | 
 |  | 
 |   net_request_ = | 
 |       context->CreateRequest(url, DEFAULT_PRIORITY, this).release(); | 
 |   net_request_->set_method(base::SysNSStringToUTF8([request_ HTTPMethod])); | 
 |  | 
 |   net_request_->set_site_for_cookies(GURLWithNSURL([request_ mainDocumentURL])); | 
 |  | 
 | #if !defined(NDEBUG) | 
 |   DVLOG(2) << "From client:"; | 
 |   LogNSURLRequest(request_); | 
 | #endif  // !defined(NDEBUG) | 
 |  | 
 |   CopyHttpHeaders(request_, net_request_); | 
 |  | 
 |   [client_ didCreateNativeRequest:net_request_]; | 
 |   SetLoadFlags(); | 
 |  | 
 |   if ([request_ HTTPBodyStream]) { | 
 |     NSInputStream* input_stream = [request_ HTTPBodyStream]; | 
 |     stream_delegate_ = | 
 |         [[CRWHTTPStreamDelegate alloc] initWithHttpProtocolHandlerCore:this]; | 
 |     [input_stream setDelegate:stream_delegate_]; | 
 |     [input_stream scheduleInRunLoop:[NSRunLoop currentRunLoop] | 
 |                             forMode:NSDefaultRunLoopMode]; | 
 |     [input_stream open]; | 
 |  | 
 |     if (net_request_->extra_request_headers().HasHeader( | 
 |             HttpRequestHeaders::kContentLength)) { | 
 |       // The request will be started when the stream is fully read. | 
 |       return; | 
 |     } | 
 |  | 
 |     std::unique_ptr<ChunkedDataStreamUploader> uploader = | 
 |         std::make_unique<ChunkedDataStreamUploader>(this); | 
 |     chunked_uploader_ = uploader->GetWeakPtr(); | 
 |     net_request_->set_upload(std::move(uploader)); | 
 |   } else if ([request_ HTTPBody]) { | 
 |     NSData* body = [request_ HTTPBody]; | 
 |     const NSUInteger body_length = [body length]; | 
 |     if (body_length > 0) { | 
 |       const char* source_bytes = reinterpret_cast<const char*>([body bytes]); | 
 |       std::vector<char> owned_data(source_bytes, source_bytes + body_length); | 
 |       std::unique_ptr<UploadElementReader> reader( | 
 |           new UploadOwnedBytesElementReader(&owned_data)); | 
 |       net_request_->set_upload( | 
 |           ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); | 
 |     } | 
 |   } | 
 |  | 
 |   net_request_->Start(); | 
 |   if (tracker_) | 
 |     tracker_->StartRequest(net_request_); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::Cancel() { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (net_request_ == nullptr) | 
 |     return; | 
 |  | 
 |   DVLOG(2) << "Client canceling request: " << net_request_->url().spec(); | 
 |   net_request_->Cancel(); | 
 |   StopNetRequest(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::StopNetRequest() { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (tracker_) | 
 |     tracker_->StopRequest(net_request_); | 
 |  | 
 |   if (g_metrics_delegate) { | 
 |     auto metrics = std::make_unique<net::MetricsDelegate::Metrics>(); | 
 |  | 
 |     metrics->response_end_time = base::Time::Now(); | 
 |     metrics->task = task_; | 
 |     metrics->response_info = net_request_->response_info(); | 
 |     net_request_->GetLoadTimingInfo(&metrics->load_timing_info); | 
 |  | 
 |     g_metrics_delegate->OnStopNetRequest(std::move(metrics)); | 
 |   } | 
 |  | 
 |   delete net_request_; | 
 |   net_request_ = nullptr; | 
 |   if (stream_delegate_) | 
 |     StopListeningStream([request_ HTTPBodyStream]); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::StopListeningStream(NSStream* stream) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   DCHECK(stream); | 
 |   DCHECK(stream_delegate_); | 
 |   DCHECK([stream delegate] == stream_delegate_); | 
 |   [stream setDelegate:nil]; | 
 |   [stream removeFromRunLoop:[NSRunLoop currentRunLoop] | 
 |                     forMode:NSDefaultRunLoopMode]; | 
 |   stream_delegate_ = nil; | 
 |   pending_stream_ = nil; | 
 |   // Close the stream if needed. | 
 |   switch ([stream streamStatus]) { | 
 |     case NSStreamStatusOpening: | 
 |     case NSStreamStatusOpen: | 
 |     case NSStreamStatusReading: | 
 |     case NSStreamStatusWriting: | 
 |     case NSStreamStatusAtEnd: | 
 |       [stream close]; | 
 |       break; | 
 |     case NSStreamStatusNotOpen: | 
 |     case NSStreamStatusClosed: | 
 |     case NSStreamStatusError: | 
 |       break; | 
 |     default: | 
 |       NOTREACHED() << "Unexpected stream status: " << [stream streamStatus]; | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | NSInteger HttpProtocolHandlerCore::IOSErrorCode(int os_error) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   switch (os_error) { | 
 |     case ERR_SSL_PROTOCOL_ERROR: | 
 |       return NSURLErrorClientCertificateRequired; | 
 |     case ERR_CONNECTION_RESET: | 
 |     case ERR_NETWORK_CHANGED: | 
 |       return NSURLErrorNetworkConnectionLost; | 
 |     case ERR_UNEXPECTED: | 
 |       return NSURLErrorUnknown; | 
 |     default: | 
 |       return NSURLErrorCannotConnectToHost; | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::StopRequestWithError(NSInteger ns_error_code, | 
 |                                                    int net_error_code) { | 
 |   DCHECK(net_request_ != nullptr); | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |  | 
 |   // Don't show an error message on ERR_ABORTED because this is error is often | 
 |   // fired when switching profiles (see RequestTracker::CancelRequests()). | 
 |   DLOG_IF(ERROR, net_error_code != ERR_ABORTED) | 
 |       << "HttpProtocolHandlerCore - Network error: " | 
 |       << ErrorToString(net_error_code) << " (" << net_error_code << ")"; | 
 |  | 
 |   [client_ didFailWithNSErrorCode:ns_error_code netErrorCode:net_error_code]; | 
 |   StopNetRequest(); | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::CompleteAuthentication( | 
 |     bool auth_ok, | 
 |     const base::string16& username, | 
 |     const base::string16& password) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   if (net_request_ == nullptr) | 
 |     return; | 
 |   if (auth_ok) { | 
 |     net_request_->SetAuth(AuthCredentials(username, password)); | 
 |   } else { | 
 |     net_request_->CancelAuth(); | 
 |   } | 
 | } | 
 |  | 
 | void HttpProtocolHandlerCore::StripPostSpecificHeaders( | 
 |     NSMutableURLRequest* request) { | 
 |   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   DCHECK(request); | 
 |   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString( | 
 |       HttpRequestHeaders::kContentLength)]; | 
 |   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString( | 
 |       HttpRequestHeaders::kContentType)]; | 
 |   [request setValue:nil forHTTPHeaderField:base::SysUTF8ToNSString( | 
 |       HttpRequestHeaders::kOrigin)]; | 
 | } | 
 |  | 
 | int HttpProtocolHandlerCore::OnRead(char* buffer, int buffer_length) { | 
 |   int bytes_read = 0; | 
 |   if (pending_stream_) { | 
 |     // NSInputStream read() blocks the thread until there is at least one byte | 
 |     // available, so check the status before call read(). | 
 |     if (![pending_stream_ hasBytesAvailable]) | 
 |       return ERR_IO_PENDING; | 
 |  | 
 |     bytes_read = [pending_stream_ read:reinterpret_cast<unsigned char*>(buffer) | 
 |                              maxLength:buffer_length]; | 
 |     // NSInputStream can read 0 byte when hasBytesAvailable is true, so do not | 
 |     // treat it as a failure. | 
 |     if (bytes_read < 0) { | 
 |       // If NSInputStream meets an error on read(), fail the request | 
 |       // immediately. | 
 |       DLOG(ERROR) << "Failed to read POST data: " | 
 |                   << base::SysNSStringToUTF8( | 
 |                          [[pending_stream_ streamError] description]); | 
 |       StopListeningStream(pending_stream_); | 
 |       StopRequestWithError(NSURLErrorUnknown, ERR_UNEXPECTED); | 
 |       return ERR_UNEXPECTED; | 
 |     } | 
 |   } | 
 |   return bytes_read; | 
 | } | 
 |  | 
 | }  // namespace net | 
 |  | 
 | #pragma mark - | 
 | #pragma mark CRWHTTPStreamDelegate | 
 |  | 
 | @implementation CRWHTTPStreamDelegate | 
 | - (instancetype)initWithHttpProtocolHandlerCore: | 
 |     (net::HttpProtocolHandlerCore*)core { | 
 |   DCHECK(core); | 
 |   self = [super init]; | 
 |   if (self) | 
 |     _core = core; | 
 |   return self; | 
 | } | 
 |  | 
 | - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent { | 
 |   _core->HandleStreamEvent(theStream, streamEvent); | 
 | } | 
 | @end | 
 |  | 
 | #pragma mark - | 
 | #pragma mark DeferredCancellation | 
 |  | 
 | // An object of class |DeferredCancellation| represents a deferred cancellation | 
 | // of a request. In principle this is a block posted to a thread's runloop, but | 
 | // since there is no performBlock:onThread:, this class wraps the desired | 
 | // behavior in an object. | 
 | @interface DeferredCancellation : NSObject | 
 |  | 
 | - (instancetype)initWithCore:(scoped_refptr<net::HttpProtocolHandlerCore>)core; | 
 | - (void)cancel; | 
 |  | 
 | @end | 
 |  | 
 | @implementation DeferredCancellation { | 
 |   scoped_refptr<net::HttpProtocolHandlerCore> _core; | 
 | } | 
 |  | 
 | - (instancetype)initWithCore:(scoped_refptr<net::HttpProtocolHandlerCore>)core { | 
 |   if ((self = [super init])) { | 
 |     _core = core; | 
 |   } | 
 |   return self; | 
 | } | 
 |  | 
 | - (void)cancel { | 
 |   g_protocol_handler_delegate->GetDefaultURLRequestContext() | 
 |       ->GetNetworkTaskRunner() | 
 |       ->PostTask(FROM_HERE, | 
 |                  base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core)); | 
 | } | 
 |  | 
 | @end | 
 |  | 
 | #pragma mark - | 
 | #pragma mark HttpProtocolHandler | 
 |  | 
 | @interface CRNHTTPProtocolHandler (Private) | 
 |  | 
 | - (id<CRNHTTPProtocolHandlerProxy>)getProtocolHandlerProxy; | 
 | - (scoped_refptr<net::HttpProtocolHandlerCore>)getCore; | 
 | - (NSThread*)getClientThread; | 
 | - (void)cancelRequest; | 
 |  | 
 | @end | 
 |  | 
 | // The HttpProtocolHandler is called by the iOS system to handle the | 
 | // NSURLRequest. | 
 | @implementation CRNHTTPProtocolHandler { | 
 |   scoped_refptr<net::HttpProtocolHandlerCore> _core; | 
 |   id<CRNHTTPProtocolHandlerProxy> _protocolProxy; | 
 |   __weak NSThread* _clientThread; | 
 |   BOOL _supportedURL; | 
 |   NSURLSessionTask* _task; | 
 | } | 
 |  | 
 | #pragma mark NSURLProtocol methods | 
 |  | 
 | + (BOOL)canInitWithRequest:(NSURLRequest*)request { | 
 |   DVLOG(5) << "canInitWithRequest " << net::FormatUrlRequestForLogging(request); | 
 |   return g_protocol_handler_delegate->CanHandleRequest(request); | 
 | } | 
 |  | 
 | + (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request { | 
 |   // TODO(droger): Is this used if we disable the cache of UIWebView? If it is, | 
 |   // then we need a real implementation, even though Chrome network stack does | 
 |   // not need it (GURLs are automatically canonized). | 
 |   return request; | 
 | } | 
 |  | 
 | - (instancetype)initWithRequest:(NSURLRequest*)request | 
 |                  cachedResponse:(NSCachedURLResponse*)cachedResponse | 
 |                          client:(id<NSURLProtocolClient>)client { | 
 |   DCHECK(!cachedResponse); | 
 |   self = [super initWithRequest:request | 
 |                  cachedResponse:cachedResponse | 
 |                          client:client]; | 
 |   if (self) { | 
 |     _supportedURL = g_protocol_handler_delegate->IsRequestSupported(request); | 
 |     _core = new net::HttpProtocolHandlerCore(request); | 
 |   } | 
 |   return self; | 
 | } | 
 |  | 
 | - (instancetype)initWithTask:(NSURLSessionTask*)task | 
 |               cachedResponse:(NSCachedURLResponse*)cachedResponse | 
 |                       client:(id<NSURLProtocolClient>)client { | 
 |   DCHECK(!cachedResponse); | 
 |   self = [super initWithTask:task cachedResponse:cachedResponse client:client]; | 
 |   if (self) { | 
 |     _supportedURL = | 
 |         g_protocol_handler_delegate->IsRequestSupported(task.currentRequest); | 
 |     _core = new net::HttpProtocolHandlerCore(task); | 
 |     _task = task; | 
 |   } | 
 |   return self; | 
 | } | 
 |  | 
 | #pragma mark NSURLProtocol overrides. | 
 |  | 
 | - (NSCachedURLResponse*)cachedResponse { | 
 |   // We do not use the UIWebView cache. | 
 |   // TODO(droger): Disable the UIWebView cache. | 
 |   return nil; | 
 | } | 
 |  | 
 | - (void)startLoading { | 
 |   // If the scheme is not valid, just return an error right away. | 
 |   if (!_supportedURL) { | 
 |     NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; | 
 |  | 
 |     // It is possible for URL to be nil, so check for that | 
 |     // before creating the error object. See http://crbug/349051 | 
 |     NSURL* url = [[self request] URL]; | 
 |     if (url) | 
 |       [dictionary setObject:url forKey:NSURLErrorKey]; | 
 |  | 
 |     NSError* error = [NSError errorWithDomain:NSURLErrorDomain | 
 |                                          code:NSURLErrorUnsupportedURL | 
 |                                      userInfo:dictionary]; | 
 |     [[self client] URLProtocol:self didFailWithError:error]; | 
 |     return; | 
 |   } | 
 |  | 
 |   _clientThread = [NSThread currentThread]; | 
 |  | 
 |   if (g_metrics_delegate) { | 
 |     g_metrics_delegate->OnStartNetRequest(_task); | 
 |   } | 
 |  | 
 |   // The closure passed to PostTask must to retain the _protocolProxy | 
 |   // scoped_nsobject. A call to getProtocolHandlerProxy before passing | 
 |   // _protocolProxy ensure that _protocolProxy is instanciated before passing | 
 |   // it. | 
 |   [self getProtocolHandlerProxy]; | 
 |   DCHECK(_protocolProxy); | 
 |   g_protocol_handler_delegate->GetDefaultURLRequestContext() | 
 |       ->GetNetworkTaskRunner() | 
 |       ->PostTask(FROM_HERE, base::Bind(&net::HttpProtocolHandlerCore::Start, | 
 |                                        _core, _protocolProxy)); | 
 | } | 
 |  | 
 | - (id<CRNHTTPProtocolHandlerProxy>)getProtocolHandlerProxy { | 
 |   DCHECK_EQ([NSThread currentThread], _clientThread); | 
 |   if (!_protocolProxy) { | 
 |     _protocolProxy = [[CRNHTTPProtocolHandlerProxyWithClientThread alloc] | 
 |         initWithProtocol:self | 
 |             clientThread:_clientThread | 
 |              runLoopMode:[[NSRunLoop currentRunLoop] currentMode]]; | 
 |   } | 
 |   return _protocolProxy; | 
 | } | 
 |  | 
 | - (scoped_refptr<net::HttpProtocolHandlerCore>)getCore { | 
 |   return _core; | 
 | } | 
 |  | 
 | - (NSThread*)getClientThread { | 
 |   return _clientThread; | 
 | } | 
 |  | 
 | - (void)cancelRequest { | 
 |   g_protocol_handler_delegate->GetDefaultURLRequestContext() | 
 |       ->GetNetworkTaskRunner() | 
 |       ->PostTask(FROM_HERE, | 
 |                  base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core)); | 
 |   [_protocolProxy invalidate]; | 
 | } | 
 |  | 
 | - (void)stopLoading { | 
 |   [self cancelRequest]; | 
 |   _protocolProxy = nil; | 
 | } | 
 |  | 
 | @end |