• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 #include "content/browser/loader/resource_loader.h"
6 
7 #include "base/files/file.h"
8 #include "base/files/file_util.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/run_loop.h"
11 #include "content/browser/browser_thread_impl.h"
12 #include "content/browser/loader/redirect_to_file_resource_handler.h"
13 #include "content/browser/loader/resource_loader_delegate.h"
14 #include "content/public/browser/resource_request_info.h"
15 #include "content/public/common/resource_response.h"
16 #include "content/public/test/mock_resource_context.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "content/test/test_content_browser_client.h"
19 #include "ipc/ipc_message.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/mock_file_stream.h"
22 #include "net/base/request_priority.h"
23 #include "net/cert/x509_certificate.h"
24 #include "net/ssl/client_cert_store.h"
25 #include "net/ssl/ssl_cert_request_info.h"
26 #include "net/url_request/url_request.h"
27 #include "net/url_request/url_request_job_factory_impl.h"
28 #include "net/url_request/url_request_test_job.h"
29 #include "net/url_request/url_request_test_util.h"
30 #include "storage/common/blob/shareable_file_reference.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 
33 using storage::ShareableFileReference;
34 
35 namespace content {
36 namespace {
37 
38 // Stub client certificate store that returns a preset list of certificates for
39 // each request and records the arguments of the most recent request for later
40 // inspection.
41 class ClientCertStoreStub : public net::ClientCertStore {
42  public:
ClientCertStoreStub(const net::CertificateList & certs)43   ClientCertStoreStub(const net::CertificateList& certs)
44       : response_(certs),
45         request_count_(0) {}
46 
~ClientCertStoreStub()47   virtual ~ClientCertStoreStub() {}
48 
49   // Returns |cert_authorities| field of the certificate request passed in the
50   // most recent call to GetClientCerts().
51   // TODO(ppi): Make the stub independent from the internal representation of
52   // SSLCertRequestInfo. For now it seems that we cannot neither save the
53   // scoped_refptr<> (since it is never passed to us) nor copy the entire
54   // CertificateRequestInfo (since there is no copy constructor).
requested_authorities()55   std::vector<std::string> requested_authorities() {
56     return requested_authorities_;
57   }
58 
59   // Returns the number of calls to GetClientCerts().
request_count()60   int request_count() {
61     return request_count_;
62   }
63 
64   // net::ClientCertStore:
GetClientCerts(const net::SSLCertRequestInfo & cert_request_info,net::CertificateList * selected_certs,const base::Closure & callback)65   virtual void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
66                               net::CertificateList* selected_certs,
67                               const base::Closure& callback) OVERRIDE {
68     ++request_count_;
69     requested_authorities_ = cert_request_info.cert_authorities;
70     *selected_certs = response_;
71     callback.Run();
72   }
73 
74  private:
75   const net::CertificateList response_;
76   int request_count_;
77   std::vector<std::string> requested_authorities_;
78 };
79 
80 // Arbitrary read buffer size.
81 const int kReadBufSize = 1024;
82 
83 // Dummy implementation of ResourceHandler, instance of which is needed to
84 // initialize ResourceLoader.
85 class ResourceHandlerStub : public ResourceHandler {
86  public:
ResourceHandlerStub(net::URLRequest * request)87   explicit ResourceHandlerStub(net::URLRequest* request)
88       : ResourceHandler(request),
89         read_buffer_(new net::IOBuffer(kReadBufSize)),
90         defer_request_on_will_start_(false),
91         expect_reads_(true),
92         cancel_on_read_completed_(false),
93         defer_eof_(false),
94         received_on_will_read_(false),
95         received_eof_(false),
96         received_response_completed_(false),
97         total_bytes_downloaded_(0) {
98   }
99 
100   // If true, defers the resource load in OnWillStart.
set_defer_request_on_will_start(bool defer_request_on_will_start)101   void set_defer_request_on_will_start(bool defer_request_on_will_start) {
102     defer_request_on_will_start_ = defer_request_on_will_start;
103   }
104 
105   // If true, expect OnWillRead / OnReadCompleted pairs for handling
106   // data. Otherwise, expect OnDataDownloaded.
set_expect_reads(bool expect_reads)107   void set_expect_reads(bool expect_reads) { expect_reads_ = expect_reads; }
108 
109   // If true, cancel the request in OnReadCompleted by returning false.
set_cancel_on_read_completed(bool cancel_on_read_completed)110   void set_cancel_on_read_completed(bool cancel_on_read_completed) {
111     cancel_on_read_completed_ = cancel_on_read_completed;
112   }
113 
114   // If true, cancel the request in OnReadCompleted by returning false.
set_defer_eof(bool defer_eof)115   void set_defer_eof(bool defer_eof) { defer_eof_ = defer_eof; }
116 
start_url() const117   const GURL& start_url() const { return start_url_; }
response() const118   ResourceResponse* response() const { return response_.get(); }
received_response_completed() const119   bool received_response_completed() const {
120     return received_response_completed_;
121   }
status() const122   const net::URLRequestStatus& status() const { return status_; }
total_bytes_downloaded() const123   int total_bytes_downloaded() const { return total_bytes_downloaded_; }
124 
Resume()125   void Resume() {
126     controller()->Resume();
127   }
128 
129   // ResourceHandler implementation:
OnUploadProgress(uint64 position,uint64 size)130   virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
131     NOTREACHED();
132     return true;
133   }
134 
OnRequestRedirected(const net::RedirectInfo & redirect_info,ResourceResponse * response,bool * defer)135   virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info,
136                                    ResourceResponse* response,
137                                    bool* defer) OVERRIDE {
138     NOTREACHED();
139     return true;
140   }
141 
OnResponseStarted(ResourceResponse * response,bool * defer)142   virtual bool OnResponseStarted(ResourceResponse* response,
143                                  bool* defer) OVERRIDE {
144     EXPECT_FALSE(response_.get());
145     response_ = response;
146     return true;
147   }
148 
OnWillStart(const GURL & url,bool * defer)149   virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE {
150     EXPECT_TRUE(start_url_.is_empty());
151     start_url_ = url;
152     *defer = defer_request_on_will_start_;
153     return true;
154   }
155 
OnBeforeNetworkStart(const GURL & url,bool * defer)156   virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE {
157     return true;
158   }
159 
OnWillRead(scoped_refptr<net::IOBuffer> * buf,int * buf_size,int min_size)160   virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
161                           int* buf_size,
162                           int min_size) OVERRIDE {
163     EXPECT_TRUE(expect_reads_);
164     EXPECT_FALSE(received_on_will_read_);
165     EXPECT_FALSE(received_eof_);
166     EXPECT_FALSE(received_response_completed_);
167 
168     *buf = read_buffer_;
169     *buf_size = kReadBufSize;
170     received_on_will_read_ = true;
171     return true;
172   }
173 
OnReadCompleted(int bytes_read,bool * defer)174   virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE {
175     EXPECT_TRUE(received_on_will_read_);
176     EXPECT_TRUE(expect_reads_);
177     EXPECT_FALSE(received_response_completed_);
178 
179     if (bytes_read == 0) {
180       received_eof_ = true;
181       if (defer_eof_) {
182         defer_eof_ = false;
183         *defer = true;
184       }
185     }
186 
187     // Need another OnWillRead() call before seeing an OnReadCompleted().
188     received_on_will_read_ = false;
189 
190     return !cancel_on_read_completed_;
191   }
192 
OnResponseCompleted(const net::URLRequestStatus & status,const std::string & security_info,bool * defer)193   virtual void OnResponseCompleted(const net::URLRequestStatus& status,
194                                    const std::string& security_info,
195                                    bool* defer) OVERRIDE {
196     EXPECT_FALSE(received_response_completed_);
197     if (status.is_success() && expect_reads_)
198       EXPECT_TRUE(received_eof_);
199 
200     received_response_completed_ = true;
201     status_ = status;
202   }
203 
OnDataDownloaded(int bytes_downloaded)204   virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE {
205     EXPECT_FALSE(expect_reads_);
206     total_bytes_downloaded_ += bytes_downloaded;
207   }
208 
209  private:
210   scoped_refptr<net::IOBuffer> read_buffer_;
211 
212   bool defer_request_on_will_start_;
213   bool expect_reads_;
214   bool cancel_on_read_completed_;
215   bool defer_eof_;
216 
217   GURL start_url_;
218   scoped_refptr<ResourceResponse> response_;
219   bool received_on_will_read_;
220   bool received_eof_;
221   bool received_response_completed_;
222   net::URLRequestStatus status_;
223   int total_bytes_downloaded_;
224 };
225 
226 // Test browser client that captures calls to SelectClientCertificates and
227 // records the arguments of the most recent call for later inspection.
228 class SelectCertificateBrowserClient : public TestContentBrowserClient {
229  public:
SelectCertificateBrowserClient()230   SelectCertificateBrowserClient() : call_count_(0) {}
231 
SelectClientCertificate(int render_process_id,int render_view_id,const net::HttpNetworkSession * network_session,net::SSLCertRequestInfo * cert_request_info,const base::Callback<void (net::X509Certificate *)> & callback)232   virtual void SelectClientCertificate(
233       int render_process_id,
234       int render_view_id,
235       const net::HttpNetworkSession* network_session,
236       net::SSLCertRequestInfo* cert_request_info,
237       const base::Callback<void(net::X509Certificate*)>& callback) OVERRIDE {
238     ++call_count_;
239     passed_certs_ = cert_request_info->client_certs;
240   }
241 
call_count()242   int call_count() {
243     return call_count_;
244   }
245 
passed_certs()246   net::CertificateList passed_certs() {
247     return passed_certs_;
248   }
249 
250  private:
251   net::CertificateList passed_certs_;
252   int call_count_;
253 };
254 
255 class ResourceContextStub : public MockResourceContext {
256  public:
ResourceContextStub(net::URLRequestContext * test_request_context)257   explicit ResourceContextStub(net::URLRequestContext* test_request_context)
258       : MockResourceContext(test_request_context) {}
259 
CreateClientCertStore()260   virtual scoped_ptr<net::ClientCertStore> CreateClientCertStore() OVERRIDE {
261     return dummy_cert_store_.Pass();
262   }
263 
SetClientCertStore(scoped_ptr<net::ClientCertStore> store)264   void SetClientCertStore(scoped_ptr<net::ClientCertStore> store) {
265     dummy_cert_store_ = store.Pass();
266   }
267 
268  private:
269   scoped_ptr<net::ClientCertStore> dummy_cert_store_;
270 };
271 
272 // Fails to create a temporary file with the given error.
CreateTemporaryError(base::File::Error error,const CreateTemporaryFileStreamCallback & callback)273 void CreateTemporaryError(
274     base::File::Error error,
275     const CreateTemporaryFileStreamCallback& callback) {
276   base::MessageLoop::current()->PostTask(
277       FROM_HERE,
278       base::Bind(callback, error, base::Passed(scoped_ptr<net::FileStream>()),
279                  scoped_refptr<ShareableFileReference>()));
280 }
281 
282 }  // namespace
283 
284 class ResourceLoaderTest : public testing::Test,
285                            public ResourceLoaderDelegate {
286  protected:
ResourceLoaderTest()287   ResourceLoaderTest()
288     : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
289       resource_context_(&test_url_request_context_),
290       raw_ptr_resource_handler_(NULL),
291       raw_ptr_to_request_(NULL) {
292     job_factory_.SetProtocolHandler(
293         "test", net::URLRequestTestJob::CreateProtocolHandler());
294     test_url_request_context_.set_job_factory(&job_factory_);
295   }
296 
test_url() const297   GURL test_url() const {
298     return net::URLRequestTestJob::test_url_1();
299   }
300 
test_data() const301   std::string test_data() const {
302     return net::URLRequestTestJob::test_data_1();
303   }
304 
WrapResourceHandler(scoped_ptr<ResourceHandlerStub> leaf_handler,net::URLRequest * request)305   virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
306       scoped_ptr<ResourceHandlerStub> leaf_handler,
307       net::URLRequest* request) {
308     return leaf_handler.PassAs<ResourceHandler>();
309   }
310 
SetUp()311   virtual void SetUp() OVERRIDE {
312     const int kRenderProcessId = 1;
313     const int kRenderViewId = 2;
314 
315     scoped_ptr<net::URLRequest> request(
316         resource_context_.GetRequestContext()->CreateRequest(
317             test_url(),
318             net::DEFAULT_PRIORITY,
319             NULL /* delegate */,
320             NULL /* cookie_store */));
321     raw_ptr_to_request_ = request.get();
322     ResourceRequestInfo::AllocateForTesting(request.get(),
323                                             RESOURCE_TYPE_MAIN_FRAME,
324                                             &resource_context_,
325                                             kRenderProcessId,
326                                             kRenderViewId,
327                                             MSG_ROUTING_NONE,
328                                             false);
329     scoped_ptr<ResourceHandlerStub> resource_handler(
330         new ResourceHandlerStub(request.get()));
331     raw_ptr_resource_handler_ = resource_handler.get();
332     loader_.reset(new ResourceLoader(
333         request.Pass(),
334         WrapResourceHandler(resource_handler.Pass(), raw_ptr_to_request_),
335         this));
336   }
337 
338   // ResourceLoaderDelegate:
CreateLoginDelegate(ResourceLoader * loader,net::AuthChallengeInfo * auth_info)339   virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
340       ResourceLoader* loader,
341       net::AuthChallengeInfo* auth_info) OVERRIDE {
342     return NULL;
343   }
HandleExternalProtocol(ResourceLoader * loader,const GURL & url)344   virtual bool HandleExternalProtocol(ResourceLoader* loader,
345                                       const GURL& url) OVERRIDE {
346     return false;
347   }
DidStartRequest(ResourceLoader * loader)348   virtual void DidStartRequest(ResourceLoader* loader) OVERRIDE {}
DidReceiveRedirect(ResourceLoader * loader,const GURL & new_url)349   virtual void DidReceiveRedirect(ResourceLoader* loader,
350                                   const GURL& new_url) OVERRIDE {}
DidReceiveResponse(ResourceLoader * loader)351   virtual void DidReceiveResponse(ResourceLoader* loader) OVERRIDE {}
DidFinishLoading(ResourceLoader * loader)352   virtual void DidFinishLoading(ResourceLoader* loader) OVERRIDE {}
353 
354   content::TestBrowserThreadBundle thread_bundle_;
355 
356   net::URLRequestJobFactoryImpl job_factory_;
357   net::TestURLRequestContext test_url_request_context_;
358   ResourceContextStub resource_context_;
359 
360   // The ResourceLoader owns the URLRequest and the ResourceHandler.
361   ResourceHandlerStub* raw_ptr_resource_handler_;
362   net::URLRequest* raw_ptr_to_request_;
363   scoped_ptr<ResourceLoader> loader_;
364 };
365 
366 // Verifies if a call to net::UrlRequest::Delegate::OnCertificateRequested()
367 // causes client cert store to be queried for certificates and if the returned
368 // certificates are correctly passed to the content browser client for
369 // selection.
TEST_F(ResourceLoaderTest,ClientCertStoreLookup)370 TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
371   // Set up the test client cert store.
372   net::CertificateList dummy_certs(1, scoped_refptr<net::X509Certificate>(
373       new net::X509Certificate("test", "test", base::Time(), base::Time())));
374   scoped_ptr<ClientCertStoreStub> test_store(
375       new ClientCertStoreStub(dummy_certs));
376   EXPECT_EQ(0, test_store->request_count());
377 
378   // Ownership of the |test_store| is about to be turned over to ResourceLoader.
379   // We need to keep raw pointer copies to access these objects later.
380   ClientCertStoreStub* raw_ptr_to_store = test_store.get();
381   resource_context_.SetClientCertStore(
382       test_store.PassAs<net::ClientCertStore>());
383 
384   // Prepare a dummy certificate request.
385   scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
386       new net::SSLCertRequestInfo());
387   std::vector<std::string> dummy_authority(1, "dummy");
388   cert_request_info->cert_authorities = dummy_authority;
389 
390   // Plug in test content browser client.
391   SelectCertificateBrowserClient test_client;
392   ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
393 
394   // Everything is set up. Trigger the resource loader certificate request event
395   // and run the message loop.
396   loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
397   base::RunLoop().RunUntilIdle();
398 
399   // Restore the original content browser client.
400   SetBrowserClientForTesting(old_client);
401 
402   // Check if the test store was queried against correct |cert_authorities|.
403   EXPECT_EQ(1, raw_ptr_to_store->request_count());
404   EXPECT_EQ(dummy_authority, raw_ptr_to_store->requested_authorities());
405 
406   // Check if the retrieved certificates were passed to the content browser
407   // client.
408   EXPECT_EQ(1, test_client.call_count());
409   EXPECT_EQ(dummy_certs, test_client.passed_certs());
410 }
411 
412 // Verifies if a call to net::URLRequest::Delegate::OnCertificateRequested()
413 // on a platform with a NULL client cert store still calls the content browser
414 // client for selection.
TEST_F(ResourceLoaderTest,ClientCertStoreNull)415 TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
416   // Prepare a dummy certificate request.
417   scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
418       new net::SSLCertRequestInfo());
419   std::vector<std::string> dummy_authority(1, "dummy");
420   cert_request_info->cert_authorities = dummy_authority;
421 
422   // Plug in test content browser client.
423   SelectCertificateBrowserClient test_client;
424   ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
425 
426   // Everything is set up. Trigger the resource loader certificate request event
427   // and run the message loop.
428   loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
429   base::RunLoop().RunUntilIdle();
430 
431   // Restore the original content browser client.
432   SetBrowserClientForTesting(old_client);
433 
434   // Check if the SelectClientCertificate was called on the content browser
435   // client.
436   EXPECT_EQ(1, test_client.call_count());
437   EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
438 }
439 
TEST_F(ResourceLoaderTest,ResumeCancelledRequest)440 TEST_F(ResourceLoaderTest, ResumeCancelledRequest) {
441   raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
442 
443   loader_->StartRequest();
444   loader_->CancelRequest(true);
445   static_cast<ResourceController*>(loader_.get())->Resume();
446 }
447 
448 // Tests that no invariants are broken if a ResourceHandler cancels during
449 // OnReadCompleted.
TEST_F(ResourceLoaderTest,CancelOnReadCompleted)450 TEST_F(ResourceLoaderTest, CancelOnReadCompleted) {
451   raw_ptr_resource_handler_->set_cancel_on_read_completed(true);
452 
453   loader_->StartRequest();
454   base::RunLoop().RunUntilIdle();
455 
456   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
457   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
458   EXPECT_EQ(net::URLRequestStatus::CANCELED,
459             raw_ptr_resource_handler_->status().status());
460 }
461 
462 // Tests that no invariants are broken if a ResourceHandler defers EOF.
TEST_F(ResourceLoaderTest,DeferEOF)463 TEST_F(ResourceLoaderTest, DeferEOF) {
464   raw_ptr_resource_handler_->set_defer_eof(true);
465 
466   loader_->StartRequest();
467   base::RunLoop().RunUntilIdle();
468 
469   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
470   EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
471 
472   raw_ptr_resource_handler_->Resume();
473   base::RunLoop().RunUntilIdle();
474 
475   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
476   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
477             raw_ptr_resource_handler_->status().status());
478 }
479 
480 class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest {
481  public:
ResourceLoaderRedirectToFileTest()482   ResourceLoaderRedirectToFileTest()
483       : file_stream_(NULL),
484         redirect_to_file_resource_handler_(NULL) {
485   }
486 
temp_path() const487   base::FilePath temp_path() const { return temp_path_; }
deletable_file() const488   ShareableFileReference* deletable_file() const {
489     return deletable_file_.get();
490   }
file_stream() const491   net::testing::MockFileStream* file_stream() const { return file_stream_; }
redirect_to_file_resource_handler() const492   RedirectToFileResourceHandler* redirect_to_file_resource_handler() const {
493     return redirect_to_file_resource_handler_;
494   }
495 
ReleaseLoader()496   void ReleaseLoader() {
497     file_stream_ = NULL;
498     deletable_file_ = NULL;
499     loader_.reset();
500   }
501 
WrapResourceHandler(scoped_ptr<ResourceHandlerStub> leaf_handler,net::URLRequest * request)502   virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
503       scoped_ptr<ResourceHandlerStub> leaf_handler,
504       net::URLRequest* request) OVERRIDE {
505     leaf_handler->set_expect_reads(false);
506 
507     // Make a temporary file.
508     CHECK(base::CreateTemporaryFile(&temp_path_));
509     int flags = base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
510                 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_ASYNC;
511     base::File file(temp_path_, flags);
512     CHECK(file.IsValid());
513 
514     // Create mock file streams and a ShareableFileReference.
515     scoped_ptr<net::testing::MockFileStream> file_stream(
516         new net::testing::MockFileStream(file.Pass(),
517                                          base::MessageLoopProxy::current()));
518     file_stream_ = file_stream.get();
519     deletable_file_ = ShareableFileReference::GetOrCreate(
520         temp_path_,
521         ShareableFileReference::DELETE_ON_FINAL_RELEASE,
522         BrowserThread::GetMessageLoopProxyForThread(
523             BrowserThread::FILE).get());
524 
525     // Inject them into the handler.
526     scoped_ptr<RedirectToFileResourceHandler> handler(
527         new RedirectToFileResourceHandler(
528             leaf_handler.PassAs<ResourceHandler>(), request));
529     redirect_to_file_resource_handler_ = handler.get();
530     handler->SetCreateTemporaryFileStreamFunctionForTesting(
531         base::Bind(&ResourceLoaderRedirectToFileTest::PostCallback,
532                    base::Unretained(this),
533                    base::Passed(file_stream.PassAs<net::FileStream>())));
534     return handler.PassAs<ResourceHandler>();
535   }
536 
537  private:
PostCallback(scoped_ptr<net::FileStream> file_stream,const CreateTemporaryFileStreamCallback & callback)538   void PostCallback(
539       scoped_ptr<net::FileStream> file_stream,
540       const CreateTemporaryFileStreamCallback& callback) {
541     base::MessageLoop::current()->PostTask(
542         FROM_HERE,
543         base::Bind(callback, base::File::FILE_OK,
544                    base::Passed(&file_stream), deletable_file_));
545   }
546 
547   base::FilePath temp_path_;
548   scoped_refptr<ShareableFileReference> deletable_file_;
549   // These are owned by the ResourceLoader.
550   net::testing::MockFileStream* file_stream_;
551   RedirectToFileResourceHandler* redirect_to_file_resource_handler_;
552 };
553 
554 // Tests that a RedirectToFileResourceHandler works and forwards everything
555 // downstream.
TEST_F(ResourceLoaderRedirectToFileTest,Basic)556 TEST_F(ResourceLoaderRedirectToFileTest, Basic) {
557   // Run it to completion.
558   loader_->StartRequest();
559   base::RunLoop().RunUntilIdle();
560 
561   // Check that the handler forwarded all information to the downstream handler.
562   EXPECT_EQ(temp_path(),
563             raw_ptr_resource_handler_->response()->head.download_file_path);
564   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
565   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
566   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
567             raw_ptr_resource_handler_->status().status());
568   EXPECT_EQ(test_data().size(), static_cast<size_t>(
569       raw_ptr_resource_handler_->total_bytes_downloaded()));
570 
571   // Check that the data was written to the file.
572   std::string contents;
573   ASSERT_TRUE(base::ReadFileToString(temp_path(), &contents));
574   EXPECT_EQ(test_data(), contents);
575 
576   // Release the loader and the saved reference to file. The file should be gone
577   // now.
578   ReleaseLoader();
579   base::RunLoop().RunUntilIdle();
580   EXPECT_FALSE(base::PathExists(temp_path()));
581 }
582 
583 // Tests that RedirectToFileResourceHandler handles errors in creating the
584 // temporary file.
TEST_F(ResourceLoaderRedirectToFileTest,CreateTemporaryError)585 TEST_F(ResourceLoaderRedirectToFileTest, CreateTemporaryError) {
586   // Swap out the create temporary function.
587   redirect_to_file_resource_handler()->
588       SetCreateTemporaryFileStreamFunctionForTesting(
589           base::Bind(&CreateTemporaryError, base::File::FILE_ERROR_FAILED));
590 
591   // Run it to completion.
592   loader_->StartRequest();
593   base::RunLoop().RunUntilIdle();
594 
595   // To downstream, the request was canceled.
596   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
597   EXPECT_EQ(net::URLRequestStatus::CANCELED,
598             raw_ptr_resource_handler_->status().status());
599   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
600 }
601 
602 // Tests that RedirectToFileResourceHandler handles synchronous write errors.
TEST_F(ResourceLoaderRedirectToFileTest,WriteError)603 TEST_F(ResourceLoaderRedirectToFileTest, WriteError) {
604   file_stream()->set_forced_error(net::ERR_FAILED);
605 
606   // Run it to completion.
607   loader_->StartRequest();
608   base::RunLoop().RunUntilIdle();
609 
610   // To downstream, the request was canceled sometime after it started, but
611   // before any data was written.
612   EXPECT_EQ(temp_path(),
613             raw_ptr_resource_handler_->response()->head.download_file_path);
614   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
615   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
616   EXPECT_EQ(net::URLRequestStatus::CANCELED,
617             raw_ptr_resource_handler_->status().status());
618   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
619 
620   // Release the loader. The file should be gone now.
621   ReleaseLoader();
622   base::RunLoop().RunUntilIdle();
623   EXPECT_FALSE(base::PathExists(temp_path()));
624 }
625 
626 // Tests that RedirectToFileResourceHandler handles asynchronous write errors.
TEST_F(ResourceLoaderRedirectToFileTest,WriteErrorAsync)627 TEST_F(ResourceLoaderRedirectToFileTest, WriteErrorAsync) {
628   file_stream()->set_forced_error_async(net::ERR_FAILED);
629 
630   // Run it to completion.
631   loader_->StartRequest();
632   base::RunLoop().RunUntilIdle();
633 
634   // To downstream, the request was canceled sometime after it started, but
635   // before any data was written.
636   EXPECT_EQ(temp_path(),
637             raw_ptr_resource_handler_->response()->head.download_file_path);
638   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
639   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
640   EXPECT_EQ(net::URLRequestStatus::CANCELED,
641             raw_ptr_resource_handler_->status().status());
642   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
643 
644   // Release the loader. The file should be gone now.
645   ReleaseLoader();
646   base::RunLoop().RunUntilIdle();
647   EXPECT_FALSE(base::PathExists(temp_path()));
648 }
649 
650 // Tests that RedirectToFileHandler defers completion if there are outstanding
651 // writes and accounts for errors which occur in that time.
TEST_F(ResourceLoaderRedirectToFileTest,DeferCompletion)652 TEST_F(ResourceLoaderRedirectToFileTest, DeferCompletion) {
653   // Program the MockFileStream to error asynchronously, but throttle the
654   // callback.
655   file_stream()->set_forced_error_async(net::ERR_FAILED);
656   file_stream()->ThrottleCallbacks();
657 
658   // Run it as far as it will go.
659   loader_->StartRequest();
660   base::RunLoop().RunUntilIdle();
661 
662   // At this point, the request should have completed.
663   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
664             raw_ptr_to_request_->status().status());
665 
666   // However, the resource loader stack is stuck somewhere after receiving the
667   // response.
668   EXPECT_EQ(temp_path(),
669             raw_ptr_resource_handler_->response()->head.download_file_path);
670   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
671   EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
672   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
673 
674   // Now, release the floodgates.
675   file_stream()->ReleaseCallbacks();
676   base::RunLoop().RunUntilIdle();
677 
678   // Although the URLRequest was successful, the leaf handler sees a failure
679   // because the write never completed.
680   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
681   EXPECT_EQ(net::URLRequestStatus::CANCELED,
682             raw_ptr_resource_handler_->status().status());
683 
684   // Release the loader. The file should be gone now.
685   ReleaseLoader();
686   base::RunLoop().RunUntilIdle();
687   EXPECT_FALSE(base::PathExists(temp_path()));
688 }
689 
690 // Tests that a RedirectToFileResourceHandler behaves properly when the
691 // downstream handler defers OnWillStart.
TEST_F(ResourceLoaderRedirectToFileTest,DownstreamDeferStart)692 TEST_F(ResourceLoaderRedirectToFileTest, DownstreamDeferStart) {
693   // Defer OnWillStart.
694   raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
695 
696   // Run as far as we'll go.
697   loader_->StartRequest();
698   base::RunLoop().RunUntilIdle();
699 
700   // The request should have stopped at OnWillStart.
701   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
702   EXPECT_FALSE(raw_ptr_resource_handler_->response());
703   EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
704   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
705 
706   // Now resume the request. Now we complete.
707   raw_ptr_resource_handler_->Resume();
708   base::RunLoop().RunUntilIdle();
709 
710   // Check that the handler forwarded all information to the downstream handler.
711   EXPECT_EQ(temp_path(),
712             raw_ptr_resource_handler_->response()->head.download_file_path);
713   EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
714   EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
715   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
716             raw_ptr_resource_handler_->status().status());
717   EXPECT_EQ(test_data().size(), static_cast<size_t>(
718       raw_ptr_resource_handler_->total_bytes_downloaded()));
719 
720   // Check that the data was written to the file.
721   std::string contents;
722   ASSERT_TRUE(base::ReadFileToString(temp_path(), &contents));
723   EXPECT_EQ(test_data(), contents);
724 
725   // Release the loader. The file should be gone now.
726   ReleaseLoader();
727   base::RunLoop().RunUntilIdle();
728   EXPECT_FALSE(base::PathExists(temp_path()));
729 }
730 
731 }  // namespace content
732