summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ftp/ftp_network_transaction.cc30
-rw-r--r--net/ftp/ftp_network_transaction_unittest.cc41
-rw-r--r--net/socket/socket_test_util.cc24
-rw-r--r--net/socket/socket_test_util.h10
4 files changed, 85 insertions, 20 deletions
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 5b3ff67..980e0ab 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -158,7 +158,11 @@ int FtpNetworkTransaction::ParseCtrlResponse(int* cut_pos) {
return ERR_INVALID_RESPONSE;
ctrl_responses_.push(ResponseLine(status_code, status_text));
*cut_pos = i + 1;
+
+ // Prepare to handle the next line.
scan_state = CODE;
+ status_code = 0;
+ status_text = "";
break;
default:
NOTREACHED();
@@ -172,7 +176,6 @@ int FtpNetworkTransaction::ParseCtrlResponse(int* cut_pos) {
// Used to prepare and send FTP commad.
int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
Command cmd) {
- response_message_buf_len_ = 0;
command_sent_ = cmd;
DLOG(INFO) << " >> " << command;
const char* buf = command.c_str();
@@ -199,8 +202,29 @@ int FtpNetworkTransaction::ProcessCtrlResponses() {
return rv;
}
- // TODO(phajdan.jr): Correctly handle multiple code 230 response lines after
- // PASS command.
+ // Eat multiple 230 lines after PASS.
+ if (command_sent_ == COMMAND_PASS) {
+ while (ctrl_responses_.size() > 1) {
+ if (ctrl_responses_.front().code != 230)
+ break;
+ ctrl_responses_.pop();
+ }
+ }
+
+ // Make sure there are no 230's when we want to process SYST response.
+ if (command_sent_ == COMMAND_SYST) {
+ while (!ctrl_responses_.empty()) {
+ if (ctrl_responses_.front().code != 230)
+ break;
+ ctrl_responses_.pop();
+ }
+ if (ctrl_responses_.empty()) {
+ // Read more from control socket.
+ next_state_ = STATE_CTRL_READ;
+ return rv;
+ }
+ }
+
if (ctrl_responses_.size() != 1)
return Stop(ERR_INVALID_RESPONSE);
diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc
index 51cf77b..c81fc0f 100644
--- a/net/ftp/ftp_network_transaction_unittest.cc
+++ b/net/ftp/ftp_network_transaction_unittest.cc
@@ -44,7 +44,9 @@ class FtpMockControlSocket : public DynamicMockSocket {
QUIT
};
- FtpMockControlSocket() : failure_injection_state_(NONE) {
+ FtpMockControlSocket()
+ : failure_injection_state_(NONE),
+ multiline_welcome_(false) {
Init();
}
@@ -56,8 +58,12 @@ class FtpMockControlSocket : public DynamicMockSocket {
return Verify("USER anonymous\r\n", data, PRE_PASSWD,
"331 Password needed\r\n");
case PRE_PASSWD:
- return Verify("PASS chrome@example.com\r\n", data, PRE_SYST,
- "230 Welcome\r\n");
+ {
+ const char* response_one = "230 Welcome\r\n";
+ const char* response_multi = "230 One\r\n230 Two\r\n230 Three\r\n";
+ return Verify("PASS chrome@example.com\r\n", data, PRE_SYST,
+ multiline_welcome_ ? response_multi : response_one);
+ }
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 UNIX\r\n");
case PRE_PWD:
@@ -95,6 +101,10 @@ class FtpMockControlSocket : public DynamicMockSocket {
Init();
}
+ void set_multiline_welcome(bool multiline) {
+ multiline_welcome_ = multiline;
+ }
+
protected:
void Init() {
state_ = PRE_USER;
@@ -130,6 +140,9 @@ class FtpMockControlSocket : public DynamicMockSocket {
State failure_injection_next_state_;
const char* fault_response_;
+ // If true, we will send multiple 230 lines as response after PASS.
+ bool multiline_welcome_;
+
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocket);
};
@@ -293,6 +306,12 @@ TEST_F(FtpNetworkTransactionTest, DirectoryTransaction) {
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) {
+ FtpMockControlSocketDirectoryListing ctrl_socket;
+ ctrl_socket.set_multiline_welcome(true);
+ ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
+}
+
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads2) {
FtpMockControlSocketDirectoryListing ctrl_socket;
ctrl_socket.set_short_read_limit(2);
@@ -305,11 +324,27 @@ TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads5) {
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcomeShort) {
+ FtpMockControlSocketDirectoryListing ctrl_socket;
+ // The client will not consume all three 230 lines. That's good, we want to
+ // test that scenario.
+ ctrl_socket.allow_unconsumed_reads(true);
+ ctrl_socket.set_multiline_welcome(true);
+ ctrl_socket.set_short_read_limit(5);
+ ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
+}
+
TEST_F(FtpNetworkTransactionTest, DownloadTransaction) {
FtpMockControlSocketFileDownload ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) {
+ FtpMockControlSocketFileDownload ctrl_socket;
+ ctrl_socket.set_multiline_welcome(true);
+ ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+}
+
TEST_F(FtpNetworkTransactionTest, DownloadTransactionShortReads2) {
FtpMockControlSocketFileDownload ctrl_socket;
ctrl_socket.set_short_read_limit(2);
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index fc5ddf3..25de9d3 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -248,33 +248,33 @@ void StaticMockSocket::Reset() {
}
DynamicMockSocket::DynamicMockSocket()
- : read_(false, ERR_UNEXPECTED),
- has_read_(false),
- short_read_limit_(0) {
+ : short_read_limit_(0),
+ allow_unconsumed_reads_(false) {
}
MockRead DynamicMockSocket::GetNextRead() {
- if (!has_read_)
+ if (reads_.empty())
return MockRead(true, ERR_UNEXPECTED);
- MockRead result = read_;
+ MockRead result = reads_.front();
if (short_read_limit_ == 0 || result.data_len <= short_read_limit_) {
- has_read_ = false;
+ reads_.pop_front();
} else {
result.data_len = short_read_limit_;
- read_.data += result.data_len;
- read_.data_len -= result.data_len;
+ reads_.front().data += result.data_len;
+ reads_.front().data_len -= result.data_len;
}
return result;
}
void DynamicMockSocket::Reset() {
- has_read_ = false;
+ reads_.clear();
}
void DynamicMockSocket::SimulateRead(const char* data) {
- EXPECT_FALSE(has_read_) << "Unconsumed read: " << read_.data;
- read_ = MockRead(data);
- has_read_ = true;
+ if (!allow_unconsumed_reads_) {
+ EXPECT_TRUE(reads_.empty()) << "Unconsumed read: " << reads_.front().data;
+ }
+ reads_.push_back(MockRead(data));
}
void MockClientSocketFactory::AddMockSocket(MockSocket* socket) {
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index fc288ad..93f9625 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -5,6 +5,7 @@
#ifndef NET_SOCKET_SOCKET_TEST_UTIL_H_
#define NET_SOCKET_SOCKET_TEST_UTIL_H_
+#include <deque>
#include <string>
#include <vector>
@@ -124,18 +125,23 @@ class DynamicMockSocket : public MockSocket {
int short_read_limit() const { return short_read_limit_; }
void set_short_read_limit(int limit) { short_read_limit_ = limit; }
+ void allow_unconsumed_reads(bool allow) { allow_unconsumed_reads_ = allow; }
+
protected:
// The next time there is a read from this socket, it will return |data|.
// Before calling SimulateRead next time, the previous data must be consumed.
void SimulateRead(const char* data);
private:
- MockRead read_;
- bool has_read_;
+ std::deque<MockRead> reads_;
// Max number of bytes we will read at a time. 0 means no limit.
int short_read_limit_;
+ // If true, we'll not require the client to consume all data before we
+ // mock the next read.
+ bool allow_unconsumed_reads_;
+
DISALLOW_COPY_AND_ASSIGN(DynamicMockSocket);
};