Probably broke Win7 Tests (dbg)(6). http://build.chromium.org/p/chromium.win/builders...
[chromium-blink-merge.git] / net / url_request / url_request_ftp_job.cc
blob09c6f0f7548f7b5bd61842d0b60b2fb389f4ef0f
1 // Copyright (c) 2012 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.
5 #include "net/url_request/url_request_ftp_job.h"
7 #include "base/compiler_specific.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "net/base/auth.h"
11 #include "net/base/host_port_pair.h"
12 #include "net/base/load_flags.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/net_util.h"
15 #include "net/ftp/ftp_auth_cache.h"
16 #include "net/ftp/ftp_response_info.h"
17 #include "net/ftp/ftp_transaction_factory.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/http/http_transaction_factory.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_context.h"
22 #include "net/url_request/url_request_error_job.h"
24 namespace net {
26 URLRequestFtpJob::URLRequestFtpJob(
27 URLRequest* request,
28 NetworkDelegate* network_delegate,
29 FtpTransactionFactory* ftp_transaction_factory,
30 FtpAuthCache* ftp_auth_cache)
31 : URLRequestJob(request, network_delegate),
32 priority_(DEFAULT_PRIORITY),
33 proxy_service_(request_->context()->proxy_service()),
34 pac_request_(NULL),
35 http_response_info_(NULL),
36 read_in_progress_(false),
37 ftp_transaction_factory_(ftp_transaction_factory),
38 ftp_auth_cache_(ftp_auth_cache),
39 weak_factory_(this) {
40 DCHECK(proxy_service_);
41 DCHECK(ftp_transaction_factory);
42 DCHECK(ftp_auth_cache);
45 URLRequestFtpJob::~URLRequestFtpJob() {
46 if (pac_request_)
47 proxy_service_->CancelPacRequest(pac_request_);
50 bool URLRequestFtpJob::IsSafeRedirect(const GURL& location) {
51 // Disallow all redirects.
52 return false;
55 bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
56 if (proxy_info_.is_direct()) {
57 if (ftp_transaction_->GetResponseInfo()->is_directory_listing) {
58 *mime_type = "text/vnd.chromium.ftp-dir";
59 return true;
61 } else {
62 // No special handling of MIME type is needed. As opposed to direct FTP
63 // transaction, we do not get a raw directory listing to parse.
64 return http_transaction_->GetResponseInfo()->
65 headers->GetMimeType(mime_type);
67 return false;
70 void URLRequestFtpJob::GetResponseInfo(HttpResponseInfo* info) {
71 if (http_response_info_)
72 *info = *http_response_info_;
75 HostPortPair URLRequestFtpJob::GetSocketAddress() const {
76 if (proxy_info_.is_direct()) {
77 if (!ftp_transaction_)
78 return HostPortPair();
79 return ftp_transaction_->GetResponseInfo()->socket_address;
80 } else {
81 if (!http_transaction_)
82 return HostPortPair();
83 return http_transaction_->GetResponseInfo()->socket_address;
87 void URLRequestFtpJob::SetPriority(RequestPriority priority) {
88 priority_ = priority;
89 if (http_transaction_)
90 http_transaction_->SetPriority(priority);
93 void URLRequestFtpJob::Start() {
94 DCHECK(!pac_request_);
95 DCHECK(!ftp_transaction_);
96 DCHECK(!http_transaction_);
98 int rv = OK;
99 if (request_->load_flags() & LOAD_BYPASS_PROXY) {
100 proxy_info_.UseDirect();
101 } else {
102 DCHECK_EQ(request_->context()->proxy_service(), proxy_service_);
103 rv = proxy_service_->ResolveProxy(
104 request_->url(),
105 request_->load_flags(),
106 &proxy_info_,
107 base::Bind(&URLRequestFtpJob::OnResolveProxyComplete,
108 base::Unretained(this)),
109 &pac_request_,
110 NULL,
111 request_->net_log());
113 if (rv == ERR_IO_PENDING)
114 return;
116 OnResolveProxyComplete(rv);
119 void URLRequestFtpJob::Kill() {
120 if (ftp_transaction_)
121 ftp_transaction_.reset();
122 if (http_transaction_)
123 http_transaction_.reset();
124 URLRequestJob::Kill();
125 weak_factory_.InvalidateWeakPtrs();
128 void URLRequestFtpJob::OnResolveProxyComplete(int result) {
129 pac_request_ = NULL;
131 if (result != OK) {
132 OnStartCompletedAsync(result);
133 return;
136 // Remove unsupported proxies from the list.
137 proxy_info_.RemoveProxiesWithoutScheme(
138 ProxyServer::SCHEME_DIRECT |
139 ProxyServer::SCHEME_HTTP |
140 ProxyServer::SCHEME_HTTPS);
142 // TODO(phajdan.jr): Implement proxy fallback, http://crbug.com/171495 .
143 if (proxy_info_.is_direct())
144 StartFtpTransaction();
145 else if (proxy_info_.is_http() || proxy_info_.is_https())
146 StartHttpTransaction();
147 else
148 OnStartCompletedAsync(ERR_NO_SUPPORTED_PROXIES);
151 void URLRequestFtpJob::StartFtpTransaction() {
152 // Create a transaction.
153 DCHECK(!ftp_transaction_);
155 ftp_request_info_.url = request_->url();
156 ftp_transaction_.reset(ftp_transaction_factory_->CreateTransaction());
158 // No matter what, we want to report our status as IO pending since we will
159 // be notifying our consumer asynchronously via OnStartCompleted.
160 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
161 int rv;
162 if (ftp_transaction_) {
163 rv = ftp_transaction_->Start(
164 &ftp_request_info_,
165 base::Bind(&URLRequestFtpJob::OnStartCompleted,
166 base::Unretained(this)),
167 request_->net_log());
168 if (rv == ERR_IO_PENDING)
169 return;
170 } else {
171 rv = ERR_FAILED;
173 // The transaction started synchronously, but we need to notify the
174 // URLRequest delegate via the message loop.
175 OnStartCompletedAsync(rv);
178 void URLRequestFtpJob::StartHttpTransaction() {
179 // Create a transaction.
180 DCHECK(!http_transaction_);
182 // Do not cache FTP responses sent through HTTP proxy.
183 request_->SetLoadFlags(request_->load_flags() |
184 LOAD_DISABLE_CACHE |
185 LOAD_DO_NOT_SAVE_COOKIES |
186 LOAD_DO_NOT_SEND_COOKIES);
188 http_request_info_.url = request_->url();
189 http_request_info_.method = request_->method();
190 http_request_info_.load_flags = request_->load_flags();
192 int rv = request_->context()->http_transaction_factory()->CreateTransaction(
193 priority_, &http_transaction_);
194 if (rv == OK) {
195 rv = http_transaction_->Start(
196 &http_request_info_,
197 base::Bind(&URLRequestFtpJob::OnStartCompleted,
198 base::Unretained(this)),
199 request_->net_log());
200 if (rv == ERR_IO_PENDING)
201 return;
203 // The transaction started synchronously, but we need to notify the
204 // URLRequest delegate via the message loop.
205 OnStartCompletedAsync(rv);
208 void URLRequestFtpJob::OnStartCompleted(int result) {
209 // Clear the IO_PENDING status
210 SetStatus(URLRequestStatus());
212 // Note that ftp_transaction_ may be NULL due to a creation failure.
213 if (ftp_transaction_) {
214 // FTP obviously doesn't have HTTP Content-Length header. We have to pass
215 // the content size information manually.
216 set_expected_content_size(
217 ftp_transaction_->GetResponseInfo()->expected_content_size);
220 if (result == OK) {
221 if (http_transaction_) {
222 http_response_info_ = http_transaction_->GetResponseInfo();
223 SetProxyServer(http_response_info_->proxy_server);
225 if (http_response_info_->headers->response_code() == 401 ||
226 http_response_info_->headers->response_code() == 407) {
227 HandleAuthNeededResponse();
228 return;
231 NotifyHeadersComplete();
232 } else if (ftp_transaction_ &&
233 ftp_transaction_->GetResponseInfo()->needs_auth) {
234 HandleAuthNeededResponse();
235 return;
236 } else {
237 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
241 void URLRequestFtpJob::OnStartCompletedAsync(int result) {
242 base::MessageLoop::current()->PostTask(
243 FROM_HERE,
244 base::Bind(&URLRequestFtpJob::OnStartCompleted,
245 weak_factory_.GetWeakPtr(), result));
248 void URLRequestFtpJob::OnReadCompleted(int result) {
249 read_in_progress_ = false;
250 if (result == 0) {
251 NotifyDone(URLRequestStatus());
252 } else if (result < 0) {
253 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
254 } else {
255 // Clear the IO_PENDING status
256 SetStatus(URLRequestStatus());
258 NotifyReadComplete(result);
261 void URLRequestFtpJob::RestartTransactionWithAuth() {
262 DCHECK(auth_data_.get() && auth_data_->state == AUTH_STATE_HAVE_AUTH);
264 // No matter what, we want to report our status as IO pending since we will
265 // be notifying our consumer asynchronously via OnStartCompleted.
266 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
268 int rv;
269 if (proxy_info_.is_direct()) {
270 rv = ftp_transaction_->RestartWithAuth(
271 auth_data_->credentials,
272 base::Bind(&URLRequestFtpJob::OnStartCompleted,
273 base::Unretained(this)));
274 } else {
275 rv = http_transaction_->RestartWithAuth(
276 auth_data_->credentials,
277 base::Bind(&URLRequestFtpJob::OnStartCompleted,
278 base::Unretained(this)));
280 if (rv == ERR_IO_PENDING)
281 return;
283 OnStartCompletedAsync(rv);
286 LoadState URLRequestFtpJob::GetLoadState() const {
287 if (proxy_info_.is_direct()) {
288 return ftp_transaction_ ?
289 ftp_transaction_->GetLoadState() : LOAD_STATE_IDLE;
290 } else {
291 return http_transaction_ ?
292 http_transaction_->GetLoadState() : LOAD_STATE_IDLE;
296 bool URLRequestFtpJob::NeedsAuth() {
297 return auth_data_.get() && auth_data_->state == AUTH_STATE_NEED_AUTH;
300 void URLRequestFtpJob::GetAuthChallengeInfo(
301 scoped_refptr<AuthChallengeInfo>* result) {
302 DCHECK(NeedsAuth());
304 if (http_response_info_) {
305 *result = http_response_info_->auth_challenge;
306 return;
309 scoped_refptr<AuthChallengeInfo> auth_info(new AuthChallengeInfo);
310 auth_info->is_proxy = false;
311 auth_info->challenger = HostPortPair::FromURL(request_->url());
312 // scheme and realm are kept empty.
313 DCHECK(auth_info->scheme.empty());
314 DCHECK(auth_info->realm.empty());
315 result->swap(auth_info);
318 void URLRequestFtpJob::SetAuth(const AuthCredentials& credentials) {
319 DCHECK(ftp_transaction_ || http_transaction_);
320 DCHECK(NeedsAuth());
322 auth_data_->state = AUTH_STATE_HAVE_AUTH;
323 auth_data_->credentials = credentials;
325 if (ftp_transaction_) {
326 ftp_auth_cache_->Add(request_->url().GetOrigin(),
327 auth_data_->credentials);
330 RestartTransactionWithAuth();
333 void URLRequestFtpJob::CancelAuth() {
334 DCHECK(ftp_transaction_ || http_transaction_);
335 DCHECK(NeedsAuth());
337 auth_data_->state = AUTH_STATE_CANCELED;
339 // Once the auth is cancelled, we proceed with the request as though
340 // there were no auth. Schedule this for later so that we don't cause
341 // any recursing into the caller as a result of this call.
342 OnStartCompletedAsync(OK);
345 UploadProgress URLRequestFtpJob::GetUploadProgress() const {
346 return UploadProgress();
349 bool URLRequestFtpJob::ReadRawData(IOBuffer* buf,
350 int buf_size,
351 int *bytes_read) {
352 DCHECK_NE(buf_size, 0);
353 DCHECK(bytes_read);
354 DCHECK(!read_in_progress_);
356 int rv;
357 if (proxy_info_.is_direct()) {
358 rv = ftp_transaction_->Read(buf, buf_size,
359 base::Bind(&URLRequestFtpJob::OnReadCompleted,
360 base::Unretained(this)));
361 } else {
362 rv = http_transaction_->Read(buf, buf_size,
363 base::Bind(&URLRequestFtpJob::OnReadCompleted,
364 base::Unretained(this)));
367 if (rv >= 0) {
368 *bytes_read = rv;
369 return true;
372 if (rv == ERR_IO_PENDING) {
373 read_in_progress_ = true;
374 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
375 } else {
376 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
378 return false;
381 void URLRequestFtpJob::HandleAuthNeededResponse() {
382 GURL origin = request_->url().GetOrigin();
384 if (auth_data_.get()) {
385 if (auth_data_->state == AUTH_STATE_CANCELED) {
386 NotifyHeadersComplete();
387 return;
390 if (ftp_transaction_ && auth_data_->state == AUTH_STATE_HAVE_AUTH)
391 ftp_auth_cache_->Remove(origin, auth_data_->credentials);
392 } else {
393 auth_data_ = new AuthData;
395 auth_data_->state = AUTH_STATE_NEED_AUTH;
397 FtpAuthCache::Entry* cached_auth = NULL;
398 if (ftp_transaction_ && ftp_transaction_->GetResponseInfo()->needs_auth)
399 cached_auth = ftp_auth_cache_->Lookup(origin);
400 if (cached_auth) {
401 // Retry using cached auth data.
402 SetAuth(cached_auth->credentials);
403 } else {
404 // Prompt for a username/password.
405 NotifyHeadersComplete();
409 } // namespace net