resource_loader_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
33using storage::ShareableFileReference;
34
35namespace content {
36namespace {
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.
41class ClientCertStoreStub : public net::ClientCertStore {
42 public:
43  ClientCertStoreStub(const net::CertificateList& certs)
44      : response_(certs),
45        request_count_(0) {}
46
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).
55  std::vector<std::string> requested_authorities() {
56    return requested_authorities_;
57  }
58
59  // Returns the number of calls to GetClientCerts().
60  int request_count() {
61    return request_count_;
62  }
63
64  // net::ClientCertStore:
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.
81const int kReadBufSize = 1024;
82
83// Dummy implementation of ResourceHandler, instance of which is needed to
84// initialize ResourceLoader.
85class ResourceHandlerStub : public ResourceHandler {
86 public:
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.
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.
107  void set_expect_reads(bool expect_reads) { expect_reads_ = expect_reads; }
108
109  // If true, cancel the request in OnReadCompleted by returning false.
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.
115  void set_defer_eof(bool defer_eof) { defer_eof_ = defer_eof; }
116
117  const GURL& start_url() const { return start_url_; }
118  ResourceResponse* response() const { return response_.get(); }
119  bool received_response_completed() const {
120    return received_response_completed_;
121  }
122  const net::URLRequestStatus& status() const { return status_; }
123  int total_bytes_downloaded() const { return total_bytes_downloaded_; }
124
125  void Resume() {
126    controller()->Resume();
127  }
128
129  // ResourceHandler implementation:
130  virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
131    NOTREACHED();
132    return true;
133  }
134
135  virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info,
136                                   ResourceResponse* response,
137                                   bool* defer) OVERRIDE {
138    NOTREACHED();
139    return true;
140  }
141
142  virtual bool OnResponseStarted(ResourceResponse* response,
143                                 bool* defer) OVERRIDE {
144    EXPECT_FALSE(response_.get());
145    response_ = response;
146    return true;
147  }
148
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
156  virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE {
157    return true;
158  }
159
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
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
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
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.
228class SelectCertificateBrowserClient : public TestContentBrowserClient {
229 public:
230  SelectCertificateBrowserClient() : call_count_(0) {}
231
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
242  int call_count() {
243    return call_count_;
244  }
245
246  net::CertificateList passed_certs() {
247    return passed_certs_;
248  }
249
250 private:
251  net::CertificateList passed_certs_;
252  int call_count_;
253};
254
255class ResourceContextStub : public MockResourceContext {
256 public:
257  explicit ResourceContextStub(net::URLRequestContext* test_request_context)
258      : MockResourceContext(test_request_context) {}
259
260  virtual scoped_ptr<net::ClientCertStore> CreateClientCertStore() OVERRIDE {
261    return dummy_cert_store_.Pass();
262  }
263
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.
273void 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
284class ResourceLoaderTest : public testing::Test,
285                           public ResourceLoaderDelegate {
286 protected:
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
297  GURL test_url() const {
298    return net::URLRequestTestJob::test_url_1();
299  }
300
301  std::string test_data() const {
302    return net::URLRequestTestJob::test_data_1();
303  }
304
305  virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
306      scoped_ptr<ResourceHandlerStub> leaf_handler,
307      net::URLRequest* request) {
308    return leaf_handler.PassAs<ResourceHandler>();
309  }
310
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:
339  virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
340      ResourceLoader* loader,
341      net::AuthChallengeInfo* auth_info) OVERRIDE {
342    return NULL;
343  }
344  virtual bool HandleExternalProtocol(ResourceLoader* loader,
345                                      const GURL& url) OVERRIDE {
346    return false;
347  }
348  virtual void DidStartRequest(ResourceLoader* loader) OVERRIDE {}
349  virtual void DidReceiveRedirect(ResourceLoader* loader,
350                                  const GURL& new_url) OVERRIDE {}
351  virtual void DidReceiveResponse(ResourceLoader* loader) OVERRIDE {}
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.
370TEST_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.
415TEST_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
440TEST_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.
450TEST_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.
463TEST_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
480class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest {
481 public:
482  ResourceLoaderRedirectToFileTest()
483      : file_stream_(NULL),
484        redirect_to_file_resource_handler_(NULL) {
485  }
486
487  base::FilePath temp_path() const { return temp_path_; }
488  ShareableFileReference* deletable_file() const {
489    return deletable_file_.get();
490  }
491  net::testing::MockFileStream* file_stream() const { return file_stream_; }
492  RedirectToFileResourceHandler* redirect_to_file_resource_handler() const {
493    return redirect_to_file_resource_handler_;
494  }
495
496  void ReleaseLoader() {
497    file_stream_ = NULL;
498    deletable_file_ = NULL;
499    loader_.reset();
500  }
501
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:
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.
556TEST_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.
585TEST_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.
603TEST_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.
627TEST_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.
652TEST_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.
692TEST_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