blob: 51cf77b9e407ce2c0be79ab018434bb0f2401144 [file] [log] [blame]
// Copyright (c) 2009 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/ftp/ftp_network_transaction.h"
#include "base/ref_counted.h"
#include "net/base/host_resolver.h"
#include "net/base/host_resolver_unittest.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "net/ftp/ftp_network_session.h"
#include "net/ftp/ftp_request_info.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace {
// Size we use for IOBuffers used to receive data from the test data socket.
const int kBufferSize = 128;
} // namespace
namespace net {
class FtpMockControlSocket : public DynamicMockSocket {
public:
enum State {
NONE,
PRE_USER,
PRE_PASSWD,
PRE_SYST,
PRE_PWD,
PRE_TYPE,
PRE_PASV,
PRE_SIZE,
PRE_MDTM,
PRE_LIST,
PRE_RETR,
PRE_PASV2,
PRE_CWD,
PRE_QUIT,
QUIT
};
FtpMockControlSocket() : failure_injection_state_(NONE) {
Init();
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, OK);
switch (state()) {
case PRE_USER:
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");
case PRE_SYST:
return Verify("SYST\r\n", data, PRE_PWD, "215 UNIX\r\n");
case PRE_PWD:
return Verify("PWD\r\n", data, PRE_TYPE,
"257 \"/\" is your current location\r\n");
case PRE_TYPE:
return Verify("TYPE I\r\n", data, PRE_PASV,
"200 TYPE is now 8-bit binary\r\n");
case PRE_PASV:
return Verify("PASV\r\n", data, PRE_SIZE,
"227 Entering Passive Mode (127,0,0,1,123,456)\r\n");
case PRE_QUIT:
return Verify("QUIT\r\n", data, QUIT, "221 Goodbye.\r\n");
default:
return MockWriteResult(true, ERR_UNEXPECTED);
}
}
void InjectFailure(State state, State next_state, const char* response) {
DCHECK_EQ(NONE, failure_injection_state_);
DCHECK_NE(NONE, state);
DCHECK_NE(NONE, next_state);
DCHECK_NE(state, next_state);
failure_injection_state_ = state;
failure_injection_next_state_ = next_state;
fault_response_ = response;
}
State state() const {
return state_;
}
virtual void Reset() {
DynamicMockSocket::Reset();
Init();
}
protected:
void Init() {
state_ = PRE_USER;
SimulateRead("220 host TestFTPd\r\n");
}
// If protocol fault injection has been requested, adjusts state and mocked
// read and returns true.
bool InjectFault() {
if (state_ != failure_injection_state_)
return false;
SimulateRead(fault_response_);
state_ = failure_injection_next_state_;
return true;
}
MockWriteResult Verify(const std::string& expected,
const std::string& data,
State next_state,
const char* next_read) {
EXPECT_EQ(expected, data);
if (expected == data) {
state_ = next_state;
SimulateRead(next_read);
return MockWriteResult(true, OK);
}
return MockWriteResult(true, ERR_UNEXPECTED);
}
private:
State state_;
State failure_injection_state_;
State failure_injection_next_state_;
const char* fault_response_;
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocket);
};
class FtpMockControlSocketDirectoryListing : public FtpMockControlSocket {
public:
FtpMockControlSocketDirectoryListing() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, OK);
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /\r\n", data, PRE_MDTM,
"550 I can only retrieve regular files\r\n");
case PRE_MDTM:
return Verify("MDTM /\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
return Verify("RETR /\r\n", data, PRE_PASV2,
"550 Can't download directory\r\n");
case PRE_PASV2:
// Parser should also accept format without parentheses.
return Verify("PASV\r\n", data, PRE_CWD,
"227 Entering Passive Mode 127,0,0,1,123,456\r\n");
case PRE_CWD:
return Verify("CWD /\r\n", data, PRE_LIST, "200 OK\r\n");
case PRE_LIST:
// TODO(phajdan.jr): Also test with "150 Accepted Data Connection".
return Verify("LIST\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpMockControlSocket::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketDirectoryListing);
};
class FtpMockControlSocketFileDownload : public FtpMockControlSocket {
public:
FtpMockControlSocketFileDownload() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, OK);
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /file\r\n", data, PRE_MDTM,
"213 18\r\n");
case PRE_MDTM:
return Verify("MDTM /file\r\n", data, PRE_RETR,
"213 20070221112533\r\n");
case PRE_RETR:
// TODO(phajdan.jr): Also test with "150 Accepted Data Connection".
return Verify("RETR /file\r\n", data, PRE_QUIT, "200 OK\r\n");
default:
return FtpMockControlSocket::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketFileDownload);
};
class FtpMockControlSocketFileDownloadRetrFail
: public FtpMockControlSocketFileDownload {
public:
FtpMockControlSocketFileDownloadRetrFail() {
}
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
return MockWriteResult(true, OK);
switch (state()) {
case PRE_PASV2:
return Verify("PASV\r\n", data, PRE_CWD,
"227 Entering Passive Mode (127,0,0,1,123,456)\r\n");
case PRE_CWD:
return Verify("CWD /file\r\n", data, PRE_QUIT,
"500 file is a directory\r\n");
default:
return FtpMockControlSocketFileDownload::OnWrite(data);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketFileDownloadRetrFail);
};
class FtpNetworkTransactionTest : public PlatformTest {
public:
FtpNetworkTransactionTest()
: session_(new FtpNetworkSession(new HostResolver)),
transaction_(session_.get(), &mock_socket_factory_) {
}
protected:
FtpRequestInfo GetRequestInfo(const std::string& url) {
FtpRequestInfo info;
info.url = GURL(url);
return info;
}
void ExecuteTransaction(FtpMockControlSocket* ctrl_socket,
const char* request,
int expected_result) {
std::string mock_data("mock-data");
MockRead data_reads[] = {
MockRead(mock_data.c_str()),
};
// TODO(phajdan.jr): FTP transaction should not open two data sockets.
StaticMockSocket data_socket1(data_reads, NULL);
StaticMockSocket data_socket2(data_reads, NULL);
mock_socket_factory_.AddMockSocket(ctrl_socket);
mock_socket_factory_.AddMockSocket(&data_socket1);
mock_socket_factory_.AddMockSocket(&data_socket2);
FtpRequestInfo request_info = GetRequestInfo(request);
ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, &callback_));
EXPECT_EQ(expected_result, callback_.WaitForResult());
EXPECT_EQ(FtpMockControlSocket::QUIT, ctrl_socket->state());
if (expected_result == OK) {
scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
memset(io_buffer->data(), 0, kBufferSize);
ASSERT_EQ(ERR_IO_PENDING,
transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
EXPECT_EQ(static_cast<int>(mock_data.length()),
callback_.WaitForResult());
EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length()));
}
}
void TransactionFailHelper(FtpMockControlSocket* ctrl_socket,
const char* request,
FtpMockControlSocket::State state,
FtpMockControlSocket::State next_state,
const char* response,
int expected_result) {
ctrl_socket->InjectFailure(state, next_state, response);
ExecuteTransaction(ctrl_socket, request, expected_result);
}
scoped_refptr<FtpNetworkSession> session_;
MockClientSocketFactory mock_socket_factory_;
FtpNetworkTransaction transaction_;
TestCompletionCallback callback_;
};
TEST_F(FtpNetworkTransactionTest, FailedLookup) {
FtpRequestInfo request_info = GetRequestInfo("ftp://badhost");
scoped_refptr<RuleBasedHostMapper> mapper(new RuleBasedHostMapper());
mapper->AddSimulatedFailure("badhost");
ScopedHostMapper scoped_mapper(mapper.get());
ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, &callback_));
EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransaction) {
FtpMockControlSocketDirectoryListing ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads2) {
FtpMockControlSocketDirectoryListing ctrl_socket;
ctrl_socket.set_short_read_limit(2);
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionShortReads5) {
FtpMockControlSocketDirectoryListing ctrl_socket;
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, DownloadTransactionShortReads2) {
FtpMockControlSocketFileDownload ctrl_socket;
ctrl_socket.set_short_read_limit(2);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionShortReads5) {
FtpMockControlSocketFileDownload ctrl_socket;
ctrl_socket.set_short_read_limit(5);
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailUser) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_USER,
FtpMockControlSocket::PRE_QUIT,
"500 no such user\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPass) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_PASSWD,
FtpMockControlSocket::PRE_QUIT,
"530 Login authentication failed\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailSyst) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_SYST,
FtpMockControlSocket::PRE_PWD,
"500 failed syst\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPwd) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_PWD,
FtpMockControlSocket::PRE_QUIT,
"500 failed pwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailType) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_TYPE,
FtpMockControlSocket::PRE_QUIT,
"500 failed type\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_PASV,
FtpMockControlSocket::PRE_QUIT,
"500 failed pasv\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMalformedMdtm) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_MDTM,
FtpMockControlSocket::PRE_RETR,
"213 foobar\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailMdtm) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_MDTM,
FtpMockControlSocket::PRE_RETR,
"500 failed mdtm\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv2) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_PASV2,
FtpMockControlSocket::PRE_QUIT,
"500 failed pasv\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailCwd) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_CWD,
FtpMockControlSocket::PRE_QUIT,
"500 failed cwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailList) {
FtpMockControlSocketDirectoryListing ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host",
FtpMockControlSocket::PRE_LIST,
FtpMockControlSocket::PRE_QUIT,
"500 failed list\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailUser) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_USER,
FtpMockControlSocket::PRE_QUIT,
"500 no such user\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPass) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_PASSWD,
FtpMockControlSocket::PRE_QUIT,
"530 Login authentication failed\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailSyst) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_SYST,
FtpMockControlSocket::PRE_PWD,
"500 failed syst\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPwd) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_PWD,
FtpMockControlSocket::PRE_QUIT,
"500 failed pwd\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailType) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_TYPE,
FtpMockControlSocket::PRE_QUIT,
"500 failed type\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPasv) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_PASV,
FtpMockControlSocket::PRE_QUIT,
"500 failed pasv\r\n",
ERR_FAILED);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailMdtm) {
FtpMockControlSocketFileDownload ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_MDTM,
FtpMockControlSocket::PRE_RETR,
"500 failed mdtm\r\n",
OK);
}
TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailRetr) {
FtpMockControlSocketFileDownloadRetrFail ctrl_socket;
TransactionFailHelper(&ctrl_socket,
"ftp://host/file",
FtpMockControlSocket::PRE_RETR,
FtpMockControlSocket::PRE_PASV2,
"500 failed retr\r\n",
ERR_FAILED);
}
} // namespace net