summaryrefslogtreecommitdiff
path: root/chromium/net/url_request/url_request_ftp_job.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/url_request/url_request_ftp_job.cc')
-rw-r--r--chromium/net/url_request/url_request_ftp_job.cc407
1 files changed, 407 insertions, 0 deletions
diff --git a/chromium/net/url_request/url_request_ftp_job.cc b/chromium/net/url_request/url_request_ftp_job.cc
new file mode 100644
index 00000000000..eda245ca761
--- /dev/null
+++ b/chromium/net/url_request/url_request_ftp_job.cc
@@ -0,0 +1,407 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/url_request/url_request_ftp_job.h"
+
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/auth.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/ftp/ftp_auth_cache.h"
+#include "net/ftp/ftp_response_info.h"
+#include "net/ftp/ftp_transaction_factory.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_transaction_factory.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_error_job.h"
+
+namespace net {
+
+URLRequestFtpJob::URLRequestFtpJob(
+ URLRequest* request,
+ NetworkDelegate* network_delegate,
+ FtpTransactionFactory* ftp_transaction_factory,
+ FtpAuthCache* ftp_auth_cache)
+ : URLRequestJob(request, network_delegate),
+ priority_(DEFAULT_PRIORITY),
+ proxy_service_(request_->context()->proxy_service()),
+ pac_request_(NULL),
+ http_response_info_(NULL),
+ read_in_progress_(false),
+ weak_factory_(this),
+ ftp_transaction_factory_(ftp_transaction_factory),
+ ftp_auth_cache_(ftp_auth_cache) {
+ DCHECK(proxy_service_);
+ DCHECK(ftp_transaction_factory);
+ DCHECK(ftp_auth_cache);
+}
+
+URLRequestFtpJob::~URLRequestFtpJob() {
+ if (pac_request_)
+ proxy_service_->CancelPacRequest(pac_request_);
+}
+
+bool URLRequestFtpJob::IsSafeRedirect(const GURL& location) {
+ // Disallow all redirects.
+ return false;
+}
+
+bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
+ if (proxy_info_.is_direct()) {
+ if (ftp_transaction_->GetResponseInfo()->is_directory_listing) {
+ *mime_type = "text/vnd.chromium.ftp-dir";
+ return true;
+ }
+ } else {
+ // No special handling of MIME type is needed. As opposed to direct FTP
+ // transaction, we do not get a raw directory listing to parse.
+ return http_transaction_->GetResponseInfo()->
+ headers->GetMimeType(mime_type);
+ }
+ return false;
+}
+
+void URLRequestFtpJob::GetResponseInfo(HttpResponseInfo* info) {
+ if (http_response_info_)
+ *info = *http_response_info_;
+}
+
+HostPortPair URLRequestFtpJob::GetSocketAddress() const {
+ if (proxy_info_.is_direct()) {
+ if (!ftp_transaction_)
+ return HostPortPair();
+ return ftp_transaction_->GetResponseInfo()->socket_address;
+ } else {
+ if (!http_transaction_)
+ return HostPortPair();
+ return http_transaction_->GetResponseInfo()->socket_address;
+ }
+}
+
+void URLRequestFtpJob::SetPriority(RequestPriority priority) {
+ priority_ = priority;
+ if (http_transaction_)
+ http_transaction_->SetPriority(priority);
+}
+
+void URLRequestFtpJob::Start() {
+ DCHECK(!pac_request_);
+ DCHECK(!ftp_transaction_);
+ DCHECK(!http_transaction_);
+
+ int rv = OK;
+ if (request_->load_flags() & LOAD_BYPASS_PROXY) {
+ proxy_info_.UseDirect();
+ } else {
+ DCHECK_EQ(request_->context()->proxy_service(), proxy_service_);
+ rv = proxy_service_->ResolveProxy(
+ request_->url(),
+ &proxy_info_,
+ base::Bind(&URLRequestFtpJob::OnResolveProxyComplete,
+ base::Unretained(this)),
+ &pac_request_,
+ request_->net_log());
+
+ if (rv == ERR_IO_PENDING)
+ return;
+ }
+ OnResolveProxyComplete(rv);
+}
+
+void URLRequestFtpJob::Kill() {
+ if (ftp_transaction_)
+ ftp_transaction_.reset();
+ if (http_transaction_)
+ http_transaction_.reset();
+ URLRequestJob::Kill();
+ weak_factory_.InvalidateWeakPtrs();
+}
+
+void URLRequestFtpJob::OnResolveProxyComplete(int result) {
+ pac_request_ = NULL;
+
+ if (result != OK) {
+ OnStartCompletedAsync(result);
+ return;
+ }
+
+ // Remove unsupported proxies from the list.
+ proxy_info_.RemoveProxiesWithoutScheme(
+ ProxyServer::SCHEME_DIRECT |
+ ProxyServer::SCHEME_HTTP |
+ ProxyServer::SCHEME_HTTPS);
+
+ // TODO(phajdan.jr): Implement proxy fallback, http://crbug.com/171495 .
+ if (proxy_info_.is_direct())
+ StartFtpTransaction();
+ else if (proxy_info_.is_http() || proxy_info_.is_https())
+ StartHttpTransaction();
+ else
+ OnStartCompletedAsync(ERR_NO_SUPPORTED_PROXIES);
+}
+
+void URLRequestFtpJob::StartFtpTransaction() {
+ // Create a transaction.
+ DCHECK(!ftp_transaction_);
+
+ ftp_request_info_.url = request_->url();
+ ftp_transaction_.reset(ftp_transaction_factory_->CreateTransaction());
+
+ // No matter what, we want to report our status as IO pending since we will
+ // be notifying our consumer asynchronously via OnStartCompleted.
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ int rv;
+ if (ftp_transaction_) {
+ rv = ftp_transaction_->Start(
+ &ftp_request_info_,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ base::Unretained(this)),
+ request_->net_log());
+ if (rv == ERR_IO_PENDING)
+ return;
+ } else {
+ rv = ERR_FAILED;
+ }
+ // The transaction started synchronously, but we need to notify the
+ // URLRequest delegate via the message loop.
+ OnStartCompletedAsync(rv);
+}
+
+void URLRequestFtpJob::StartHttpTransaction() {
+ // Create a transaction.
+ DCHECK(!http_transaction_);
+
+ // Do not cache FTP responses sent through HTTP proxy.
+ request_->set_load_flags(request_->load_flags() |
+ LOAD_DISABLE_CACHE |
+ LOAD_DO_NOT_SAVE_COOKIES |
+ LOAD_DO_NOT_SEND_COOKIES);
+
+ http_request_info_.url = request_->url();
+ http_request_info_.method = request_->method();
+ http_request_info_.load_flags = request_->load_flags();
+ http_request_info_.request_id = request_->identifier();
+
+ int rv = request_->context()->http_transaction_factory()->CreateTransaction(
+ priority_, &http_transaction_, NULL);
+ if (rv == OK) {
+ rv = http_transaction_->Start(
+ &http_request_info_,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ base::Unretained(this)),
+ request_->net_log());
+ if (rv == ERR_IO_PENDING)
+ return;
+ }
+ // The transaction started synchronously, but we need to notify the
+ // URLRequest delegate via the message loop.
+ OnStartCompletedAsync(rv);
+}
+
+void URLRequestFtpJob::OnStartCompleted(int result) {
+ // Clear the IO_PENDING status
+ SetStatus(URLRequestStatus());
+
+ // Note that ftp_transaction_ may be NULL due to a creation failure.
+ if (ftp_transaction_) {
+ // FTP obviously doesn't have HTTP Content-Length header. We have to pass
+ // the content size information manually.
+ set_expected_content_size(
+ ftp_transaction_->GetResponseInfo()->expected_content_size);
+ }
+
+ if (result == OK) {
+ if (http_transaction_) {
+ http_response_info_ = http_transaction_->GetResponseInfo();
+
+ if (http_response_info_->headers->response_code() == 401 ||
+ http_response_info_->headers->response_code() == 407) {
+ HandleAuthNeededResponse();
+ return;
+ }
+ }
+ NotifyHeadersComplete();
+ } else if (ftp_transaction_ &&
+ ftp_transaction_->GetResponseInfo()->needs_auth) {
+ HandleAuthNeededResponse();
+ return;
+ } else {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
+ }
+}
+
+void URLRequestFtpJob::OnStartCompletedAsync(int result) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ weak_factory_.GetWeakPtr(), result));
+}
+
+void URLRequestFtpJob::OnReadCompleted(int result) {
+ read_in_progress_ = false;
+ if (result == 0) {
+ NotifyDone(URLRequestStatus());
+ } else if (result < 0) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
+ } else {
+ // Clear the IO_PENDING status
+ SetStatus(URLRequestStatus());
+ }
+ NotifyReadComplete(result);
+}
+
+void URLRequestFtpJob::RestartTransactionWithAuth() {
+ DCHECK(auth_data_.get() && auth_data_->state == AUTH_STATE_HAVE_AUTH);
+
+ // No matter what, we want to report our status as IO pending since we will
+ // be notifying our consumer asynchronously via OnStartCompleted.
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+
+ int rv;
+ if (proxy_info_.is_direct()) {
+ rv = ftp_transaction_->RestartWithAuth(
+ auth_data_->credentials,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ base::Unretained(this)));
+ } else {
+ rv = http_transaction_->RestartWithAuth(
+ auth_data_->credentials,
+ base::Bind(&URLRequestFtpJob::OnStartCompleted,
+ base::Unretained(this)));
+ }
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ OnStartCompletedAsync(rv);
+}
+
+LoadState URLRequestFtpJob::GetLoadState() const {
+ if (proxy_info_.is_direct()) {
+ return ftp_transaction_ ?
+ ftp_transaction_->GetLoadState() : LOAD_STATE_IDLE;
+ } else {
+ return http_transaction_ ?
+ http_transaction_->GetLoadState() : LOAD_STATE_IDLE;
+ }
+}
+
+bool URLRequestFtpJob::NeedsAuth() {
+ return auth_data_.get() && auth_data_->state == AUTH_STATE_NEED_AUTH;
+}
+
+void URLRequestFtpJob::GetAuthChallengeInfo(
+ scoped_refptr<AuthChallengeInfo>* result) {
+ DCHECK(NeedsAuth());
+
+ if (http_response_info_) {
+ *result = http_response_info_->auth_challenge;
+ return;
+ }
+
+ scoped_refptr<AuthChallengeInfo> auth_info(new AuthChallengeInfo);
+ auth_info->is_proxy = false;
+ auth_info->challenger = HostPortPair::FromURL(request_->url());
+ // scheme and realm are kept empty.
+ DCHECK(auth_info->scheme.empty());
+ DCHECK(auth_info->realm.empty());
+ result->swap(auth_info);
+}
+
+void URLRequestFtpJob::SetAuth(const AuthCredentials& credentials) {
+ DCHECK(ftp_transaction_ || http_transaction_);
+ DCHECK(NeedsAuth());
+
+ auth_data_->state = AUTH_STATE_HAVE_AUTH;
+ auth_data_->credentials = credentials;
+
+ if (ftp_transaction_) {
+ ftp_auth_cache_->Add(request_->url().GetOrigin(),
+ auth_data_->credentials);
+ }
+
+ RestartTransactionWithAuth();
+}
+
+void URLRequestFtpJob::CancelAuth() {
+ DCHECK(ftp_transaction_ || http_transaction_);
+ DCHECK(NeedsAuth());
+
+ auth_data_->state = AUTH_STATE_CANCELED;
+
+ // Once the auth is cancelled, we proceed with the request as though
+ // there were no auth. Schedule this for later so that we don't cause
+ // any recursing into the caller as a result of this call.
+ OnStartCompletedAsync(OK);
+}
+
+UploadProgress URLRequestFtpJob::GetUploadProgress() const {
+ return UploadProgress();
+}
+
+bool URLRequestFtpJob::ReadRawData(IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) {
+ DCHECK_NE(buf_size, 0);
+ DCHECK(bytes_read);
+ DCHECK(!read_in_progress_);
+
+ int rv;
+ if (proxy_info_.is_direct()) {
+ rv = ftp_transaction_->Read(buf, buf_size,
+ base::Bind(&URLRequestFtpJob::OnReadCompleted,
+ base::Unretained(this)));
+ } else {
+ rv = http_transaction_->Read(buf, buf_size,
+ base::Bind(&URLRequestFtpJob::OnReadCompleted,
+ base::Unretained(this)));
+ }
+
+ if (rv >= 0) {
+ *bytes_read = rv;
+ return true;
+ }
+
+ if (rv == ERR_IO_PENDING) {
+ read_in_progress_ = true;
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ } else {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+ }
+ return false;
+}
+
+void URLRequestFtpJob::HandleAuthNeededResponse() {
+ GURL origin = request_->url().GetOrigin();
+
+ if (auth_data_.get()) {
+ if (auth_data_->state == AUTH_STATE_CANCELED) {
+ NotifyHeadersComplete();
+ return;
+ }
+
+ if (ftp_transaction_ && auth_data_->state == AUTH_STATE_HAVE_AUTH)
+ ftp_auth_cache_->Remove(origin, auth_data_->credentials);
+ } else {
+ auth_data_ = new AuthData;
+ }
+ auth_data_->state = AUTH_STATE_NEED_AUTH;
+
+ FtpAuthCache::Entry* cached_auth = NULL;
+ if (ftp_transaction_ && ftp_transaction_->GetResponseInfo()->needs_auth)
+ cached_auth = ftp_auth_cache_->Lookup(origin);
+ if (cached_auth) {
+ // Retry using cached auth data.
+ SetAuth(cached_auth->credentials);
+ } else {
+ // Prompt for a username/password.
+ NotifyHeadersComplete();
+ }
+}
+
+} // namespace net