diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-26 15:57:58 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-26 15:57:58 +0000 |
commit | 8b8a197df55d54f6bde86858b7a820e12473f304 (patch) | |
tree | aba47fac644c3ca721e4a489bfba616556c09a1a /net | |
parent | bee16aabebd2b0415acb9680d2e1b90ce9c1fd60 (diff) | |
download | chromium_src-8b8a197df55d54f6bde86858b7a820e12473f304.zip chromium_src-8b8a197df55d54f6bde86858b7a820e12473f304.tar.gz chromium_src-8b8a197df55d54f6bde86858b7a820e12473f304.tar.bz2 |
Implement RestartWithAuth for NewFtpTransaction.
TEST=Covered by net_unittests.
http://crbug.com/20112
Review URL: http://codereview.chromium.org/173270
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24449 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/ftp/ftp_network_transaction.cc | 72 | ||||
-rw-r--r-- | net/ftp/ftp_network_transaction.h | 11 | ||||
-rw-r--r-- | net/ftp/ftp_response_info.h | 9 | ||||
-rw-r--r-- | net/ftp/ftp_transaction.h | 2 | ||||
-rw-r--r-- | net/url_request/url_request_new_ftp_job.cc | 67 | ||||
-rw-r--r-- | net/url_request/url_request_new_ftp_job.h | 12 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 60 |
7 files changed, 198 insertions, 35 deletions
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc index ef896b8..6b6b8c4 100644 --- a/net/ftp/ftp_network_transaction.cc +++ b/net/ftp/ftp_network_transaction.cc @@ -36,11 +36,11 @@ FtpNetworkTransaction::FtpNetworkTransaction( request_(NULL), resolver_(session->host_resolver()), read_ctrl_buf_(new IOBuffer(kCtrlBufLen)), + ctrl_response_buffer_(new FtpCtrlResponseBuffer()), read_data_buf_len_(0), file_data_len_(0), write_command_buf_written_(0), last_error_(OK), - is_anonymous_(false), retr_failed_(false), data_connection_port_(0), socket_factory_(socket_factory), @@ -56,6 +56,15 @@ int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, load_log_ = load_log; request_ = request_info; + if (request_->url.has_username()) { + username_ = UTF8ToWide(request_->url.username()); + if (request_->url.has_password()) + password_ = UTF8ToWide(request_->url.password()); + } else { + username_ = L"anonymous"; + password_ = L"chrome@example.com"; + } + next_state_ = STATE_CTRL_INIT; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) @@ -75,7 +84,16 @@ int FtpNetworkTransaction::Stop(int error) { int FtpNetworkTransaction::RestartWithAuth(const std::wstring& username, const std::wstring& password, CompletionCallback* callback) { - return ERR_FAILED; + ResetStateForRestart(); + + username_ = username; + password_ = password; + + next_state_ = STATE_CTRL_INIT; + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + return rv; } int FtpNetworkTransaction::RestartIgnoringLastError( @@ -134,7 +152,7 @@ int FtpNetworkTransaction::SendFtpCommand(const std::string& command, // If we send a new command when we still have unprocessed responses // for previous commands, the response receiving code will have no way to know // which responses are for which command. - DCHECK(!ctrl_response_buffer_.ResponseAvailable()); + DCHECK(!ctrl_response_buffer_->ResponseAvailable()); DCHECK(!write_command_buf_); DCHECK(!write_buf_); @@ -176,7 +194,7 @@ FtpNetworkTransaction::ErrorClass FtpNetworkTransaction::GetErrorClass( } int FtpNetworkTransaction::ProcessCtrlResponse() { - FtpCtrlResponse response = ctrl_response_buffer_.PopResponse(); + FtpCtrlResponse response = ctrl_response_buffer_->PopResponse(); int rv = OK; switch (command_sent_) { @@ -230,8 +248,8 @@ int FtpNetworkTransaction::ProcessCtrlResponse() { // We may get multiple responses for some commands, // see http://crbug.com/18036. - while (ctrl_response_buffer_.ResponseAvailable() && rv == OK) { - response = ctrl_response_buffer_.PopResponse(); + while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) { + response = ctrl_response_buffer_->PopResponse(); switch (command_sent_) { case COMMAND_RETR: @@ -246,6 +264,24 @@ int FtpNetworkTransaction::ProcessCtrlResponse() { return rv; } +void FtpNetworkTransaction::ResetStateForRestart() { + command_sent_ = COMMAND_NONE; + user_callback_ = NULL; + response_ = FtpResponseInfo(); + read_ctrl_buf_ = new IOBuffer(kCtrlBufLen); + ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer()); + read_data_buf_ = NULL; + read_data_buf_len_ = 0; + file_data_len_ = 0; + write_command_buf_written_ = 0; + last_error_ = OK; + retr_failed_ = false; + data_connection_port_ = 0; + ctrl_socket_.reset(); + data_socket_.reset(); + next_state_ = STATE_NONE; +} + void FtpNetworkTransaction::DoCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(user_callback_); @@ -443,9 +479,9 @@ int FtpNetworkTransaction::DoCtrlReadComplete(int result) { if (result < 0) return Stop(result); - ctrl_response_buffer_.ConsumeData(read_ctrl_buf_->data(), result); + ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result); - if (!ctrl_response_buffer_.ResponseAvailable()) { + if (!ctrl_response_buffer_->ResponseAvailable()) { // Read more data from the control socket. next_state_ = STATE_CTRL_READ; return OK; @@ -486,14 +522,7 @@ int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { // USER Command. int FtpNetworkTransaction::DoCtrlWriteUSER() { - std::string command = "USER"; - if (request_->url.has_username()) { - command.append(" "); - command.append(request_->url.username()); - } else { - is_anonymous_ = true; - command.append(" anonymous"); - } + std::string command = "USER " + WideToUTF8(username_); next_state_ = STATE_CTRL_READ; return SendFtpCommand(command, COMMAND_USER); } @@ -522,14 +551,7 @@ int FtpNetworkTransaction::ProcessResponseUSER( // PASS command. int FtpNetworkTransaction::DoCtrlWritePASS() { - std::string command = "PASS"; - if (request_->url.has_password()) { - command.append(" "); - command.append(request_->url.password()); - } else { - command.append(" "); - command.append("chrome@example.com"); - } + std::string command = "PASS " + WideToUTF8(password_); next_state_ = STATE_CTRL_READ; return SendFtpCommand(command, COMMAND_PASS); } @@ -552,7 +574,7 @@ int FtpNetworkTransaction::ProcessResponsePASS( if (response.status_code == 503) { next_state_ = STATE_CTRL_WRITE_USER; } else { - // TODO(ibrar): Retry here. + response_.needs_auth = true; return Stop(ERR_FAILED); } break; diff --git a/net/ftp/ftp_network_transaction.h b/net/ftp/ftp_network_transaction.h index 447d271..4dbf6f4 100644 --- a/net/ftp/ftp_network_transaction.h +++ b/net/ftp/ftp_network_transaction.h @@ -85,6 +85,9 @@ class FtpNetworkTransaction : public FtpTransaction { ERROR_CLASS_PERMANENT_ERROR, }; + // Resets the members of the transaction so it can be restarted. + void ResetStateForRestart(); + void DoCallback(int result); void OnIOComplete(int result); @@ -167,7 +170,7 @@ class FtpNetworkTransaction : public FtpTransaction { // User buffer passed to the Read method for control socket. scoped_refptr<IOBuffer> read_ctrl_buf_; - FtpCtrlResponseBuffer ctrl_response_buffer_; + scoped_ptr<FtpCtrlResponseBuffer> ctrl_response_buffer_; scoped_refptr<IOBuffer> read_data_buf_; int read_data_buf_len_; @@ -186,7 +189,11 @@ class FtpNetworkTransaction : public FtpTransaction { int last_error_; - bool is_anonymous_; + // We get username and password as wstrings in RestartWithAuth, so they are + // also kept as wstrings here. + std::wstring username_; + std::wstring password_; + bool retr_failed_; std::string data_connection_ip_; diff --git a/net/ftp/ftp_response_info.h b/net/ftp/ftp_response_info.h index b3c3361..9c94064 100644 --- a/net/ftp/ftp_response_info.h +++ b/net/ftp/ftp_response_info.h @@ -5,17 +5,18 @@ #ifndef NET_FTP_FTP_RESPONSE_INFO_H_ #define NET_FTP_FTP_RESPONSE_INFO_H_ -#include "net/base/auth.h" +#include "base/time.h" namespace net { class FtpResponseInfo { public: - FtpResponseInfo() : is_directory_listing(false) { + FtpResponseInfo() : needs_auth(false), is_directory_listing(false) { } - // Non-null when authentication is required. - scoped_refptr<AuthChallengeInfo> auth_challenge; + // True if authentication failed and valid authentication credentials are + // needed. + bool needs_auth; // The time at which the request was made that resulted in this response. // For cached responses, this time could be "far" in the past. diff --git a/net/ftp/ftp_transaction.h b/net/ftp/ftp_transaction.h index 3d629746..da7f73e 100644 --- a/net/ftp/ftp_transaction.h +++ b/net/ftp/ftp_transaction.h @@ -11,8 +11,8 @@ namespace net { -class FtpRequestInfo; class FtpResponseInfo; +class FtpRequestInfo; class LoadLog; // Represents a single FTP transaction. diff --git a/net/url_request/url_request_new_ftp_job.cc b/net/url_request/url_request_new_ftp_job.cc index f467357..16865a1 100644 --- a/net/url_request/url_request_new_ftp_job.cc +++ b/net/url_request/url_request_new_ftp_job.cc @@ -8,6 +8,7 @@ #include "base/file_version_info.h" #include "base/message_loop.h" #include "base/sys_string_conversions.h" +#include "net/base/auth.h" #include "net/base/escape.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" @@ -60,7 +61,6 @@ string16 RawByteSequenceToFilename(const char* raw_filename, URLRequestNewFtpJob::URLRequestNewFtpJob(URLRequest* request) : URLRequestJob(request), - server_auth_state_(net::AUTH_STATE_DONT_NEED_AUTH), response_info_(NULL), dir_listing_buf_size_(0), ALLOW_THIS_IN_INITIALIZER_LIST( @@ -106,6 +106,48 @@ net::LoadState URLRequestNewFtpJob::GetLoadState() const { transaction_->GetLoadState() : net::LOAD_STATE_IDLE; } +bool URLRequestNewFtpJob::NeedsAuth() { + // Note that we only have to worry about cases where an actual FTP server + // requires auth (and not a proxy), because connecting to FTP via proxy + // effectively means the browser communicates via HTTP, and uses HTTP's + // Proxy-Authenticate protocol when proxy servers require auth. + return server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH; +} + +void URLRequestNewFtpJob::GetAuthChallengeInfo( + scoped_refptr<net::AuthChallengeInfo>* result) { + DCHECK((server_auth_ != NULL) && + (server_auth_->state == net::AUTH_STATE_NEED_AUTH)); + scoped_refptr<net::AuthChallengeInfo> auth_info = new net::AuthChallengeInfo; + auth_info->is_proxy = false; + auth_info->host_and_port = ASCIIToWide( + net::GetHostAndPort(request_->url())); + auth_info->scheme = L""; + auth_info->realm = L""; + result->swap(auth_info); +} + +void URLRequestNewFtpJob::SetAuth(const std::wstring& username, + const std::wstring& password) { + DCHECK(NeedsAuth()); + server_auth_->state = net::AUTH_STATE_HAVE_AUTH; + server_auth_->username = username; + server_auth_->password = password; + + RestartTransactionWithAuth(); +} + +void URLRequestNewFtpJob::CancelAuth() { + DCHECK(NeedsAuth()); + server_auth_->state = net::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. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestNewFtpJob::OnStartCompleted, net::OK)); +} + bool URLRequestNewFtpJob::ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read) { @@ -250,6 +292,10 @@ void URLRequestNewFtpJob::OnStartCompleted(int result) { SetStatus(URLRequestStatus()); if (result == net::OK) { NotifyHeadersComplete(); + } else if (transaction_->GetResponseInfo()->needs_auth) { + server_auth_ = new net::AuthData(); + server_auth_->state = net::AUTH_STATE_NEED_AUTH; + NotifyHeadersComplete(); } else { NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); } @@ -273,6 +319,25 @@ void URLRequestNewFtpJob::OnReadCompleted(int result) { NotifyReadComplete(result); } +void URLRequestNewFtpJob::RestartTransactionWithAuth() { + DCHECK(server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH); + + response_info_ = NULL; + + // 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 = transaction_->RestartWithAuth(server_auth_->username, + server_auth_->password, + &start_callback_); + if (rv == net::ERR_IO_PENDING) + return; + + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestNewFtpJob::OnStartCompleted, rv)); +} + void URLRequestNewFtpJob::StartTransaction() { // Create a transaction. DCHECK(!transaction_.get()); diff --git a/net/url_request/url_request_new_ftp_job.h b/net/url_request/url_request_new_ftp_job.h index ebdb0e4..d7a9b25 100644 --- a/net/url_request/url_request_new_ftp_job.h +++ b/net/url_request/url_request_new_ftp_job.h @@ -31,6 +31,12 @@ class URLRequestNewFtpJob : public URLRequestJob { virtual void Start(); virtual void Kill(); virtual net::LoadState GetLoadState() const; + virtual bool NeedsAuth(); + virtual void GetAuthChallengeInfo( + scoped_refptr<net::AuthChallengeInfo>* auth_info); + virtual void SetAuth(const std::wstring& username, + const std::wstring& password); + virtual void CancelAuth(); // TODO(ibrar): Yet to give another look at this function. virtual uint64 GetUploadProgress() const { return 0; } @@ -42,9 +48,9 @@ class URLRequestNewFtpJob : public URLRequestJob { void OnStartCompleted(int result); void OnReadCompleted(int result); - int ProcessFtpDir(net::IOBuffer *buf, int buf_size, int bytes_read); + void RestartTransactionWithAuth(); - net::AuthState server_auth_state_; + int ProcessFtpDir(net::IOBuffer *buf, int buf_size, int bytes_read); net::FtpRequestInfo request_info_; scoped_ptr<net::FtpTransaction> transaction_; @@ -60,6 +66,8 @@ class URLRequestNewFtpJob : public URLRequestJob { bool read_in_progress_; std::string encoding_; + scoped_refptr<net::AuthData> server_auth_; + // Keep a reference to the url request context to be sure it's not deleted // before us. scoped_refptr<URLRequestContext> context_; diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index b224c2e..49f01a2 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -1972,6 +1972,36 @@ TEST_F(URLRequestTest, FTPCheckWrongPassword) { } } +TEST_F(URLRequestTest, FTPCheckWrongPasswordRestart) { + // Pass wrong login credentials in the request URL. + scoped_refptr<FTPTestServer> server = + FTPTestServer::CreateServer(L"", "chrome", "wrong_password"); + ASSERT_TRUE(NULL != server.get()); + FilePath app_path; + PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + app_path = app_path.AppendASCII("LICENSE"); + TestDelegate d; + // Set correct login credentials. The delegate will be asked for them when + // the initial login with wrong credentials will fail. + d.set_username(L"chrome"); + d.set_password(L"chrome"); + { + TestURLRequest r(server->TestServerPage("/LICENSE"), &d); + r.Start(); + EXPECT_TRUE(r.is_pending()); + + MessageLoop::current()->Run(); + + int64 file_size = 0; + file_util::GetFileSize(app_path, &file_size); + + EXPECT_FALSE(r.is_pending()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_FALSE(d.received_data_before_response()); + EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size)); + } +} + TEST_F(URLRequestTest, FTPCheckWrongUser) { scoped_refptr<FTPTestServer> server = FTPTestServer::CreateServer(L"", "wrong_user", "chrome"); @@ -1996,3 +2026,33 @@ TEST_F(URLRequestTest, FTPCheckWrongUser) { EXPECT_EQ(d.bytes_received(), 0); } } + +TEST_F(URLRequestTest, FTPCheckWrongUserRestart) { + // Pass wrong login credentials in the request URL. + scoped_refptr<FTPTestServer> server = + FTPTestServer::CreateServer(L"", "wrong_user", "chrome"); + ASSERT_TRUE(NULL != server.get()); + FilePath app_path; + PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + app_path = app_path.AppendASCII("LICENSE"); + TestDelegate d; + // Set correct login credentials. The delegate will be asked for them when + // the initial login with wrong credentials will fail. + d.set_username(L"chrome"); + d.set_password(L"chrome"); + { + TestURLRequest r(server->TestServerPage("/LICENSE"), &d); + r.Start(); + EXPECT_TRUE(r.is_pending()); + + MessageLoop::current()->Run(); + + int64 file_size = 0; + file_util::GetFileSize(app_path, &file_size); + + EXPECT_FALSE(r.is_pending()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_FALSE(d.received_data_before_response()); + EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size)); + } +} |