We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / url_request / url_request_ftp_job.cc
blobd774714246e66fee311c84c8f26c3b3ace365dfc
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/profiler/scoped_tracker.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "net/base/auth.h"
12 #include "net/base/host_port_pair.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/net_util.h"
16 #include "net/ftp/ftp_auth_cache.h"
17 #include "net/ftp/ftp_response_info.h"
18 #include "net/ftp/ftp_transaction_factory.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/http/http_transaction_factory.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_context.h"
23 #include "net/url_request/url_request_error_job.h"
25 namespace net {
27 URLRequestFtpJob::URLRequestFtpJob(
28 URLRequest* request,
29 NetworkDelegate* network_delegate,
30 FtpTransactionFactory* ftp_transaction_factory,
31 FtpAuthCache* ftp_auth_cache)
32 : URLRequestJob(request, network_delegate),
33 priority_(DEFAULT_PRIORITY),
34 proxy_service_(request_->context()->proxy_service()),
35 pac_request_(NULL),
36 http_response_info_(NULL),
37 read_in_progress_(false),
38 ftp_transaction_factory_(ftp_transaction_factory),
39 ftp_auth_cache_(ftp_auth_cache),
40 weak_factory_(this) {
41 DCHECK(proxy_service_);
42 DCHECK(ftp_transaction_factory);
43 DCHECK(ftp_auth_cache);
46 URLRequestFtpJob::~URLRequestFtpJob() {
47 if (pac_request_)
48 proxy_service_->CancelPacRequest(pac_request_);
51 bool URLRequestFtpJob::IsSafeRedirect(const GURL& location) {
52 // Disallow all redirects.
53 return false;
56 bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
57 if (proxy_info_.is_direct()) {
58 if (ftp_transaction_->GetResponseInfo()->is_directory_listing) {
59 *mime_type = "text/vnd.chromium.ftp-dir";
60 return true;
62 } else {
63 // No special handling of MIME type is needed. As opposed to direct FTP
64 // transaction, we do not get a raw directory listing to parse.
65 return http_transaction_->GetResponseInfo()->
66 headers->GetMimeType(mime_type);
68 return false;
71 void URLRequestFtpJob::GetResponseInfo(HttpResponseInfo* info) {
72 if (http_response_info_)
73 *info = *http_response_info_;
76 HostPortPair URLRequestFtpJob::GetSocketAddress() const {
77 if (proxy_info_.is_direct()) {
78 if (!ftp_transaction_)
79 return HostPortPair();
80 return ftp_transaction_->GetResponseInfo()->socket_address;
81 } else {
82 if (!http_transaction_)
83 return HostPortPair();
84 return http_transaction_->GetResponseInfo()->socket_address;
88 void URLRequestFtpJob::SetPriority(RequestPriority priority) {
89 priority_ = priority;
90 if (http_transaction_)
91 http_transaction_->SetPriority(priority);
94 void URLRequestFtpJob::Start() {
95 DCHECK(!pac_request_);
96 DCHECK(!ftp_transaction_);
97 DCHECK(!http_transaction_);
99 int rv = OK;
100 if (request_->load_flags() & LOAD_BYPASS_PROXY) {
101 proxy_info_.UseDirect();
102 } else {
103 DCHECK_EQ(request_->context()->proxy_service(), proxy_service_);
104 rv = proxy_service_->ResolveProxy(
105 request_->url(),
106 request_->load_flags(),
107 &proxy_info_,
108 base::Bind(&URLRequestFtpJob::OnResolveProxyComplete,
109 base::Unretained(this)),
110 &pac_request_,
111 NULL,
112 request_->net_log());
114 if (rv == ERR_IO_PENDING)
115 return;
117 OnResolveProxyComplete(rv);
120 void URLRequestFtpJob::Kill() {
121 if (ftp_transaction_)
122 ftp_transaction_.reset();
123 if (http_transaction_)
124 http_transaction_.reset();
125 URLRequestJob::Kill();
126 weak_factory_.InvalidateWeakPtrs();
129 void URLRequestFtpJob::OnResolveProxyComplete(int result) {
130 pac_request_ = NULL;
132 if (result != OK) {
133 OnStartCompletedAsync(result);
134 return;
137 // Remove unsupported proxies from the list.
138 proxy_info_.RemoveProxiesWithoutScheme(
139 ProxyServer::SCHEME_DIRECT |
140 ProxyServer::SCHEME_HTTP |
141 ProxyServer::SCHEME_HTTPS);
143 // TODO(phajdan.jr): Implement proxy fallback, http://crbug.com/171495 .
144 if (proxy_info_.is_direct())
145 StartFtpTransaction();
146 else if (proxy_info_.is_http() || proxy_info_.is_https())
147 StartHttpTransaction();
148 else
149 OnStartCompletedAsync(ERR_NO_SUPPORTED_PROXIES);
152 void URLRequestFtpJob::StartFtpTransaction() {
153 // Create a transaction.
154 DCHECK(!ftp_transaction_);
156 ftp_request_info_.url = request_->url();
157 ftp_transaction_.reset(ftp_transaction_factory_->CreateTransaction());
159 // No matter what, we want to report our status as IO pending since we will
160 // be notifying our consumer asynchronously via OnStartCompleted.
161 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
162 int rv;
163 if (ftp_transaction_) {
164 rv = ftp_transaction_->Start(
165 &ftp_request_info_,
166 base::Bind(&URLRequestFtpJob::OnStartCompleted,
167 base::Unretained(this)),
168 request_->net_log());
169 if (rv == ERR_IO_PENDING)
170 return;
171 } else {
172 rv = ERR_FAILED;
174 // The transaction started synchronously, but we need to notify the
175 // URLRequest delegate via the message loop.
176 OnStartCompletedAsync(rv);
179 void URLRequestFtpJob::StartHttpTransaction() {
180 // Create a transaction.
181 DCHECK(!http_transaction_);
183 // Do not cache FTP responses sent through HTTP proxy.
184 request_->SetLoadFlags(request_->load_flags() |
185 LOAD_DISABLE_CACHE |
186 LOAD_DO_NOT_SAVE_COOKIES |
187 LOAD_DO_NOT_SEND_COOKIES);
189 http_request_info_.url = request_->url();
190 http_request_info_.method = request_->method();
191 http_request_info_.load_flags = request_->load_flags();
193 int rv = request_->context()->http_transaction_factory()->CreateTransaction(
194 priority_, &http_transaction_);
195 if (rv == OK) {
196 rv = http_transaction_->Start(
197 &http_request_info_,
198 base::Bind(&URLRequestFtpJob::OnStartCompleted,
199 base::Unretained(this)),
200 request_->net_log());
201 if (rv == ERR_IO_PENDING)
202 return;
204 // The transaction started synchronously, but we need to notify the
205 // URLRequest delegate via the message loop.
206 OnStartCompletedAsync(rv);
209 void URLRequestFtpJob::OnStartCompleted(int result) {
210 // Clear the IO_PENDING status
211 SetStatus(URLRequestStatus());
213 // Note that ftp_transaction_ may be NULL due to a creation failure.
214 if (ftp_transaction_) {
215 // FTP obviously doesn't have HTTP Content-Length header. We have to pass
216 // the content size information manually.
217 set_expected_content_size(
218 ftp_transaction_->GetResponseInfo()->expected_content_size);
221 if (result == OK) {
222 if (http_transaction_) {
223 http_response_info_ = http_transaction_->GetResponseInfo();
224 SetProxyServer(http_response_info_->proxy_server);
226 if (http_response_info_->headers->response_code() == 401 ||
227 http_response_info_->headers->response_code() == 407) {
228 HandleAuthNeededResponse();
229 return;
232 NotifyHeadersComplete();
233 } else if (ftp_transaction_ &&
234 ftp_transaction_->GetResponseInfo()->needs_auth) {
235 HandleAuthNeededResponse();
236 return;
237 } else {
238 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
242 void URLRequestFtpJob::OnStartCompletedAsync(int result) {
243 base::MessageLoop::current()->PostTask(
244 FROM_HERE,
245 base::Bind(&URLRequestFtpJob::OnStartCompleted,
246 weak_factory_.GetWeakPtr(), result));
249 void URLRequestFtpJob::OnReadCompleted(int result) {
250 read_in_progress_ = false;
251 if (result == 0) {
252 NotifyDone(URLRequestStatus());
253 } else if (result < 0) {
254 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
255 } else {
256 // Clear the IO_PENDING status
257 SetStatus(URLRequestStatus());
259 NotifyReadComplete(result);
262 void URLRequestFtpJob::RestartTransactionWithAuth() {
263 DCHECK(auth_data_.get() && auth_data_->state == AUTH_STATE_HAVE_AUTH);
265 // No matter what, we want to report our status as IO pending since we will
266 // be notifying our consumer asynchronously via OnStartCompleted.
267 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
269 int rv;
270 if (proxy_info_.is_direct()) {
271 rv = ftp_transaction_->RestartWithAuth(
272 auth_data_->credentials,
273 base::Bind(&URLRequestFtpJob::OnStartCompleted,
274 base::Unretained(this)));
275 } else {
276 rv = http_transaction_->RestartWithAuth(
277 auth_data_->credentials,
278 base::Bind(&URLRequestFtpJob::OnStartCompleted,
279 base::Unretained(this)));
281 if (rv == ERR_IO_PENDING)
282 return;
284 OnStartCompletedAsync(rv);
287 LoadState URLRequestFtpJob::GetLoadState() const {
288 if (proxy_info_.is_direct()) {
289 return ftp_transaction_ ?
290 ftp_transaction_->GetLoadState() : LOAD_STATE_IDLE;
291 } else {
292 return http_transaction_ ?
293 http_transaction_->GetLoadState() : LOAD_STATE_IDLE;
297 bool URLRequestFtpJob::NeedsAuth() {
298 return auth_data_.get() && auth_data_->state == AUTH_STATE_NEED_AUTH;
301 void URLRequestFtpJob::GetAuthChallengeInfo(
302 scoped_refptr<AuthChallengeInfo>* result) {
303 DCHECK(NeedsAuth());
305 if (http_response_info_) {
306 *result = http_response_info_->auth_challenge;
307 return;
310 scoped_refptr<AuthChallengeInfo> auth_info(new AuthChallengeInfo);
311 auth_info->is_proxy = false;
312 auth_info->challenger = HostPortPair::FromURL(request_->url());
313 // scheme and realm are kept empty.
314 DCHECK(auth_info->scheme.empty());
315 DCHECK(auth_info->realm.empty());
316 result->swap(auth_info);
319 void URLRequestFtpJob::SetAuth(const AuthCredentials& credentials) {
320 DCHECK(ftp_transaction_ || http_transaction_);
321 DCHECK(NeedsAuth());
323 auth_data_->state = AUTH_STATE_HAVE_AUTH;
324 auth_data_->credentials = credentials;
326 if (ftp_transaction_) {
327 ftp_auth_cache_->Add(request_->url().GetOrigin(),
328 auth_data_->credentials);
331 RestartTransactionWithAuth();
334 void URLRequestFtpJob::CancelAuth() {
335 DCHECK(ftp_transaction_ || http_transaction_);
336 DCHECK(NeedsAuth());
338 auth_data_->state = AUTH_STATE_CANCELED;
340 // Once the auth is cancelled, we proceed with the request as though
341 // there were no auth. Schedule this for later so that we don't cause
342 // any recursing into the caller as a result of this call.
343 OnStartCompletedAsync(OK);
346 UploadProgress URLRequestFtpJob::GetUploadProgress() const {
347 return UploadProgress();
350 bool URLRequestFtpJob::ReadRawData(IOBuffer* buf,
351 int buf_size,
352 int *bytes_read) {
353 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
354 tracked_objects::ScopedTracker tracking_profile(
355 FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 URLRequestFtpJob::ReadRawData"));
357 DCHECK_NE(buf_size, 0);
358 DCHECK(bytes_read);
359 DCHECK(!read_in_progress_);
361 int rv;
362 if (proxy_info_.is_direct()) {
363 rv = ftp_transaction_->Read(buf, buf_size,
364 base::Bind(&URLRequestFtpJob::OnReadCompleted,
365 base::Unretained(this)));
366 } else {
367 rv = http_transaction_->Read(buf, buf_size,
368 base::Bind(&URLRequestFtpJob::OnReadCompleted,
369 base::Unretained(this)));
372 if (rv >= 0) {
373 *bytes_read = rv;
374 return true;
377 if (rv == ERR_IO_PENDING) {
378 read_in_progress_ = true;
379 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
380 } else {
381 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
383 return false;
386 void URLRequestFtpJob::HandleAuthNeededResponse() {
387 GURL origin = request_->url().GetOrigin();
389 if (auth_data_.get()) {
390 if (auth_data_->state == AUTH_STATE_CANCELED) {
391 NotifyHeadersComplete();
392 return;
395 if (ftp_transaction_ && auth_data_->state == AUTH_STATE_HAVE_AUTH)
396 ftp_auth_cache_->Remove(origin, auth_data_->credentials);
397 } else {
398 auth_data_ = new AuthData;
400 auth_data_->state = AUTH_STATE_NEED_AUTH;
402 FtpAuthCache::Entry* cached_auth = NULL;
403 if (ftp_transaction_ && ftp_transaction_->GetResponseInfo()->needs_auth)
404 cached_auth = ftp_auth_cache_->Lookup(origin);
405 if (cached_auth) {
406 // Retry using cached auth data.
407 SetAuth(cached_auth->credentials);
408 } else {
409 // Prompt for a username/password.
410 NotifyHeadersComplete();
414 } // namespace net