summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-28 20:30:09 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-28 20:30:09 +0000
commitc2972193bd0d0c2354d0862444b7ce4662d52d77 (patch)
treea6cbb8ce07493d2dd6aa9062f44a741e5082739a
parent1b2bb88355feef67a2b67968f0c4a91d16799e38 (diff)
downloadchromium_src-c2972193bd0d0c2354d0862444b7ce4662d52d77.zip
chromium_src-c2972193bd0d0c2354d0862444b7ce4662d52d77.tar.gz
chromium_src-c2972193bd0d0c2354d0862444b7ce4662d52d77.tar.bz2
Rework FTP control response parsing code and fix socket Write misuse.
It also fixes other minor issues in the code, and makes ftp.vim.org work! TEST=Visit ftp.vim.org on Linux with Chromium compiled in Debug mode. You should see directory listing after a short while. BUG=http://crbug.com/15792 Review URL: http://codereview.chromium.org/149368 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21881 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/ftp/ftp_ctrl_response_buffer.cc107
-rw-r--r--net/ftp/ftp_ctrl_response_buffer.h101
-rw-r--r--net/ftp/ftp_ctrl_response_buffer_unittest.cc152
-rw-r--r--net/ftp/ftp_network_transaction.cc297
-rw-r--r--net/ftp/ftp_network_transaction.h69
-rw-r--r--net/ftp/ftp_network_transaction_unittest.cc12
-rw-r--r--net/net.gyp3
7 files changed, 524 insertions, 217 deletions
diff --git a/net/ftp/ftp_ctrl_response_buffer.cc b/net/ftp/ftp_ctrl_response_buffer.cc
new file mode 100644
index 0000000..b60ae81
--- /dev/null
+++ b/net/ftp/ftp_ctrl_response_buffer.cc
@@ -0,0 +1,107 @@
+// 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_ctrl_response_buffer.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+// static
+const int FtpCtrlResponse::kInvalidStatusCode = -1;
+
+int FtpCtrlResponseBuffer::ConsumeData(const char* data, int data_length) {
+ buffer_.append(std::string(data, data_length));
+ ExtractFullLinesFromBuffer();
+
+ while (!lines_.empty()) {
+ ParsedLine line = lines_.front();
+ lines_.pop();
+
+ if (line_buf_.empty()) {
+ if (!line.is_complete)
+ return ERR_INVALID_RESPONSE;
+
+ if (line.is_multiline) {
+ line_buf_ = line.status_text;
+ response_buf_.status_code = line.status_code;
+ } else {
+ response_buf_.status_code = line.status_code;
+ response_buf_.lines.push_back(line.status_text);
+ responses_.push(response_buf_);
+
+ // Prepare to handle following lines.
+ response_buf_ = FtpCtrlResponse();
+ line_buf_.clear();
+ }
+ } else {
+ if (!line.is_complete || line.status_code != response_buf_.status_code) {
+ line_buf_.append(line.raw_text);
+ continue;
+ }
+
+ response_buf_.lines.push_back(line_buf_);
+
+ line_buf_ = line.status_text;
+ DCHECK_EQ(line.status_code, response_buf_.status_code);
+
+ if (!line.is_multiline) {
+ response_buf_.lines.push_back(line_buf_);
+ responses_.push(response_buf_);
+
+ // Prepare to handle following lines.
+ response_buf_ = FtpCtrlResponse();
+ line_buf_.clear();
+ }
+ }
+ }
+
+ return OK;
+}
+
+// static
+FtpCtrlResponseBuffer::ParsedLine FtpCtrlResponseBuffer::ParseLine(
+ const std::string& line) {
+ ParsedLine result;
+
+ if (line.length() >= 3) {
+ if (StringToInt(line.substr(0, 3), &result.status_code))
+ result.has_status_code = (100 <= result.status_code &&
+ result.status_code <= 599);
+ if (result.has_status_code && line.length() >= 4 && line[3] == ' ') {
+ result.is_complete = true;
+ } else if (result.has_status_code && line.length() >= 4 && line[3] == '-') {
+ result.is_complete = true;
+ result.is_multiline = true;
+ }
+ }
+
+ if (result.is_complete) {
+ result.status_text = line.substr(4);
+ } else {
+ result.status_text = line;
+ }
+
+ result.raw_text = line;
+
+ return result;
+}
+
+void FtpCtrlResponseBuffer::ExtractFullLinesFromBuffer() {
+ std::string line_buf;
+ int cut_pos = 0;
+ for (size_t i = 0; i < buffer_.length(); i++) {
+ line_buf.push_back(buffer_[i]);
+ if (i >= 1 && buffer_[i - 1] == '\r' && buffer_[i] == '\n') {
+ lines_.push(ParseLine(line_buf.substr(0, line_buf.length() - 2)));
+ cut_pos = i + 1;
+ line_buf.clear();
+ }
+ }
+ buffer_.erase(0, cut_pos);
+}
+
+} // namespace net
diff --git a/net/ftp/ftp_ctrl_response_buffer.h b/net/ftp/ftp_ctrl_response_buffer.h
new file mode 100644
index 0000000..33f04b9
--- /dev/null
+++ b/net/ftp/ftp_ctrl_response_buffer.h
@@ -0,0 +1,101 @@
+// 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.
+
+#ifndef NET_FTP_FTP_CTRL_RESPONSE_BUFFER_H_
+#define NET_FTP_FTP_CTRL_RESPONSE_BUFFER_H_
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+struct FtpCtrlResponse {
+ static const int kInvalidStatusCode;
+
+ FtpCtrlResponse() : status_code(kInvalidStatusCode) {
+ }
+
+ int status_code; // Three-digit status code.
+ std::vector<std::string> lines; // Response lines, without CRLFs.
+};
+
+class FtpCtrlResponseBuffer {
+ public:
+ FtpCtrlResponseBuffer() {}
+
+ // Called when data is received from the control socket. Returns error code.
+ int ConsumeData(const char* data, int data_length);
+
+ bool ResponseAvailable() const {
+ return !responses_.empty();
+ }
+
+ // Returns the next response. It is an error to call this function
+ // unless ResponseAvailable returns true.
+ FtpCtrlResponse PopResponse() {
+ FtpCtrlResponse result = responses_.front();
+ responses_.pop();
+ return result;
+ }
+
+ private:
+ struct ParsedLine {
+ ParsedLine()
+ : has_status_code(false),
+ is_multiline(false),
+ is_complete(false),
+ status_code(FtpCtrlResponse::kInvalidStatusCode) {
+ }
+
+ // Indicates that this line begins with a valid 3-digit status code.
+ bool has_status_code;
+
+ // Indicates that this line has the dash (-) after the code, which
+ // means a multiline response.
+ bool is_multiline;
+
+ // Indicates that this line could be parsed as a complete and valid
+ // response line, without taking into account preceding lines (which
+ // may change its meaning into a continuation of the previous line).
+ bool is_complete;
+
+ // Part of response parsed as status code.
+ int status_code;
+
+ // Part of response parsed as status text.
+ std::string status_text;
+
+ // Text before parsing, without ending CRLF.
+ std::string raw_text;
+ };
+
+ static ParsedLine ParseLine(const std::string& line);
+
+ void ExtractFullLinesFromBuffer();
+
+ // We keep not-yet-parsed data in a string buffer.
+ std::string buffer_;
+
+ std::queue<ParsedLine> lines_;
+
+ // When parsing a multiline response, we don't know beforehand if a line
+ // will have a continuation. So always store last line of multiline response
+ // so we can append the continuation to it.
+ std::string line_buf_;
+
+ // Keep the response data while we add all lines to it.
+ FtpCtrlResponse response_buf_;
+
+ // As we read full responses (possibly multiline), we add them to the queue.
+ std::queue<FtpCtrlResponse> responses_;
+
+ DISALLOW_COPY_AND_ASSIGN(FtpCtrlResponseBuffer);
+};
+
+} // namespace net
+
+#endif // NET_FTP_FTP_CTRL_RESPONSE_BUFFER_H_
diff --git a/net/ftp/ftp_ctrl_response_buffer_unittest.cc b/net/ftp/ftp_ctrl_response_buffer_unittest.cc
new file mode 100644
index 0000000..c2aeee2
--- /dev/null
+++ b/net/ftp/ftp_ctrl_response_buffer_unittest.cc
@@ -0,0 +1,152 @@
+// 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_ctrl_response_buffer.h"
+
+#include <string.h>
+
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FtpCtrlResponseBufferTest : public testing::Test {
+ protected:
+ int PushDataToBuffer(const char* data) {
+ return buffer_.ConsumeData(data, strlen(data));
+ }
+
+ net::FtpCtrlResponseBuffer buffer_;
+};
+
+TEST_F(FtpCtrlResponseBufferTest, Basic) {
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("200 Status Text\r\n"));
+ EXPECT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(200, response.status_code);
+ ASSERT_EQ(1U, response.lines.size());
+ EXPECT_EQ("Status Text", response.lines[0]);
+}
+
+TEST_F(FtpCtrlResponseBufferTest, Chunks) {
+ EXPECT_EQ(net::OK, PushDataToBuffer("20"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(net::OK, PushDataToBuffer("0 Status"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(net::OK, PushDataToBuffer(" Text"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(net::OK, PushDataToBuffer("\r"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(net::OK, PushDataToBuffer("\n"));
+ EXPECT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(200, response.status_code);
+ ASSERT_EQ(1U, response.lines.size());
+ EXPECT_EQ("Status Text", response.lines[0]);
+}
+
+TEST_F(FtpCtrlResponseBufferTest, Continuation) {
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-FirstLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-SecondLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230 LastLine\r\n"));
+ EXPECT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(230, response.status_code);
+ ASSERT_EQ(3U, response.lines.size());
+ EXPECT_EQ("FirstLine", response.lines[0]);
+ EXPECT_EQ("SecondLine", response.lines[1]);
+ EXPECT_EQ("LastLine", response.lines[2]);
+}
+
+TEST_F(FtpCtrlResponseBufferTest, MultilineContinuation) {
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-FirstLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("Continued\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-SecondLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("215 Continued\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230 LastLine\r\n"));
+ EXPECT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(230, response.status_code);
+ ASSERT_EQ(3U, response.lines.size());
+ EXPECT_EQ("FirstLineContinued", response.lines[0]);
+ EXPECT_EQ("SecondLine215 Continued", response.lines[1]);
+ EXPECT_EQ("LastLine", response.lines[2]);
+}
+
+TEST_F(FtpCtrlResponseBufferTest, SimilarContinuation) {
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-FirstLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ // Notice the space at the start of the line. It should be recognized
+ // as a continuation, and not the last line.
+ EXPECT_EQ(net::OK, PushDataToBuffer(" 230 Continued\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230 TrueLastLine\r\n"));
+ EXPECT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(230, response.status_code);
+ ASSERT_EQ(2U, response.lines.size());
+ EXPECT_EQ("FirstLine 230 Continued", response.lines[0]);
+ EXPECT_EQ("TrueLastLine", response.lines[1]);
+}
+
+// The nesting of multi-line responses is not allowed.
+TEST_F(FtpCtrlResponseBufferTest, NoNesting) {
+ EXPECT_EQ(net::OK, PushDataToBuffer("230-FirstLine\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("300-Continuation\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("300 Still continuation\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+
+ EXPECT_EQ(net::OK, PushDataToBuffer("230 Real End\r\n"));
+ ASSERT_TRUE(buffer_.ResponseAvailable());
+
+ net::FtpCtrlResponse response = buffer_.PopResponse();
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+ EXPECT_EQ(230, response.status_code);
+ ASSERT_EQ(2U, response.lines.size());
+ EXPECT_EQ("FirstLine300-Continuation300 Still continuation",
+ response.lines[0]);
+ EXPECT_EQ("Real End", response.lines[1]);
+}
+
+TEST_F(FtpCtrlResponseBufferTest, NonNumericResponse) {
+ EXPECT_EQ(net::ERR_INVALID_RESPONSE, PushDataToBuffer("Non-numeric\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+}
+
+TEST_F(FtpCtrlResponseBufferTest, OutOfRangeResponse) {
+ EXPECT_EQ(net::ERR_INVALID_RESPONSE, PushDataToBuffer("777 OK?\r\n"));
+ EXPECT_FALSE(buffer_.ResponseAvailable());
+}
+
+} // namespace
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 980e0ab..e783590 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -34,10 +34,7 @@ FtpNetworkTransaction::FtpNetworkTransaction(
session_(session),
request_(NULL),
resolver_(session->host_resolver()),
- response_message_buf_(new IOBufferWithSize(kCtrlBufLen)),
- response_message_buf_len_(0),
- read_ctrl_buf_(new ReusedIOBuffer(response_message_buf_,
- response_message_buf_->size())),
+ read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
read_data_buf_len_(0),
file_data_len_(0),
last_error_(OK),
@@ -119,163 +116,80 @@ uint64 FtpNetworkTransaction::GetUploadProgress() const {
return 0;
}
-int FtpNetworkTransaction::ParseCtrlResponse(int* cut_pos) {
- enum {
- CODE, // Three-digit status code.
- TEXT, // The text after code, not including the space after code.
- ENDLINE, // We expect a CRLF at end of each line.
- } scan_state = CODE;
-
- *cut_pos = 0; // Index of first unparsed character.
-
- // Store parsed code and text for current line.
- int status_code = 0;
- std::string status_text;
-
- const char* data = response_message_buf_->data();
- for (int i = 0; i < response_message_buf_len_; i++) {
- switch (scan_state) {
- case CODE:
- if (data[i] == ' ' || data[i] == '\t') {
- if (status_code < 100 || status_code > 599)
- return ERR_INVALID_RESPONSE;
- scan_state = TEXT;
- break;
- }
- if (data[i] < '0' || data[i] > '9')
- return ERR_INVALID_RESPONSE;
- status_code = 10 * status_code + (data[i] - '0');
- break;
- case TEXT:
- if (data[i] == '\r') {
- scan_state = ENDLINE;
- break;
- }
- status_text.push_back(data[i]);
- break;
- case ENDLINE:
- if (data[i] != '\n')
- 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();
- return ERR_UNEXPECTED;
- }
- }
-
- return OK;
-}
-
// Used to prepare and send FTP commad.
int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
Command cmd) {
- command_sent_ = cmd;
- DLOG(INFO) << " >> " << command;
- const char* buf = command.c_str();
- int buf_len = command.size();
+ DCHECK(!write_command_buf_);
DCHECK(!write_buf_);
- write_buf_ = new IOBuffer(buf_len + 2);
- memcpy(write_buf_->data(), buf, buf_len);
- memcpy(write_buf_->data() + buf_len, kCRLF, 2);
- buf_len += 2;
+ DLOG(INFO) << " >> " << command;
- return ctrl_socket_->Write(write_buf_, buf_len, &io_callback_);
+ command_sent_ = cmd;
+ write_buf_written_ = 0;
+ write_command_buf_ = new IOBufferWithSize(command.length() + 2);
+ write_buf_ = new ReusedIOBuffer(write_command_buf_,
+ write_command_buf_->size());
+ memcpy(write_command_buf_->data(), command.data(), command.length());
+ memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
+
+ next_state_ = STATE_CTRL_WRITE_COMMAND;
+ return OK;
}
-int FtpNetworkTransaction::ProcessCtrlResponses() {
- int rv = OK;
- if (command_sent_ == COMMAND_NONE) {
- while (!ctrl_responses_.empty()) {
- ResponseLine line = ctrl_responses_.front();
- ctrl_responses_.pop();
- if (GetErrorClass(line.code) != ERROR_CLASS_OK)
- return Stop(ERR_FAILED);
- }
- next_state_ = STATE_CTRL_WRITE_USER;
- return rv;
- }
-
- // 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);
+int FtpNetworkTransaction::ProcessCtrlResponse() {
+ FtpCtrlResponse response = ctrl_response_buffer_.PopResponse();
- ResponseLine response_line = ctrl_responses_.front();
- ctrl_responses_.pop();
+ // We always expect only one response, even if it's multiline.
+ DCHECK(!ctrl_response_buffer_.ResponseAvailable());
+ int rv = OK;
switch (command_sent_) {
+ case COMMAND_NONE:
+ // TODO(phajdan.jr): Check for errors in the welcome message.
+ next_state_ = STATE_CTRL_WRITE_USER;
+ break;
case COMMAND_USER:
- rv = ProcessResponseUSER(response_line);
+ rv = ProcessResponseUSER(response);
break;
case COMMAND_PASS:
- rv = ProcessResponsePASS(response_line);
+ rv = ProcessResponsePASS(response);
break;
case COMMAND_ACCT:
- rv = ProcessResponseACCT(response_line);
+ rv = ProcessResponseACCT(response);
break;
case COMMAND_SYST:
- rv = ProcessResponseSYST(response_line);
+ rv = ProcessResponseSYST(response);
break;
case COMMAND_PWD:
- rv = ProcessResponsePWD(response_line);
+ rv = ProcessResponsePWD(response);
break;
case COMMAND_TYPE:
- rv = ProcessResponseTYPE(response_line);
+ rv = ProcessResponseTYPE(response);
break;
case COMMAND_PASV:
- rv = ProcessResponsePASV(response_line);
+ rv = ProcessResponsePASV(response);
break;
case COMMAND_SIZE:
- rv = ProcessResponseSIZE(response_line);
+ rv = ProcessResponseSIZE(response);
break;
case COMMAND_RETR:
- rv = ProcessResponseRETR(response_line);
+ rv = ProcessResponseRETR(response);
break;
case COMMAND_CWD:
- rv = ProcessResponseCWD(response_line);
+ rv = ProcessResponseCWD(response);
break;
case COMMAND_LIST:
- rv = ProcessResponseLIST(response_line);
+ rv = ProcessResponseLIST(response);
break;
case COMMAND_MDTM:
- rv = ProcessResponseMDTM(response_line);
+ rv = ProcessResponseMDTM(response);
break;
case COMMAND_QUIT:
- rv = ProcessResponseQUIT(response_line);
+ rv = ProcessResponseQUIT(response);
break;
default:
DLOG(INFO) << "Missing Command response handling!";
return ERR_FAILED;
}
- DCHECK(ctrl_responses_.empty());
return rv;
}
@@ -331,6 +245,13 @@ int FtpNetworkTransaction::DoLoop(int result) {
case STATE_CTRL_READ_COMPLETE:
rv = DoCtrlReadComplete(rv);
break;
+ case STATE_CTRL_WRITE_COMMAND:
+ DCHECK(rv == OK);
+ rv = DoCtrlWriteCommand();
+ break;
+ case STATE_CTRL_WRITE_COMMAND_COMPLETE:
+ rv = DoCtrlWriteCommandComplete(rv);
+ break;
case STATE_CTRL_WRITE_USER:
DCHECK(rv == OK);
rv = DoCtrlWriteUSER();
@@ -461,14 +382,15 @@ int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
}
int FtpNetworkTransaction::DoCtrlRead() {
- if (write_buf_) // Clear the write buffer
- write_buf_ = NULL;
+ // Clear the write buffer.
+ write_command_buf_ = NULL;
+ write_buf_ = NULL;
next_state_ = STATE_CTRL_READ_COMPLETE;
read_ctrl_buf_->data()[0] = 0;
return ctrl_socket_->Read(
read_ctrl_buf_,
- response_message_buf_->size() - response_message_buf_len_,
+ kCtrlBufLen,
&io_callback_);
}
@@ -476,31 +398,41 @@ int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
if (result < 0)
return Stop(ERR_FAILED);
- response_message_buf_len_ += result;
+ ctrl_response_buffer_.ConsumeData(read_ctrl_buf_->data(), result);
- int cut_pos;
- int rv = ParseCtrlResponse(&cut_pos);
-
- if (rv != OK)
- return Stop(rv);
+ if (!ctrl_response_buffer_.ResponseAvailable()) {
+ // Read more data from the control socket.
+ next_state_ = STATE_CTRL_READ;
+ return OK;
+ }
- if (cut_pos > 0) {
- // Parsed at least one response line.
- DCHECK_GE(response_message_buf_len_, cut_pos);
- memmove(response_message_buf_->data(),
- response_message_buf_->data() + cut_pos,
- response_message_buf_len_ - cut_pos);
- response_message_buf_len_ -= cut_pos;
+ return ProcessCtrlResponse();
+}
- rv = ProcessCtrlResponses();
+int FtpNetworkTransaction::DoCtrlWriteCommand() {
+ write_buf_->SetOffset(write_buf_written_);
+ int rv = ctrl_socket_->Write(write_buf_,
+ write_command_buf_->size() - write_buf_written_,
+ &io_callback_);
+ if (rv == ERR_IO_PENDING) {
+ next_state_ = STATE_CTRL_WRITE_COMMAND_COMPLETE;
+ return rv;
} else {
- // Incomplete response line. Read more.
- next_state_ = STATE_CTRL_READ;
+ return DoCtrlWriteCommandComplete(rv);
}
+}
- read_ctrl_buf_->SetOffset(response_message_buf_len_);
+int FtpNetworkTransaction::DoCtrlWriteCommandComplete(int result) {
+ if (result < 0)
+ return result;
- return rv;
+ write_buf_written_ += result;
+ if (write_buf_written_ == write_command_buf_->size()) {
+ next_state_ = STATE_CTRL_READ;
+ } else {
+ next_state_ = STATE_CTRL_WRITE_COMMAND;
+ }
+ return OK;
}
// FTP Commands and responses
@@ -519,8 +451,9 @@ int FtpNetworkTransaction::DoCtrlWriteUSER() {
return SendFtpCommand(command, COMMAND_USER);
}
-int FtpNetworkTransaction::ProcessResponseUSER(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseUSER(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_OK:
next_state_ = STATE_CTRL_WRITE_SYST;
break;
@@ -528,7 +461,7 @@ int FtpNetworkTransaction::ProcessResponseUSER(const ResponseLine& response) {
next_state_ = STATE_CTRL_WRITE_PASS;
break;
case ERROR_CLASS_ERROR_RETRY:
- if (response.code == 421)
+ if (response.status_code == 421)
return Stop(ERR_FAILED);
break;
case ERROR_CLASS_ERROR:
@@ -553,8 +486,9 @@ int FtpNetworkTransaction::DoCtrlWritePASS() {
return SendFtpCommand(command, COMMAND_PASS);
}
-int FtpNetworkTransaction::ProcessResponsePASS(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponsePASS(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_OK:
next_state_ = STATE_CTRL_WRITE_SYST;
break;
@@ -562,12 +496,12 @@ int FtpNetworkTransaction::ProcessResponsePASS(const ResponseLine& response) {
next_state_ = STATE_CTRL_WRITE_ACCT;
break;
case ERROR_CLASS_ERROR_RETRY:
- if (response.code == 421) {
+ if (response.status_code == 421) {
// TODO(ibrar): Retry here.
}
return Stop(ERR_FAILED);
case ERROR_CLASS_ERROR:
- if (response.code == 503) {
+ if (response.status_code == 503) {
next_state_ = STATE_CTRL_WRITE_USER;
} else {
// TODO(ibrar): Retry here.
@@ -587,8 +521,9 @@ int FtpNetworkTransaction::DoCtrlWriteSYST() {
return SendFtpCommand(command, COMMAND_SYST);
}
-int FtpNetworkTransaction::ProcessResponseSYST(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseSYST(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -616,8 +551,8 @@ int FtpNetworkTransaction::DoCtrlWritePWD() {
return SendFtpCommand(command, COMMAND_PWD);
}
-int FtpNetworkTransaction::ProcessResponsePWD(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -642,8 +577,9 @@ int FtpNetworkTransaction::DoCtrlWriteTYPE() {
return SendFtpCommand(command, COMMAND_TYPE);
}
-int FtpNetworkTransaction::ProcessResponseTYPE(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseTYPE(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -668,8 +604,9 @@ int FtpNetworkTransaction::DoCtrlWriteACCT() {
return SendFtpCommand(command, COMMAND_ACCT);
}
-int FtpNetworkTransaction::ProcessResponseACCT(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseACCT(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -697,20 +634,23 @@ int FtpNetworkTransaction::DoCtrlWritePASV() {
// There are two way we can receive IP address and port.
// (127,0,0,1,23,21) IP address and port encapsulate in ().
// 127,0,0,1,23,21 IP address and port without ().
-int FtpNetworkTransaction::ProcessResponsePASV(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponsePASV(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
const char* ptr;
int i0, i1, i2, i3, p0, p1;
- ptr = response.text.c_str(); // Try with bracket.
+ if (response.lines.size() != 1)
+ return Stop(ERR_INVALID_RESPONSE);
+ ptr = response.lines[0].c_str(); // Try with bracket.
while (*ptr && *ptr != '(')
++ptr;
if (*ptr) {
++ptr;
} else {
- ptr = response.text.c_str(); // Try without bracket.
+ ptr = response.lines[0].c_str(); // Try without bracket.
while (*ptr && *ptr != ',')
++ptr;
while (*ptr && *ptr != ' ')
@@ -748,12 +688,15 @@ int FtpNetworkTransaction::DoCtrlWriteSIZE() {
return SendFtpCommand(command, COMMAND_SIZE);
}
-int FtpNetworkTransaction::ProcessResponseSIZE(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseSIZE(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
break;
case ERROR_CLASS_OK:
- if (!StringToInt(response.text, &file_data_len_))
+ if (response.lines.size() != 1)
+ return Stop(ERR_INVALID_RESPONSE);
+ if (!StringToInt(response.lines[0], &file_data_len_))
return Stop(ERR_INVALID_RESPONSE);
if (file_data_len_ < 0)
return Stop(ERR_INVALID_RESPONSE);
@@ -784,8 +727,9 @@ int FtpNetworkTransaction::DoCtrlWriteRETR() {
return SendFtpCommand(command, COMMAND_RETR);
}
-int FtpNetworkTransaction::ProcessResponseRETR(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseRETR(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
break;
case ERROR_CLASS_OK:
@@ -795,7 +739,8 @@ int FtpNetworkTransaction::ProcessResponseRETR(const ResponseLine& response) {
next_state_ = STATE_CTRL_WRITE_PASV;
break;
case ERROR_CLASS_ERROR_RETRY:
- if (response.code == 421 || response.code == 425 || response.code == 426)
+ if (response.status_code == 421 || response.status_code == 425 ||
+ response.status_code == 426)
return Stop(ERR_FAILED);
return ERR_FAILED; // TODO(ibrar): Retry here.
case ERROR_CLASS_ERROR:
@@ -823,8 +768,9 @@ int FtpNetworkTransaction::DoCtrlWriteMDTM() {
return SendFtpCommand(command, COMMAND_MDTM);
}
-int FtpNetworkTransaction::ProcessResponseMDTM(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseMDTM(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -857,8 +803,8 @@ int FtpNetworkTransaction::DoCtrlWriteCWD() {
return SendFtpCommand(command, COMMAND_CWD);
}
-int FtpNetworkTransaction::ProcessResponseCWD(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_FAILED);
case ERROR_CLASS_OK:
@@ -883,10 +829,10 @@ int FtpNetworkTransaction::DoCtrlWriteLIST() {
return SendFtpCommand(command, COMMAND_LIST);
}
-int FtpNetworkTransaction::ProcessResponseLIST(const ResponseLine& response) {
- switch (GetErrorClass(response.code)) {
+int FtpNetworkTransaction::ProcessResponseLIST(
+ const FtpCtrlResponse& response) {
+ switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
- response_message_buf_len_ = 0; // Clear the response buffer.
next_state_ = STATE_CTRL_READ;
break;
case ERROR_CLASS_OK:
@@ -912,7 +858,8 @@ int FtpNetworkTransaction::DoCtrlWriteQUIT() {
return SendFtpCommand(command, COMMAND_QUIT);
}
-int FtpNetworkTransaction::ProcessResponseQUIT(const ResponseLine& response) {
+int FtpNetworkTransaction::ProcessResponseQUIT(
+ const FtpCtrlResponse& response) {
ctrl_socket_->Disconnect();
return last_error_;
}
diff --git a/net/ftp/ftp_network_transaction.h b/net/ftp/ftp_network_transaction.h
index 90c53e4..5ca098c 100644
--- a/net/ftp/ftp_network_transaction.h
+++ b/net/ftp/ftp_network_transaction.h
@@ -8,11 +8,13 @@
#include <string>
#include <queue>
#include <utility>
+#include <vector>
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "net/base/address_list.h"
#include "net/base/host_resolver.h"
+#include "net/ftp/ftp_ctrl_response_buffer.h"
#include "net/ftp/ftp_response_info.h"
#include "net/ftp/ftp_transaction.h"
@@ -72,24 +74,12 @@ class FtpNetworkTransaction : public FtpTransaction {
// the requested action did not take place.
};
- struct ResponseLine {
- ResponseLine(int code, const std::string& text) : code(code), text(text) {
- }
-
- int code; // Three-digit status code.
- std::string text; // Text after the code, without ending CRLF.
- };
-
void DoCallback(int result);
void OnIOComplete(int result);
// Executes correct ProcessResponse + command_name function based on last
// issued command. Returns error code.
- int ProcessCtrlResponses();
-
- // Parses as much as possible from response_message_buf_. Puts index of the
- // first unparsed character in cut_pos. Returns error code.
- int ParseCtrlResponse(int* cut_pos);
+ int ProcessCtrlResponse();
int SendFtpCommand(const std::string& command, Command cmd);
@@ -113,32 +103,34 @@ class FtpNetworkTransaction : public FtpTransaction {
int DoCtrlConnectComplete(int result);
int DoCtrlRead();
int DoCtrlReadComplete(int result);
+ int DoCtrlWriteCommand();
+ int DoCtrlWriteCommandComplete(int result);
int DoCtrlWriteUSER();
- int ProcessResponseUSER(const ResponseLine& response);
+ int ProcessResponseUSER(const FtpCtrlResponse& response);
int DoCtrlWritePASS();
- int ProcessResponsePASS(const ResponseLine& response);
+ int ProcessResponsePASS(const FtpCtrlResponse& response);
int DoCtrlWriteACCT();
- int ProcessResponseACCT(const ResponseLine& response);
+ int ProcessResponseACCT(const FtpCtrlResponse& response);
int DoCtrlWriteSYST();
- int ProcessResponseSYST(const ResponseLine& response);
+ int ProcessResponseSYST(const FtpCtrlResponse& response);
int DoCtrlWritePWD();
- int ProcessResponsePWD(const ResponseLine& response);
+ int ProcessResponsePWD(const FtpCtrlResponse& response);
int DoCtrlWriteTYPE();
- int ProcessResponseTYPE(const ResponseLine& response);
+ int ProcessResponseTYPE(const FtpCtrlResponse& response);
int DoCtrlWritePASV();
- int ProcessResponsePASV(const ResponseLine& response);
+ int ProcessResponsePASV(const FtpCtrlResponse& response);
int DoCtrlWriteRETR();
- int ProcessResponseRETR(const ResponseLine& response);
+ int ProcessResponseRETR(const FtpCtrlResponse& response);
int DoCtrlWriteSIZE();
- int ProcessResponseSIZE(const ResponseLine& response);
+ int ProcessResponseSIZE(const FtpCtrlResponse& response);
int DoCtrlWriteCWD();
- int ProcessResponseCWD(const ResponseLine& response);
+ int ProcessResponseCWD(const FtpCtrlResponse& response);
int DoCtrlWriteLIST();
- int ProcessResponseLIST(const ResponseLine& response);
+ int ProcessResponseLIST(const FtpCtrlResponse& response);
int DoCtrlWriteMDTM();
- int ProcessResponseMDTM(const ResponseLine& response);
+ int ProcessResponseMDTM(const FtpCtrlResponse& response);
int DoCtrlWriteQUIT();
- int ProcessResponseQUIT(const ResponseLine& response);
+ int ProcessResponseQUIT(const FtpCtrlResponse& response);
int DoDataResolveHost();
int DoDataResolveHostComplete(int result);
@@ -161,22 +153,25 @@ class FtpNetworkTransaction : public FtpTransaction {
SingleRequestHostResolver resolver_;
AddressList addresses_;
- // As we read full response lines, we parse them and add to the queue.
- std::queue<ResponseLine> ctrl_responses_;
+ // User buffer passed to the Read method for control socket.
+ scoped_refptr<IOBuffer> read_ctrl_buf_;
- // Buffer holding not-yet-parsed control socket responses.
- scoped_refptr<IOBufferWithSize> response_message_buf_;
- int response_message_buf_len_;
-
- // User buffer passed to the Read method. It actually writes to the
- // response_message_buf_ at correct offset.
- scoped_refptr<ReusedIOBuffer> read_ctrl_buf_;
+ FtpCtrlResponseBuffer ctrl_response_buffer_;
scoped_refptr<IOBuffer> read_data_buf_;
int read_data_buf_len_;
int file_data_len_;
- scoped_refptr<IOBuffer> write_buf_;
+ // Buffer holding the command line to be written to the control socket.
+ scoped_refptr<IOBufferWithSize> write_command_buf_;
+
+ // Buffer passed to the Write method of control socket. It actually writes
+ // to the write_command_buf_ at correct offset.
+ scoped_refptr<ReusedIOBuffer> write_buf_;
+
+ // Number of bytes from write_command_buf_ that we've already sent to the
+ // server.
+ int write_buf_written_;
int last_error_;
@@ -201,6 +196,8 @@ class FtpNetworkTransaction : public FtpTransaction {
STATE_CTRL_CONNECT_COMPLETE,
STATE_CTRL_READ,
STATE_CTRL_READ_COMPLETE,
+ STATE_CTRL_WRITE_COMMAND,
+ STATE_CTRL_WRITE_COMMAND_COMPLETE,
STATE_CTRL_WRITE_USER,
STATE_CTRL_WRITE_PASS,
STATE_CTRL_WRITE_ACCT,
diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc
index d549b2f..7726416 100644
--- a/net/ftp/ftp_network_transaction_unittest.cc
+++ b/net/ftp/ftp_network_transaction_unittest.cc
@@ -51,7 +51,7 @@ class FtpMockControlSocket : public DynamicMockSocket {
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
- return MockWriteResult(true, OK);
+ return MockWriteResult(true, data.length());
switch (state()) {
case PRE_USER:
return Verify("USER anonymous\r\n", data, PRE_PASSWD,
@@ -59,7 +59,7 @@ class FtpMockControlSocket : public DynamicMockSocket {
case PRE_PASSWD:
{
const char* response_one = "230 Welcome\r\n";
- const char* response_multi = "230 One\r\n230 Two\r\n230 Three\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);
}
@@ -128,7 +128,7 @@ class FtpMockControlSocket : public DynamicMockSocket {
if (expected == data) {
state_ = next_state;
SimulateRead(next_read);
- return MockWriteResult(true, OK);
+ return MockWriteResult(true, data.length());
}
return MockWriteResult(true, ERR_UNEXPECTED);
}
@@ -152,7 +152,7 @@ class FtpMockControlSocketDirectoryListing : public FtpMockControlSocket {
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
- return MockWriteResult(true, OK);
+ return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /\r\n", data, PRE_MDTM,
@@ -188,7 +188,7 @@ class FtpMockControlSocketFileDownload : public FtpMockControlSocket {
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
- return MockWriteResult(true, OK);
+ return MockWriteResult(true, data.length());
switch (state()) {
case PRE_SIZE:
return Verify("SIZE /file\r\n", data, PRE_MDTM,
@@ -216,7 +216,7 @@ class FtpMockControlSocketFileDownloadRetrFail
virtual MockWriteResult OnWrite(const std::string& data) {
if (InjectFault())
- return MockWriteResult(true, OK);
+ return MockWriteResult(true, data.length());
switch (state()) {
case PRE_PASV2:
return Verify("PASV\r\n", data, PRE_CWD,
diff --git a/net/net.gyp b/net/net.gyp
index f52caaf..fb01d8b 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -191,6 +191,8 @@
'disk_cache/trace.h',
'ftp/ftp_auth_cache.cc',
'ftp/ftp_auth_cache.h',
+ 'ftp/ftp_ctrl_response_buffer.cc',
+ 'ftp/ftp_ctrl_response_buffer.h',
'ftp/ftp_directory_parser.cc',
'ftp/ftp_directory_parser.h',
'ftp/ftp_network_layer.cc',
@@ -474,6 +476,7 @@
'disk_cache/mapped_file_unittest.cc',
'disk_cache/storage_block_unittest.cc',
'ftp/ftp_auth_cache_unittest.cc',
+ 'ftp/ftp_ctrl_response_buffer_unittest.cc',
'ftp/ftp_network_transaction_unittest.cc',
'http/des_unittest.cc',
'http/http_auth_cache_unittest.cc',