Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: ios/net/crn_http_protocol_handler.mm

Issue 994823004: [iOS] Upstream //ios/net (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698