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 } 212