- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / safe_browsing / two_phase_uploader.cc
1 // Copyright 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 "chrome/browser/safe_browsing/two_phase_uploader.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/task_runner.h"
10 #include "net/base/net_errors.h"
11 #include "net/http/http_response_headers.h"
12 #include "net/url_request/url_fetcher.h"
13 #include "net/url_request/url_fetcher_delegate.h"
14 #include "net/url_request/url_request_status.h"
15
16 namespace {
17
18 // Header sent on initial request to start the two phase upload process.
19 const char* kStartHeader = "x-goog-resumable: start";
20
21 // Header returned on initial response with URL to use for the second phase.
22 const char* kLocationHeader = "Location";
23
24 const char* kUploadContentType = "application/octet-stream";
25
26 class TwoPhaseUploaderImpl : public net::URLFetcherDelegate,
27                              public TwoPhaseUploader {
28  public:
29   TwoPhaseUploaderImpl(net::URLRequestContextGetter* url_request_context_getter,
30                        base::TaskRunner* file_task_runner,
31                        const GURL& base_url,
32                        const std::string& metadata,
33                        const base::FilePath& file_path,
34                        const ProgressCallback& progress_callback,
35                        const FinishCallback& finish_callback);
36   virtual ~TwoPhaseUploaderImpl();
37
38   // Begins the upload process.
39   virtual void Start() OVERRIDE;
40
41   // net::URLFetcherDelegate implementation:
42   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
43   virtual void OnURLFetchUploadProgress(const net::URLFetcher* source,
44                                         int64 current,
45                                         int64 total) OVERRIDE;
46
47  private:
48   void UploadMetadata();
49   void UploadFile();
50   void Finish(int net_error, int response_code, const std::string& response);
51
52   State state_;
53   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
54   scoped_refptr<base::TaskRunner> file_task_runner_;
55   GURL base_url_;
56   GURL upload_url_;
57   std::string metadata_;
58   const base::FilePath file_path_;
59   ProgressCallback progress_callback_;
60   FinishCallback finish_callback_;
61
62   scoped_ptr<net::URLFetcher> url_fetcher_;
63
64   DISALLOW_COPY_AND_ASSIGN(TwoPhaseUploaderImpl);
65 };
66
67 TwoPhaseUploaderImpl::TwoPhaseUploaderImpl(
68     net::URLRequestContextGetter* url_request_context_getter,
69     base::TaskRunner* file_task_runner,
70     const GURL& base_url,
71     const std::string& metadata,
72     const base::FilePath& file_path,
73     const ProgressCallback& progress_callback,
74     const FinishCallback& finish_callback)
75     : state_(STATE_NONE),
76       url_request_context_getter_(url_request_context_getter),
77       file_task_runner_(file_task_runner),
78       base_url_(base_url),
79       metadata_(metadata),
80       file_path_(file_path),
81       progress_callback_(progress_callback),
82       finish_callback_(finish_callback) {
83 }
84
85 TwoPhaseUploaderImpl::~TwoPhaseUploaderImpl() {
86   DCHECK(CalledOnValidThread());
87 }
88
89 void TwoPhaseUploaderImpl::Start() {
90   DCHECK(CalledOnValidThread());
91   DCHECK_EQ(STATE_NONE, state_);
92
93   UploadMetadata();
94 }
95
96 void TwoPhaseUploaderImpl::OnURLFetchComplete(const net::URLFetcher* source) {
97   DCHECK(CalledOnValidThread());
98   net::URLRequestStatus status = source->GetStatus();
99   int response_code = source->GetResponseCode();
100
101   DVLOG(1) << __FUNCTION__ << " " << source->GetURL().spec()
102            << " " << status.status() << " " << response_code;
103
104   if (!status.is_success()) {
105     LOG(ERROR) << "URLFetcher failed, status=" << status.status()
106                << " err=" << status.error();
107     Finish(status.error(), response_code, std::string());
108     return;
109   }
110
111   std::string response;
112   source->GetResponseAsString(&response);
113
114   switch (state_) {
115     case UPLOAD_METADATA:
116       {
117         if (response_code != 201) {
118           LOG(ERROR) << "Invalid response to initial request: "
119                      << response_code;
120           Finish(net::OK, response_code, response);
121           return;
122         }
123         std::string location;
124         if (!source->GetResponseHeaders()->EnumerateHeader(
125               NULL, kLocationHeader, &location)) {
126           LOG(ERROR) << "no location header";
127           Finish(net::OK, response_code, std::string());
128           return;
129         }
130         DVLOG(1) << "upload location: " << location;
131         upload_url_ = GURL(location);
132         UploadFile();
133         break;
134       }
135     case UPLOAD_FILE:
136       if (response_code != 200) {
137           LOG(ERROR) << "Invalid response to upload request: "
138                      << response_code;
139       } else {
140         state_ = STATE_SUCCESS;
141       }
142       Finish(net::OK, response_code, response);
143       return;
144     default:
145       NOTREACHED();
146   };
147 }
148
149 void TwoPhaseUploaderImpl::OnURLFetchUploadProgress(
150     const net::URLFetcher* source,
151     int64 current,
152     int64 total) {
153   DCHECK(CalledOnValidThread());
154   DVLOG(3) << __FUNCTION__ << " " << source->GetURL().spec()
155            << " " << current << "/" << total;
156   if (state_ == UPLOAD_FILE && !progress_callback_.is_null())
157     progress_callback_.Run(current, total);
158 }
159
160 void TwoPhaseUploaderImpl::UploadMetadata() {
161   DCHECK(CalledOnValidThread());
162   state_ = UPLOAD_METADATA;
163   url_fetcher_.reset(net::URLFetcher::Create(base_url_, net::URLFetcher::POST,
164                                              this));
165   url_fetcher_->SetRequestContext(url_request_context_getter_.get());
166   url_fetcher_->SetExtraRequestHeaders(kStartHeader);
167   url_fetcher_->SetUploadData(kUploadContentType, metadata_);
168   url_fetcher_->Start();
169 }
170
171 void TwoPhaseUploaderImpl::UploadFile() {
172   DCHECK(CalledOnValidThread());
173   state_ = UPLOAD_FILE;
174
175   url_fetcher_.reset(net::URLFetcher::Create(upload_url_, net::URLFetcher::PUT,
176                                              this));
177   url_fetcher_->SetRequestContext(url_request_context_getter_.get());
178   url_fetcher_->SetUploadFilePath(
179       kUploadContentType, file_path_, 0, kuint64max, file_task_runner_);
180   url_fetcher_->Start();
181 }
182
183 void TwoPhaseUploaderImpl::Finish(int net_error,
184                                   int response_code,
185                                   const std::string& response) {
186   DCHECK(CalledOnValidThread());
187   finish_callback_.Run(state_, net_error, response_code, response);
188 }
189
190 }  // namespace
191
192 // static
193 TwoPhaseUploaderFactory* TwoPhaseUploader::factory_ = NULL;
194
195 // static
196 TwoPhaseUploader* TwoPhaseUploader::Create(
197     net::URLRequestContextGetter* url_request_context_getter,
198     base::TaskRunner* file_task_runner,
199     const GURL& base_url,
200     const std::string& metadata,
201     const base::FilePath& file_path,
202     const ProgressCallback& progress_callback,
203     const FinishCallback& finish_callback) {
204   if (!TwoPhaseUploader::factory_)
205     return new TwoPhaseUploaderImpl(
206         url_request_context_getter, file_task_runner, base_url, metadata,
207         file_path, progress_callback, finish_callback);
208   return TwoPhaseUploader::factory_->CreateTwoPhaseUploader(
209       url_request_context_getter, file_task_runner, base_url, metadata,
210       file_path, progress_callback, finish_callback);
211 }