1// Copyright 2014 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 "mojo/tools/package_manager/package_fetcher.h"
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/files/file_util.h"
10#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
11
12namespace mojo {
13
14PackageFetcher::PackageFetcher(NetworkService* network_service,
15                               PackageFetcherDelegate* delegate,
16                               const GURL& url)
17    : delegate_(delegate),
18      url_(url) {
19  network_service->CreateURLLoader(Get(&loader_));
20
21  URLRequestPtr request(URLRequest::New());
22  request->url = url.spec();
23  request->auto_follow_redirects = true;
24
25  loader_->Start(request.Pass(),
26                 base::Bind(&PackageFetcher::OnReceivedResponse,
27                            base::Unretained(this)));
28}
29
30PackageFetcher::~PackageFetcher() {
31}
32
33void PackageFetcher::OnReceivedResponse(URLResponsePtr response) {
34  if (response->error) {
35    printf("Got error %d (%s) for %s\n",
36           response->error->code,
37           response->error->description.get().c_str(),
38           url_.spec().c_str());
39    delegate_->FetchFailed(url_);
40    return;
41  }
42
43  if (!base::CreateTemporaryFile(&output_file_name_)) {
44    delegate_->FetchFailed(url_);
45    return;
46  }
47  output_file_.Initialize(output_file_name_,
48      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
49  if (!output_file_.IsValid()) {
50    base::DeleteFile(output_file_name_, false);
51    delegate_->FetchFailed(url_);
52    // Danger: may be deleted now.
53    return;
54  }
55
56  body_ = response->body.Pass();
57  ReadData(MOJO_RESULT_OK);
58  // Danger: may be deleted now.
59}
60
61void PackageFetcher::ReadData(MojoResult) {
62  char buf[4096];
63  uint32_t num_bytes = sizeof(buf);
64  MojoResult result = ReadDataRaw(body_.get(), buf, &num_bytes,
65                                  MOJO_READ_DATA_FLAG_NONE);
66  if (result == MOJO_RESULT_SHOULD_WAIT) {
67    WaitToReadMore();
68  } else if (result == MOJO_RESULT_OK) {
69    if (output_file_.WriteAtCurrentPos(buf, num_bytes) !=
70        static_cast<int>(num_bytes)) {
71      // Clean up the output file.
72      output_file_.Close();
73      base::DeleteFile(output_file_name_, false);
74
75      delegate_->FetchFailed(url_);
76      // Danger: may be deleted now.
77      return;
78    }
79    WaitToReadMore();
80  } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
81    // Done.
82    output_file_.Close();
83    delegate_->FetchSucceeded(url_, output_file_name_);
84    // Danger: may be deleted now.
85  }
86}
87
88void PackageFetcher::WaitToReadMore() {
89  handle_watcher_.Start(
90      body_.get(),
91      MOJO_HANDLE_SIGNAL_READABLE,
92      MOJO_DEADLINE_INDEFINITE,
93      base::Bind(&PackageFetcher::ReadData, base::Unretained(this)));
94}
95
96}  // namespace mojo
97