| // Copyright (c) 2011 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 <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/logging.h" |
| #include "base/strings/string_piece.h" |
| #include "base/values.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/parse_number.h" |
| #include "net/log/net_log_event_type.h" |
| |
| namespace net { |
| |
| // static |
| const int FtpCtrlResponse::kInvalidStatusCode = -1; |
| |
| FtpCtrlResponse::FtpCtrlResponse() : status_code(kInvalidStatusCode) {} |
| |
| FtpCtrlResponse::FtpCtrlResponse(const FtpCtrlResponse& other) = default; |
| |
| FtpCtrlResponse::~FtpCtrlResponse() = default; |
| |
| FtpCtrlResponseBuffer::FtpCtrlResponseBuffer(const NetLogWithSource& net_log) |
| : multiline_(false), net_log_(net_log) {} |
| |
| FtpCtrlResponseBuffer::~FtpCtrlResponseBuffer() = default; |
| |
| int FtpCtrlResponseBuffer::ConsumeData(const char* data, int data_length) { |
| buffer_.append(data, data_length); |
| ExtractFullLinesFromBuffer(); |
| |
| while (!lines_.empty()) { |
| ParsedLine line = lines_.front(); |
| lines_.pop(); |
| |
| if (multiline_) { |
| 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(); |
| multiline_ = false; |
| } |
| } else { |
| if (!line.is_complete) |
| return ERR_INVALID_RESPONSE; |
| |
| response_buf_.status_code = line.status_code; |
| if (line.is_multiline) { |
| line_buf_ = line.status_text; |
| multiline_ = true; |
| } else { |
| response_buf_.lines.push_back(line.status_text); |
| responses_.push(response_buf_); |
| |
| // Prepare to handle following lines. |
| response_buf_ = FtpCtrlResponse(); |
| line_buf_.clear(); |
| } |
| } |
| } |
| |
| return OK; |
| } |
| |
| namespace { |
| |
| std::unique_ptr<base::Value> NetLogFtpCtrlResponseCallback( |
| const FtpCtrlResponse* response, |
| NetLogCaptureMode capture_mode) { |
| std::unique_ptr<base::ListValue> lines(new base::ListValue()); |
| lines->AppendStrings(response->lines); |
| |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| dict->SetInteger("status_code", response->status_code); |
| dict->Set("lines", std::move(lines)); |
| return std::move(dict); |
| } |
| |
| } // namespace |
| |
| FtpCtrlResponse FtpCtrlResponseBuffer::PopResponse() { |
| FtpCtrlResponse result = responses_.front(); |
| responses_.pop(); |
| |
| net_log_.AddEvent(NetLogEventType::FTP_CONTROL_RESPONSE, |
| base::Bind(&NetLogFtpCtrlResponseCallback, &result)); |
| |
| return result; |
| } |
| |
| FtpCtrlResponseBuffer::ParsedLine::ParsedLine() |
| : has_status_code(false), |
| is_multiline(false), |
| is_complete(false), |
| status_code(FtpCtrlResponse::kInvalidStatusCode) { |
| } |
| |
| FtpCtrlResponseBuffer::ParsedLine::ParsedLine(const ParsedLine& other) = |
| default; |
| |
| // static |
| FtpCtrlResponseBuffer::ParsedLine FtpCtrlResponseBuffer::ParseLine( |
| const std::string& line) { |
| ParsedLine result; |
| |
| if (line.length() >= 3) { |
| if (ParseInt32(base::StringPiece(line.begin(), line.begin() + 3), |
| ParseIntFormat::NON_NEGATIVE, &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() { |
| int cut_pos = 0; |
| for (size_t i = 0; i < buffer_.length(); i++) { |
| if (i >= 1 && buffer_[i - 1] == '\r' && buffer_[i] == '\n') { |
| lines_.push(ParseLine(buffer_.substr(cut_pos, i - cut_pos - 1))); |
| cut_pos = i + 1; |
| } |
| } |
| buffer_.erase(0, cut_pos); |
| } |
| |
| } // namespace net |