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.h" |
| 6 |
| 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" |
| 35 |
| 36 namespace net { |
| 37 class HttpProtocolHandlerCore; |
| 38 } |
| 39 |
| 40 namespace { |
| 41 |
| 42 // Size of the buffer used to read the net::URLRequest. |
| 43 const int kIOBufferSize = 4096; |
| 44 |
| 45 // Global instance of the HTTPProtocolHandlerDelegate. |
| 46 net::HTTPProtocolHandlerDelegate* g_protocol_handler_delegate = nullptr; |
| 47 |
| 48 // Empty callback. |
| 49 void DoNothing(bool flag) {} |
| 50 |
| 51 } // namespace |
| 52 |
| 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; |
| 59 } |
| 60 - (instancetype)initWithHttpProtocolHandlerCore: |
| 61 (net::HttpProtocolHandlerCore*)core; |
| 62 // NSStreamDelegate method. |
| 63 - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent; |
| 64 @end |
| 65 |
| 66 #pragma mark - |
| 67 #pragma mark HttpProtocolHandlerCore |
| 68 |
| 69 namespace net { |
| 70 |
| 71 // static |
| 72 void HTTPProtocolHandlerDelegate::SetInstance( |
| 73 HTTPProtocolHandlerDelegate* delegate) { |
| 74 g_protocol_handler_delegate = delegate; |
| 75 } |
| 76 |
| 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. |
| 84 |
| 85 // Implementation notes from Apple's "Read Me About CustomHttpProtocolHandler": |
| 86 // |
| 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: |
| 96 // |
| 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. |
| 108 // |
| 109 // The -URLProtocol:didFailWithError: callback can be made at any time |
| 110 // (although keep in mind the following point). |
| 111 // |
| 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); |
| 134 |
| 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; |
| 148 |
| 149 private: |
| 150 friend class base::RefCountedThreadSafe<HttpProtocolHandlerCore, |
| 151 HttpProtocolHandlerCore>; |
| 152 friend class base::DeleteHelper<HttpProtocolHandlerCore>; |
| 153 ~HttpProtocolHandlerCore() override; |
| 154 |
| 155 // RefCountedThreadSafe traits implementation: |
| 156 static void Destruct(const HttpProtocolHandlerCore* x); |
| 157 |
| 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); |
| 182 |
| 183 base::ThreadChecker thread_checker_; |
| 184 |
| 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_; |
| 197 |
| 198 // This cannot be a scoped pointer because it must be deleted on the IO |
| 199 // thread. |
| 200 URLRequest* net_request_; |
| 201 |
| 202 base::WeakPtr<RequestTracker> tracker_; |
| 203 |
| 204 DISALLOW_COPY_AND_ASSIGN(HttpProtocolHandlerCore); |
| 205 }; |
| 206 |
| 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]]]; |
| 221 } |
| 222 |
| 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 } |
| 268 } |
| 269 |
| 270 #pragma mark URLRequest::Delegate methods |
| 271 |
| 272 void HttpProtocolHandlerCore::OnReceivedRedirect( |
| 273 URLRequest* request, |
| 274 const RedirectInfo& redirect_info, |
| 275 bool* /* defer_redirect */) { |
| 276 DCHECK(thread_checker_.CalledOnValidThread()); |
| 277 |
| 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; |
| 285 |
| 286 if (!new_url.is_valid()) { |
| 287 StopRequestWithError(NSURLErrorBadURL, ERR_INVALID_URL); |
| 288 return; |
| 289 } |
| 290 |
| 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]; |
| 295 |
| 296 if (stream_delegate_.get()) |
| 297 StopListeningStream([request_ HTTPBodyStream]); |
| 298 |
| 299 // TODO(droger): See if we can share some code with URLRequest::Redirect() in |
| 300 // net/net_url_request/url_request.cc. |
| 301 |
| 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 } |
| 325 |
| 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 } |
| 338 |
| 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(); |
| 349 } |
| 350 |
| 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 } |
| 370 } |
| 371 |
| 372 void HttpProtocolHandlerCore::OnCertificateRequested( |
| 373 URLRequest* request, |
| 374 SSLCertRequestInfo* cert_request_info) { |
| 375 DCHECK(thread_checker_.CalledOnValidThread()); |
| 376 |
| 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); |
| 382 } |
| 383 |
| 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 } |
| 390 } |
| 391 |
| 392 void HttpProtocolHandlerCore::CancelAfterSSLError(void) { |
| 393 DCHECK(thread_checker_.CalledOnValidThread()); |
| 394 if (net_request_ != nullptr) { |
| 395 // Cancel the request. |
| 396 net_request_->Cancel(); |
| 397 |
| 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 } |
| 402 } |
| 403 |
| 404 void HttpProtocolHandlerCore::SSLErrorCallback(bool carryOn) { |
| 405 DCHECK(thread_checker_.CalledOnValidThread()); |
| 406 if (carryOn) |
| 407 ContinueAfterSSLError(); |
| 408 else |
| 409 CancelAfterSSLError(); |
| 410 } |
| 411 |
| 412 void HttpProtocolHandlerCore::HostStateCallback(bool carryOn) { |
| 413 DCHECK(thread_checker_.CalledOnValidThread()); |
| 414 if (carryOn) |
| 415 StartReading(); |
| 416 else |
| 417 CancelAfterSSLError(); |
| 418 } |
| 419 |
| 420 void HttpProtocolHandlerCore::OnSSLCertificateError(URLRequest* request, |
| 421 const SSLInfo& ssl_info, |
| 422 bool is_hsts_host) { |
| 423 DCHECK(thread_checker_.CalledOnValidThread()); |
| 424 |
| 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 } |
| 447 } |
| 448 |
| 449 void HttpProtocolHandlerCore::OnResponseStarted(URLRequest* request) { |
| 450 DCHECK(thread_checker_.CalledOnValidThread()); |
| 451 |
| 452 if (net_request_ == nullptr) |
| 453 return; |
| 454 |
| 455 const URLRequestStatus& status = request->status(); |
| 456 if (!status.is_success()) { |
| 457 int error = status.error(); |
| 458 StopRequestWithError(IOSErrorCode(error), error); |
| 459 return; |
| 460 } |
| 461 |
| 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 } |
| 473 |
| 474 StartReading(); |
| 475 } |
| 476 |
| 477 void HttpProtocolHandlerCore::StartReading() { |
| 478 DCHECK(thread_checker_.CalledOnValidThread()); |
| 479 if (net_request_ == nullptr) |
| 480 return; |
| 481 |
| 482 NSURLResponse* response = GetNSURLResponseForRequest(net_request_); |
| 483 #if !defined(NDEBUG) |
| 484 DVLOG(2) << "To client:"; |
| 485 LogNSURLResponse(response); |
| 486 #endif // !defined(NDEBUG) |
| 487 |
| 488 if (tracker_) { |
| 489 tracker_->CaptureHeaders(net_request_); |
| 490 long long expectedContentLength = [response expectedContentLength]; |
| 491 if (expectedContentLength > 0) |
| 492 tracker_->CaptureExpectedLength(net_request_, expectedContentLength); |
| 493 |
| 494 // Add clients from tracker. |
| 495 PushClients( |
| 496 tracker_->ClientsHandlingRequestAndResponse(*net_request_, response)); |
| 497 } |
| 498 |
| 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]; |
| 502 |
| 503 int bytes_read = 0; |
| 504 |
| 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 } |
| 511 } |
| 512 |
| 513 void HttpProtocolHandlerCore::OnReadCompleted(URLRequest* request, |
| 514 int bytes_read) { |
| 515 DCHECK(thread_checker_.CalledOnValidThread()); |
| 516 |
| 517 if (net_request_ == nullptr) |
| 518 return; |
| 519 |
| 520 base::scoped_nsobject<NSMutableData> data([[NSMutableData alloc] init]); |
| 521 |
| 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 } |
| 538 |
| 539 if (tracker_) |
| 540 tracker_->CaptureReceivedBytes(request, total_byte_read); |
| 541 |
| 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 } |
| 564 } |
| 565 |
| 566 HttpProtocolHandlerCore::~HttpProtocolHandlerCore() { |
| 567 DCHECK(thread_checker_.CalledOnValidThread()); |
| 568 [top_level_client_ cancelAuthRequest]; |
| 569 DCHECK(!net_request_); |
| 570 DCHECK(!stream_delegate_); |
| 571 } |
| 572 |
| 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); |
| 582 } |
| 583 |
| 584 void HttpProtocolHandlerCore::SetLoadFlags() { |
| 585 DCHECK(thread_checker_.CalledOnValidThread()); |
| 586 |
| 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; |
| 591 |
| 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); |
| 634 } |
| 635 |
| 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]); |
| 644 |
| 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 } |
| 653 |
| 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 } |
| 663 |
| 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 } |
| 674 |
| 675 const URLRequestContext* context = |
| 676 tracker_ ? tracker_->GetRequestContext() |
| 677 : g_protocol_handler_delegate->GetDefaultURLRequestContext() |
| 678 ->GetURLRequestContext(); |
| 679 DCHECK(context); |
| 680 |
| 681 net_request_ = |
| 682 context->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr).release(); |
| 683 net_request_->set_method(base::SysNSStringToUTF8([request_ HTTPMethod])); |
| 684 |
| 685 net_request_->set_first_party_for_cookies( |
| 686 GURLWithNSURL([request_ mainDocumentURL])); |
| 687 |
| 688 #if !defined(NDEBUG) |
| 689 DVLOG(2) << "From client:"; |
| 690 LogNSURLRequest(request_); |
| 691 #endif // !defined(NDEBUG) |
| 692 |
| 693 CopyHttpHeaders(request_, net_request_); |
| 694 |
| 695 // Add network clients. |
| 696 if (tracker_) |
| 697 PushClients(tracker_->ClientsHandlingRequest(*net_request_)); |
| 698 |
| 699 [top_level_client_ didCreateNativeRequest:net_request_]; |
| 700 SetLoadFlags(); |
| 701 |
| 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 } |
| 713 |
| 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 } |
| 724 |
| 725 net_request_->Start(); |
| 726 if (tracker_) |
| 727 tracker_->StartRequest(net_request_); |
| 728 } |
| 729 |
| 730 void HttpProtocolHandlerCore::Cancel() { |
| 731 DCHECK(thread_checker_.CalledOnValidThread()); |
| 732 if (net_request_ == nullptr) |
| 733 return; |
| 734 |
| 735 DVLOG(2) << "Client canceling request: " << net_request_->url().spec(); |
| 736 net_request_->Cancel(); |
| 737 StopNetRequest(); |
| 738 } |
| 739 |
| 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]); |
| 748 } |
| 749 |
| 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 } |
| 776 } |
| 777 |
| 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 } |
| 791 } |
| 792 |
| 793 void HttpProtocolHandlerCore::StopRequestWithError(NSInteger ns_error_code, |
| 794 int net_error_code) { |
| 795 DCHECK(net_request_ != nullptr); |
| 796 DCHECK(thread_checker_.CalledOnValidThread()); |
| 797 |
| 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 << ")"; |
| 803 |
| 804 [top_level_client_ didFailWithNSErrorCode:ns_error_code |
| 805 netErrorCode:net_error_code]; |
| 806 StopNetRequest(); |
| 807 } |
| 808 |
| 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 } |
| 821 } |
| 822 |
| 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)]; |
| 833 } |
| 834 |
| 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; |
| 840 } |
| 841 |
| 842 void HttpProtocolHandlerCore::PushClients(NSArray* clients) { |
| 843 DCHECK(thread_checker_.CalledOnValidThread()); |
| 844 for (id<CRNNetworkClientProtocol> client in clients) |
| 845 PushClient(client); |
| 846 } |
| 847 |
| 848 } // namespace net |
| 849 |
| 850 #pragma mark - |
| 851 #pragma mark CRWHTTPStreamDelegate |
| 852 |
| 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; |
| 861 } |
| 862 |
| 863 - (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent { |
| 864 _core->HandleStreamEvent(theStream, streamEvent); |
| 865 } |
| 866 @end |
| 867 |
| 868 #pragma mark - |
| 869 #pragma mark HttpProtocolHandler |
| 870 |
| 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; |
| 877 } |
| 878 |
| 879 #pragma mark NSURLProtocol methods |
| 880 |
| 881 + (BOOL)canInitWithRequest:(NSURLRequest*)request { |
| 882 DVLOG(5) << "canInitWithRequest " << net::FormatUrlRequestForLogging(request); |
| 883 return g_protocol_handler_delegate->CanHandleRequest(request); |
| 884 } |
| 885 |
| 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; |
| 891 } |
| 892 |
| 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; |
| 905 } |
| 906 |
| 907 #pragma mark NSURLProtocol overrides. |
| 908 |
| 909 - (NSCachedURLResponse*)cachedResponse { |
| 910 // We do not use the UIWebView cache. |
| 911 // TODO(droger): Disable the UIWebView cache. |
| 912 return nil; |
| 913 } |
| 914 |
| 915 - (void)startLoading { |
| 916 // If the scheme is not valid, just return an error right away. |
| 917 if (!_supportedURL) { |
| 918 NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; |
| 919 |
| 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]; |
| 925 |
| 926 NSError* error = [NSError errorWithDomain:NSURLErrorDomain |
| 927 code:NSURLErrorUnsupportedURL |
| 928 userInfo:dictionary]; |
| 929 [[self client] URLProtocol:self didFailWithError:error]; |
| 930 return; |
| 931 } |
| 932 |
| 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)); |
| 941 } |
| 942 |
| 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(); |
| 950 } |
| 951 |
| 952 @end |
OLD | NEW |