diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-29 15:27:44 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-29 15:27:44 +0000 |
commit | 6c7e65208cedc0ff790738a67794474c679ee578 (patch) | |
tree | 71ac7941a02e76e13705e75e712679286931326a /net/tools/flip_server | |
parent | 29239893bfafc535339be2adf7845a5010213a51 (diff) | |
download | chromium_src-6c7e65208cedc0ff790738a67794474c679ee578.zip chromium_src-6c7e65208cedc0ff790738a67794474c679ee578.tar.gz chromium_src-6c7e65208cedc0ff790738a67794474c679ee578.tar.bz2 |
Break out balsa and epoll_server from net/tools/flip_server.
balsa is an HTTP headers/framing abstraction used by code in net/tools/flip_server and net/tools/quic.
epoll_server is a event-driven server abstraction based on (as the name suggested) epoll. It is also used by code in net/tools/flip_server and net/tools/quic.
Since these are both shared across different layers, it makes sense to split them into different directories.
Review URL: https://codereview.chromium.org/25085002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@225890 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/tools/flip_server')
36 files changed, 24 insertions, 8025 deletions
diff --git a/net/tools/flip_server/acceptor_thread.h b/net/tools/flip_server/acceptor_thread.h index f967fac..7c758d9 100644 --- a/net/tools/flip_server/acceptor_thread.h +++ b/net/tools/flip_server/acceptor_thread.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "base/threading/simple_thread.h" -#include "net/tools/flip_server/epoll_server.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/flip_server/sm_interface.h" #include "openssl/ssl.h" diff --git a/net/tools/flip_server/balsa_enums.h b/net/tools/flip_server/balsa_enums.h deleted file mode 100644 index 4273ee4..0000000 --- a/net/tools/flip_server/balsa_enums.h +++ /dev/null @@ -1,111 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_BALSA_ENUMS_H_ -#define NET_TOOLS_FLIP_SERVER_BALSA_ENUMS_H_ - -namespace net { - -struct BalsaFrameEnums { - enum ParseState { - PARSE_ERROR, - READING_HEADER_AND_FIRSTLINE, - READING_CHUNK_LENGTH, - READING_CHUNK_EXTENSION, - READING_CHUNK_DATA, - READING_CHUNK_TERM, - READING_LAST_CHUNK_TERM, - READING_TRAILER, - READING_UNTIL_CLOSE, - READING_CONTENT, - MESSAGE_FULLY_READ, - NUM_STATES, - }; - - enum ErrorCode { - NO_ERROR = 0, // A sentinel value for convenience, none of the callbacks - // should ever see this error code. - // Header parsing errors - // Note that adding one to many of the REQUEST errors yields the - // appropriate RESPONSE error. - // Particularly, when parsing the first line of a request or response, - // there are three sequences of non-whitespace regardless of whether or - // not it is a request or response. These are listed below, in order. - // - // firstline_a firstline_b firstline_c - // REQ: method request_uri version - // RESP: version statuscode reason - // - // As you can see, the first token is the 'method' field for a request, - // and 'version' field for a response. We call the first non whitespace - // token firstline_a, the second firstline_b, and the third token - // followed by [^\r\n]*) firstline_c. - // - // This organization is important, as it lets us determine the error code - // to use without a branch based on is_response. Instead, we simply add - // is_response to the response error code-- If is_response is true, then - // we'll get the response error code, thanks to the fact that the error - // code numbers are organized to ensure that response error codes always - // precede request error codes. - // | Triggered - // | while processing - // | this NONWS - // | sequence... - NO_STATUS_LINE_IN_RESPONSE, // | - NO_REQUEST_LINE_IN_REQUEST, // | - FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION, // | firstline_a - FAILED_TO_FIND_WS_AFTER_REQUEST_METHOD, // | firstline_a - FAILED_TO_FIND_WS_AFTER_RESPONSE_STATUSCODE, // | firstline_b - FAILED_TO_FIND_WS_AFTER_REQUEST_REQUEST_URI, // | firstline_b - FAILED_TO_FIND_NL_AFTER_RESPONSE_REASON_PHRASE, // | firstline_c - FAILED_TO_FIND_NL_AFTER_REQUEST_HTTP_VERSION, // | firstline_c - - FAILED_CONVERTING_STATUS_CODE_TO_INT, - REQUEST_URI_TOO_LONG, // Request URI greater than kMaxUrlLen. - - HEADERS_TOO_LONG, - UNPARSABLE_CONTENT_LENGTH, - // Warning: there may be a body but there was no content-length/chunked - // encoding - MAYBE_BODY_BUT_NO_CONTENT_LENGTH, - - // This is used if a body is required for a request. - REQUIRED_BODY_BUT_NO_CONTENT_LENGTH, - - HEADER_MISSING_COLON, - - // Chunking errors - INVALID_CHUNK_LENGTH, - CHUNK_LENGTH_OVERFLOW, - - // Other errors. - CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO, - CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT, - MULTIPLE_CONTENT_LENGTH_KEYS, - MULTIPLE_TRANSFER_ENCODING_KEYS, - UNKNOWN_TRANSFER_ENCODING, - INVALID_HEADER_FORMAT, - - // A detected internal inconsistency was found. - INTERNAL_LOGIC_ERROR, - - NUM_ERROR_CODES - }; - static const char* ParseStateToString(ParseState error_code); - static const char* ErrorCodeToString(ErrorCode error_code); -}; - -struct BalsaHeadersEnums { - enum ContentLengthStatus { - INVALID_CONTENT_LENGTH, - CONTENT_LENGTH_OVERFLOW, - NO_CONTENT_LENGTH, - VALID_CONTENT_LENGTH, - }; -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BALSA_ENUMS_H_ - diff --git a/net/tools/flip_server/balsa_frame.cc b/net/tools/flip_server/balsa_frame.cc deleted file mode 100644 index dead665..0000000 --- a/net/tools/flip_server/balsa_frame.cc +++ /dev/null @@ -1,1597 +0,0 @@ -// 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/tools/flip_server/balsa_frame.h" - -#include <assert.h> -#if __SSE2__ -#include <emmintrin.h> -#endif // __SSE2__ -#include <strings.h> - -#include <limits> -#include <string> -#include <utility> -#include <vector> - -#include "base/logging.h" -#include "base/port.h" -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" -#include "net/tools/flip_server/buffer_interface.h" -#include "net/tools/flip_server/simple_buffer.h" -#include "net/tools/flip_server/split.h" -#include "net/tools/flip_server/string_piece_utils.h" - -namespace net { - -// Constants holding some header names for headers which can affect the way the -// HTTP message is framed, and so must be processed specially: -static const char kContentLength[] = "content-length"; -static const size_t kContentLengthSize = sizeof(kContentLength) - 1; -static const char kTransferEncoding[] = "transfer-encoding"; -static const size_t kTransferEncodingSize = sizeof(kTransferEncoding) - 1; - -BalsaFrame::BalsaFrame() - : last_char_was_slash_r_(false), - saw_non_newline_char_(false), - start_was_space_(true), - chunk_length_character_extracted_(false), - is_request_(true), - request_was_head_(false), - max_header_length_(16 * 1024), - max_request_uri_length_(2048), - visitor_(&do_nothing_visitor_), - chunk_length_remaining_(0), - content_length_remaining_(0), - last_slash_n_loc_(NULL), - last_recorded_slash_n_loc_(NULL), - last_slash_n_idx_(0), - term_chars_(0), - parse_state_(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE), - last_error_(BalsaFrameEnums::NO_ERROR), - headers_(NULL) { -} - -BalsaFrame::~BalsaFrame() {} - -void BalsaFrame::Reset() { - last_char_was_slash_r_ = false; - saw_non_newline_char_ = false; - start_was_space_ = true; - chunk_length_character_extracted_ = false; - // is_request_ = true; // not reset between messages. - // request_was_head_ = false; // not reset between messages. - // max_header_length_ = 4096; // not reset between messages. - // max_request_uri_length_ = 2048; // not reset between messages. - // visitor_ = &do_nothing_visitor_; // not reset between messages. - chunk_length_remaining_ = 0; - content_length_remaining_ = 0; - last_slash_n_loc_ = NULL; - last_recorded_slash_n_loc_ = NULL; - last_slash_n_idx_ = 0; - term_chars_ = 0; - parse_state_ = BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE; - last_error_ = BalsaFrameEnums::NO_ERROR; - lines_.clear(); - if (headers_ != NULL) { - headers_->Clear(); - } -} - -const char* BalsaFrameEnums::ParseStateToString( - BalsaFrameEnums::ParseState error_code) { - switch (error_code) { - case PARSE_ERROR: - return "PARSE_ERROR"; - case READING_HEADER_AND_FIRSTLINE: - return "READING_HEADER_AND_FIRSTLINE"; - case READING_CHUNK_LENGTH: - return "READING_CHUNK_LENGTH"; - case READING_CHUNK_EXTENSION: - return "READING_CHUNK_EXTENSION"; - case READING_CHUNK_DATA: - return "READING_CHUNK_DATA"; - case READING_CHUNK_TERM: - return "READING_CHUNK_TERM"; - case READING_LAST_CHUNK_TERM: - return "READING_LAST_CHUNK_TERM"; - case READING_TRAILER: - return "READING_TRAILER"; - case READING_UNTIL_CLOSE: - return "READING_UNTIL_CLOSE"; - case READING_CONTENT: - return "READING_CONTENT"; - case MESSAGE_FULLY_READ: - return "MESSAGE_FULLY_READ"; - case NUM_STATES: - return "UNKNOWN_STATE"; - } - return "UNKNOWN_STATE"; -} - -const char* BalsaFrameEnums::ErrorCodeToString( - BalsaFrameEnums::ErrorCode error_code) { - switch (error_code) { - case NO_ERROR: - return "NO_ERROR"; - case NO_STATUS_LINE_IN_RESPONSE: - return "NO_STATUS_LINE_IN_RESPONSE"; - case NO_REQUEST_LINE_IN_REQUEST: - return "NO_REQUEST_LINE_IN_REQUEST"; - case FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION: - return "FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION"; - case FAILED_TO_FIND_WS_AFTER_REQUEST_METHOD: - return "FAILED_TO_FIND_WS_AFTER_REQUEST_METHOD"; - case FAILED_TO_FIND_WS_AFTER_RESPONSE_STATUSCODE: - return "FAILED_TO_FIND_WS_AFTER_RESPONSE_STATUSCODE"; - case FAILED_TO_FIND_WS_AFTER_REQUEST_REQUEST_URI: - return "FAILED_TO_FIND_WS_AFTER_REQUEST_REQUEST_URI"; - case FAILED_TO_FIND_NL_AFTER_RESPONSE_REASON_PHRASE: - return "FAILED_TO_FIND_NL_AFTER_RESPONSE_REASON_PHRASE"; - case FAILED_TO_FIND_NL_AFTER_REQUEST_HTTP_VERSION: - return "FAILED_TO_FIND_NL_AFTER_REQUEST_HTTP_VERSION"; - case FAILED_CONVERTING_STATUS_CODE_TO_INT: - return "FAILED_CONVERTING_STATUS_CODE_TO_INT"; - case REQUEST_URI_TOO_LONG: - return "REQUEST_URI_TOO_LONG"; - case HEADERS_TOO_LONG: - return "HEADERS_TOO_LONG"; - case UNPARSABLE_CONTENT_LENGTH: - return "UNPARSABLE_CONTENT_LENGTH"; - case MAYBE_BODY_BUT_NO_CONTENT_LENGTH: - return "MAYBE_BODY_BUT_NO_CONTENT_LENGTH"; - case REQUIRED_BODY_BUT_NO_CONTENT_LENGTH: - return "REQUIRED_BODY_BUT_NO_CONTENT_LENGTH"; - case HEADER_MISSING_COLON: - return "HEADER_MISSING_COLON"; - case INVALID_CHUNK_LENGTH: - return "INVALID_CHUNK_LENGTH"; - case CHUNK_LENGTH_OVERFLOW: - return "CHUNK_LENGTH_OVERFLOW"; - case CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO: - return "CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO"; - case CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT: - return "CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT"; - case MULTIPLE_CONTENT_LENGTH_KEYS: - return "MULTIPLE_CONTENT_LENGTH_KEYS"; - case MULTIPLE_TRANSFER_ENCODING_KEYS: - return "MULTIPLE_TRANSFER_ENCODING_KEYS"; - case UNKNOWN_TRANSFER_ENCODING: - return "UNKNOWN_TRANSFER_ENCODING"; - case INVALID_HEADER_FORMAT: - return "INVALID_HEADER_FORMAT"; - case INTERNAL_LOGIC_ERROR: - return "INTERNAL_LOGIC_ERROR"; - case NUM_ERROR_CODES: - return "UNKNOWN_ERROR"; - } - return "UNKNOWN_ERROR"; -} - -// Summary: -// Parses the first line of either a request or response. -// Note that in the case of a detected warning, error_code will be set -// but the function will not return false. -// Exactly zero or one warning or error (but not both) may be detected -// by this function. -// Note that this function will not write the data of the first-line -// into the header's buffer (that should already have been done elsewhere). -// -// Pre-conditions: -// begin != end -// *begin should be a character which is > ' '. This implies that there -// is at least one non-whitespace characters between [begin, end). -// headers is a valid pointer to a BalsaHeaders class. -// error_code is a valid pointer to a BalsaFrameEnums::ErrorCode value. -// Entire first line must exist between [begin, end) -// Exactly zero or one newlines -may- exist between [begin, end) -// [begin, end) should exist in the header's buffer. -// -// Side-effects: -// headers will be modified -// error_code may be modified if either a warning or error is detected -// -// Returns: -// True if no error (as opposed to warning) is detected. -// False if an error (as opposed to warning) is detected. - -// -// If there is indeed non-whitespace in the line, then the following -// will take care of this for you: -// while (*begin <= ' ') ++begin; -// ProcessFirstLine(begin, end, is_request, &headers, &error_code); -// -bool ParseHTTPFirstLine(const char* begin, - const char* end, - bool is_request, - size_t max_request_uri_length, - BalsaHeaders* headers, - BalsaFrameEnums::ErrorCode* error_code) { - const char* current = begin; - // HTTP firstlines all have the following structure: - // LWS NONWS LWS NONWS LWS NONWS NOTCRLF CRLF - // [\t \r\n]+ [^\t ]+ [\t ]+ [^\t ]+ [\t ]+ [^\t ]+ [^\r\n]+ "\r\n" - // ws1 nws1 ws2 nws2 ws3 nws3 ws4 - // | [-------) [-------) [----------------) - // REQ: method request_uri version - // RESP: version statuscode reason - // - // The first NONWS->LWS component we'll call firstline_a. - // The second firstline_b, and the third firstline_c. - // - // firstline_a goes from nws1 to (but not including) ws2 - // firstline_b goes from nws2 to (but not including) ws3 - // firstline_c goes from nws3 to (but not including) ws4 - // - // In the code: - // ws1 == whitespace_1_idx_ - // nws1 == non_whitespace_1_idx_ - // ws2 == whitespace_2_idx_ - // nws2 == non_whitespace_2_idx_ - // ws3 == whitespace_3_idx_ - // nws3 == non_whitespace_3_idx_ - // ws4 == whitespace_4_idx_ - - // Kill all whitespace (including '\r\n') at the end of the line. - --end; - if (*end != '\n') { - *error_code = BalsaFrameEnums::INTERNAL_LOGIC_ERROR; - LOG(DFATAL) << "INTERNAL_LOGIC_ERROR Headers: \n" - << headers->OriginalHeadersForDebugging(); - return false; - } - while (begin < end && *end <= ' ') { - --end; - } - DCHECK(*end != '\n'); - if (*end == '\n') { - *error_code = BalsaFrameEnums::INTERNAL_LOGIC_ERROR; - LOG(DFATAL) << "INTERNAL_LOGIC_ERROR Headers: \n" - << headers->OriginalHeadersForDebugging(); - return false; - } - ++end; - - // The two following statements should not be possible. - if (end == begin) { - *error_code = BalsaFrameEnums::INTERNAL_LOGIC_ERROR; - LOG(DFATAL) << "INTERNAL_LOGIC_ERROR Headers: \n" - << headers->OriginalHeadersForDebugging(); - return false; - } - - // whitespace_1_idx_ - headers->whitespace_1_idx_ = current - begin; - // This loop is commented out as it is never used in current code. This is - // true only because we don't begin parsing the headers at all until we've - // encountered a non whitespace character at the beginning of the stream, at - // which point we begin our demarcation of header-start. If we did -not- do - // this (for instance, only looked for [\r\n] instead of (< ' ')), this loop - // would be necessary for the proper functioning of this parsing. - // This is left here as this function may (in the future) be refactored out - // of the BalsaFrame class so that it may be shared between code in - // BalsaFrame and BalsaHeaders (where it would be used in some variant of the - // set_first_line() function (at which point it would be necessary). -#if 0 - while (*current <= ' ') { - ++current; - } -#endif - // non_whitespace_1_idx_ - headers->non_whitespace_1_idx_ = current - begin; - do { - // The first time through, we're guaranteed that the current character - // won't be a whitespace (else the loop above wouldn't have terminated). - // That implies that we're guaranteed to get at least one non-whitespace - // character if we get into this loop at all. - ++current; - if (current == end) { - headers->whitespace_2_idx_ = current - begin; - headers->non_whitespace_2_idx_ = current - begin; - headers->whitespace_3_idx_ = current - begin; - headers->non_whitespace_3_idx_ = current - begin; - headers->whitespace_4_idx_ = current - begin; - // FAILED_TO_FIND_WS_AFTER_REQUEST_METHOD for request - // FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION for response - *error_code = - static_cast<BalsaFrameEnums::ErrorCode>( - BalsaFrameEnums::FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION + - is_request); - if (!is_request) { // FAILED_TO_FIND_WS_AFTER_RESPONSE_VERSION - return false; - } - goto output_exhausted; - } - } while (*current > ' '); - // whitespace_2_idx_ - headers->whitespace_2_idx_ = current - begin; - do { - ++current; - // Note that due to the loop which consumes all of the whitespace - // at the end of the line, current can never == end while in this function. - } while (*current <= ' '); - // non_whitespace_2_idx_ - headers->non_whitespace_2_idx_ = current - begin; - do { - ++current; - if (current == end) { - headers->whitespace_3_idx_ = current - begin; - headers->non_whitespace_3_idx_ = current - begin; - headers->whitespace_4_idx_ = current - begin; - // FAILED_TO_FIND_START_OF_REQUEST_REQUEST_URI for request - // FAILED_TO_FIND_START_OF_RESPONSE_STATUSCODE for response - *error_code = - static_cast<BalsaFrameEnums::ErrorCode>( - BalsaFrameEnums::FAILED_TO_FIND_WS_AFTER_RESPONSE_STATUSCODE - + is_request); - goto output_exhausted; - } - } while (*current > ' '); - // whitespace_3_idx_ - headers->whitespace_3_idx_ = current - begin; - do { - ++current; - // Note that due to the loop which consumes all of the whitespace - // at the end of the line, current can never == end while in this function. - } while (*current <= ' '); - // non_whitespace_3_idx_ - headers->non_whitespace_3_idx_ = current - begin; - headers->whitespace_4_idx_ = end - begin; - - output_exhausted: - // Note that we don't fail the parse immediately when parsing of the - // firstline fails. Depending on the protocol type, we may want to accept - // a firstline with only one or two elements, e.g., for HTTP/0.9: - // GET\r\n - // or - // GET /\r\n - // should be parsed without issue (though the visitor should know that - // parsing the entire line was not exactly as it should be). - // - // Eventually, these errors may be removed alltogether, as the visitor can - // detect them on its own by examining the size of the various fields. - // headers->set_first_line(non_whitespace_1_idx_, current); - - if (is_request) { - if ((headers->whitespace_3_idx_ - headers->non_whitespace_2_idx_) > - max_request_uri_length) { - // For requests, we need at least the method. We could assume that a - // blank URI means "/". If version isn't stated, it should be assumed - // to be HTTP/0.9 by the visitor. - *error_code = BalsaFrameEnums::REQUEST_URI_TOO_LONG; - return false; - } - } else { - headers->parsed_response_code_ = 0; - { - const char* parsed_response_code_current = - begin + headers->non_whitespace_2_idx_; - const char* parsed_response_code_end = begin + headers->whitespace_3_idx_; - const size_t kMaxDiv10 = std::numeric_limits<size_t>::max() / 10; - - // Convert a string of [0-9]* into an int. - // Note that this allows for the conversion of response codes which - // are outside the bounds of normal HTTP response codes (no checking - // is done to ensure that these are valid-- they're merely parsed)! - while (parsed_response_code_current < parsed_response_code_end) { - if (*parsed_response_code_current < '0' || - *parsed_response_code_current > '9') { - *error_code = BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT; - return false; - } - size_t status_code_x_10 = headers->parsed_response_code_ * 10; - uint8 c = *parsed_response_code_current - '0'; - if ((headers->parsed_response_code_ > kMaxDiv10) || - (std::numeric_limits<size_t>::max() - status_code_x_10) < c) { - // overflow. - *error_code = BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT; - return false; - } - headers->parsed_response_code_ = status_code_x_10 + c; - ++parsed_response_code_current; - } - } - } - return true; -} - -// begin - beginning of the firstline -// end - end of the firstline -// -// A precondition for this function is that there is non-whitespace between -// [begin, end). If this precondition is not met, the function will not perform -// as expected (and bad things may happen, and it will eat your first, second, -// and third unborn children!). -// -// Another precondition for this function is that [begin, end) includes -// at most one newline, which must be at the end of the line. -void BalsaFrame::ProcessFirstLine(const char* begin, const char* end) { - BalsaFrameEnums::ErrorCode previous_error = last_error_; - if (!ParseHTTPFirstLine(begin, - end, - is_request_, - max_request_uri_length_, - headers_, - &last_error_)) { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleHeaderError(this); - return; - } - if (previous_error != last_error_) { - visitor_->HandleHeaderWarning(this); - } - - if (is_request_) { - int version_length = - headers_->whitespace_4_idx_ - headers_->non_whitespace_3_idx_; - visitor_->ProcessRequestFirstLine( - begin + headers_->non_whitespace_1_idx_, - headers_->whitespace_4_idx_ - headers_->non_whitespace_1_idx_, - begin + headers_->non_whitespace_1_idx_, - headers_->whitespace_2_idx_ - headers_->non_whitespace_1_idx_, - begin + headers_->non_whitespace_2_idx_, - headers_->whitespace_3_idx_ - headers_->non_whitespace_2_idx_, - begin + headers_->non_whitespace_3_idx_, - version_length); - if (version_length == 0) - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - } else { - visitor_->ProcessResponseFirstLine( - begin + headers_->non_whitespace_1_idx_, - headers_->whitespace_4_idx_ - headers_->non_whitespace_1_idx_, - begin + headers_->non_whitespace_1_idx_, - headers_->whitespace_2_idx_ - headers_->non_whitespace_1_idx_, - begin + headers_->non_whitespace_2_idx_, - headers_->whitespace_3_idx_ - headers_->non_whitespace_2_idx_, - begin + headers_->non_whitespace_3_idx_, - headers_->whitespace_4_idx_ - headers_->non_whitespace_3_idx_); - } -} - -// 'stream_begin' points to the first character of the headers buffer. -// 'line_begin' points to the first character of the line. -// 'current' points to a char which is ':'. -// 'line_end' points to the position of '\n' + 1. -// 'line_begin' points to the position of first character of line. -void BalsaFrame::CleanUpKeyValueWhitespace( - const char* stream_begin, - const char* line_begin, - const char* current, - const char* line_end, - HeaderLineDescription* current_header_line) { - const char* colon_loc = current; - DCHECK_LT(colon_loc, line_end); - DCHECK_EQ(':', *colon_loc); - DCHECK_EQ(':', *current); - DCHECK_GE(' ', *line_end) - << "\"" << std::string(line_begin, line_end) << "\""; - - // TODO(fenix): Investigate whether or not the bounds tests in the - // while loops here are redundant, and if so, remove them. - --current; - while (current > line_begin && *current <= ' ') --current; - current += (current != colon_loc); - current_header_line->key_end_idx = current - stream_begin; - - current = colon_loc; - DCHECK_EQ(':', *current); - ++current; - while (current < line_end && *current <= ' ') ++current; - current_header_line->value_begin_idx = current - stream_begin; - - DCHECK_GE(current_header_line->key_end_idx, - current_header_line->first_char_idx); - DCHECK_GE(current_header_line->value_begin_idx, - current_header_line->key_end_idx); - DCHECK_GE(current_header_line->last_char_idx, - current_header_line->value_begin_idx); -} - -inline void BalsaFrame::FindColonsAndParseIntoKeyValue() { - DCHECK(!lines_.empty()); - const char* stream_begin = headers_->OriginalHeaderStreamBegin(); - // The last line is always just a newline (and is uninteresting). - const Lines::size_type lines_size_m1 = lines_.size() - 1; -#if __SSE2__ - const __v16qi colons = { ':', ':', ':', ':', ':', ':', ':', ':', - ':', ':', ':', ':', ':', ':', ':', ':'}; - const char* header_lines_end_m16 = headers_->OriginalHeaderStreamEnd() - 16; -#endif // __SSE2__ - const char* current = stream_begin + lines_[1].first; - // This code is a bit more subtle than it may appear at first glance. - // This code looks for a colon in the current line... but it also looks - // beyond the current line. If there is no colon in the current line, then - // for each subsequent line (until the colon which -has- been found is - // associated with a line), no searching for a colon will be performed. In - // this way, we minimize the amount of bytes we have scanned for a colon. - for (Lines::size_type i = 1; i < lines_size_m1;) { - const char* line_begin = stream_begin + lines_[i].first; - - // Here we handle possible continuations. Note that we do not replace - // the '\n' in the line before a continuation (at least, as of now), - // which implies that any code which looks for a value must deal with - // "\r\n", etc -within- the line (and not just at the end of it). - for (++i; i < lines_size_m1; ++i) { - const char c = *(stream_begin + lines_[i].first); - if (c > ' ') { - // Not a continuation, so stop. Note that if the 'original' i = 1, - // and the next line is not a continuation, we'll end up with i = 2 - // when we break. This handles the incrementing of i for the outer - // loop. - break; - } - } - const char* line_end = stream_begin + lines_[i - 1].second; - DCHECK_LT(line_begin - stream_begin, line_end - stream_begin); - - // We cleanup the whitespace at the end of the line before doing anything - // else of interest as it allows us to do nothing when irregularly formatted - // headers are parsed (e.g. those with only keys, only values, or no colon). - // - // We're guaranteed to have *line_end > ' ' while line_end >= line_begin. - --line_end; - DCHECK_EQ('\n', *line_end) - << "\"" << std::string(line_begin, line_end) << "\""; - while (*line_end <= ' ' && line_end > line_begin) { - --line_end; - } - ++line_end; - DCHECK_GE(' ', *line_end); - DCHECK_LT(line_begin, line_end); - - // We use '0' for the block idx, because we're always writing to the first - // block from the framer (we do this because the framer requires that the - // entire header sequence be in a contiguous buffer). - headers_->header_lines_.push_back( - HeaderLineDescription(line_begin - stream_begin, - line_end - stream_begin, - line_end - stream_begin, - line_end - stream_begin, - 0)); - if (current >= line_end) { - last_error_ = BalsaFrameEnums::HEADER_MISSING_COLON; - visitor_->HandleHeaderWarning(this); - // Then the next colon will not be found within this header line-- time - // to try again with another header-line. - continue; - } else if (current < line_begin) { - // When this condition is true, the last detected colon was part of a - // previous line. We reset to the beginning of the line as we don't care - // about the presence of any colon before the beginning of the current - // line. - current = line_begin; - } -#if __SSE2__ - while (current < header_lines_end_m16) { - __m128i header_bytes = - _mm_loadu_si128(reinterpret_cast<const __m128i *>(current)); - __m128i colon_cmp = - _mm_cmpeq_epi8(header_bytes, reinterpret_cast<__m128i>(colons)); - int colon_msk = _mm_movemask_epi8(colon_cmp); - if (colon_msk == 0) { - current += 16; - continue; - } - current += (ffs(colon_msk) - 1); - if (current > line_end) { - break; - } - goto found_colon; - } -#endif // __SSE2__ - for (; current < line_end; ++current) { - if (*current != ':') { - continue; - } - goto found_colon; - } - // If we've gotten to here, then there was no colon - // in the line. The arguments we passed into the construction - // for the HeaderLineDescription object should be OK-- it assumes - // that the entire content is 'key' by default (which is true, as - // there was no colon, there can be no value). Note that this is a - // construct which is technically not allowed by the spec. - last_error_ = BalsaFrameEnums::HEADER_MISSING_COLON; - visitor_->HandleHeaderWarning(this); - continue; - found_colon: - DCHECK_EQ(*current, ':'); - DCHECK_LE(current - stream_begin, line_end - stream_begin); - DCHECK_LE(stream_begin - stream_begin, current - stream_begin); - - HeaderLineDescription& current_header_line = headers_->header_lines_.back(); - current_header_line.key_end_idx = current - stream_begin; - current_header_line.value_begin_idx = current_header_line.key_end_idx; - if (current < line_end) { - ++current_header_line.key_end_idx; - - CleanUpKeyValueWhitespace(stream_begin, - line_begin, - current, - line_end, - ¤t_header_line); - } - } -} - -void BalsaFrame::ProcessContentLengthLine( - HeaderLines::size_type line_idx, - BalsaHeadersEnums::ContentLengthStatus* status, - size_t* length) { - const HeaderLineDescription& header_line = headers_->header_lines_[line_idx]; - const char* stream_begin = headers_->OriginalHeaderStreamBegin(); - const char* line_end = stream_begin + header_line.last_char_idx; - const char* value_begin = (stream_begin + header_line.value_begin_idx); - - if (value_begin >= line_end) { - // There is no non-whitespace value data. -#if DEBUGFRAMER - LOG(INFO) << "invalid content-length -- no non-whitespace value data"; -#endif - *status = BalsaHeadersEnums::INVALID_CONTENT_LENGTH; - return; - } - - *length = 0; - while (value_begin < line_end) { - if (*value_begin < '0' || *value_begin > '9') { - // bad! content-length found, and couldn't parse all of it! - *status = BalsaHeadersEnums::INVALID_CONTENT_LENGTH; -#if DEBUGFRAMER - LOG(INFO) << "invalid content-length - non numeric character detected"; -#endif // DEBUGFRAMER - return; - } - const size_t kMaxDiv10 = std::numeric_limits<size_t>::max() / 10; - size_t length_x_10 = *length * 10; - const unsigned char c = *value_begin - '0'; - if (*length > kMaxDiv10 || - (std::numeric_limits<size_t>::max() - length_x_10) < c) { - *status = BalsaHeadersEnums::CONTENT_LENGTH_OVERFLOW; -#if DEBUGFRAMER - LOG(INFO) << "content-length overflow"; -#endif // DEBUGFRAMER - return; - } - *length = length_x_10 + c; - ++value_begin; - } -#if DEBUGFRAMER - LOG(INFO) << "content_length parsed: " << *length; -#endif // DEBUGFRAMER - *status = BalsaHeadersEnums::VALID_CONTENT_LENGTH; -} - -void BalsaFrame::ProcessTransferEncodingLine(HeaderLines::size_type line_idx) { - const HeaderLineDescription& header_line = headers_->header_lines_[line_idx]; - const char* stream_begin = headers_->OriginalHeaderStreamBegin(); - const char* line_end = stream_begin + header_line.last_char_idx; - const char* value_begin = stream_begin + header_line.value_begin_idx; - size_t value_length = line_end - value_begin; - - if ((value_length == 7) && - !strncasecmp(value_begin, "chunked", 7)) { - headers_->transfer_encoding_is_chunked_ = true; - } else if ((value_length == 8) && - !strncasecmp(value_begin, "identity", 8)) { - headers_->transfer_encoding_is_chunked_ = false; - } else { - last_error_ = BalsaFrameEnums::UNKNOWN_TRANSFER_ENCODING; - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleHeaderError(this); - return; - } -} - -namespace { -bool SplitStringPiece(base::StringPiece original, char delim, - base::StringPiece* before, base::StringPiece* after) { - const char* p = original.data(); - const char* end = p + original.size(); - - while (p != end) { - if (*p == delim) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != delim) { - // Skip to the next occurence of the delimiter. - } - *before = base::StringPiece(start, p - start); - if (p != end) - *after = base::StringPiece(p + 1, end - (p + 1)); - else - *after = base::StringPiece(""); - StringPieceUtils::RemoveWhitespaceContext(before); - StringPieceUtils::RemoveWhitespaceContext(after); - return true; - } - } - - *before = original; - *after = ""; - return false; -} - -// TODO(phython): Fix this function to properly deal with quoted values. -// E.g. ";;foo", "\";;\"", or \"aa; -// The last example, the semi-colon is a separator between extensions. -void ProcessChunkExtensionsManual(base::StringPiece all_extensions, - BalsaHeaders* extensions) { - base::StringPiece extension; - base::StringPiece remaining; - StringPieceUtils::RemoveWhitespaceContext(&all_extensions); - SplitStringPiece(all_extensions, ';', &extension, &remaining); - while (!extension.empty()) { - base::StringPiece key; - base::StringPiece value; - SplitStringPiece(extension, '=', &key, &value); - if (!value.empty()) { - // Strip quotation marks if they exist. - if (!value.empty() && value[0] == '"') - value.remove_prefix(1); - if (!value.empty() && value[value.length() - 1] == '"') - value.remove_suffix(1); - } - - extensions->AppendHeader(key, value); - - StringPieceUtils::RemoveWhitespaceContext(&remaining); - SplitStringPiece(remaining, ';', &extension, &remaining); - } -} - -// TODO(phython): Fix this function to properly deal with quoted values. -// E.g. ";;foo", "\";;\"", or \"aa; -// The last example, the semi-colon is a separator between extensions. -void ProcessChunkExtensionsGoogle3(const char* input, size_t size, - BalsaHeaders* extensions) { - std::vector<base::StringPiece> key_values; - SplitStringPieceToVector(base::StringPiece(input, size), ";", - &key_values, true); - for (unsigned int i = 0; i < key_values.size(); ++i) { - base::StringPiece key = key_values[i].substr(0, key_values[i].find('=')); - base::StringPiece value; - if (key.length() < key_values[i].length()) { - value = key_values[i].substr(key.length() + 1); - // Remove any leading and trailing whitespace. - StringPieceUtils::RemoveWhitespaceContext(&value); - - // Strip quotation marks if they exist. - if (!value.empty() && value[0] == '"') - value.remove_prefix(1); - if (!value.empty() && value[value.length() - 1] == '"') - value.remove_suffix(1); - } - - // Strip the key whitespace after checking that there is a value. - StringPieceUtils::RemoveWhitespaceContext(&key); - extensions->AppendHeader(key, value); - } -} - -} // anonymous namespace - -void BalsaFrame::ProcessChunkExtensions(const char* input, size_t size, - BalsaHeaders* extensions) { -#if 0 - ProcessChunkExtensionsGoogle3(input, size, extensions); -#else - ProcessChunkExtensionsManual(base::StringPiece(input, size), extensions); -#endif -} - -void BalsaFrame::ProcessHeaderLines() { - HeaderLines::size_type content_length_idx = 0; - HeaderLines::size_type transfer_encoding_idx = 0; - - DCHECK(!lines_.empty()); -#if DEBUGFRAMER - LOG(INFO) << "******@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@**********\n"; -#endif // DEBUGFRAMER - - // There is no need to attempt to process headers if no header lines exist. - // There are at least two lines in the message which are not header lines. - // These two non-header lines are the first line of the message, and the - // last line of the message (which is an empty line). - // Thus, we test to see if we have more than two lines total before attempting - // to parse any header lines. - if (lines_.size() > 2) { - const char* stream_begin = headers_->OriginalHeaderStreamBegin(); - - // Then, for the rest of the header data, we parse these into key-value - // pairs. - FindColonsAndParseIntoKeyValue(); - // At this point, we've parsed all of the headers. Time to look for those - // headers which we require for framing. - const HeaderLines::size_type - header_lines_size = headers_->header_lines_.size(); - for (HeaderLines::size_type i = 0; i < header_lines_size; ++i) { - const HeaderLineDescription& current_header_line = - headers_->header_lines_[i]; - const char* key_begin = - (stream_begin + current_header_line.first_char_idx); - const char* key_end = (stream_begin + current_header_line.key_end_idx); - const size_t key_len = key_end - key_begin; - const char c = *key_begin; -#if DEBUGFRAMER - LOG(INFO) << "[" << i << "]: " << std::string(key_begin, key_len) - << " c: '" << c << "' key_len: " << key_len; -#endif // DEBUGFRAMER - // If a header begins with either lowercase or uppercase 'c' or 't', then - // the header may be one of content-length, connection, content-encoding - // or transfer-encoding. These headers are special, as they change the way - // that the message is framed, and so the framer is required to search - // for them. - - - if (c == 'c' || c == 'C') { - if ((key_len == kContentLengthSize) && - 0 == strncasecmp(key_begin, kContentLength, kContentLengthSize)) { - BalsaHeadersEnums::ContentLengthStatus content_length_status = - BalsaHeadersEnums::NO_CONTENT_LENGTH; - size_t length = 0; - ProcessContentLengthLine(i, &content_length_status, &length); - if (content_length_idx != 0) { // then we've already seen one! - if ((headers_->content_length_status_ != content_length_status) || - ((headers_->content_length_status_ == - BalsaHeadersEnums::VALID_CONTENT_LENGTH) && - length != headers_->content_length_)) { - last_error_ = BalsaFrameEnums::MULTIPLE_CONTENT_LENGTH_KEYS; - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleHeaderError(this); - return; - } - continue; - } else { - content_length_idx = i + 1; - headers_->content_length_status_ = content_length_status; - headers_->content_length_ = length; - content_length_remaining_ = length; - } - - } - } else if (c == 't' || c == 'T') { - if ((key_len == kTransferEncodingSize) && - 0 == strncasecmp(key_begin, kTransferEncoding, - kTransferEncodingSize)) { - if (transfer_encoding_idx != 0) { - last_error_ = BalsaFrameEnums::MULTIPLE_TRANSFER_ENCODING_KEYS; - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleHeaderError(this); - return; - } - transfer_encoding_idx = i + 1; - } - } else if (i == 0 && (key_len == 0 || c == ' ')) { - last_error_ = BalsaFrameEnums::INVALID_HEADER_FORMAT; - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleHeaderError(this); - return; - } - } - if (headers_->transfer_encoding_is_chunked_) { - headers_->content_length_ = 0; - headers_->content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; - content_length_remaining_ = 0; - } - if (transfer_encoding_idx != 0) { - ProcessTransferEncodingLine(transfer_encoding_idx - 1); - } - } -} - -void BalsaFrame::AssignParseStateAfterHeadersHaveBeenParsed() { - // For responses, can't have a body if the request was a HEAD, or if it is - // one of these response-codes. rfc2616 section 4.3 - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - if (is_request_ || - !(request_was_head_ || - (headers_->parsed_response_code_ >= 100 && - headers_->parsed_response_code_ < 200) || - (headers_->parsed_response_code_ == 204) || - (headers_->parsed_response_code_ == 304))) { - // Then we can have a body. - if (headers_->transfer_encoding_is_chunked_) { - // Note that - // if ( Transfer-Encoding: chunked && Content-length: ) - // then Transfer-Encoding: chunked trumps. - // This is as specified in the spec. - // rfc2616 section 4.4.3 - parse_state_ = BalsaFrameEnums::READING_CHUNK_LENGTH; - } else { - // Errors parsing content-length definitely can cause - // protocol errors/warnings - switch (headers_->content_length_status_) { - // If we have a content-length, and it is parsed - // properly, there are two options. - // 1) zero content, in which case the message is done, and - // 2) nonzero content, in which case we have to - // consume the body. - case BalsaHeadersEnums::VALID_CONTENT_LENGTH: - if (headers_->content_length_ == 0) { - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - } else { - parse_state_ = BalsaFrameEnums::READING_CONTENT; - } - break; - case BalsaHeadersEnums::CONTENT_LENGTH_OVERFLOW: - case BalsaHeadersEnums::INVALID_CONTENT_LENGTH: - // If there were characters left-over after parsing the - // content length, we should flag an error and stop. - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::UNPARSABLE_CONTENT_LENGTH; - visitor_->HandleHeaderError(this); - break; - // We can have: no transfer-encoding, no content length, and no - // connection: close... - // Unfortunately, this case doesn't seem to be covered in the spec. - // We'll assume that the safest thing to do here is what the google - // binaries before 2008 already do, which is to assume that - // everything until the connection is closed is body. - case BalsaHeadersEnums::NO_CONTENT_LENGTH: - if (is_request_) { - base::StringPiece method = headers_->request_method(); - // POSTs and PUTs should have a detectable body length. If they - // do not we consider it an error. - if ((method.size() == 4 && - strncmp(method.data(), "POST", 4) == 0) || - (method.size() == 3 && - strncmp(method.data(), "PUT", 3) == 0)) { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = - BalsaFrameEnums::REQUIRED_BODY_BUT_NO_CONTENT_LENGTH; - visitor_->HandleHeaderError(this); - break; - } - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - } else { - parse_state_ = BalsaFrameEnums::READING_UNTIL_CLOSE; - last_error_ = BalsaFrameEnums::MAYBE_BODY_BUT_NO_CONTENT_LENGTH; - visitor_->HandleHeaderWarning(this); - } - break; - // The COV_NF_... statements here provide hints to the apparatus - // which computes coverage reports/ratios that this code is never - // intended to be executed, and should technically be impossible. - // COV_NF_START - default: - LOG(FATAL) << "Saw a content_length_status: " - << headers_->content_length_status_ << " which is unknown."; - // COV_NF_END - } - } - } -} - -size_t BalsaFrame::ProcessHeaders(const char* message_start, - size_t message_length) { - const char* const original_message_start = message_start; - const char* const message_end = message_start + message_length; - const char* message_current = message_start; - const char* checkpoint = message_start; - - if (message_length == 0) { - goto bottom; - } - - while (message_current < message_end) { - size_t base_idx = headers_->GetReadableBytesFromHeaderStream(); - - // Yes, we could use strchr (assuming null termination), or - // memchr, but as it turns out that is slower than this tight loop - // for the input that we see. - if (!saw_non_newline_char_) { - do { - const char c = *message_current; - if (c != '\r' && c != '\n') { - if (c <= ' ') { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::NO_REQUEST_LINE_IN_REQUEST; - visitor_->HandleHeaderError(this); - goto bottom; - } else { - saw_non_newline_char_ = true; - checkpoint = message_start = message_current; - goto read_real_message; - } - } - ++message_current; - } while (message_current < message_end); - goto bottom; // this is necessary to skip 'last_char_was_slash_r' checks - } else { - read_real_message: - // Note that SSE2 can be enabled on certain piii platforms. -#if __SSE2__ - { - const char* const message_end_m16 = message_end - 16; - __v16qi newlines = { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n', - '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' }; - while (message_current < message_end_m16) { - // What this does (using compiler intrinsics): - // - // Load 16 '\n's into an xmm register - // Load 16 bytes of currennt message into an xmm register - // Do byte-wise equals on those two xmm registers - // Take the first bit of each byte, and put that into the first - // 16 bits of a mask - // If the mask is zero, no '\n' found. increment by 16 and try again - // Else scan forward to find the first set bit. - // Increment current by the index of the first set bit - // (ffs returns index of first set bit + 1) - __m128i msg_bytes = - _mm_loadu_si128(const_cast<__m128i *>( - reinterpret_cast<const __m128i *>(message_current))); - __m128i newline_cmp = - _mm_cmpeq_epi8(msg_bytes, reinterpret_cast<__m128i>(newlines)); - int newline_msk = _mm_movemask_epi8(newline_cmp); - if (newline_msk == 0) { - message_current += 16; - continue; - } - message_current += (ffs(newline_msk) - 1); - const size_t relative_idx = message_current - message_start; - const size_t message_current_idx = 1 + base_idx + relative_idx; - lines_.push_back(std::make_pair(last_slash_n_idx_, - message_current_idx)); - if (lines_.size() == 1) { - headers_->WriteFromFramer(checkpoint, - 1 + message_current - checkpoint); - checkpoint = message_current + 1; - const char* begin = headers_->OriginalHeaderStreamBegin(); -#if DEBUGFRAMER - LOG(INFO) << "First line " << std::string(begin, lines_[0].second); - LOG(INFO) << "is_request_: " << is_request_; -#endif - ProcessFirstLine(begin, begin + lines_[0].second); - if (parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ) - goto process_lines; - else if (parse_state_ == BalsaFrameEnums::PARSE_ERROR) - goto bottom; - } - const size_t chars_since_last_slash_n = (message_current_idx - - last_slash_n_idx_); - last_slash_n_idx_ = message_current_idx; - if (chars_since_last_slash_n > 2) { - // We have a slash-n, but the last slash n was - // more than 2 characters away from this. Thus, we know - // that this cannot be an end-of-header. - ++message_current; - continue; - } - if ((chars_since_last_slash_n == 1) || - (((message_current > message_start) && - (*(message_current - 1) == '\r')) || - (last_char_was_slash_r_))) { - goto process_lines; - } - ++message_current; - } - } -#endif // __SSE2__ - while (message_current < message_end) { - if (*message_current != '\n') { - ++message_current; - continue; - } - const size_t relative_idx = message_current - message_start; - const size_t message_current_idx = 1 + base_idx + relative_idx; - lines_.push_back(std::make_pair(last_slash_n_idx_, - message_current_idx)); - if (lines_.size() == 1) { - headers_->WriteFromFramer(checkpoint, - 1 + message_current - checkpoint); - checkpoint = message_current + 1; - const char* begin = headers_->OriginalHeaderStreamBegin(); -#if DEBUGFRAMER - LOG(INFO) << "First line " << std::string(begin, lines_[0].second); - LOG(INFO) << "is_request_: " << is_request_; -#endif - ProcessFirstLine(begin, begin + lines_[0].second); - if (parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ) - goto process_lines; - else if (parse_state_ == BalsaFrameEnums::PARSE_ERROR) - goto bottom; - } - const size_t chars_since_last_slash_n = (message_current_idx - - last_slash_n_idx_); - last_slash_n_idx_ = message_current_idx; - if (chars_since_last_slash_n > 2) { - // false positive. - ++message_current; - continue; - } - if ((chars_since_last_slash_n == 1) || - (((message_current > message_start) && - (*(message_current - 1) == '\r')) || - (last_char_was_slash_r_))) { - goto process_lines; - } - ++message_current; - } - } - continue; - process_lines: - ++message_current; - DCHECK(message_current >= message_start); - if (message_current > message_start) { - headers_->WriteFromFramer(checkpoint, message_current - checkpoint); - } - - // Check if we have exceeded maximum headers length - // Although we check for this limit before and after we call this function - // we check it here as well to make sure that in case the visitor changed - // the max_header_length_ (for example after processing the first line) - // we handle it gracefully. - if (headers_->GetReadableBytesFromHeaderStream() > max_header_length_) { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::HEADERS_TOO_LONG; - visitor_->HandleHeaderError(this); - goto bottom; - } - - // Since we know that we won't be writing any more bytes of the header, - // we tell that to the headers object. The headers object may make - // more efficient allocation decisions when this is signaled. - headers_->DoneWritingFromFramer(); - { - const char* readable_ptr = NULL; - size_t readable_size = 0; - headers_->GetReadablePtrFromHeaderStream(&readable_ptr, &readable_size); - visitor_->ProcessHeaderInput(readable_ptr, readable_size); - } - - // Ok, now that we've written everything into our header buffer, it is - // time to process the header lines (extract proper values for headers - // which are important for framing). - ProcessHeaderLines(); - if (parse_state_ == BalsaFrameEnums::PARSE_ERROR) { - goto bottom; - } - AssignParseStateAfterHeadersHaveBeenParsed(); - if (parse_state_ == BalsaFrameEnums::PARSE_ERROR) { - goto bottom; - } - visitor_->ProcessHeaders(*headers_); - visitor_->HeaderDone(); - if (parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ) { - visitor_->MessageDone(); - } - goto bottom; - } - // If we've gotten to here, it means that we've consumed all of the - // available input. We need to record whether or not the last character we - // saw was a '\r' so that a subsequent call to ProcessInput correctly finds - // a header framing that is split across the two calls. - last_char_was_slash_r_ = (*(message_end - 1) == '\r'); - DCHECK(message_current >= message_start); - if (message_current > message_start) { - headers_->WriteFromFramer(checkpoint, message_current - checkpoint); - } - bottom: - return message_current - original_message_start; -} - - -size_t BalsaFrame::BytesSafeToSplice() const { - switch (parse_state_) { - case BalsaFrameEnums::READING_CHUNK_DATA: - return chunk_length_remaining_; - case BalsaFrameEnums::READING_UNTIL_CLOSE: - return std::numeric_limits<size_t>::max(); - case BalsaFrameEnums::READING_CONTENT: - return content_length_remaining_; - default: - return 0; - } -} - -void BalsaFrame::BytesSpliced(size_t bytes_spliced) { - switch (parse_state_) { - case BalsaFrameEnums::READING_CHUNK_DATA: - if (chunk_length_remaining_ >= bytes_spliced) { - chunk_length_remaining_ -= bytes_spliced; - if (chunk_length_remaining_ == 0) { - parse_state_ = BalsaFrameEnums::READING_CHUNK_TERM; - } - return; - } else { - last_error_ = - BalsaFrameEnums::CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT; - goto error_exit; - } - - case BalsaFrameEnums::READING_UNTIL_CLOSE: - return; - - case BalsaFrameEnums::READING_CONTENT: - if (content_length_remaining_ >= bytes_spliced) { - content_length_remaining_ -= bytes_spliced; - if (content_length_remaining_ == 0) { - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - visitor_->MessageDone(); - } - return; - } else { - last_error_ = - BalsaFrameEnums::CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT; - goto error_exit; - } - - default: - last_error_ = BalsaFrameEnums::CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO; - goto error_exit; - } - - error_exit: - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - visitor_->HandleBodyError(this); -}; - -// You may note that the state-machine contained within this function has both -// switch and goto labels for nearly the same thing. For instance, the -// following two labels refer to the same code block: -// label_reading_chunk_data: -// case BalsaFrameEnums::READING_CHUNK_DATA: -// The 'case' statement is required for the switch statement which occurs when -// ProcessInput is invoked. The goto label is required as the state-machine -// does not use a computed goto in any subsequent operations. -// -// Since several states exit the state machine for various reasons, there is -// also one label at the bottom of the function. When it is appropriate to -// return from the function, that part of the state machine instead issues a -// goto bottom; This results in less code duplication, and makes debugging -// easier (as you can add a statement to a section of code which is guaranteed -// to be invoked when the function is exiting. -size_t BalsaFrame::ProcessInput(const char* input, size_t size) { - const char* current = input; - const char* on_entry = current; - const char* end = current + size; -#if DEBUGFRAMER - LOG(INFO) << "\n==============" - << BalsaFrameEnums::ParseStateToString(parse_state_) - << "===============\n"; -#endif // DEBUGFRAMER - - DCHECK(headers_ != NULL); - if (headers_ == NULL) return 0; - - if (parse_state_ == BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE) { - const size_t header_length = headers_->GetReadableBytesFromHeaderStream(); - // Yes, we still have to check this here as the user can change the - // max_header_length amount! - // Also it is possible that we have reached the maximum allowed header size, - // and we have more to consume (remember we are still inside - // READING_HEADER_AND_FIRSTLINE) in which case we directly declare an error. - if (header_length > max_header_length_ || - (header_length == max_header_length_ && size > 0)) { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::HEADERS_TOO_LONG; - visitor_->HandleHeaderError(this); - goto bottom; - } - size_t bytes_to_process = max_header_length_ - header_length; - if (bytes_to_process > size) { - bytes_to_process = size; - } - current += ProcessHeaders(input, bytes_to_process); - // If we are still reading headers check if we have crossed the headers - // limit. Note that we check for >= as opposed to >. This is because if - // header_length_after equals max_header_length_ and we are still in the - // parse_state_ BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE we know for - // sure that the headers limit will be crossed later on - if (parse_state_ == BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE) { - // Note that headers_ is valid only if we are still reading headers. - const size_t header_length_after = - headers_->GetReadableBytesFromHeaderStream(); - if (header_length_after >= max_header_length_) { - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::HEADERS_TOO_LONG; - visitor_->HandleHeaderError(this); - } - } - goto bottom; - } else if (parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ || - parse_state_ == BalsaFrameEnums::PARSE_ERROR) { - // Can do nothing more 'till we're reset. - goto bottom; - } - - while (current < end) { - switch (parse_state_) { - label_reading_chunk_length: - case BalsaFrameEnums::READING_CHUNK_LENGTH: - // In this state we read the chunk length. - // Note that once we hit a character which is not in: - // [0-9;A-Fa-f\n], we transition to a different state. - // - { - // If we used strtol, etc, we'd have to buffer this line. - // This is more annoying than simply doing the conversion - // here. This code accounts for overflow. - static const signed char buf[] = { - // %0 %1 %2 %3 %4 %5 %6 %7 %8 \t \n %b %c \r %e %f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, - // %10 %11 %12 %13 %14 %15 %16 %17 %18 %19 %1a %1b %1c %1d %1e %1f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // ' ' %21 %22 %23 %24 %25 %26 %27 %28 %29 %2a %2b %2c %2d %2e %2f - -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // %30 %31 %32 %33 %34 %35 %36 %37 %38 %39 %3a ';' %3c %3d %3e %3f - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -2, -1, -1, -1, -1, - // %40 'A' 'B' 'C' 'D' 'E' 'F' %47 %48 %49 %4a %4b %4c %4d %4e %4f - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // %50 %51 %52 %53 %54 %55 %56 %57 %58 %59 %5a %5b %5c %5d %5e %5f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // %60 'a' 'b' 'c' 'd' 'e' 'f' %67 %68 %69 %6a %6b %6c %6d %6e %6f - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // %70 %71 %72 %73 %74 %75 %76 %77 %78 %79 %7a %7b %7c %7d %7e %7f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - }; - // valid cases: - // "09123\n" // -> 09123 - // "09123\r\n" // -> 09123 - // "09123 \n" // -> 09123 - // "09123 \r\n" // -> 09123 - // "09123 12312\n" // -> 09123 - // "09123 12312\r\n" // -> 09123 - // "09123; foo=bar\n" // -> 09123 - // "09123; foo=bar\r\n" // -> 09123 - // "FFFFFFFFFFFFFFFF\r\n" // -> FFFFFFFFFFFFFFFF - // "FFFFFFFFFFFFFFFF 22\r\n" // -> FFFFFFFFFFFFFFFF - // invalid cases: - // "[ \t]+[^\n]*\n" - // "FFFFFFFFFFFFFFFFF\r\n" (would overflow) - // "\r\n" - // "\n" - while (current < end) { - const char c = *current; - ++current; - const signed char addition = buf[static_cast<int>(c)]; - if (addition >= 0) { - chunk_length_character_extracted_ = true; - size_t length_x_16 = chunk_length_remaining_ * 16; - const size_t kMaxDiv16 = std::numeric_limits<size_t>::max() / 16; - if ((chunk_length_remaining_ > kMaxDiv16) || - ((std::numeric_limits<size_t>::max() - length_x_16) < - static_cast<size_t>(addition))) { - // overflow -- asked for a chunk-length greater than 2^64 - 1!! - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::CHUNK_LENGTH_OVERFLOW; - visitor_->ProcessBodyInput(on_entry, current - on_entry); - visitor_->HandleChunkingError(this); - goto bottom; - } - chunk_length_remaining_ = length_x_16 + addition; - continue; - } - - if (!chunk_length_character_extracted_ || addition == -1) { - // ^[0-9;A-Fa-f][ \t\n] -- was not matched, either because no - // characters were converted, or an unexpected character was - // seen. - parse_state_ = BalsaFrameEnums::PARSE_ERROR; - last_error_ = BalsaFrameEnums::INVALID_CHUNK_LENGTH; - visitor_->ProcessBodyInput(on_entry, current - on_entry); - visitor_->HandleChunkingError(this); - goto bottom; - } - - --current; - parse_state_ = BalsaFrameEnums::READING_CHUNK_EXTENSION; - visitor_->ProcessChunkLength(chunk_length_remaining_); - goto label_reading_chunk_extension; - } - } - visitor_->ProcessBodyInput(on_entry, current - on_entry); - goto bottom; // case BalsaFrameEnums::READING_CHUNK_LENGTH - - label_reading_chunk_extension: - case BalsaFrameEnums::READING_CHUNK_EXTENSION: - { - // TODO(phython): Convert this scanning to be 16 bytes at a time if - // there is data to be read. - const char* extensions_start = current; - size_t extensions_length = 0; - while (current < end) { - const char c = *current; - if (c == '\r' || c == '\n') { - extensions_length = - (extensions_start == current) ? - 0 : - current - extensions_start - 1; - } - - ++current; - if (c == '\n') { - chunk_length_character_extracted_ = false; - visitor_->ProcessChunkExtensions( - extensions_start, extensions_length); - if (chunk_length_remaining_ != 0) { - parse_state_ = BalsaFrameEnums::READING_CHUNK_DATA; - goto label_reading_chunk_data; - } - HeaderFramingFound('\n'); - parse_state_ = BalsaFrameEnums::READING_LAST_CHUNK_TERM; - goto label_reading_last_chunk_term; - } - } - visitor_->ProcessChunkExtensions( - extensions_start, extensions_length); - } - - visitor_->ProcessBodyInput(on_entry, current - on_entry); - goto bottom; // case BalsaFrameEnums::READING_CHUNK_EXTENSION - - label_reading_chunk_data: - case BalsaFrameEnums::READING_CHUNK_DATA: - while (current < end) { - if (chunk_length_remaining_ == 0) { - break; - } - // read in the chunk - size_t bytes_remaining = end - current; - size_t consumed_bytes = (chunk_length_remaining_ < bytes_remaining) ? - chunk_length_remaining_ : bytes_remaining; - const char* tmp_current = current + consumed_bytes; - visitor_->ProcessBodyInput(on_entry, tmp_current - on_entry); - visitor_->ProcessBodyData(current, consumed_bytes); - on_entry = current = tmp_current; - chunk_length_remaining_ -= consumed_bytes; - } - if (chunk_length_remaining_ == 0) { - parse_state_ = BalsaFrameEnums::READING_CHUNK_TERM; - goto label_reading_chunk_term; - } - visitor_->ProcessBodyInput(on_entry, current - on_entry); - goto bottom; // case BalsaFrameEnums::READING_CHUNK_DATA - - label_reading_chunk_term: - case BalsaFrameEnums::READING_CHUNK_TERM: - while (current < end) { - const char c = *current; - ++current; - - if (c == '\n') { - parse_state_ = BalsaFrameEnums::READING_CHUNK_LENGTH; - goto label_reading_chunk_length; - } - } - visitor_->ProcessBodyInput(on_entry, current - on_entry); - goto bottom; // case BalsaFrameEnums::READING_CHUNK_TERM - - label_reading_last_chunk_term: - case BalsaFrameEnums::READING_LAST_CHUNK_TERM: - while (current < end) { - const char c = *current; - - if (!HeaderFramingFound(c)) { - // If not, however, since the spec only suggests that the - // client SHOULD indicate the presence of trailers, we get to - // *test* that they did or didn't. - // If all of the bytes we've seen since: - // OPTIONAL_WS 0 OPTIONAL_STUFF CRLF - // are either '\r', or '\n', then we can assume that we don't yet - // know if we need to parse headers, or if the next byte will make - // the HeaderFramingFound condition (above) true. - if (HeaderFramingMayBeFound()) { - // If true, then we have seen only characters '\r' or '\n'. - ++current; - - // Lets try again! There is no state change here. - continue; - } else { - // If (!HeaderFramingMayBeFound()), then we know that we must be - // reading the first non CRLF character of a trailer. - parse_state_ = BalsaFrameEnums::READING_TRAILER; - visitor_->ProcessBodyInput(on_entry, current - on_entry); - on_entry = current; - goto label_reading_trailer; - } - } else { - // If we've found a "\r\n\r\n", then the message - // is done. - ++current; - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - visitor_->ProcessBodyInput(on_entry, current - on_entry); - visitor_->MessageDone(); - goto bottom; - } - break; // from while loop - } - visitor_->ProcessBodyInput(on_entry, current - on_entry); - goto bottom; // case BalsaFrameEnums::READING_LAST_CHUNK_TERM - - label_reading_trailer: - case BalsaFrameEnums::READING_TRAILER: - while (current < end) { - const char c = *current; - ++current; - // TODO(fenix): If we ever care about trailers as part of framing, - // deal with them here (see below for part of the 'solution') - // if (LineFramingFound(c)) { - // trailer_lines_.push_back(make_pair(start_of_line_, - // trailer_length_ - 1)); - // start_of_line_ = trailer_length_; - // } - if (HeaderFramingFound(c)) { - // ProcessTrailers(visitor_, &trailers_); - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - visitor_->ProcessTrailerInput(on_entry, current - on_entry); - visitor_->MessageDone(); - goto bottom; - } - } - visitor_->ProcessTrailerInput(on_entry, current - on_entry); - break; // case BalsaFrameEnums::READING_TRAILER - - // Note that there is no label: - // 'label_reading_until_close' - // here. This is because the state-machine exists immediately after - // reading the headers instead of transitioning here (as it would - // do if it was consuming all the data it could, all the time). - case BalsaFrameEnums::READING_UNTIL_CLOSE: - { - const size_t bytes_remaining = end - current; - if (bytes_remaining > 0) { - visitor_->ProcessBodyInput(current, bytes_remaining); - visitor_->ProcessBodyData(current, bytes_remaining); - current += bytes_remaining; - } - } - goto bottom; // case BalsaFrameEnums::READING_UNTIL_CLOSE - - // label_reading_content: - case BalsaFrameEnums::READING_CONTENT: -#if DEBUGFRAMER - LOG(INFO) << "ReadingContent: " << content_length_remaining_; -#endif // DEBUGFRAMER - while (content_length_remaining_ && current < end) { - // read in the content - const size_t bytes_remaining = end - current; - const size_t consumed_bytes = - (content_length_remaining_ < bytes_remaining) ? - content_length_remaining_ : bytes_remaining; - visitor_->ProcessBodyInput(current, consumed_bytes); - visitor_->ProcessBodyData(current, consumed_bytes); - current += consumed_bytes; - content_length_remaining_ -= consumed_bytes; - } - if (content_length_remaining_ == 0) { - parse_state_ = BalsaFrameEnums::MESSAGE_FULLY_READ; - visitor_->MessageDone(); - } - goto bottom; // case BalsaFrameEnums::READING_CONTENT - - default: - // The state-machine should never be in a state that isn't handled - // above. This is a glaring logic error, and we should do something - // drastic to ensure that this gets looked-at and fixed. - LOG(FATAL) << "Unknown state: " << parse_state_ // COV_NF_LINE - << " memory corruption?!"; // COV_NF_LINE - } - } - bottom: -#if DEBUGFRAMER - LOG(INFO) << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n" - << std::string(input, current) - << "\n$$$$$$$$$$$$$$" - << BalsaFrameEnums::ParseStateToString(parse_state_) - << "$$$$$$$$$$$$$$$" - << " consumed: " << (current - input); - if (Error()) { - LOG(INFO) << BalsaFrameEnums::ErrorCodeToString(ErrorCode()); - } -#endif // DEBUGFRAMER - return current - input; -} - -const uint32 BalsaFrame::kValidTerm1; -const uint32 BalsaFrame::kValidTerm1Mask; -const uint32 BalsaFrame::kValidTerm2; -const uint32 BalsaFrame::kValidTerm2Mask; - -} // namespace net diff --git a/net/tools/flip_server/balsa_frame.h b/net/tools/flip_server/balsa_frame.h deleted file mode 100644 index 0d3b372..0000000 --- a/net/tools/flip_server/balsa_frame.h +++ /dev/null @@ -1,265 +0,0 @@ -// 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. - -#ifndef NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_ -#define NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_ - -#include <strings.h> - -#include <utility> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/port.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" -#include "net/tools/flip_server/buffer_interface.h" -#include "net/tools/flip_server/http_message_constants.h" -#include "net/tools/flip_server/simple_buffer.h" - -// For additional debug output, uncomment the following: -// #define DEBUGFRAMER 1 - -namespace net { - -// BalsaFrame is a 'Model' of a framer (haha). -// It exists as a proof of concept headers framer. -class BalsaFrame { - public: - typedef std::vector<std::pair<size_t, size_t> > Lines; - - typedef BalsaHeaders::HeaderLineDescription HeaderLineDescription; - typedef BalsaHeaders::HeaderLines HeaderLines; - typedef BalsaHeaders::HeaderTokenList HeaderTokenList; - - // TODO(fenix): get rid of the 'kValidTerm*' stuff by using the 'since last - // index' strategy. Note that this implies getting rid of the HeaderFramed() - - static const uint32 kValidTerm1 = '\n' << 16 | - '\r' << 8 | - '\n'; - static const uint32 kValidTerm1Mask = 0xFF << 16 | - 0xFF << 8 | - 0xFF; - static const uint32 kValidTerm2 = '\n' << 8 | - '\n'; - static const uint32 kValidTerm2Mask = 0xFF << 8 | - 0xFF; - BalsaFrame(); - ~BalsaFrame(); - - // Reset reinitializes all the member variables of the framer and clears the - // attached header object (but doesn't change the pointer value headers_). - void Reset(); - - const BalsaHeaders* const_balsa_headers() const { return headers_; } - BalsaHeaders* balsa_headers() { return headers_; } - // The method set_balsa_headers clears the headers provided and attaches them - // to the framer. This is a required step before the framer will process any - // input message data. - // To detach the header object from the framer, use set_balsa_headers(NULL). - void set_balsa_headers(BalsaHeaders* headers) { - if (headers_ != headers) { - headers_ = headers; - } - if (headers_) { - // Clear the headers if they are non-null, even if the new headers are - // the same as the old. - headers_->Clear(); - } - } - - void set_balsa_visitor(BalsaVisitorInterface* visitor) { - visitor_ = visitor; - if (visitor_ == NULL) { - visitor_ = &do_nothing_visitor_; - } - } - - void set_is_request(bool is_request) { is_request_ = is_request; } - - bool is_request() const { - return is_request_; - } - - void set_request_was_head(bool request_was_head) { - request_was_head_ = request_was_head; - } - - bool request_was_head() const { - return request_was_head_; - } - - void set_max_header_length(size_t max_header_length) { - max_header_length_ = max_header_length; - } - - size_t max_header_length() const { - return max_header_length_; - } - - void set_max_request_uri_length(size_t max_request_uri_length) { - max_request_uri_length_ = max_request_uri_length; - } - - size_t max_request_uri_length() const { - return max_request_uri_length_; - } - - - bool MessageFullyRead() { - return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ; - } - - BalsaFrameEnums::ParseState ParseState() const { return parse_state_; } - - - bool Error() { - return parse_state_ == BalsaFrameEnums::PARSE_ERROR; - } - - BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; } - - const BalsaHeaders* headers() const { return headers_; } - BalsaHeaders* mutable_headers() { return headers_; } - - size_t BytesSafeToSplice() const; - void BytesSpliced(size_t bytes_spliced); - - size_t ProcessInput(const char* input, size_t size); - - // Parses input and puts the key, value chunk extensions into extensions. - // TODO(phython): Find a better data structure to put the extensions into. - static void ProcessChunkExtensions(const char* input, size_t size, - BalsaHeaders* extensions); - - protected: - // The utils object needs access to the ParseTokenList in order to do its - // job. - friend class BalsaHeadersTokenUtils; - - inline void ProcessContentLengthLine( - size_t line_idx, - BalsaHeadersEnums::ContentLengthStatus* status, - size_t* length); - - inline void ProcessTransferEncodingLine(size_t line_idx); - - void ProcessFirstLine(const char* begin, - const char* end); - - void CleanUpKeyValueWhitespace( - const char* stream_begin, - const char* line_begin, - const char* current, - const char* line_end, - HeaderLineDescription* current_header_line); - - void FindColonsAndParseIntoKeyValue(); - - void ProcessHeaderLines(); - - inline size_t ProcessHeaders(const char* message_start, - size_t message_length); - - void AssignParseStateAfterHeadersHaveBeenParsed(); - - inline bool LineFramingFound(char current_char) { - return current_char == '\n'; - } - - // TODO(fenix): get rid of the following function and its uses (and - // replace with something more efficient) - inline bool HeaderFramingFound(char current_char) { - // Note that the 'if (current_char == '\n' ...)' test exists to ensure that - // the HeaderFramingMayBeFound test works properly. In benchmarking done on - // 2/13/2008, the 'if' actually speeds up performance of the function - // anyway.. - if (current_char == '\n' || current_char == '\r') { - term_chars_ <<= 8; - // This is necessary IFF architecture has > 8 bit char. Alas, I'm - // paranoid. - term_chars_ |= current_char & 0xFF; - - if ((term_chars_ & kValidTerm1Mask) == kValidTerm1) { - term_chars_ = 0; - return true; - } - if ((term_chars_ & kValidTerm2Mask) == kValidTerm2) { - term_chars_ = 0; - return true; - } - } else { - term_chars_ = 0; - } - return false; - } - - inline bool HeaderFramingMayBeFound() const { - return term_chars_ != 0; - } - - private: - class DoNothingBalsaVisitor : public BalsaVisitorInterface { - virtual void ProcessBodyInput(const char *input, size_t size) OVERRIDE {} - virtual void ProcessBodyData(const char *input, size_t size) OVERRIDE {} - virtual void ProcessHeaderInput(const char *input, size_t size) OVERRIDE {} - virtual void ProcessTrailerInput(const char *input, size_t size) OVERRIDE {} - virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE {} - virtual void ProcessRequestFirstLine(const char* line_input, - size_t line_length, - const char* method_input, - size_t method_length, - const char* request_uri_input, - size_t request_uri_length, - const char* version_input, - size_t version_length) OVERRIDE {} - virtual void ProcessResponseFirstLine(const char *line_input, - size_t line_length, - const char *version_input, - size_t version_length, - const char *status_input, - size_t status_length, - const char *reason_input, - size_t reason_length) OVERRIDE {} - virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {} - virtual void ProcessChunkExtensions(const char *input, - size_t size) OVERRIDE {} - virtual void HeaderDone() OVERRIDE {} - virtual void MessageDone() OVERRIDE {} - virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {} - virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {} - virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {} - virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {} - }; - - bool last_char_was_slash_r_; - bool saw_non_newline_char_; - bool start_was_space_; - bool chunk_length_character_extracted_; - bool is_request_; // This is not reset in Reset() - bool request_was_head_; // This is not reset in Reset() - size_t max_header_length_; // This is not reset in Reset() - size_t max_request_uri_length_; // This is not reset in Reset() - BalsaVisitorInterface* visitor_; - size_t chunk_length_remaining_; - size_t content_length_remaining_; - const char* last_slash_n_loc_; - const char* last_recorded_slash_n_loc_; - size_t last_slash_n_idx_; - uint32 term_chars_; - BalsaFrameEnums::ParseState parse_state_; - BalsaFrameEnums::ErrorCode last_error_; - - Lines lines_; - - BalsaHeaders* headers_; // This is not reset to NULL in Reset(). - DoNothingBalsaVisitor do_nothing_visitor_; -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_ - diff --git a/net/tools/flip_server/balsa_frame_test.cc b/net/tools/flip_server/balsa_frame_test.cc deleted file mode 100644 index 3bb9a42..0000000 --- a/net/tools/flip_server/balsa_frame_test.cc +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2013 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/tools/flip_server/balsa_frame.h" - -#include <iterator> - -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -using ::base::StringPiece; -using ::testing::_; -using ::testing::InSequence; -using ::testing::SaveArg; - -class Visitor : public BalsaVisitorInterface { - public: - virtual ~Visitor() {} - MOCK_METHOD2(ProcessBodyInput, void(const char*, size_t)); - MOCK_METHOD2(ProcessBodyData, void(const char*, size_t)); - MOCK_METHOD2(ProcessHeaderInput, void(const char*, size_t)); - MOCK_METHOD2(ProcessTrailerInput, void(const char*, size_t)); - MOCK_METHOD1(ProcessHeaders, void(const BalsaHeaders&)); - MOCK_METHOD8(ProcessRequestFirstLine, void(const char*, - size_t, - const char*, - size_t, - const char*, - size_t, - const char*, - size_t)); - MOCK_METHOD8(ProcessResponseFirstLine, void(const char*, - size_t, - const char*, - size_t, - const char*, - size_t, - const char*, - size_t)); - MOCK_METHOD2(ProcessChunkExtensions, void(const char*, size_t)); - MOCK_METHOD1(ProcessChunkLength, void(size_t)); - MOCK_METHOD0(HeaderDone, void()); - MOCK_METHOD0(MessageDone, void()); - MOCK_METHOD1(HandleHeaderError, void(BalsaFrame*)); - MOCK_METHOD1(HandleHeaderWarning, void(BalsaFrame*)); - MOCK_METHOD1(HandleChunkingError, void(BalsaFrame*)); - MOCK_METHOD1(HandleBodyError, void(BalsaFrame*)); -}; - -class BalsaFrameTest : public ::testing::Test { - public: - virtual void SetUp() OVERRIDE { - frame_.reset(new BalsaFrame); - frame_headers_.reset(new BalsaHeaders); - visitor_.reset(new Visitor); - frame_->set_balsa_visitor(visitor_.get()); - }; - - protected: - scoped_ptr<BalsaFrame> frame_; - scoped_ptr<BalsaHeaders> frame_headers_; - scoped_ptr<Visitor> visitor_; -}; - -TEST_F(BalsaFrameTest, EmptyFrame) { - ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, - frame_->ParseState()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(NULL, frame_->const_balsa_headers()); - ASSERT_EQ(NULL, frame_->balsa_headers()); - ASSERT_EQ(NULL, frame_->headers()); - ASSERT_EQ(NULL, frame_->mutable_headers()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - ASSERT_TRUE(frame_->is_request()); - ASSERT_FALSE(frame_->request_was_head()); -} - -TEST_F(BalsaFrameTest, EmptyRequest) { - const char input[] = "\r\n"; - frame_->set_balsa_headers(frame_headers_.get()); - - { - InSequence s; - // No visitor callback should be called. - } - size_t read = frame_->ProcessInput(input, strlen(input)); - EXPECT_EQ(2u, read); - ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, - frame_->ParseState()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(BalsaFrameEnums::NO_ERROR, frame_->ErrorCode()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -TEST_F(BalsaFrameTest, GetRequest) { - const char input[] = "GET / HTTP/1.0\r\nkey1: value1\r\n\r\n"; - const char* line = NULL; - size_t line_length = 0; - const char* method = NULL; - size_t method_length = 0; - const char* request_uri = NULL; - size_t request_uri_length = 0; - const char* version = NULL; - size_t version_length = 0; - const char* header = NULL; - size_t header_length = 0; - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)) - .WillOnce(DoAll(SaveArg<0>(&line), - SaveArg<1>(&line_length), - SaveArg<2>(&method), - SaveArg<3>(&method_length), - SaveArg<4>(&request_uri), - SaveArg<5>(&request_uri_length), - SaveArg<6>(&version), - SaveArg<7>(&version_length))); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(*visitor_, MessageDone()); - } - - frame_->set_balsa_headers(frame_headers_.get()); - ASSERT_EQ(frame_headers_.get(), frame_->const_balsa_headers()); - ASSERT_EQ(frame_headers_.get(), frame_->balsa_headers()); - ASSERT_EQ(frame_headers_.get(), frame_->headers()); - ASSERT_EQ(frame_headers_.get(), frame_->mutable_headers()); - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - ASSERT_EQ("GET / HTTP/1.0", StringPiece(line, line_length)); - ASSERT_EQ("GET", StringPiece(method, method_length)); - ASSERT_EQ("/", StringPiece(request_uri, request_uri_length)); - ASSERT_EQ("HTTP/1.0", StringPiece(version, version_length)); - ASSERT_EQ(input, StringPiece(header, header_length)); -} - -TEST_F(BalsaFrameTest, HeadResponse) { - const char input[] = "HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n"; - const char* line = NULL; - size_t line_length = 0; - const char* version = NULL; - size_t version_length = 0; - const char* status = NULL; - size_t status_length = 0; - const char* reason = NULL; - size_t reason_length = 0; - const char* header = NULL; - size_t header_length = 0; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - frame_->set_request_was_head(true); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)) - .WillOnce(DoAll(SaveArg<0>(&line), - SaveArg<1>(&line_length), - SaveArg<2>(&version), - SaveArg<3>(&version_length), - SaveArg<4>(&status), - SaveArg<5>(&status_length), - SaveArg<6>(&reason), - SaveArg<7>(&reason_length))); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(*visitor_, MessageDone()); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - - ASSERT_EQ("HTTP/1.1 200 OK", StringPiece(line, line_length)); - ASSERT_EQ("HTTP/1.1", StringPiece(version, version_length)); - ASSERT_EQ("200", StringPiece(status, status_length)); - ASSERT_EQ("OK", StringPiece(reason, reason_length)); - ASSERT_EQ("HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n", - StringPiece(header, header_length)); -} - -TEST_F(BalsaFrameTest, GetResponse) { - const char input[] = "HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello, world\r\n"; - const char* line = NULL; - size_t line_length = 0; - const char* version = NULL; - size_t version_length = 0; - const char* status = NULL; - size_t status_length = 0; - const char* reason = NULL; - size_t reason_length = 0; - const char* header = NULL; - size_t header_length = 0; - const char* body = NULL; - size_t body_length = 0; - const char* body_data = NULL; - size_t body_data_length = 0; - testing::MockFunction<void(int)> checkpoint; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)) - .WillOnce(DoAll(SaveArg<0>(&line), - SaveArg<1>(&line_length), - SaveArg<2>(&version), - SaveArg<3>(&version_length), - SaveArg<4>(&status), - SaveArg<5>(&status_length), - SaveArg<6>(&reason), - SaveArg<7>(&reason_length))); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(checkpoint, Call(0)); - EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body), SaveArg<1>(&body_length))); - EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body_data), SaveArg<1>(&body_data_length))); - EXPECT_CALL(*visitor_, MessageDone()); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(65u, read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - checkpoint.Call(0); - read += frame_->ProcessInput(&input[read], strlen(input) - read); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - - ASSERT_EQ("HTTP/1.1 200 OK", StringPiece(line, line_length)); - ASSERT_EQ("HTTP/1.1", StringPiece(version, version_length)); - ASSERT_EQ("200", StringPiece(status, status_length)); - ASSERT_EQ("OK", StringPiece(reason, reason_length)); - ASSERT_EQ("HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n", - StringPiece(header, header_length)); - ASSERT_EQ("hello, world\r\n", StringPiece(body, body_length)); - ASSERT_EQ("hello, world\r\n", StringPiece(body_data, body_data_length)); -} - -TEST_F(BalsaFrameTest, Reset) { - const char input[] = "GET / HTTP/1.0\r\nkey1: value1\r\n\r\n"; - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(*visitor_, MessageDone()); - } - - frame_->set_balsa_headers(frame_headers_.get()); - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - - frame_->Reset(); - ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, - frame_->ParseState()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); -} - -TEST_F(BalsaFrameTest, InvalidStatusCode) { - const char input[] = "HTTP/1.1 InvalidStatusCode OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello, world\r\n"; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(30u, read); - ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); - ASSERT_EQ(BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT, - frame_->ErrorCode()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_TRUE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -TEST_F(BalsaFrameTest, ResetError) { - const char input[] = "HTTP/1.1 InvalidStatusCode OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello, world\r\n"; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(30u, read); - ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); - ASSERT_EQ(BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT, - frame_->ErrorCode()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_TRUE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - - frame_->Reset(); - ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, - frame_->ParseState()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); -} - -TEST_F(BalsaFrameTest, RequestURITooLong) { - const char input[] = "GET / HTTP/1.0\r\n\r\n"; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_max_request_uri_length(0); - - { - InSequence s; - EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(15u, read); - ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); - ASSERT_EQ(BalsaFrameEnums::REQUEST_URI_TOO_LONG, frame_->ErrorCode()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_TRUE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -TEST_F(BalsaFrameTest, HeadersTooLong) { - const char input[] = "GET / HTTP/1.0\r\n\r\n"; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_max_header_length(0); - - { - InSequence s; - EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(0u, read); - ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); - ASSERT_EQ(BalsaFrameEnums::HEADERS_TOO_LONG, frame_->ErrorCode()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_TRUE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -TEST_F(BalsaFrameTest, InvalidHeader) { - const char input[] = "GET / HTTP/1.0\r\n" - "foo bar baz\r\n" - "Content-Type: text/plain\r\n\r\n"; - const char* line = NULL; - size_t line_length = 0; - const char* method = NULL; - size_t method_length = 0; - const char* request_uri = NULL; - size_t request_uri_length = 0; - const char* version = NULL; - size_t version_length = 0; - - frame_->set_balsa_headers(frame_headers_.get()); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)) - .WillOnce(DoAll(SaveArg<0>(&line), - SaveArg<1>(&line_length), - SaveArg<2>(&method), - SaveArg<3>(&method_length), - SaveArg<4>(&request_uri), - SaveArg<5>(&request_uri_length), - SaveArg<6>(&version), - SaveArg<7>(&version_length))); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); - EXPECT_CALL(*visitor_, HandleHeaderWarning(frame_.get())); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(*visitor_, MessageDone()); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_EQ(BalsaFrameEnums::HEADER_MISSING_COLON, frame_->ErrorCode()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - ASSERT_EQ("GET / HTTP/1.0", StringPiece(line, line_length)); - ASSERT_EQ("GET", StringPiece(method, method_length)); - ASSERT_EQ("/", StringPiece(request_uri, request_uri_length)); - ASSERT_EQ("HTTP/1.0", StringPiece(version, version_length)); - ASSERT_EQ(2, std::distance(frame_headers_->header_lines_begin(), - frame_headers_->header_lines_end())); -} - -TEST_F(BalsaFrameTest, GetResponseSplit) { - const char input[] = "HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello"; - const char input2[] = ", world\r\n"; - const char* body1 = NULL; - size_t body1_length = 0; - const char* body1_data = NULL; - size_t body1_data_length = 0; - const char* body2 = NULL; - size_t body2_length = 0; - const char* body2_data = NULL; - size_t body2_data_length = 0; - testing::MockFunction<void(int)> checkpoint; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(checkpoint, Call(0)); - EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body1), SaveArg<1>(&body1_length))); - EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body1_data), - SaveArg<1>(&body1_data_length))); - EXPECT_CALL(checkpoint, Call(1)); - EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body2), SaveArg<1>(&body2_length))); - EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) - .WillOnce(DoAll(SaveArg<0>(&body2_data), - SaveArg<1>(&body2_data_length))); - EXPECT_CALL(*visitor_, MessageDone()); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(65u, read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - checkpoint.Call(0); - read += frame_->ProcessInput(&input[read], strlen(input) - read); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - checkpoint.Call(1); - ASSERT_EQ(9u, frame_->BytesSafeToSplice()); - read = frame_->ProcessInput(input2, strlen(input2)); - ASSERT_EQ(strlen(input2), read); - - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); - ASSERT_EQ("hello", StringPiece(body1, body1_length)); - ASSERT_EQ("hello", StringPiece(body1_data, body1_data_length)); - ASSERT_EQ(", world\r\n", StringPiece(body2, body2_length)); - ASSERT_EQ(", world\r\n", StringPiece(body2_data, body2_data_length)); -} - -TEST_F(BalsaFrameTest, GetResponseBytesSpliced) { - const char input[] = "HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello"; - testing::MockFunction<void(int)> checkpoint; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(checkpoint, Call(0)); - EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)); - EXPECT_CALL(*visitor_, ProcessBodyData(_, _)); - EXPECT_CALL(checkpoint, Call(1)); - EXPECT_CALL(checkpoint, Call(2)); - EXPECT_CALL(*visitor_, MessageDone()); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(65u, read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - checkpoint.Call(0); - read += frame_->ProcessInput(&input[read], strlen(input) - read); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - ASSERT_EQ(9u, frame_->BytesSafeToSplice()); - checkpoint.Call(1); - frame_->BytesSpliced(5); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - ASSERT_EQ(4u, frame_->BytesSafeToSplice()); - checkpoint.Call(2); - frame_->BytesSpliced(4); - ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); - - ASSERT_TRUE(frame_->MessageFullyRead()); - ASSERT_FALSE(frame_->Error()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -TEST_F(BalsaFrameTest, GetResponseBytesSplicedTooMany) { - const char input[] = "HTTP/1.1 200 OK\r\n" - "Content-type: text/plain\r\n" - "Content-Length: 14\r\n\r\n" - "hello"; - testing::MockFunction<void(int)> checkpoint; - - frame_->set_balsa_headers(frame_headers_.get()); - frame_->set_is_request(false); - - { - InSequence s; - EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); - EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); - EXPECT_CALL(*visitor_, ProcessHeaders(_)); - EXPECT_CALL(*visitor_, HeaderDone()); - EXPECT_CALL(checkpoint, Call(0)); - EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)); - EXPECT_CALL(*visitor_, ProcessBodyData(_, _)); - EXPECT_CALL(checkpoint, Call(1)); - EXPECT_CALL(*visitor_, HandleBodyError(frame_.get())); - } - - size_t read = frame_->ProcessInput(input, strlen(input)); - ASSERT_EQ(65u, read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - checkpoint.Call(0); - read += frame_->ProcessInput(&input[read], strlen(input) - read); - ASSERT_EQ(strlen(input), read); - ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); - ASSERT_EQ(9u, frame_->BytesSafeToSplice()); - checkpoint.Call(1); - frame_->BytesSpliced(99); - ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); - ASSERT_FALSE(frame_->MessageFullyRead()); - ASSERT_TRUE(frame_->Error()); - ASSERT_EQ( - BalsaFrameEnums::CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT, - frame_->ErrorCode()); - ASSERT_EQ(0u, frame_->BytesSafeToSplice()); -} - -} // namespace - -} // namespace net diff --git a/net/tools/flip_server/balsa_headers.cc b/net/tools/flip_server/balsa_headers.cc deleted file mode 100644 index 040c012..0000000 --- a/net/tools/flip_server/balsa_headers.cc +++ /dev/null @@ -1,965 +0,0 @@ -// 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/tools/flip_server/balsa_headers.h" - -#include <stdio.h> -#include <algorithm> -#include <ext/hash_set> -#include <string> -#include <utility> -#include <vector> - -#include "base/logging.h" -#include "base/port.h" -#include "base/strings/stringprintf.h" -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/buffer_interface.h" -#include "net/tools/flip_server/simple_buffer.h" -#include "third_party/tcmalloc/chromium/src/base/googleinit.h" -// #include "util/gtl/iterator_adaptors-inl.h" -// #include "util/gtl/map-util.h" - -namespace { - -const char kContentLength[] = "Content-Length"; -const char kTransferEncoding[] = "Transfer-Encoding"; -const char kSpaceChar = ' '; - -__gnu_cxx::hash_set<base::StringPiece, - net::StringPieceCaseHash, - net::StringPieceCaseEqual> g_multivalued_headers; - -void InitMultivaluedHeaders() { - g_multivalued_headers.insert("accept"); - g_multivalued_headers.insert("accept-charset"); - g_multivalued_headers.insert("accept-encoding"); - g_multivalued_headers.insert("accept-language"); - g_multivalued_headers.insert("accept-ranges"); - g_multivalued_headers.insert("allow"); - g_multivalued_headers.insert("cache-control"); - g_multivalued_headers.insert("connection"); - g_multivalued_headers.insert("content-encoding"); - g_multivalued_headers.insert("content-language"); - g_multivalued_headers.insert("expect"); - g_multivalued_headers.insert("if-match"); - g_multivalued_headers.insert("if-none-match"); - g_multivalued_headers.insert("pragma"); - g_multivalued_headers.insert("proxy-authenticate"); - g_multivalued_headers.insert("te"); - g_multivalued_headers.insert("trailer"); - g_multivalued_headers.insert("transfer-encoding"); - g_multivalued_headers.insert("upgrade"); - g_multivalued_headers.insert("vary"); - g_multivalued_headers.insert("via"); - g_multivalued_headers.insert("warning"); - g_multivalued_headers.insert("www-authenticate"); - // Not mentioned in RFC 2616, but it can have multiple values. - g_multivalued_headers.insert("set-cookie"); -} - -REGISTER_MODULE_INITIALIZER(multivalued_headers, InitMultivaluedHeaders()); - -const int kFastToBufferSize = 32; // I think 22 is adequate, but anyway.. - -} // namespace - -namespace net { - -const size_t BalsaBuffer::kDefaultBlocksize; - -BalsaHeaders::iterator_base::iterator_base() : headers_(NULL), idx_(0) { } - -BalsaHeaders::iterator_base::iterator_base(const iterator_base& it) - : headers_(it.headers_), - idx_(it.idx_) { -} - -std::ostream& BalsaHeaders::iterator_base::operator<<(std::ostream& os) const { - os << "[" << this->headers_ << ", " << this->idx_ << "]"; - return os; -} - -BalsaHeaders::iterator_base::iterator_base(const BalsaHeaders* headers, - HeaderLines::size_type index) - : headers_(headers), - idx_(index) { -} - -BalsaBuffer::~BalsaBuffer() { - CleanupBlocksStartingFrom(0); -} - -// Returns the total amount of memory used by the buffer blocks. -size_t BalsaBuffer::GetTotalBufferBlockSize() const { - size_t buffer_size = 0; - for (Blocks::const_iterator iter = blocks_.begin(); - iter != blocks_.end(); - ++iter) { - buffer_size += iter->buffer_size; - } - return buffer_size; -} - -void BalsaBuffer::WriteToContiguousBuffer(const base::StringPiece& sp) { - if (sp.empty()) { - return; - } - CHECK(can_write_to_contiguous_buffer_); - DCHECK_GE(blocks_.size(), 1u); - if (blocks_[0].buffer == NULL && sp.size() <= blocksize_) { - blocks_[0] = AllocBlock(); - memcpy(blocks_[0].start_of_unused_bytes(), sp.data(), sp.size()); - } else if (blocks_[0].bytes_free < sp.size()) { - // the first block isn't big enough, resize it. - const size_t old_storage_size_used = blocks_[0].bytes_used(); - const size_t new_storage_size = old_storage_size_used + sp.size(); - char* new_storage = new char[new_storage_size]; - char* old_storage = blocks_[0].buffer; - if (old_storage_size_used) { - memcpy(new_storage, old_storage, old_storage_size_used); - } - memcpy(new_storage + old_storage_size_used, sp.data(), sp.size()); - blocks_[0].buffer = new_storage; - blocks_[0].bytes_free = sp.size(); - blocks_[0].buffer_size = new_storage_size; - delete[] old_storage; - } else { - memcpy(blocks_[0].start_of_unused_bytes(), sp.data(), sp.size()); - } - blocks_[0].bytes_free -= sp.size(); -} - -base::StringPiece BalsaBuffer::Write(const base::StringPiece& sp, - Blocks::size_type* block_buffer_idx) { - if (sp.empty()) { - return sp; - } - char* storage = Reserve(sp.size(), block_buffer_idx); - memcpy(storage, sp.data(), sp.size()); - return base::StringPiece(storage, sp.size()); -} - -char* BalsaBuffer::Reserve(size_t size, - Blocks::size_type* block_buffer_idx) { - // There should always be a 'first_block', even if it - // contains nothing. - DCHECK_GE(blocks_.size(), 1u); - BufferBlock* block = NULL; - Blocks::size_type block_idx = can_write_to_contiguous_buffer_ ? 1 : 0; - for (; block_idx < blocks_.size(); ++block_idx) { - if (blocks_[block_idx].bytes_free >= size) { - block = &blocks_[block_idx]; - break; - } - } - if (block == NULL) { - if (blocksize_ < size) { - blocks_.push_back(AllocCustomBlock(size)); - } else { - blocks_.push_back(AllocBlock()); - } - block = &blocks_.back(); - } - - char* storage = block->start_of_unused_bytes(); - block->bytes_free -= size; - if (block_buffer_idx) { - *block_buffer_idx = block_idx; - } - return storage; -} - -void BalsaBuffer::Clear() { - CHECK(!blocks_.empty()); - if (blocksize_ == blocks_[0].buffer_size) { - CleanupBlocksStartingFrom(1); - blocks_[0].bytes_free = blocks_[0].buffer_size; - } else { - CleanupBlocksStartingFrom(0); - blocks_.push_back(AllocBlock()); - } - DCHECK_GE(blocks_.size(), 1u); - can_write_to_contiguous_buffer_ = true; -} - -void BalsaBuffer::Swap(BalsaBuffer* b) { - blocks_.swap(b->blocks_); - std::swap(can_write_to_contiguous_buffer_, - b->can_write_to_contiguous_buffer_); - std::swap(blocksize_, b->blocksize_); -} - -void BalsaBuffer::CopyFrom(const BalsaBuffer& b) { - CleanupBlocksStartingFrom(0); - blocks_.resize(b.blocks_.size()); - for (Blocks::size_type i = 0; i < blocks_.size(); ++i) { - blocks_[i] = CopyBlock(b.blocks_[i]); - } - blocksize_ = b.blocksize_; - can_write_to_contiguous_buffer_ = b.can_write_to_contiguous_buffer_; -} - -BalsaBuffer::BalsaBuffer() - : blocksize_(kDefaultBlocksize), can_write_to_contiguous_buffer_(true) { - blocks_.push_back(AllocBlock()); -} - -BalsaBuffer::BalsaBuffer(size_t blocksize) : - blocksize_(blocksize), can_write_to_contiguous_buffer_(true) { - blocks_.push_back(AllocBlock()); -} - -BalsaBuffer::BufferBlock BalsaBuffer::AllocBlock() { - return AllocCustomBlock(blocksize_); -} - -BalsaBuffer::BufferBlock BalsaBuffer::AllocCustomBlock(size_t blocksize) { - return BufferBlock(new char[blocksize], blocksize, blocksize); -} - -BalsaBuffer::BufferBlock BalsaBuffer::CopyBlock(const BufferBlock& b) { - BufferBlock block = b; - if (b.buffer == NULL) { - return block; - } - - block.buffer = new char[b.buffer_size]; - memcpy(block.buffer, b.buffer, b.bytes_used()); - return block; -} - -void BalsaBuffer::CleanupBlocksStartingFrom(Blocks::size_type start_idx) { - for (Blocks::size_type i = start_idx; i < blocks_.size(); ++i) { - delete[] blocks_[i].buffer; - } - blocks_.resize(start_idx); -} - -BalsaHeaders::const_header_lines_key_iterator::const_header_lines_key_iterator( - const const_header_lines_key_iterator& other) - : iterator_base(other), - key_(other.key_) { -} - -BalsaHeaders::const_header_lines_key_iterator::const_header_lines_key_iterator( - const BalsaHeaders* headers, - HeaderLines::size_type index, - const base::StringPiece& key) - : iterator_base(headers, index), - key_(key) { -} - -BalsaHeaders::const_header_lines_key_iterator::const_header_lines_key_iterator( - const BalsaHeaders* headers, - HeaderLines::size_type index) - : iterator_base(headers, index) { -} - -BalsaHeaders::BalsaHeaders() - : balsa_buffer_(4096), - content_length_(0), - content_length_status_(BalsaHeadersEnums::NO_CONTENT_LENGTH), - parsed_response_code_(0), - firstline_buffer_base_idx_(0), - whitespace_1_idx_(0), - non_whitespace_1_idx_(0), - whitespace_2_idx_(0), - non_whitespace_2_idx_(0), - whitespace_3_idx_(0), - non_whitespace_3_idx_(0), - whitespace_4_idx_(0), - end_of_firstline_idx_(0), - transfer_encoding_is_chunked_(false) { -} - -BalsaHeaders::~BalsaHeaders() {} - -void BalsaHeaders::Clear() { - balsa_buffer_.Clear(); - transfer_encoding_is_chunked_ = false; - content_length_ = 0; - content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; - parsed_response_code_ = 0; - firstline_buffer_base_idx_ = 0; - whitespace_1_idx_ = 0; - non_whitespace_1_idx_ = 0; - whitespace_2_idx_ = 0; - non_whitespace_2_idx_ = 0; - whitespace_3_idx_ = 0; - non_whitespace_3_idx_ = 0; - whitespace_4_idx_ = 0; - end_of_firstline_idx_ = 0; - header_lines_.clear(); -} - -void BalsaHeaders::Swap(BalsaHeaders* other) { - // Protect against swapping with self. - if (this == other) return; - - balsa_buffer_.Swap(&other->balsa_buffer_); - - bool tmp_bool = transfer_encoding_is_chunked_; - transfer_encoding_is_chunked_ = other->transfer_encoding_is_chunked_; - other->transfer_encoding_is_chunked_ = tmp_bool; - - size_t tmp_size_t = content_length_; - content_length_ = other->content_length_; - other->content_length_ = tmp_size_t; - - BalsaHeadersEnums::ContentLengthStatus tmp_status = - content_length_status_; - content_length_status_ = other->content_length_status_; - other->content_length_status_ = tmp_status; - - tmp_size_t = parsed_response_code_; - parsed_response_code_ = other->parsed_response_code_; - other->parsed_response_code_ = tmp_size_t; - - BalsaBuffer::Blocks::size_type tmp_blk_idx = firstline_buffer_base_idx_; - firstline_buffer_base_idx_ = other->firstline_buffer_base_idx_; - other->firstline_buffer_base_idx_ = tmp_blk_idx; - - tmp_size_t = whitespace_1_idx_; - whitespace_1_idx_ = other->whitespace_1_idx_; - other->whitespace_1_idx_ = tmp_size_t; - - tmp_size_t = non_whitespace_1_idx_; - non_whitespace_1_idx_ = other->non_whitespace_1_idx_; - other->non_whitespace_1_idx_ = tmp_size_t; - - tmp_size_t = whitespace_2_idx_; - whitespace_2_idx_ = other->whitespace_2_idx_; - other->whitespace_2_idx_ = tmp_size_t; - - tmp_size_t = non_whitespace_2_idx_; - non_whitespace_2_idx_ = other->non_whitespace_2_idx_; - other->non_whitespace_2_idx_ = tmp_size_t; - - tmp_size_t = whitespace_3_idx_; - whitespace_3_idx_ = other->whitespace_3_idx_; - other->whitespace_3_idx_ = tmp_size_t; - - tmp_size_t = non_whitespace_3_idx_; - non_whitespace_3_idx_ = other->non_whitespace_3_idx_; - other->non_whitespace_3_idx_ = tmp_size_t; - - tmp_size_t = whitespace_4_idx_; - whitespace_4_idx_ = other->whitespace_4_idx_; - other->whitespace_4_idx_ = tmp_size_t; - - tmp_size_t = end_of_firstline_idx_; - end_of_firstline_idx_ = other->end_of_firstline_idx_; - other->end_of_firstline_idx_ = tmp_size_t; - - swap(header_lines_, other->header_lines_); -} - -void BalsaHeaders::CopyFrom(const BalsaHeaders& other) { - // Protect against copying with self. - if (this == &other) return; - - balsa_buffer_.CopyFrom(other.balsa_buffer_); - transfer_encoding_is_chunked_ = other.transfer_encoding_is_chunked_; - content_length_ = other.content_length_; - content_length_status_ = other.content_length_status_; - parsed_response_code_ = other.parsed_response_code_; - firstline_buffer_base_idx_ = other.firstline_buffer_base_idx_; - whitespace_1_idx_ = other.whitespace_1_idx_; - non_whitespace_1_idx_ = other.non_whitespace_1_idx_; - whitespace_2_idx_ = other.whitespace_2_idx_; - non_whitespace_2_idx_ = other.non_whitespace_2_idx_; - whitespace_3_idx_ = other.whitespace_3_idx_; - non_whitespace_3_idx_ = other.non_whitespace_3_idx_; - whitespace_4_idx_ = other.whitespace_4_idx_; - end_of_firstline_idx_ = other.end_of_firstline_idx_; - header_lines_ = other.header_lines_; -} - -void BalsaHeaders::AddAndMakeDescription(const base::StringPiece& key, - const base::StringPiece& value, - HeaderLineDescription* d) { - CHECK(d != NULL); - // + 2 to size for ": " - size_t line_size = key.size() + 2 + value.size(); - BalsaBuffer::Blocks::size_type block_buffer_idx = 0; - char* storage = balsa_buffer_.Reserve(line_size, &block_buffer_idx); - size_t base_idx = storage - GetPtr(block_buffer_idx); - - char* cur_loc = storage; - memcpy(cur_loc, key.data(), key.size()); - cur_loc += key.size(); - *cur_loc = ':'; - ++cur_loc; - *cur_loc = ' '; - ++cur_loc; - memcpy(cur_loc, value.data(), value.size()); - *d = HeaderLineDescription(base_idx, - base_idx + key.size(), - base_idx + key.size() + 2, - base_idx + key.size() + 2 + value.size(), - block_buffer_idx); -} - -void BalsaHeaders::AppendOrPrependAndMakeDescription( - const base::StringPiece& key, - const base::StringPiece& value, - bool append, - HeaderLineDescription* d) { - // Figure out how much space we need to reserve for the new header size. - size_t old_value_size = d->last_char_idx - d->value_begin_idx; - if (old_value_size == 0) { - AddAndMakeDescription(key, value, d); - return; - } - base::StringPiece old_value(GetPtr(d->buffer_base_idx) + d->value_begin_idx, - old_value_size); - - BalsaBuffer::Blocks::size_type block_buffer_idx = 0; - // + 3 because we potentially need to add ": ", and "," to the line. - size_t new_size = key.size() + 3 + old_value_size + value.size(); - char* storage = balsa_buffer_.Reserve(new_size, &block_buffer_idx); - size_t base_idx = storage - GetPtr(block_buffer_idx); - - base::StringPiece first_value = old_value; - base::StringPiece second_value = value; - if (!append) { // !append == prepend - first_value = value; - second_value = old_value; - } - char* cur_loc = storage; - memcpy(cur_loc, key.data(), key.size()); - cur_loc += key.size(); - *cur_loc = ':'; - ++cur_loc; - *cur_loc = ' '; - ++cur_loc; - memcpy(cur_loc, first_value.data(), first_value.size()); - cur_loc += first_value.size(); - *cur_loc = ','; - ++cur_loc; - memcpy(cur_loc, second_value.data(), second_value.size()); - - *d = HeaderLineDescription(base_idx, - base_idx + key.size(), - base_idx + key.size() + 2, - base_idx + new_size, - block_buffer_idx); -} - -// Removes all keys value pairs with key 'key' starting at 'start'. -void BalsaHeaders::RemoveAllOfHeaderStartingAt(const base::StringPiece& key, - HeaderLines::iterator start) { - while (start != header_lines_.end()) { - start->skip = true; - ++start; - start = GetHeaderLinesIterator(key, start); - } -} - -void BalsaHeaders::HackHeader(const base::StringPiece& key, - const base::StringPiece& value) { - // See TODO in balsa_headers.h - const HeaderLines::iterator end = header_lines_.end(); - const HeaderLines::iterator begin = header_lines_.begin(); - HeaderLines::iterator i = GetHeaderLinesIteratorNoSkip(key, begin); - if (i != end) { - // First, remove all of the header lines including this one. We want to - // remove before replacing, in case our replacement ends up being appended - // at the end (and thus would be removed by this call) - RemoveAllOfHeaderStartingAt(key, i); - // Now add the replacement, at this location. - AddAndMakeDescription(key, value, &(*i)); - return; - } - AppendHeader(key, value); -} - -void BalsaHeaders::HackAppendToHeader(const base::StringPiece& key, - const base::StringPiece& append_value) { - // See TODO in balsa_headers.h - const HeaderLines::iterator end = header_lines_.end(); - const HeaderLines::iterator begin = header_lines_.begin(); - - HeaderLines::iterator i = GetHeaderLinesIterator(key, begin); - if (i == end) { - HackHeader(key, append_value); - return; - } - - AppendOrPrependAndMakeDescription(key, append_value, true, &(*i)); -} - -void BalsaHeaders::ReplaceOrAppendHeader(const base::StringPiece& key, - const base::StringPiece& value) { - const HeaderLines::iterator end = header_lines_.end(); - const HeaderLines::iterator begin = header_lines_.begin(); - HeaderLines::iterator i = GetHeaderLinesIterator(key, begin); - if (i != end) { - // First, remove all of the header lines including this one. We want to - // remove before replacing, in case our replacement ends up being appended - // at the end (and thus would be removed by this call) - RemoveAllOfHeaderStartingAt(key, i); - // Now, take the first instance and replace it. This will remove the - // 'skipped' tag if the replacement is done in-place. - AddAndMakeDescription(key, value, &(*i)); - return; - } - AppendHeader(key, value); -} - -void BalsaHeaders::AppendHeader(const base::StringPiece& key, - const base::StringPiece& value) { - HeaderLineDescription hld; - AddAndMakeDescription(key, value, &hld); - header_lines_.push_back(hld); -} - -void BalsaHeaders::AppendToHeader(const base::StringPiece& key, - const base::StringPiece& value) { - AppendOrPrependToHeader(key, value, true); -} - -void BalsaHeaders::PrependToHeader(const base::StringPiece& key, - const base::StringPiece& value) { - AppendOrPrependToHeader(key, value, false); -} - -base::StringPiece BalsaHeaders::GetValueFromHeaderLineDescription( - const HeaderLineDescription& line) const { - DCHECK_GE(line.last_char_idx, line.value_begin_idx); - return base::StringPiece(GetPtr(line.buffer_base_idx) + line.value_begin_idx, - line.last_char_idx - line.value_begin_idx); -} - -const base::StringPiece BalsaHeaders::GetHeader( - const base::StringPiece& key) const { - DCHECK(!IsMultivaluedHeader(key)) - << "Header '" << key << "' may consist of multiple lines. Do not " - << "use BalsaHeaders::GetHeader() or you may be missing some of its " - << "values."; - const HeaderLines::const_iterator end = header_lines_.end(); - const HeaderLines::const_iterator begin = header_lines_.begin(); - HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin); - if (i == end) { - return base::StringPiece(NULL, 0); - } - return GetValueFromHeaderLineDescription(*i); -} - -BalsaHeaders::const_header_lines_iterator BalsaHeaders::GetHeaderPosition( - const base::StringPiece& key) const { - const HeaderLines::const_iterator end = header_lines_.end(); - const HeaderLines::const_iterator begin = header_lines_.begin(); - HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin); - if (i == end) { - return header_lines_end(); - } - - return const_header_lines_iterator(this, (i - begin)); -} - -BalsaHeaders::const_header_lines_key_iterator BalsaHeaders::GetIteratorForKey( - const base::StringPiece& key) const { - HeaderLines::const_iterator i = - GetConstHeaderLinesIterator(key, header_lines_.begin()); - if (i == header_lines_.end()) { - return header_lines_key_end(); - } - - const HeaderLines::const_iterator begin = header_lines_.begin(); - return const_header_lines_key_iterator(this, (i - begin), key); -} - -void BalsaHeaders::AppendOrPrependToHeader(const base::StringPiece& key, - const base::StringPiece& value, - bool append) { - HeaderLines::iterator i = GetHeaderLinesIterator(key, header_lines_.begin()); - if (i == header_lines_.end()) { - // The header did not exist already. Instead of appending to an existing - // header simply append the key/value pair to the headers. - AppendHeader(key, value); - return; - } - HeaderLineDescription hld = *i; - - AppendOrPrependAndMakeDescription(key, value, append, &hld); - - // Invalidate the old header line and add the new one. - i->skip = true; - header_lines_.push_back(hld); -} - -BalsaHeaders::HeaderLines::const_iterator -BalsaHeaders::GetConstHeaderLinesIterator( - const base::StringPiece& key, - BalsaHeaders::HeaderLines::const_iterator start) const { - const HeaderLines::const_iterator end = header_lines_.end(); - for (HeaderLines::const_iterator i = start; i != end; ++i) { - const HeaderLineDescription& line = *i; - if (line.skip) { - continue; - } - const size_t key_len = line.key_end_idx - line.first_char_idx; - - if (key_len != key.size()) { - continue; - } - if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, - key.data(), key_len) == 0) { - DCHECK_GE(line.last_char_idx, line.value_begin_idx); - return i; - } - } - return end; -} - -BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIteratorNoSkip( - const base::StringPiece& key, - BalsaHeaders::HeaderLines::iterator start) { - const HeaderLines::iterator end = header_lines_.end(); - for (HeaderLines::iterator i = start; i != end; ++i) { - const HeaderLineDescription& line = *i; - const size_t key_len = line.key_end_idx - line.first_char_idx; - - if (key_len != key.size()) { - continue; - } - if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, - key.data(), key_len) == 0) { - DCHECK_GE(line.last_char_idx, line.value_begin_idx); - return i; - } - } - return end; -} - -BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIterator( - const base::StringPiece& key, - BalsaHeaders::HeaderLines::iterator start) { - const HeaderLines::iterator end = header_lines_.end(); - for (HeaderLines::iterator i = start; i != end; ++i) { - const HeaderLineDescription& line = *i; - if (line.skip) { - continue; - } - const size_t key_len = line.key_end_idx - line.first_char_idx; - - if (key_len != key.size()) { - continue; - } - if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, - key.data(), key_len) == 0) { - DCHECK_GE(line.last_char_idx, line.value_begin_idx); - return i; - } - } - return end; -} - -void BalsaHeaders::GetAllOfHeader( - const base::StringPiece& key, std::vector<base::StringPiece>* out) const { - for (const_header_lines_key_iterator it = GetIteratorForKey(key); - it != header_lines_end(); ++it) { - out->push_back(it->second); - } -} - -bool BalsaHeaders::HasNonEmptyHeader(const base::StringPiece& key) const { - for (const_header_lines_key_iterator it = GetIteratorForKey(key); - it != header_lines_key_end(); ++it) { - if (!it->second.empty()) - return true; - } - return false; -} - -void BalsaHeaders::GetAllOfHeaderAsString(const base::StringPiece& key, - std::string* out) const { - const_header_lines_iterator it = header_lines_begin(); - const_header_lines_iterator end = header_lines_end(); - - for (; it != end; ++it) { - if (key == it->first) { - if (!out->empty()) { - out->append(","); - } - out->append(std::string(it->second.data(), it->second.size())); - } - } -} - -// static -bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) { - return g_multivalued_headers.find(header) != g_multivalued_headers.end(); -} - -void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) { - HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin()); - RemoveAllOfHeaderStartingAt(key, it); -} - -void BalsaHeaders::RemoveAllHeadersWithPrefix(const base::StringPiece& key) { - for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) { - if (header_lines_[i].skip) { - continue; - } - HeaderLineDescription& line = header_lines_[i]; - const size_t key_len = line.key_end_idx - line.first_char_idx; - if (key_len < key.size()) { - // If the key given to us is longer than this header, don't consider it. - continue; - } - if (!strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, - key.data(), key.size())) { - line.skip = true; - } - } -} - -size_t BalsaHeaders::GetMemoryUsedLowerBound() const { - return (sizeof(*this) + - balsa_buffer_.GetTotalBufferBlockSize() + - header_lines_.capacity() * sizeof(HeaderLineDescription)); -} - -size_t BalsaHeaders::GetSizeForWriteBuffer() const { - // First add the space required for the first line + CRLF - size_t write_buf_size = whitespace_4_idx_ - non_whitespace_1_idx_ + 2; - // Then add the space needed for each header line to write out + CRLF. - const HeaderLines::size_type end = header_lines_.size(); - for (HeaderLines::size_type i = 0; i < end; ++i) { - const HeaderLineDescription& line = header_lines_[i]; - if (!line.skip) { - // Add the key size and ": ". - write_buf_size += line.key_end_idx - line.first_char_idx + 2; - // Add the value size and the CRLF - write_buf_size += line.last_char_idx - line.value_begin_idx + 2; - } - } - // Finally tag on the terminal CRLF. - return write_buf_size + 2; -} - -void BalsaHeaders::DumpToString(std::string* str) const { - const base::StringPiece firstline = first_line(); - const int buffer_length = - OriginalHeaderStreamEnd() - OriginalHeaderStreamBegin(); - // First check whether the header object is empty. - if (firstline.empty() && buffer_length == 0) { - str->append("\n<empty header>\n"); - return; - } - - // Then check whether the header is in a partially parsed state. If so, just - // dump the raw data. - if (balsa_buffer_.can_write_to_contiguous_buffer()) { - base::StringAppendF(str, "\n<incomplete header len: %d>\n%.*s\n", - buffer_length, buffer_length, - OriginalHeaderStreamBegin()); - return; - } - - // If the header is complete, then just dump them with the logical key value - // pair. - str->reserve(str->size() + GetSizeForWriteBuffer()); - base::StringAppendF(str, "\n %.*s\n", - static_cast<int>(firstline.size()), - firstline.data()); - BalsaHeaders::const_header_lines_iterator i = header_lines_begin(); - for (; i != header_lines_end(); ++i) { - base::StringAppendF(str, " %.*s: %.*s\n", - static_cast<int>(i->first.size()), i->first.data(), - static_cast<int>(i->second.size()), i->second.data()); - } -} - -void BalsaHeaders::SetFirstLine(const base::StringPiece& line) { - base::StringPiece new_line = balsa_buffer_.Write(line, - &firstline_buffer_base_idx_); - whitespace_1_idx_ = new_line.data() - GetPtr(firstline_buffer_base_idx_); - non_whitespace_1_idx_ = whitespace_1_idx_; - whitespace_4_idx_ = whitespace_1_idx_ + line.size(); - whitespace_2_idx_ = whitespace_4_idx_; - non_whitespace_2_idx_ = whitespace_4_idx_; - whitespace_3_idx_ = whitespace_4_idx_; - non_whitespace_3_idx_ = whitespace_4_idx_; - end_of_firstline_idx_ = whitespace_4_idx_; -} - -void BalsaHeaders::SetContentLength(size_t length) { - // If the content-length is already the one we want, don't do anything. - if (content_length_status_ == BalsaHeadersEnums::VALID_CONTENT_LENGTH && - content_length_ == length) { - return; - } - const base::StringPiece content_length(kContentLength, - sizeof(kContentLength) - 1); - // If header state indicates that there is either a content length or - // transfer encoding header, remove them before adding the new content - // length. There is always the possibility that client can manually add - // either header directly and cause content_length_status_ or - // transfer_encoding_is_chunked_ to be inconsistent with the actual header. - // In the interest of efficiency, however, we will assume that clients will - // use the header object correctly and thus we will not scan the all headers - // each time this function is called. - if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) { - RemoveAllOfHeader(content_length); - } else if (transfer_encoding_is_chunked_) { - const base::StringPiece transfer_encoding(kTransferEncoding, - sizeof(kTransferEncoding) - 1); - RemoveAllOfHeader(transfer_encoding); - transfer_encoding_is_chunked_ = false; - } - content_length_status_ = BalsaHeadersEnums::VALID_CONTENT_LENGTH; - content_length_ = length; - // FastUInt64ToBuffer is supposed to use a maximum of kFastToBufferSize bytes. - char buffer[kFastToBufferSize]; - int len_converted = snprintf(buffer, sizeof(buffer), "%zd", length); - CHECK_GT(len_converted, 0); - const base::StringPiece length_str(buffer, len_converted); - AppendHeader(content_length, length_str); -} - -void BalsaHeaders::SetChunkEncoding(bool chunk_encode) { - if (transfer_encoding_is_chunked_ == chunk_encode) { - return; - } - if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH && - chunk_encode) { - // Want to change to chunk encoding, but have content length. Arguably we - // can leave this step out, since transfer-encoding overrides - // content-length. - const base::StringPiece content_length(kContentLength, - sizeof(kContentLength) - 1); - RemoveAllOfHeader(content_length); - content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; - content_length_ = 0; - } - const base::StringPiece transfer_encoding(kTransferEncoding, - sizeof(kTransferEncoding) - 1); - if (chunk_encode) { - const char kChunked[] = "chunked"; - const base::StringPiece chunked(kChunked, sizeof(kChunked) - 1); - AppendHeader(transfer_encoding, chunked); - } else { - RemoveAllOfHeader(transfer_encoding); - } - transfer_encoding_is_chunked_ = chunk_encode; -} - -// See the comment about this function in the header file for a -// warning about its usage. -void BalsaHeaders::SetFirstlineFromStringPieces( - const base::StringPiece& firstline_a, - const base::StringPiece& firstline_b, - const base::StringPiece& firstline_c) { - size_t line_size = (firstline_a.size() + - firstline_b.size() + - firstline_c.size() + - 2); - char* storage = balsa_buffer_.Reserve(line_size, &firstline_buffer_base_idx_); - char* cur_loc = storage; - - memcpy(cur_loc, firstline_a.data(), firstline_a.size()); - cur_loc += firstline_a.size(); - - *cur_loc = ' '; - ++cur_loc; - - memcpy(cur_loc, firstline_b.data(), firstline_b.size()); - cur_loc += firstline_b.size(); - - *cur_loc = ' '; - ++cur_loc; - - memcpy(cur_loc, firstline_c.data(), firstline_c.size()); - - whitespace_1_idx_ = storage - GetPtr(firstline_buffer_base_idx_); - non_whitespace_1_idx_ = whitespace_1_idx_; - whitespace_2_idx_ = non_whitespace_1_idx_ + firstline_a.size(); - non_whitespace_2_idx_ = whitespace_2_idx_ + 1; - whitespace_3_idx_ = non_whitespace_2_idx_ + firstline_b.size(); - non_whitespace_3_idx_ = whitespace_3_idx_ + 1; - whitespace_4_idx_ = non_whitespace_3_idx_ + firstline_c.size(); - end_of_firstline_idx_ = whitespace_4_idx_; -} - -void BalsaHeaders::SetRequestMethod(const base::StringPiece& method) { - // This is the first of the three parts of the firstline. - if (method.size() <= (whitespace_2_idx_ - non_whitespace_1_idx_)) { - non_whitespace_1_idx_ = whitespace_2_idx_ - method.size(); - char* stream_begin = GetPtr(firstline_buffer_base_idx_); - memcpy(stream_begin + non_whitespace_1_idx_, - method.data(), - method.size()); - } else { - // The new method is too large to fit in the space available for the old - // one, so we have to reformat the firstline. - SetFirstlineFromStringPieces(method, request_uri(), request_version()); - } -} - -void BalsaHeaders::SetResponseVersion(const base::StringPiece& version) { - // Note: There is no difference between request_method() and - // response_Version(). Thus, a function to set one is equivalent to a - // function to set the other. We maintain two functions for this as it is - // much more descriptive, and makes code more understandable. - SetRequestMethod(version); -} - -void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) { - SetFirstlineFromStringPieces(request_method(), uri, request_version()); -} - -void BalsaHeaders::SetResponseCode(const base::StringPiece& code) { - // Note: There is no difference between request_uri() and response_code(). - // Thus, a function to set one is equivalent to a function to set the other. - // We maintain two functions for this as it is much more descriptive, and - // makes code more understandable. - SetRequestUri(code); -} - -void BalsaHeaders::SetParsedResponseCodeAndUpdateFirstline( - size_t parsed_response_code) { - char buffer[kFastToBufferSize]; - int len_converted = snprintf(buffer, sizeof(buffer), - "%zd", parsed_response_code); - CHECK_GT(len_converted, 0); - SetResponseCode(base::StringPiece(buffer, len_converted)); -} - -void BalsaHeaders::SetRequestVersion(const base::StringPiece& version) { - // This is the last of the three parts of the firstline. - // Since whitespace_3_idx and non_whitespace_3_idx may point to the same - // place, we ensure below that any available space includes space for a - // litteral space (' ') character between the second component and the third - // component. If the space between whitespace_3_idx_ and - // end_of_firstline_idx_ is >= to version.size() + 1 (for the space), then we - // can update the firstline in-place. - char* stream_begin = GetPtr(firstline_buffer_base_idx_); - if (version.size() + 1 <= end_of_firstline_idx_ - whitespace_3_idx_) { - *(stream_begin + whitespace_3_idx_) = kSpaceChar; - non_whitespace_3_idx_ = whitespace_3_idx_ + 1; - whitespace_4_idx_ = non_whitespace_3_idx_ + version.size(); - memcpy(stream_begin + non_whitespace_3_idx_, - version.data(), - version.size()); - } else { - // The new version is to large to fit in the space available for the old - // one, so we have to reformat the firstline. - SetFirstlineFromStringPieces(request_method(), request_uri(), version); - } -} - -void BalsaHeaders::SetResponseReasonPhrase(const base::StringPiece& reason) { - // Note: There is no difference between request_version() and - // response_reason_phrase(). Thus, a function to set one is equivalent to a - // function to set the other. We maintain two functions for this as it is - // much more descriptive, and makes code more understandable. - SetRequestVersion(reason); -} - -} // namespace net diff --git a/net/tools/flip_server/balsa_headers.h b/net/tools/flip_server/balsa_headers.h deleted file mode 100644 index 2c8b083..0000000 --- a/net/tools/flip_server/balsa_headers.h +++ /dev/null @@ -1,1138 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_BALSA_HEADERS_H_ -#define NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_H_ - -#include <algorithm> -#include <iosfwd> -#include <iterator> -#include <string> -#include <utility> -#include <vector> - -#include "base/logging.h" -#include "base/port.h" -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/string_piece_utils.h" - -namespace net { - -// WARNING: -// Note that -no- char* returned by any function in this -// file is null-terminated. - -// This class exists to service the specific needs of BalsaHeaders. -// -// Functional goals: -// 1) provide a backing-store for all of the StringPieces that BalsaHeaders -// returns. Every StringPiece returned from BalsaHeaders should remain -// valid until the BalsaHeader's object is cleared, or the header-line is -// erased. -// 2) provide a backing-store for BalsaFrame, which requires contiguous memory -// for its fast-path parsing functions. Note that the cost of copying is -// less than the cost of requiring the parser to do slow-path parsing, as -// it would have to check for bounds every byte, instead of every 16 bytes. -// -// This class is optimized for the case where headers are stored in one of two -// buffers. It doesn't make a lot of effort to densely pack memory-- in fact, -// it -may- be somewhat memory inefficient. This possible inefficiency allows a -// certain simplicity of implementation and speed which makes it worthwhile. -// If, in the future, better memory density is required, it should be possible -// to reuse the abstraction presented by this object to achieve those goals. -// -// In the most common use-case, this memory inefficiency should be relatively -// small. -// -// Alternate implementations of BalsaBuffer may include: -// - vector of strings, one per header line (similar to HTTPHeaders) -// - densely packed strings: -// - keep a sorted array/map of free-space linked lists or numbers. -// - use the entry that most closely first your needs. -// - at this point, perhaps just use a vector of strings, and let -// the allocator do the right thing. -// -class BalsaBuffer { - public: - static const size_t kDefaultBlocksize = 4096; - // We have two friends here. These exist as friends as we - // want to allow access to the constructors for the test - // class and the Balsa* classes. We put this into the - // header file as we want this class to be inlined into the - // BalsaHeaders implementation, yet be testable. - friend class BalsaBufferTestSpouse; - friend class BalsaHeaders; - friend class BalsaBufferTest; - - // The BufferBlock is a structure used internally by the - // BalsaBuffer class to store the base buffer pointers to - // each block, as well as the important metadata for buffer - // sizes and bytes free. - struct BufferBlock { - public: - char* buffer; - size_t buffer_size; - size_t bytes_free; - - size_t bytes_used() const { - return buffer_size - bytes_free; - } - char* start_of_unused_bytes() const { - return buffer + bytes_used(); - } - - BufferBlock() : buffer(NULL), buffer_size(0), bytes_free(0) {} - ~BufferBlock() {} - - BufferBlock(char* buf, size_t size, size_t free) : - buffer(buf), buffer_size(size), bytes_free(free) {} - // Yes we want this to be copyable (it gets stuck into vectors). - // For this reason, we don't use scoped ptrs, etc. here-- it - // is more efficient to manage this memory externally to this - // object. - }; - - typedef std::vector<BufferBlock> Blocks; - - ~BalsaBuffer(); - - // Returns the total amount of memory used by the buffer blocks. - size_t GetTotalBufferBlockSize() const; - - const char* GetPtr(Blocks::size_type block_idx) const { - DCHECK_LT(block_idx, blocks_.size()) - << block_idx << ", " << blocks_.size(); - return blocks_[block_idx].buffer; - } - - char* GetPtr(Blocks::size_type block_idx) { - DCHECK_LT(block_idx, blocks_.size()) - << block_idx << ", " << blocks_.size(); - return blocks_[block_idx].buffer; - } - - // This function is different from Write(), as it ensures that the data - // stored via subsequent calls to this function are all contiguous (and in - // the order in which these writes happened). This is essentially the same - // as a string append. - // - // You may call this function at any time between object - // construction/Clear(), and the calling of the - // NoMoreWriteToContiguousBuffer() function. - // - // You must not call this function after the NoMoreWriteToContiguousBuffer() - // function is called, unless a Clear() has been called since. - // If you do, the program will abort(). - // - // This condition is placed upon this code so that calls to Write() can - // append to the buffer in the first block safely, and without invaliding - // the StringPiece which it returns. - // - // This function's main intended user is the BalsaFrame class, which, - // for reasons of efficiency, requires that the buffer from which it parses - // the headers be contiguous. - // - void WriteToContiguousBuffer(const base::StringPiece& sp); - - void NoMoreWriteToContiguousBuffer() { - can_write_to_contiguous_buffer_ = false; - } - - // Takes a StringPiece and writes it to "permanent" storage, then returns a - // StringPiece which points to that data. If block_idx != NULL, it will be - // assigned the index of the block into which the data was stored. - // Note that the 'permanent' storage in which it stores data may be in - // the first block IFF the NoMoreWriteToContiguousBuffer function has - // been called since the last Clear/Construction. - base::StringPiece Write(const base::StringPiece& sp, - Blocks::size_type* block_buffer_idx); - - // Reserves "permanent" storage of the size indicated. Returns a pointer to - // the beginning of that storage, and assigns the index of the block used to - // block_buffer_idx. This function uses the first block IFF the - // NoMoreWriteToContiguousBuffer function has been called since the last - // Clear/Construction. - char* Reserve(size_t size, Blocks::size_type* block_buffer_idx); - - void Clear(); - - void Swap(BalsaBuffer* b); - - void CopyFrom(const BalsaBuffer& b); - - const char* StartOfFirstBlock() const { - return blocks_[0].buffer; - } - - const char* EndOfFirstBlock() const { - return blocks_[0].buffer + blocks_[0].bytes_used(); - } - - bool can_write_to_contiguous_buffer() const { - return can_write_to_contiguous_buffer_; - } - size_t blocksize() const { return blocksize_; } - Blocks::size_type num_blocks() const { return blocks_.size(); } - size_t buffer_size(size_t idx) const { return blocks_[idx].buffer_size; } - size_t bytes_used(size_t idx) const { return blocks_[idx].bytes_used(); } - - protected: - BalsaBuffer(); - - explicit BalsaBuffer(size_t blocksize); - - BufferBlock AllocBlock(); - - BufferBlock AllocCustomBlock(size_t blocksize); - - BufferBlock CopyBlock(const BufferBlock& b); - - // Cleans up the object. - // The block at start_idx, and all subsequent blocks - // will be cleared and have associated memory deleted. - void CleanupBlocksStartingFrom(Blocks::size_type start_idx); - - // A container of BufferBlocks - Blocks blocks_; - - // The default allocation size for a block. - // In general, blocksize_ bytes will be allocated for - // each buffer. - size_t blocksize_; - - // If set to true, then the first block cannot be used for Write() calls as - // the WriteToContiguous... function will modify the base pointer for this - // block, and the Write() calls need to be sure that the base pointer will - // not be changing in order to provide the user with StringPieces which - // continue to be valid. - bool can_write_to_contiguous_buffer_; -}; - -//////////////////////////////////////////////////////////////////////////////// - -// All of the functions in the BalsaHeaders class use string pieces, by either -// using the StringPiece class, or giving an explicit size and char* (as these -// are the native representation for these string pieces). -// This is done for several reasons. -// 1) This minimizes copying/allocation/deallocation as compared to using -// string parameters -// 2) This reduces the number of strlen() calls done (as the length of any -// string passed in is relatively likely to be known at compile time, and for -// those strings passed back we obviate the need for a strlen() to determine -// the size of new storage allocations if a new allocation is required. -// 3) This class attempts to store all of its data in two linear buffers in -// order to enhance the speed of parsing and writing out to a buffer. As a -// result, many string pieces are -not- terminated by '\0', and are not -// c-strings. Since this is the case, we must delineate the length of the -// string explicitly via a length. -// -// WARNING: The side effect of using StringPiece is that if the underlying -// buffer changes (due to modifying the headers) the StringPieces which point -// to the data which was modified, may now contain "garbage", and should not -// be dereferenced. -// For example, If you fetch some component of the first-line, (request or -// response), and then you modify the first line, the StringPieces you -// originally received from the original first-line may no longer be valid). -// -// StringPieces pointing to pieces of header lines which have not been -// erased() or modified should be valid until the object is cleared or -// destroyed. - -class BalsaHeaders { - public: - struct HeaderLineDescription { - HeaderLineDescription(size_t first_character_index, - size_t key_end_index, - size_t value_begin_index, - size_t last_character_index, - size_t buffer_base_index) : - first_char_idx(first_character_index), - key_end_idx(key_end_index), - value_begin_idx(value_begin_index), - last_char_idx(last_character_index), - buffer_base_idx(buffer_base_index), - skip(false) {} - - HeaderLineDescription() : - first_char_idx(0), - key_end_idx(0), - value_begin_idx(0), - last_char_idx(0), - buffer_base_idx(0), - skip(false) {} - - size_t first_char_idx; - size_t key_end_idx; - size_t value_begin_idx; - size_t last_char_idx; - BalsaBuffer::Blocks::size_type buffer_base_idx; - bool skip; - }; - - typedef std::vector<base::StringPiece> HeaderTokenList; - friend bool ParseHTTPFirstLine(const char* begin, - const char* end, - bool is_request, - size_t max_request_uri_length, - BalsaHeaders* headers, - BalsaFrameEnums::ErrorCode* error_code); - - protected: - typedef std::vector<HeaderLineDescription> HeaderLines; - - // Why these base classes (iterator_base, reverse_iterator_base)? Well, if - // we do want to export both iterator and const_iterator types (currently we - // only have const_iterator), then this is useful to avoid code duplication. - // Additionally, having this base class makes comparisons of iterators of - // different types (they're different types to ensure that operator= and - // constructors do not work in the places where they're expected to not work) - // work properly. There could be as many as 4 iterator types, all based on - // the same data as iterator_base... so it makes sense to simply have some - // base classes. - - class iterator_base { - public: - friend class BalsaHeaders; - friend class reverse_iterator_base; - typedef std::pair<base::StringPiece, base::StringPiece> StringPiecePair; - typedef StringPiecePair value_type; - typedef value_type& reference; - typedef value_type* pointer; - - typedef std::forward_iterator_tag iterator_category; - typedef ptrdiff_t difference_type; - - typedef iterator_base self; - - // default constructor. - iterator_base(); - - // copy constructor. - iterator_base(const iterator_base& it); - - reference operator*() const { - return Lookup(idx_); - } - - pointer operator->() const { - return &(this->operator*()); - } - - bool operator==(const self& it) const { - return idx_ == it.idx_; - } - - bool operator<(const self& it) const { - return idx_ < it.idx_; - } - - bool operator<=(const self& it) const { - return idx_ <= it.idx_; - } - - bool operator!=(const self& it) const { - return !(*this == it); - } - - bool operator>(const self& it) const { - return it < *this; - } - - bool operator>=(const self& it) const { - return it <= *this; - } - - // This mainly exists so that we can have interesting output for - // unittesting. The EXPECT_EQ, EXPECT_NE functions require that - // operator<< work for the classes it sees. It would be better if there - // was an additional traits-like system for the gUnit output... but oh - // well. - std::ostream& operator<<(std::ostream& os) const; - - protected: - iterator_base(const BalsaHeaders* headers, HeaderLines::size_type index); - - void increment() { - const HeaderLines& header_lines = headers_->header_lines_; - const HeaderLines::size_type header_lines_size = header_lines.size(); - const HeaderLines::size_type original_idx = idx_; - do { - ++idx_; - } while (idx_ < header_lines_size && header_lines[idx_].skip == true); - // The condition below exists so that ++(end() - 1) == end(), even - // if there are only 'skip == true' elements between the end() iterator - // and the end of the vector of HeaderLineDescriptions. - // TODO(fenix): refactor this list so that we don't have to do - // linear scanning through skipped headers (and this condition is - // then unnecessary) - if (idx_ == header_lines_size) { - idx_ = original_idx + 1; - } - } - - void decrement() { - const HeaderLines& header_lines = headers_->header_lines_; - const HeaderLines::size_type header_lines_size = header_lines.size(); - const HeaderLines::size_type original_idx = idx_; - do { - --idx_; - } while (idx_ < header_lines_size && header_lines[idx_].skip == true); - // The condition below exists so that --(rbegin() + 1) == rbegin(), even - // if there are only 'skip == true' elements between the rbegin() iterator - // and the beginning of the vector of HeaderLineDescriptions. - // TODO(fenix): refactor this list so that we don't have to do - // linear scanning through skipped headers (and this condition is - // then unnecessary) - if (idx_ > header_lines_size) { - idx_ = original_idx - 1; - } - } - - reference Lookup(HeaderLines::size_type index) const { - DCHECK_LT(index, headers_->header_lines_.size()); - const HeaderLineDescription& line = headers_->header_lines_[index]; - const char* stream_begin = headers_->GetPtr(line.buffer_base_idx); - value_ = value_type( - base::StringPiece(stream_begin + line.first_char_idx, - line.key_end_idx - line.first_char_idx), - base::StringPiece(stream_begin + line.value_begin_idx, - line.last_char_idx - line.value_begin_idx)); - DCHECK_GE(line.key_end_idx, line.first_char_idx); - DCHECK_GE(line.last_char_idx, line.value_begin_idx); - return value_; - } - - const BalsaHeaders* headers_; - HeaderLines::size_type idx_; - mutable StringPiecePair value_; - }; - - class reverse_iterator_base : public iterator_base { - public: - typedef reverse_iterator_base self; - typedef iterator_base::reference reference; - typedef iterator_base::pointer pointer; - using iterator_base::headers_; - using iterator_base::idx_; - - reverse_iterator_base() : iterator_base() {} - - // This constructor is no explicit purposely. - reverse_iterator_base(const iterator_base& it) : // NOLINT - iterator_base(it) { - } - - self& operator=(const iterator_base& it) { - idx_ = it.idx_; - headers_ = it.headers_; - return *this; - } - - self& operator=(const reverse_iterator_base& it) { - idx_ = it.idx_; - headers_ = it.headers_; - return *this; - } - - reference operator*() const { - return Lookup(idx_ - 1); - } - - pointer operator->() const { - return &(this->operator*()); - } - - reverse_iterator_base(const reverse_iterator_base& it) : - iterator_base(it) { } - - protected: - void increment() { - --idx_; - iterator_base::decrement(); - ++idx_; - } - - void decrement() { - ++idx_; - iterator_base::increment(); - --idx_; - } - - reverse_iterator_base(const BalsaHeaders* headers, - HeaderLines::size_type index) : - iterator_base(headers, index) {} - }; - - public: - class const_header_lines_iterator : public iterator_base { - friend class BalsaHeaders; - public: - typedef const_header_lines_iterator self; - const_header_lines_iterator() : iterator_base() {} - - const_header_lines_iterator(const const_header_lines_iterator& it) : - iterator_base(it.headers_, it.idx_) {} - - self& operator++() { - iterator_base::increment(); - return *this; - } - - self& operator--() { - iterator_base::decrement(); - return *this; - } - protected: - const_header_lines_iterator(const BalsaHeaders* headers, - HeaderLines::size_type index) : - iterator_base(headers, index) {} - }; - - class const_reverse_header_lines_iterator : public reverse_iterator_base { - public: - typedef const_reverse_header_lines_iterator self; - const_reverse_header_lines_iterator() : reverse_iterator_base() {} - - const_reverse_header_lines_iterator( - const const_header_lines_iterator& it) : - reverse_iterator_base(it.headers_, it.idx_) {} - - const_reverse_header_lines_iterator( - const const_reverse_header_lines_iterator& it) : - reverse_iterator_base(it.headers_, it.idx_) {} - - const_header_lines_iterator base() { - return const_header_lines_iterator(headers_, idx_); - } - - self& operator++() { - reverse_iterator_base::increment(); - return *this; - } - - self& operator--() { - reverse_iterator_base::decrement(); - return *this; - } - protected: - const_reverse_header_lines_iterator(const BalsaHeaders* headers, - HeaderLines::size_type index) : - reverse_iterator_base(headers, index) {} - - friend class BalsaHeaders; - }; - - // An iterator that only stops at lines with a particular key. - // See also GetIteratorForKey. - // - // Check against header_lines_key_end() to determine when iteration is - // finished. header_lines_end() will also work. - class const_header_lines_key_iterator : public iterator_base { - friend class BalsaHeaders; - public: - typedef const_header_lines_key_iterator self; - const_header_lines_key_iterator(const const_header_lines_key_iterator&); - - self& operator++() { - do { - iterator_base::increment(); - } while (!AtEnd() && - !StringPieceUtils::EqualIgnoreCase(key_, (**this).first)); - return *this; - } - - void operator++(int ignore) { - ++(*this); - } - - // Only forward-iteration makes sense, so no operator-- defined. - - private: - const_header_lines_key_iterator(const BalsaHeaders* headers, - HeaderLines::size_type index, - const base::StringPiece& key); - - // Should only be used for creating an end iterator. - const_header_lines_key_iterator(const BalsaHeaders* headers, - HeaderLines::size_type index); - - bool AtEnd() const { - return *this >= headers_->header_lines_end(); - } - - base::StringPiece key_; - }; - - // TODO(fenix): Revisit the amount of bytes initially allocated to the second - // block of the balsa_buffer_. It may make sense to pre-allocate some amount - // (roughly the amount we'd append in new headers such as X-User-Ip, etc.) - BalsaHeaders(); - ~BalsaHeaders(); - - const_header_lines_iterator header_lines_begin() { - return HeaderLinesBeginHelper<const_header_lines_iterator>(); - } - - const_header_lines_iterator header_lines_begin() const { - return HeaderLinesBeginHelper<const_header_lines_iterator>(); - } - - const_header_lines_iterator header_lines_end() { - return HeaderLinesEndHelper<const_header_lines_iterator>(); - } - - const_header_lines_iterator header_lines_end() const { - return HeaderLinesEndHelper<const_header_lines_iterator>(); - } - - const_reverse_header_lines_iterator header_lines_rbegin() { - return const_reverse_header_lines_iterator(header_lines_end()); - } - - const_reverse_header_lines_iterator header_lines_rbegin() const { - return const_reverse_header_lines_iterator(header_lines_end()); - } - - const_reverse_header_lines_iterator header_lines_rend() { - return const_reverse_header_lines_iterator(header_lines_begin()); - } - - const_reverse_header_lines_iterator header_lines_rend() const { - return const_reverse_header_lines_iterator(header_lines_begin()); - } - - const_header_lines_key_iterator header_lines_key_end() const { - return HeaderLinesEndHelper<const_header_lines_key_iterator>(); - } - - void erase(const const_header_lines_iterator& it) { - DCHECK_EQ(it.headers_, this); - DCHECK_LT(it.idx_, header_lines_.size()); - DCHECK_GE(it.idx_, 0u); - header_lines_[it.idx_].skip = true; - } - - void Clear(); - - void Swap(BalsaHeaders* other); - - void CopyFrom(const BalsaHeaders& other); - - void HackHeader(const base::StringPiece& key, const base::StringPiece& value); - - // Same as AppendToHeader, except that it will attempt to preserve - // header ordering. - // Note that this will always append to an existing header, if available, - // without moving the header around, or collapsing multiple header lines - // with the same key together. For this reason, it only 'attempts' to - // preserve header ordering. - // TODO(fenix): remove this function and rename all occurances - // of it in the code to AppendToHeader when the condition above - // has been satisified. - void HackAppendToHeader(const base::StringPiece& key, - const base::StringPiece& value); - - // Replaces header entries with key 'key' if they exist, or appends - // a new header if none exist. See 'AppendHeader' below for additional - // comments about ContentLength and TransferEncoding headers. Note that this - // will allocate new storage every time that it is called. - // TODO(fenix): modify this function to reuse existing storage - // if it is available. - void ReplaceOrAppendHeader(const base::StringPiece& key, - const base::StringPiece& value); - - // Append a new header entry to the header object. Clients who wish to append - // Content-Length header should use SetContentLength() method instead of - // adding the content length header using AppendHeader (manually adding the - // content length header will not update the content_length_ and - // content_length_status_ values). - // Similarly, clients who wish to add or remove the transfer encoding header - // in order to apply or remove chunked encoding should use SetChunkEncoding() - // instead. - void AppendHeader(const base::StringPiece& key, - const base::StringPiece& value); - - // Appends ',value' to an existing header named 'key'. If no header with the - // correct key exists, it will call AppendHeader(key, value). Calling this - // function on a key which exists several times in the headers will produce - // unpredictable results. - void AppendToHeader(const base::StringPiece& key, - const base::StringPiece& value); - - // Prepends 'value,' to an existing header named 'key'. If no header with the - // correct key exists, it will call AppendHeader(key, value). Calling this - // function on a key which exists several times in the headers will produce - // unpredictable results. - void PrependToHeader(const base::StringPiece& key, - const base::StringPiece& value); - - const base::StringPiece GetHeader(const base::StringPiece& key) const; - - // Iterates over all currently valid header lines, appending their - // values into the vector 'out', in top-to-bottom order. - // Header-lines which have been erased are not currently valid, and - // will not have their values appended. Empty values will be - // represented as empty string. If 'key' doesn't exist in the headers at - // all, out will not be changed. We do not clear the vector out - // before adding new entries. If there are header lines with matching - // key but empty value then they are also added to the vector out. - // (Basically empty values are not treated in any special manner). - // - // Example: - // Input header: - // "GET / HTTP/1.0\r\n" - // "key1: v1\r\n" - // "key1: \r\n" - // "key1:\r\n" - // "key1: v1\r\n" - // "key1:v2\r\n" - // - // vector out is initially: ["foo"] - // vector out after GetAllOfHeader("key1", &out) is: - // ["foo", "v1", "", "", "v2", "v1", "v2"] - - void GetAllOfHeader(const base::StringPiece& key, - std::vector<base::StringPiece>* out) const; - - // Joins all values for key into a comma-separated string in out. - // More efficient than calling JoinStrings on result of GetAllOfHeader if - // you don't need the intermediate vector<StringPiece>. - void GetAllOfHeaderAsString(const base::StringPiece& key, - std::string* out) const; - - // Returns true if RFC 2616 Section 14 indicates that header can - // have multiple values. - static bool IsMultivaluedHeader(const base::StringPiece& header); - - // Determine if a given header is present. - inline bool HasHeader(const base::StringPiece& key) const { - return (GetConstHeaderLinesIterator(key, header_lines_.begin()) != - header_lines_.end()); - } - - // Returns true iff any header 'key' exists with non-empty value. - bool HasNonEmptyHeader(const base::StringPiece& key) const; - - const_header_lines_iterator GetHeaderPosition( - const base::StringPiece& key) const; - - // Returns a forward-only iterator that only stops at lines matching key. - // String backing 'key' must remain valid for lifetime of iterator. - // - // Check returned iterator against header_lines_key_end() to determine when - // iteration is finished. - const_header_lines_key_iterator GetIteratorForKey( - const base::StringPiece& key) const; - - void RemoveAllOfHeader(const base::StringPiece& key); - - // Removes all headers starting with 'key' [case insensitive] - void RemoveAllHeadersWithPrefix(const base::StringPiece& key); - - // Returns the lower bound of memory used by this header object, including - // all internal buffers and data structure. Some of the memory used cannot be - // directly measure. For example, memory used for bookkeeping by standard - // containers. - size_t GetMemoryUsedLowerBound() const; - - // Returns the upper bound on the required buffer space to fully write out - // the header object (this include the first line, all header lines, and the - // final CRLF that marks the ending of the header). - size_t GetSizeForWriteBuffer() const; - - // The following WriteHeader* methods are template member functions that - // place one requirement on the Buffer class: it must implement a Write - // method that takes a pointer and a length. The buffer passed in is not - // required to be stretchable. For non-stretchable buffers, the user must - // call GetSizeForWriteBuffer() to find out the upper bound on the output - // buffer space required to make sure that the entire header is serialized. - // BalsaHeaders will not check that there is adequate space in the buffer - // object during the write. - - // Writes the entire header and the final CRLF that marks the end of the HTTP - // header section to the buffer. After this method returns, no more header - // data should be written to the buffer. - template <typename Buffer> - void WriteHeaderAndEndingToBuffer(Buffer* buffer) const { - WriteToBuffer(buffer); - WriteHeaderEndingToBuffer(buffer); - } - - // Writes the final CRLF to the buffer to terminate the HTTP header section. - // After this method returns, no more header data should be written to the - // buffer. - template <typename Buffer> - static void WriteHeaderEndingToBuffer(Buffer* buffer) { - buffer->Write("\r\n", 2); - } - - // Writes the entire header to the buffer without the CRLF that terminates - // the HTTP header. This lets users append additional header lines using - // WriteHeaderLineToBuffer and then terminate the header with - // WriteHeaderEndingToBuffer as the header is serialized to the - // buffer, without having to first copy the header. - template <typename Buffer> - void WriteToBuffer(Buffer* buffer) const { - // write the first line. - const size_t firstline_len = whitespace_4_idx_ - non_whitespace_1_idx_; - const char* stream_begin = GetPtr(firstline_buffer_base_idx_); - buffer->Write(stream_begin + non_whitespace_1_idx_, firstline_len); - buffer->Write("\r\n", 2); - const HeaderLines::size_type end = header_lines_.size(); - for (HeaderLines::size_type i = 0; i < end; ++i) { - const HeaderLineDescription& line = header_lines_[i]; - if (line.skip) { - continue; - } - const char* line_ptr = GetPtr(line.buffer_base_idx); - WriteHeaderLineToBuffer( - buffer, - base::StringPiece(line_ptr + line.first_char_idx, - line.key_end_idx - line.first_char_idx), - base::StringPiece(line_ptr + line.value_begin_idx, - line.last_char_idx - line.value_begin_idx)); - } - } - - // Takes a header line in the form of a key/value pair and append it to the - // buffer. This function should be called after WriteToBuffer to - // append additional header lines to the header without copying the header. - // When the user is done with appending to the buffer, - // WriteHeaderEndingToBuffer must be used to terminate the HTTP - // header in the buffer. This method is a no-op if key is empty. - template <typename Buffer> - static void WriteHeaderLineToBuffer(Buffer* buffer, - const base::StringPiece& key, - const base::StringPiece& value) { - // if the key is empty, we don't want to write the rest because it - // will not be a well-formed header line. - if (!key.empty()) { - buffer->Write(key.data(), key.size()); - buffer->Write(": ", 2); - buffer->Write(value.data(), value.size()); - buffer->Write("\r\n", 2); - } - } - - // Dump the textural representation of the header object to a string, which - // is suitable for writing out to logs. All CRLF will be printed out as \n. - // This function can be called on a header object in any state. Raw header - // data will be printed out if the header object is not completely parsed, - // e.g., when there was an error in the middle of parsing. - // The header content is appended to the string; the original content is not - // cleared. - void DumpToString(std::string* str) const; - - const base::StringPiece first_line() const { - DCHECK_GE(whitespace_4_idx_, non_whitespace_1_idx_); - return base::StringPiece(BeginningOfFirstLine() + non_whitespace_1_idx_, - whitespace_4_idx_ - non_whitespace_1_idx_); - } - - // Returns the parsed value of the response code if it has been parsed. - // Guaranteed to return 0 when unparsed (though it is a much better idea to - // verify that the BalsaFrame had no errors while parsing). - // This may return response codes which are outside the normal bounds of - // HTTP response codes-- it is up to the user of this class to ensure that - // the response code is one which is interpretable. - size_t parsed_response_code() const { return parsed_response_code_; } - - const base::StringPiece request_method() const { - DCHECK_GE(whitespace_2_idx_, non_whitespace_1_idx_); - return base::StringPiece(BeginningOfFirstLine() + non_whitespace_1_idx_, - whitespace_2_idx_ - non_whitespace_1_idx_); - } - - const base::StringPiece response_version() const { - // Note: There is no difference between request_method() and - // response_version(). They both could be called - // GetFirstTokenFromFirstline()... but that wouldn't be anywhere near as - // descriptive. - return request_method(); - } - - const base::StringPiece request_uri() const { - DCHECK_GE(whitespace_3_idx_, non_whitespace_2_idx_); - return base::StringPiece(BeginningOfFirstLine() + non_whitespace_2_idx_, - whitespace_3_idx_ - non_whitespace_2_idx_); - } - - const base::StringPiece response_code() const { - // Note: There is no difference between request_uri() and response_code(). - // They both could be called GetSecondtTokenFromFirstline(), but, as noted - // in an earlier comment, that wouldn't be as descriptive. - return request_uri(); - } - - const base::StringPiece request_version() const { - DCHECK_GE(whitespace_4_idx_, non_whitespace_3_idx_); - return base::StringPiece(BeginningOfFirstLine() + non_whitespace_3_idx_, - whitespace_4_idx_ - non_whitespace_3_idx_); - } - - const base::StringPiece response_reason_phrase() const { - // Note: There is no difference between request_version() and - // response_reason_phrase(). They both could be called - // GetThirdTokenFromFirstline(), but, as noted in an earlier comment, that - // wouldn't be as descriptive. - return request_version(); - } - - // Note that SetFirstLine will not update the internal indices for the - // various bits of the first-line (and may set them all to zero). - // If you'd like to use the accessors for the various bits of the firstline, - // then you should use the Set* functions, or SetFirstlineFromStringPieces, - // below, instead. - // - void SetFirstlineFromStringPieces(const base::StringPiece& firstline_a, - const base::StringPiece& firstline_b, - const base::StringPiece& firstline_c); - - void SetRequestFirstlineFromStringPieces(const base::StringPiece& method, - const base::StringPiece& uri, - const base::StringPiece& version) { - SetFirstlineFromStringPieces(method, uri, version); - } - - void SetResponseFirstlineFromStringPieces( - const base::StringPiece& version, - const base::StringPiece& code, - const base::StringPiece& reason_phrase) { - SetFirstlineFromStringPieces(version, code, reason_phrase); - } - - // These functions are exactly the same, except that their names are - // different. This is done so that the code using this class is more - // expressive. - void SetRequestMethod(const base::StringPiece& method); - void SetResponseVersion(const base::StringPiece& version); - - void SetRequestUri(const base::StringPiece& uri); - void SetResponseCode(const base::StringPiece& code); - void set_parsed_response_code(size_t parsed_response_code) { - parsed_response_code_ = parsed_response_code; - } - void SetParsedResponseCodeAndUpdateFirstline(size_t parsed_response_code); - - // These functions are exactly the same, except that their names are - // different. This is done so that the code using this class is more - // expressive. - void SetRequestVersion(const base::StringPiece& version); - void SetResponseReasonPhrase(const base::StringPiece& reason_phrase); - - // The biggest problem with SetFirstLine is that we don't want to use a - // separate buffer for it. The second biggest problem with it is that the - // first biggest problem requires that we store offsets into a buffer instead - // of pointers into a buffer. Cuteness aside, SetFirstLine doesn't parse - // the individual fields of the firstline, and so accessors to those fields - // will not work properly after calling SetFirstLine. If you want those - // accessors to work, use the Set* functions above this one. - // SetFirstLine is stuff useful, however, if all you care about is correct - // serialization with the rest of the header object. - void SetFirstLine(const base::StringPiece& line); - - // Simple accessors to some of the internal state - bool transfer_encoding_is_chunked() const { - return transfer_encoding_is_chunked_; - } - - static bool ResponseCodeImpliesNoBody(int code) { - // From HTTP spec section 6.1.1 all 1xx responses must not have a body, - // as well as 204 No Content and 304 Not Modified. - return ((code >= 100) && (code <= 199)) || (code == 204) || (code == 304); - } - - // Note: never check this for requests. Nothing bad will happen if you do, - // but spec does not allow requests framed by connection close. - // TODO(vitaliyl): refactor. - bool is_framed_by_connection_close() const { - // We declare that response is framed by connection close if it has no - // content-length, no transfer encoding, and is allowed to have a body by - // the HTTP spec. - // parsed_response_code_ is 0 for requests, so ResponseCodeImpliesNoBody - // will return false. - return (content_length_status_ == BalsaHeadersEnums::NO_CONTENT_LENGTH) && - !transfer_encoding_is_chunked_ && - !ResponseCodeImpliesNoBody(parsed_response_code_); - } - - size_t content_length() const { return content_length_; } - BalsaHeadersEnums::ContentLengthStatus content_length_status() const { - return content_length_status_; - } - - // SetContentLength and SetChunkEncoding modifies the header object to use - // content-length and transfer-encoding headers in a consistent manner. They - // set all internal flags and status so client can get a consistent view from - // various accessors. - void SetContentLength(size_t length); - void SetChunkEncoding(bool chunk_encode); - - protected: - friend class BalsaFrame; - friend class SpdyFrame; - friend class HTTPMessage; - friend class BalsaHeadersTokenUtils; - - const char* BeginningOfFirstLine() const { - return GetPtr(firstline_buffer_base_idx_); - } - - char* GetPtr(BalsaBuffer::Blocks::size_type block_idx) { - return balsa_buffer_.GetPtr(block_idx); - } - - const char* GetPtr(BalsaBuffer::Blocks::size_type block_idx) const { - return balsa_buffer_.GetPtr(block_idx); - } - - void WriteFromFramer(const char* ptr, size_t size) { - balsa_buffer_.WriteToContiguousBuffer(base::StringPiece(ptr, size)); - } - - void DoneWritingFromFramer() { - balsa_buffer_.NoMoreWriteToContiguousBuffer(); - } - - const char* OriginalHeaderStreamBegin() const { - return balsa_buffer_.StartOfFirstBlock(); - } - - const char* OriginalHeaderStreamEnd() const { - return balsa_buffer_.EndOfFirstBlock(); - } - - size_t GetReadableBytesFromHeaderStream() const { - return OriginalHeaderStreamEnd() - OriginalHeaderStreamBegin(); - } - - void GetReadablePtrFromHeaderStream(const char** p, size_t* s) { - *p = OriginalHeaderStreamBegin(); - *s = GetReadableBytesFromHeaderStream(); - } - - base::StringPiece GetValueFromHeaderLineDescription( - const HeaderLineDescription& line) const; - - void AddAndMakeDescription(const base::StringPiece& key, - const base::StringPiece& value, - HeaderLineDescription* d); - - void AppendOrPrependAndMakeDescription(const base::StringPiece& key, - const base::StringPiece& value, - bool append, - HeaderLineDescription* d); - - // Removes all header lines with the given key starting at start. - void RemoveAllOfHeaderStartingAt(const base::StringPiece& key, - HeaderLines::iterator start); - - // If the 'key' does not exist in the headers, calls - // AppendHeader(key, value). Otherwise if append is true, appends ',value' - // to the first existing header with key 'key'. If append is false, prepends - // 'value,' to the first existing header with key 'key'. - void AppendOrPrependToHeader(const base::StringPiece& key, - const base::StringPiece& value, - bool append); - - HeaderLines::const_iterator GetConstHeaderLinesIterator( - const base::StringPiece& key, - HeaderLines::const_iterator start) const; - - HeaderLines::iterator GetHeaderLinesIteratorNoSkip( - const base::StringPiece& key, - HeaderLines::iterator start); - - HeaderLines::iterator GetHeaderLinesIterator( - const base::StringPiece& key, - HeaderLines::iterator start); - - template <typename IteratorType> - const IteratorType HeaderLinesBeginHelper() const { - if (header_lines_.empty()) { - return IteratorType(this, 0); - } - const HeaderLines::size_type header_lines_size = header_lines_.size(); - for (HeaderLines::size_type i = 0; i < header_lines_size; ++i) { - if (header_lines_[i].skip == false) { - return IteratorType(this, i); - } - } - return IteratorType(this, 0); - } - - template <typename IteratorType> - const IteratorType HeaderLinesEndHelper() const { - if (header_lines_.empty()) { - return IteratorType(this, 0); - } - const HeaderLines::size_type header_lines_size = header_lines_.size(); - HeaderLines::size_type i = header_lines_size; - do { - --i; - if (header_lines_[i].skip == false) { - return IteratorType(this, i + 1); - } - } while (i != 0); - return IteratorType(this, 0); - } - - // At the moment, this function will always return the original headers. - // In the future, it may not do so after erasing header lines, modifying - // header lines, or modifying the first line. - // For this reason, it is strongly suggested that use of this function is - // only acceptable for the purpose of debugging parse errors seen by the - // BalsaFrame class. - base::StringPiece OriginalHeadersForDebugging() const { - return base::StringPiece(OriginalHeaderStreamBegin(), - OriginalHeaderStreamEnd() - OriginalHeaderStreamBegin()); - } - - BalsaBuffer balsa_buffer_; - - size_t content_length_; - BalsaHeadersEnums::ContentLengthStatus content_length_status_; - size_t parsed_response_code_; - // HTTP firstlines all have the following structure: - // LWS NONWS LWS NONWS LWS NONWS NOTCRLF CRLF - // [\t \r\n]+ [^\t ]+ [\t ]+ [^\t ]+ [\t ]+ [^\t ]+ [^\r\n]+ "\r\n" - // ws1 nws1 ws2 nws2 ws3 nws3 ws4 - // | [-------) [-------) [----------------) - // REQ: method request_uri version - // RESP: version statuscode reason - // - // The first NONWS->LWS component we'll call firstline_a. - // The second firstline_b, and the third firstline_c. - // - // firstline_a goes from nws1 to (but not including) ws2 - // firstline_b goes from nws2 to (but not including) ws3 - // firstline_c goes from nws3 to (but not including) ws4 - // - // In the code: - // ws1 == whitespace_1_idx_ - // nws1 == non_whitespace_1_idx_ - // ws2 == whitespace_2_idx_ - // nws2 == non_whitespace_2_idx_ - // ws3 == whitespace_3_idx_ - // nws3 == non_whitespace_3_idx_ - // ws4 == whitespace_4_idx_ - BalsaBuffer::Blocks::size_type firstline_buffer_base_idx_; - size_t whitespace_1_idx_; - size_t non_whitespace_1_idx_; - size_t whitespace_2_idx_; - size_t non_whitespace_2_idx_; - size_t whitespace_3_idx_; - size_t non_whitespace_3_idx_; - size_t whitespace_4_idx_; - size_t end_of_firstline_idx_; - - bool transfer_encoding_is_chunked_; - - HeaderLines header_lines_; -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_H_ diff --git a/net/tools/flip_server/balsa_headers_test.cc b/net/tools/flip_server/balsa_headers_test.cc deleted file mode 100644 index 241fee1..0000000 --- a/net/tools/flip_server/balsa_headers_test.cc +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2013 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/tools/flip_server/balsa_headers.h" - -#include <iterator> -#include <string> -#include <vector> - -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -using ::base::StringPiece; - -class BalsaBufferTest : public ::testing::Test { - public: - virtual void SetUp() OVERRIDE { - buffer_.reset(new BalsaBuffer); - anotherBuffer_.reset(new BalsaBuffer); - } - - protected: - scoped_ptr<BalsaBuffer> buffer_; - scoped_ptr<BalsaBuffer> anotherBuffer_; -}; - -namespace { - -class BalsaHeadersTest: public ::testing::Test { - public: - virtual void SetUp() OVERRIDE { - headers_.reset(new BalsaHeaders); - } - - protected: - scoped_ptr<BalsaHeaders> headers_; -}; - -class StringBuffer { - public: - void Write(const char* p, size_t size) { - string_ += std::string(p, size); - } - const std::string& string() {return string_;} - - private: - std::string string_; -}; - -TEST_F(BalsaBufferTest, EmptyBuffer) { - ASSERT_EQ(1u, buffer_->num_blocks()); -} - -TEST_F(BalsaBufferTest, Write) { - size_t index1, index2; - StringPiece sp1 = buffer_->Write(StringPiece("hello"), &index1); - StringPiece sp2 = buffer_->Write(StringPiece(", world"), &index2); - - ASSERT_EQ(2u, buffer_->num_blocks()); - ASSERT_EQ("hello", sp1); - ASSERT_EQ(", world", sp2); - ASSERT_EQ(1u, index1); - ASSERT_EQ(1u, index2); - ASSERT_EQ("hello, world", - StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); -} - -TEST_F(BalsaBufferTest, WriteLongData) { - size_t index1, index2, index3; - std::string as(2, 'a'); - std::string bs(BalsaBuffer::kDefaultBlocksize + 1, 'b'); - std::string cs(4, 'c'); - - StringPiece sp1 = buffer_->Write(as, &index1); - StringPiece sp2 = buffer_->Write(bs, &index2); - StringPiece sp3 = buffer_->Write(cs, &index3); - - ASSERT_EQ(3u, buffer_->num_blocks()); - ASSERT_EQ(as, sp1); - ASSERT_EQ(bs, sp2); - ASSERT_EQ(cs, sp3); - ASSERT_EQ(1u, index1); - ASSERT_EQ(2u, index2); - ASSERT_EQ(1u, index3); - ASSERT_EQ("aacccc", StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); - ASSERT_EQ(sp2, StringPiece(buffer_->GetPtr(2), buffer_->bytes_used(2))); -} - -TEST_F(BalsaBufferTest, WriteToContiguousBuffer) { - std::string as(2, 'a'); - std::string bs(BalsaBuffer::kDefaultBlocksize + 1, 'b'); - std::string cs(4, 'c'); - - buffer_->WriteToContiguousBuffer(as); - buffer_->WriteToContiguousBuffer(bs); - buffer_->WriteToContiguousBuffer(cs); - - ASSERT_EQ(1u, buffer_->num_blocks()); - ASSERT_EQ(as + bs + cs, - StringPiece(buffer_->GetPtr(0), buffer_->bytes_used(0))); -} - -TEST_F(BalsaBufferTest, NoMoreWriteToContiguousBuffer) { - size_t index1, index2; - StringPiece sp1 = buffer_->Write(StringPiece("hello"), &index1); - buffer_->NoMoreWriteToContiguousBuffer(); - StringPiece sp2 = buffer_->Write(StringPiece(", world"), &index2); - - ASSERT_EQ(2u, buffer_->num_blocks()); - ASSERT_EQ("hello", sp1); - ASSERT_EQ(", world", sp2); - ASSERT_EQ(1u, index1); - ASSERT_EQ(0u, index2); - ASSERT_EQ(sp1, StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); - ASSERT_EQ(sp2, StringPiece(buffer_->GetPtr(0), buffer_->bytes_used(0))); -} - -TEST_F(BalsaBufferTest, Clear) { - buffer_->Write("hello", NULL); - ASSERT_EQ(2u, buffer_->num_blocks()); - buffer_->Clear(); - ASSERT_EQ(1u, buffer_->num_blocks()); -} - -TEST_F(BalsaBufferTest, Swap) { - buffer_->Write("hello", NULL); - - ASSERT_EQ(2u, buffer_->num_blocks()); - ASSERT_EQ(1u, anotherBuffer_->num_blocks()); - - buffer_->Swap(anotherBuffer_.get()); - - ASSERT_EQ(1u, buffer_->num_blocks()); - ASSERT_EQ(2u, anotherBuffer_->num_blocks()); - ASSERT_EQ("hello", - StringPiece(anotherBuffer_->GetPtr(1), - anotherBuffer_->bytes_used(1))); -} - -TEST_F(BalsaBufferTest, CopyFrom) { - buffer_->Write("hello", NULL); - - ASSERT_EQ(2u, buffer_->num_blocks()); - ASSERT_EQ(1u, anotherBuffer_->num_blocks()); - - anotherBuffer_->CopyFrom(*buffer_); - - ASSERT_EQ(2u, buffer_->num_blocks()); - ASSERT_EQ(2u, anotherBuffer_->num_blocks()); - ASSERT_EQ("hello", StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); - ASSERT_EQ("hello", - StringPiece(anotherBuffer_->GetPtr(1), - anotherBuffer_->bytes_used(1))); -} - -TEST_F(BalsaHeadersTest, AppendHeader) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key3", "value3"); - headers_->AppendHeader("key3", "value3.1"); - headers_->AppendHeader("key3", "value3.2"); - - ASSERT_EQ(5, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - ASSERT_EQ("value1", headers_->GetHeader("key1")); - ASSERT_EQ("value2", headers_->GetHeader("key2")); - ASSERT_EQ("value3", headers_->GetHeader("key3")); - - std::vector<base::StringPiece> v1, v2, v3; - std::string s1, s2, s3; - headers_->GetAllOfHeader("key1", &v1); - headers_->GetAllOfHeader("key2", &v2); - headers_->GetAllOfHeader("key3", &v3); - headers_->GetAllOfHeaderAsString("key1", &s1); - headers_->GetAllOfHeaderAsString("key2", &s2); - headers_->GetAllOfHeaderAsString("key3", &s3); - - ASSERT_EQ(1u, v1.size()); - ASSERT_EQ(1u, v2.size()); - ASSERT_EQ(3u, v3.size()); - ASSERT_EQ("value1", v1[0]); - ASSERT_EQ("value2", v2[0]); - ASSERT_EQ("value3", v3[0]); - ASSERT_EQ("value3.1", v3[1]); - ASSERT_EQ("value3.2", v3[2]); - ASSERT_EQ("value1", s1); - ASSERT_EQ("value2", s2); - ASSERT_EQ("value3,value3.1,value3.2", s3); -} - -TEST_F(BalsaHeadersTest, ReplaceOrAppendHeader) { - headers_->ReplaceOrAppendHeader("key1", "value1"); - headers_->ReplaceOrAppendHeader("key1", "value2"); - - ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - ASSERT_EQ("value2", headers_->GetHeader("key1")); - - std::vector<base::StringPiece> v; - headers_->GetAllOfHeader("key1", &v); - - ASSERT_EQ(1u, v.size()); - ASSERT_EQ("value2", v[0]); -} - -TEST_F(BalsaHeadersTest, AppendToHeader) { - headers_->AppendToHeader("key1", "value1"); - headers_->AppendToHeader("keY1", "value2"); - - ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - ASSERT_EQ("value1,value2", headers_->GetHeader("key1")); - - std::vector<base::StringPiece> v; - std::string s; - headers_->GetAllOfHeader("key1", &v); - headers_->GetAllOfHeaderAsString("keY1", &s); - - ASSERT_EQ(1u, v.size()); - ASSERT_EQ("value1,value2", v[0]); - ASSERT_EQ("value1,value2", s); -} - -TEST_F(BalsaHeadersTest, PrepentToHeader) { - headers_->PrependToHeader("key1", "value1"); - headers_->PrependToHeader("key1", "value2"); - - ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - ASSERT_EQ("value2,value1", headers_->GetHeader("key1")); - - std::vector<base::StringPiece> v; - std::string s; - headers_->GetAllOfHeader("key1", &v); - headers_->GetAllOfHeaderAsString("key1", &s); - - ASSERT_EQ(1u, v.size()); - ASSERT_EQ("value2,value1", v[0]); - ASSERT_EQ("value2,value1", s); -} - -TEST_F(BalsaHeadersTest, HasHeader) { - headers_->AppendHeader("key1", "value1"); - - ASSERT_TRUE(headers_->HasHeader("key1")); - ASSERT_FALSE(headers_->HasHeader("value1")); - ASSERT_FALSE(headers_->HasHeader("key2")); -} - -TEST_F(BalsaHeadersTest, HasNonEmptyHeader) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", ""); - - ASSERT_TRUE(headers_->HasNonEmptyHeader("key1")); - ASSERT_FALSE(headers_->HasNonEmptyHeader("key2")); - ASSERT_FALSE(headers_->HasNonEmptyHeader("key3")); -} - -TEST_F(BalsaHeadersTest, GetHeaderPosition) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key3", "value3"); - - BalsaHeaders::const_header_lines_iterator i = - headers_->GetHeaderPosition("key2"); - - ASSERT_EQ(headers_->header_lines_end(), - headers_->GetHeaderPosition("foobar")); - ASSERT_EQ(headers_->header_lines_begin(), - headers_->GetHeaderPosition("key1")); - ASSERT_NE(headers_->header_lines_end(), i); - ASSERT_EQ("key2", i->first); - ASSERT_EQ("value2", i->second); - ++i; - ASSERT_EQ("key3", i->first); - ASSERT_EQ("value3", i->second); - ++i; - ASSERT_EQ(headers_->header_lines_end(), i); -} - -TEST_F(BalsaHeadersTest, GetIteratorForKey) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key1", "value1.1"); - headers_->AppendHeader("key3", "value3"); - headers_->AppendHeader("KEY1", "value1.2"); - - BalsaHeaders::const_header_lines_key_iterator i = - headers_->GetIteratorForKey("key1"); - - ASSERT_EQ(headers_->header_lines_key_end(), - headers_->GetIteratorForKey("foobar")); - ASSERT_NE(headers_->header_lines_key_end(), i); - ASSERT_EQ("key1", i->first); - ASSERT_EQ("value1", i->second); - ++i; - ASSERT_EQ("key1", i->first); - ASSERT_EQ("value1.1", i->second); - ++i; - ASSERT_EQ("KEY1", i->first); - ASSERT_EQ("value1.2", i->second); - ++i; - ASSERT_EQ(headers_->header_lines_key_end(), i); -} - -TEST_F(BalsaHeadersTest, RemoveAllOfHeader) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key1", "value1.1"); - headers_->AppendHeader("key3", "value3"); - headers_->AppendHeader("key1", "value1.2"); - headers_->AppendHeader("kEY1", "value1.3"); - - ASSERT_EQ(6, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - headers_->RemoveAllOfHeader("key1"); - ASSERT_EQ(2, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); -} - -TEST_F(BalsaHeadersTest, RemoveAllHeadersWithPrefix) { - headers_->AppendHeader("1key", "value1"); - headers_->AppendHeader("2key", "value2"); - headers_->AppendHeader("1kEz", "value1.1"); - headers_->AppendHeader("key3", "value3"); - headers_->AppendHeader("1KEEjkladf", "value1.2"); - - ASSERT_EQ(5, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); - headers_->RemoveAllHeadersWithPrefix("1ke"); - ASSERT_EQ(2, std::distance(headers_->header_lines_begin(), - headers_->header_lines_end())); -} - -TEST_F(BalsaHeadersTest, WriteRequestHeaderAndEndingToBuffer) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key1", "value1.1"); - - headers_->SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); - - std::string expected = "GET / HTTP/1.0\r\n" - "key1: value1\r\n" - "key2: value2\r\n" - "key1: value1.1\r\n\r\n"; - StringBuffer buffer; - headers_->WriteHeaderAndEndingToBuffer(&buffer); - ASSERT_EQ(expected, buffer.string()); -} - -TEST_F(BalsaHeadersTest, WriteResponseHeaderAndEndingToBuffer) { - headers_->AppendHeader("key1", "value1"); - headers_->AppendHeader("key2", "value2"); - headers_->AppendHeader("key1", "value1.1"); - - headers_->SetResponseFirstlineFromStringPieces("HTTP/1.0", "200", "OK"); - - std::string expected = "HTTP/1.0 200 OK\r\n" - "key1: value1\r\n" - "key2: value2\r\n" - "key1: value1.1\r\n\r\n"; - StringBuffer buffer; - headers_->WriteHeaderAndEndingToBuffer(&buffer); - ASSERT_EQ(expected, buffer.string()); -} - -TEST_F(BalsaHeadersTest, RequestFirstLine) { - headers_->SetRequestFirstlineFromStringPieces("HEAD", "/path", "HTTP/1.1"); - - ASSERT_EQ("HEAD /path HTTP/1.1", headers_->first_line()); - ASSERT_EQ("HEAD", headers_->request_method()); - ASSERT_EQ("/path", headers_->request_uri()); - ASSERT_EQ("HTTP/1.1", headers_->request_version()); -} - -TEST_F(BalsaHeadersTest, ResponseFirstLine) { - headers_->SetRequestFirstlineFromStringPieces("HTTP/1.0", "403", "FORBIDDEN"); - - ASSERT_EQ("HTTP/1.0 403 FORBIDDEN", headers_->first_line()); - ASSERT_EQ("HTTP/1.0", headers_->response_version()); - ASSERT_EQ("403", headers_->response_code()); - ASSERT_EQ("FORBIDDEN", headers_->response_reason_phrase()); -} - -} // namespace - -} // namespace net diff --git a/net/tools/flip_server/balsa_headers_token_utils.cc b/net/tools/flip_server/balsa_headers_token_utils.cc deleted file mode 100644 index b47debf..0000000 --- a/net/tools/flip_server/balsa_headers_token_utils.cc +++ /dev/null @@ -1,142 +0,0 @@ -// 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/tools/flip_server/balsa_headers_token_utils.h" -#include "net/tools/flip_server/string_piece_utils.h" - -namespace net { - -inline void BalsaHeadersTokenUtils::TokenizeHeaderLine( - const BalsaHeaders& headers, - const BalsaHeaders::HeaderLineDescription& header_line, - BalsaHeaders::HeaderTokenList* tokens) { - CHECK(tokens); - - // Find where this line is stored - const char* stream_begin = headers.GetPtr(header_line.buffer_base_idx); - - // Determine the boundaries of the value - const char* value_begin = stream_begin + header_line.value_begin_idx; - const char* line_end = stream_begin + header_line.last_char_idx; - - // Tokenize - ParseTokenList(value_begin, line_end, tokens); -} - -void BalsaHeadersTokenUtils::RemoveLastTokenFromHeaderValue( - const base::StringPiece& key, BalsaHeaders* headers) { - BalsaHeaders::HeaderLines::iterator it = - headers->GetHeaderLinesIterator(key, headers->header_lines_.begin()); - if (it == headers->header_lines_.end()) { - DLOG(WARNING) << "Attempting to remove last token from a non-existent " - << "header \"" << key << "\""; - return; - } - - // Find the last line with that key. - BalsaHeaders::HeaderLines::iterator header_line; - do { - header_line = it; - it = headers->GetHeaderLinesIterator(key, it + 1); - } - while (it != headers->header_lines_.end()); - - // Tokenize just that line. - BalsaHeaders::HeaderTokenList tokens; - TokenizeHeaderLine(*headers, *header_line, &tokens); - - if (tokens.empty()) { - DLOG(WARNING) << "Attempting to remove a token from an empty header value " - << "for header \"" << key << "\""; - header_line->skip = true; // remove the whole line - } else if (tokens.size() == 1) { - header_line->skip = true; // remove the whole line - } else { - // Shrink the line size and leave the extra data in the buffer. - const base::StringPiece& new_last_token = tokens[tokens.size() - 2]; - const char* last_char_address = - new_last_token.data() + new_last_token.size() - 1; - const char* stream_begin = headers->GetPtr(header_line->buffer_base_idx); - - header_line->last_char_idx = last_char_address - stream_begin + 1; - } -} - -bool BalsaHeadersTokenUtils::CheckHeaderForLastToken( - const BalsaHeaders& headers, - const base::StringPiece& key, - const base::StringPiece& token) { - BalsaHeaders::const_header_lines_key_iterator it = - headers.GetIteratorForKey(key); - if (it == headers.header_lines_key_end()) - return false; - - // Find the last line - BalsaHeaders::const_header_lines_key_iterator header_line = it; - do { - header_line = it; - ++it; - } - while (it != headers.header_lines_key_end()); - - // Tokenize just that line - BalsaHeaders::HeaderTokenList tokens; - ParseTokenList(header_line->second.begin(), header_line->second.end(), - &tokens); - - return !tokens.empty() && - StringPieceUtils::StartsWithIgnoreCase(tokens.back(), token); -} - -void BalsaHeadersTokenUtils::TokenizeHeaderValue( - const BalsaHeaders& headers, - const base::StringPiece& key, - BalsaHeaders::HeaderTokenList* tokens) { - CHECK(tokens); - tokens->clear(); - - // We may have more then 1 line with the same header key. Tokenize them all - // and stick all the tokens into the same list. - for (BalsaHeaders::const_header_lines_key_iterator header_line = - headers.GetIteratorForKey(key); - header_line != headers.header_lines_key_end(); ++header_line) { - ParseTokenList(header_line->second.begin(), header_line->second.end(), - tokens); - } -} - -void BalsaHeadersTokenUtils::ParseTokenList( - const char* start, - const char* end, - BalsaHeaders::HeaderTokenList* tokens) { - if (start == end) { - return; - } - while (true) { - // search for first nonwhitespace, non separator char. - while (*start == ',' || *start <= ' ') { - ++start; - if (start == end) { - return; - } - } - // found. marked. - const char* nws = start; - - // search for next whitspace or separator char. - while (*start != ',' && *start > ' ') { - ++start; - if (start == end) { - if (nws != start) { - tokens->push_back(base::StringPiece(nws, start - nws)); - } - return; - } - } - tokens->push_back(base::StringPiece(nws, start - nws)); - } -} - -} // namespace net - diff --git a/net/tools/flip_server/balsa_headers_token_utils.h b/net/tools/flip_server/balsa_headers_token_utils.h deleted file mode 100644 index f50c606..0000000 --- a/net/tools/flip_server/balsa_headers_token_utils.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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. - -// Utility class that performs basic operations on header value tokens: parsing -// them out, checking for presense of certain tokens, and removing them. - -#ifndef NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_TOKEN_UTILS_H_ -#define NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_TOKEN_UTILS_H_ - -#include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_headers.h" - -namespace net { - -class BalsaHeadersTokenUtils { - public: - // All the functions below respect multiple header lines with the same key. - - // Checks whether the last header token matches a given value. Useful to - // check the outer-most content or transfer-encoding, for example. In the - // presence of multiple header lines with given key, the last token of the - // last line is compared. - static bool CheckHeaderForLastToken(const BalsaHeaders& headers, - const base::StringPiece& key, - const base::StringPiece& token); - - // Tokenizes header value for a given key. In the presence of multiple lines - // with that key, all of them will be tokenized and tokens will be added to - // the list in the order in which they are encountered. - static void TokenizeHeaderValue(const BalsaHeaders& headers, - const base::StringPiece& key, - BalsaHeaders::HeaderTokenList* tokens); - - // Removes the last token from the header value. In the presence of multiple - // header lines with given key, will remove the last token of the last line. - // Can be useful if the last encoding has to be removed. - static void RemoveLastTokenFromHeaderValue(const base::StringPiece& key, - BalsaHeaders* headers); - - // Given a pointer to the beginning and the end of the header value - // in some buffer, populates tokens list with beginning and end indices - // of all tokens present in the value string. - static void ParseTokenList(const char* start, - const char* end, - BalsaHeaders::HeaderTokenList* tokens); - - private: - // Helper function to tokenize a header line once we have its description. - static void TokenizeHeaderLine( - const BalsaHeaders& headers, - const BalsaHeaders::HeaderLineDescription& line, - BalsaHeaders::HeaderTokenList* tokens); - - BalsaHeadersTokenUtils(); // Prohibit instantiation -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_TOKEN_UTILS_H_ - diff --git a/net/tools/flip_server/balsa_visitor_interface.h b/net/tools/flip_server/balsa_visitor_interface.h deleted file mode 100644 index 2542723..0000000 --- a/net/tools/flip_server/balsa_visitor_interface.h +++ /dev/null @@ -1,180 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_BALSA_VISITOR_INTERFACE_H_ -#define NET_TOOLS_FLIP_SERVER_BALSA_VISITOR_INTERFACE_H_ - -#include <cstddef> - -namespace net { - -class BalsaFrame; -class BalsaHeaders; - -// By default the BalsaFrame instantiates a class derived from this interface -// which does absolutely nothing. If you'd prefer to have interesting -// functionality execute when any of the below functions are called by the -// BalsaFrame, then you should subclass it, and set an instantiation of your -// subclass as the current visitor for the BalsaFrame class using -// BalsaFrame::set_visitor(). -class BalsaVisitorInterface { - public: - virtual ~BalsaVisitorInterface() {} - - // Summary: - // This is how the BalsaFrame passes you the raw input which it knows to - // be a part of the body. To be clear, every byte of the Balsa which isn't - // part of the header (or its framing), or trailers will be passed through - // this function. This includes data as well as chunking framing. - // Arguments: - // input - contains the bytes available for read. - // size - contains the number of bytes it is safe to read from input. - virtual void ProcessBodyInput(const char *input, size_t size) = 0; - - // Summary: - // This is like ProcessBodyInput, but it will only include those parts of - // the body which would be stored by a program such as wget, i.e. the bytes - // indicating chunking (it will have been omitted). Trailers will not be - // passed in through this function-- they'll be passed in through - // ProcessTrailers. - // Arguments: - // input - contains the bytes available for read. - // size - contains the number of bytes it is safe to read from input. - virtual void ProcessBodyData(const char *input, size_t size) = 0; - - // Summary: - // BalsaFrame passes the raw header data through this function. This is - // not cleaned up in any way. - // Arguments: - // input - contains the bytes available for read. - // size - contains the number of bytes it is safe to read from input. - virtual void ProcessHeaderInput(const char *input, size_t size) = 0; - - // Summary: - // BalsaFrame passes the raw trailer data through this function. This is - // not cleaned up in any way. Note that trailers only occur in a message - // if there was a chunked encoding, and not always then. - // - // Arguments: - // input - contains the bytes available for read. - // size - contains the number of bytes it is safe to read from input. - virtual void ProcessTrailerInput(const char *input, size_t size) = 0; - - // Summary: - // Since the BalsaFrame already has to parse the headers in order to - // determine proper framing, it might as well pass the parsed and - // cleaned-up results to whatever might need it. This function exists for - // that purpose-- parsed headers are passed into this function. - // Arguments: - // headers - contains the parsed headers in the order in which - // they occured in the header. - virtual void ProcessHeaders(const BalsaHeaders& headers) = 0; - - // Summary: - // Called when the first line of the message is parsed, in this case, for a - // request. - // Arguments: - // line_input - pointer to the beginning of the first line string. - // line_length - length of the first line string. (i.e. the numer of - // bytes it is safe to read from line_ptr) - // method_input - pointer to the beginning of the method string - // method_length - length of the method string (i.e. the number - // of bytes it is safe to read from method_input) - // request_uri_input - pointer to the beginning of the request uri - // string. - // request_uri_length - length of the method string (i.e. the number - // of bytes it is safe to read from method_input) - // version_input - pointer to the beginning of the version string. - // version_length - length of the version string (i.e. the number - // of bytes it i ssafe to read from version_input) - virtual void ProcessRequestFirstLine(const char* line_input, - size_t line_length, - const char* method_input, - size_t method_length, - const char* request_uri_input, - size_t request_uri_length, - const char* version_input, - size_t version_length) = 0; - - // Summary: - // Called when the first line of the message is parsed, in this case, for a - // response. - // Arguments: - // line_input - pointer to the beginning of the first line string. - // line_length - length of the first line string. (i.e. the numer of - // bytes it is safe to read from line_ptr) - // version_input - pointer to the beginning of the version string. - // version_length - length of the version string (i.e. the number - // of bytes it i ssafe to read from version_input) - // status_input - pointer to the beginning of the status string - // status_length - length of the status string (i.e. the number - // of bytes it is safe to read from status_input) - // reason_input - pointer to the beginning of the reason string - // reason_length - length of the reason string (i.e. the number - // of bytes it is safe to read from reason_input) - virtual void ProcessResponseFirstLine(const char *line_input, - size_t line_length, - const char *version_input, - size_t version_length, - const char *status_input, - size_t status_length, - const char *reason_input, - size_t reason_length) = 0; - - // Called when a chunk length is parsed. - // Arguments: - // chunk length - the length of the next incoming chunk. - virtual void ProcessChunkLength(size_t chunk_length) = 0; - - // Summary: - // BalsaFrame passes the raw chunk extension data through this function. - // The data is not cleaned up at all, use - // BalsaFrame::ProcessChunkExtentions to get the parsed and cleaned up - // chunk extensions. - // - // Arguments: - // input - contains the bytes available for read. - // size - contains the number of bytes it is safe to read from input. - virtual void ProcessChunkExtensions(const char* input, size_t size) = 0; - - // Summary: - // Called when the header is framed and processed. - virtual void HeaderDone() = 0; - - // Summary: - // Called when the message is framed and processed. - virtual void MessageDone() = 0; - - // Summary: - // Called when an error is detected while parsing the header. - // Arguments: - // framer - the framer in which an error occured. - virtual void HandleHeaderError(BalsaFrame* framer) = 0; - - // Summary: - // Called when something meriting a warning is detected while - // parsing the header. - // Arguments: - // framer - the framer in which an error occured. - virtual void HandleHeaderWarning(BalsaFrame* framer) = 0; - - // Summary: - // Called when an error is detected while parsing a chunk. - // Arguments: - // framer - the framer in which an error occured. - virtual void HandleChunkingError(BalsaFrame* framer) = 0; - - // Summary: - // Called when an error is detected while handling the entity-body. - // Currently, this can only be called when there is an error - // with the BytesSpliced() function, but in the future other interesting - // errors could occur. - // Arguments: - // framer - the framer in which an error occured. - virtual void HandleBodyError(BalsaFrame* framer) = 0; -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BALSA_VISITOR_INTERFACE_H_ diff --git a/net/tools/flip_server/buffer_interface.h b/net/tools/flip_server/buffer_interface.h deleted file mode 100644 index ec061c9..0000000 --- a/net/tools/flip_server/buffer_interface.h +++ /dev/null @@ -1,121 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_BUFFER_INTERFACE_H__ -#define NET_TOOLS_FLIP_SERVER_BUFFER_INTERFACE_H__ - -namespace net { - -class BufferInterface { - public: - - // Returns the bytes which can be read from the buffer. There is no - // guarantee that the bytes are contiguous. - virtual int ReadableBytes() const = 0; - - // Summary: - // returns the size of this buffer - // Returns: - // size of this buffer. - virtual int BufferSize() const = 0; - - // Summary: - // returns the number of bytes free in this buffer. - // Returns: - // number of bytes free. - virtual int BytesFree() const = 0; - - // Summary: - // Returns true if empty. - // Returns: - // true - if empty - // false - otherwise - virtual bool Empty() const = 0; - - // Summary: - // Returns true if the buffer is full. - virtual bool Full() const = 0; - - // Summary: - // returns the number of characters written. - // appends up-to-'size' bytes to the buffer. - // Args: - // bytes - bytes which are read, and copied into the buffer. - // size - number of bytes which are read and copied. - // this number shall be >= 0. - virtual int Write(const char* bytes, int size) = 0; - - // Summary: - // Gets a pointer which can be written to (assigned to). - // this pointer (and size) can be used in functions like - // recv() or read(), etc. - // If *size is zero upon returning from this function, that it - // is unsafe to dereference *ptr. - // Args: - // ptr - assigned a pointer to which we can write - // size - the amount of data (in bytes) that it is safe to write to ptr. - virtual void GetWritablePtr(char **ptr, int* size) const = 0; - - // Summary: - // Gets a pointer which can be read from - // this pointer (and size) can be used in functions like - // send() or write(), etc. - // If *size is zero upon returning from this function, that it - // is unsafe to dereference *ptr. - // Args: - // ptr - assigned a pointer from which we may read - // size - the amount of data (in bytes) that it is safe to read - virtual void GetReadablePtr(char **ptr, int* size) const = 0; - - // Summary: - // Reads bytes out of the buffer, and writes them into 'bytes'. - // Returns the number of bytes read. - // Consumes bytes from the buffer (possibly, but not necessarily - // rendering them free) - // Args: - // bytes - the pointer into which bytes are read from this buffer - // and written into - // size - number of bytes which are read and copied. - // this number shall be >= 0. - // Returns: - // the number of bytes read from 'bytes' - virtual int Read(char* bytes, int size) = 0; - - // Summary: - // removes all data from the buffer - virtual void Clear() = 0; - - // Summary: - // reserves contiguous writable empty space in the buffer of size bytes. - // Returns true if the reservation is successful. - // If a derive class chooses not to implement reservation, its - // implementation should return false. - virtual bool Reserve(int size) = 0; - - // Summary: - // removes the oldest 'amount_to_consume' characters from this buffer, - // Args: - // amount_to_advance - .. this should be self-explanatory =) - // this number shall be >= 0. - virtual void AdvanceReadablePtr(int amount_to_advance) = 0; - - // Summary: - // Moves the internal pointers around such that the - // amount of data specified here is expected to - // already be resident (as if it was Written) - // Args: - // amount_to_advance - self explanatory. - // this number shall be >= 0. - virtual void AdvanceWritablePtr(int amount_to_advance) = 0; - - virtual ~BufferInterface() {} - - protected: - BufferInterface() {} -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_BUFFER_INTERFACE__H__ - diff --git a/net/tools/flip_server/epoll_server.cc b/net/tools/flip_server/epoll_server.cc deleted file mode 100644 index 0e09a6d..0000000 --- a/net/tools/flip_server/epoll_server.cc +++ /dev/null @@ -1,820 +0,0 @@ -// 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/tools/flip_server/epoll_server.h" - -#include <unistd.h> // For read, pipe, close and write. -#include <stdlib.h> // for abort -#include <errno.h> // for errno and strerror_r -#include <algorithm> -#include <utility> -#include <vector> - -#include "base/logging.h" -#include "base/timer/timer.h" - -// Design notes: An efficient implementation of ready list has the following -// desirable properties: -// -// A. O(1) insertion into/removal from the list in any location. -// B. Once the callback is found by hash lookup using the fd, the lookup of -// corresponding entry in the list is O(1). -// C. Safe insertion into/removal from the list during list iteration. (The -// ready list's purpose is to enable completely event driven I/O model. -// Thus, all the interesting bits happen in the callback. It is critical -// to not place any restriction on the API during list iteration. -// -// The current implementation achieves these goals with the following design: -// -// - The ready list is constructed as a doubly linked list to enable O(1) -// insertion/removal (see man 3 queue). -// - The forward and backward links are directly embedded inside the -// CBAndEventMask struct. This enables O(1) lookup in the list for a given -// callback. (Techincally, we could've used std::list of hash_set::iterator, -// and keep a list::iterator in CBAndEventMask to achieve the same effect. -// However, iterators have two problems: no way to portably invalidate them, -// and no way to tell whether an iterator is singular or not. The only way to -// overcome these issues is to keep bools in both places, but that throws off -// memory alignment (up to 7 wasted bytes for each bool). The extra level of -// indirection will also likely be less cache friendly. Direct manipulation -// of link pointers makes it easier to retrieve the CBAndEventMask from the -// list, easier to check whether an CBAndEventMask is in the list, uses less -// memory (save 32 bytes/fd), and does not affect cache usage (we need to -// read in the struct to use the callback anyway).) -// - Embed the fd directly into CBAndEventMask and switch to using hash_set. -// This removes the need to store hash_map::iterator in the list just so that -// we can get both the fd and the callback. -// - The ready list is "one shot": each entry is removed before OnEvent is -// called. This removes the mutation-while-iterating problem. -// - Use two lists to keep track of callbacks. The ready_list_ is the one used -// for registration. Before iteration, the ready_list_ is swapped into the -// tmp_list_. Once iteration is done, tmp_list_ will be empty, and -// ready_list_ will have all the new ready fds. - -// The size we use for buffers passed to strerror_r -static const int kErrorBufferSize = 256; - -namespace net { - -// Clears the pipe and returns. Used for waking the epoll server up. -class ReadPipeCallback : public EpollCallbackInterface { - public: - virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE { - DCHECK(event->in_events == EPOLLIN); - int data; - int data_read = 1; - // Read until the pipe is empty. - while (data_read > 0) { - data_read = read(fd, &data, sizeof(data)); - } - } - virtual void OnShutdown(EpollServer *eps, int fd) OVERRIDE {} - virtual void OnRegistration(EpollServer*, int, int) OVERRIDE {} - virtual void OnModification(int, int) OVERRIDE {} // COV_NF_LINE - virtual void OnUnregistration(int, bool) OVERRIDE {} // COV_NF_LINE -}; - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -EpollServer::EpollServer() - : epoll_fd_(epoll_create(1024)), - timeout_in_us_(0), - recorded_now_in_us_(0), - ready_list_size_(0), - wake_cb_(new ReadPipeCallback), - read_fd_(-1), - write_fd_(-1), - in_wait_for_events_and_execute_callbacks_(false), - in_shutdown_(false) { - // ensure that the epoll_fd_ is valid. - CHECK_NE(epoll_fd_, -1); - LIST_INIT(&ready_list_); - LIST_INIT(&tmp_list_); - - int pipe_fds[2]; - if (pipe(pipe_fds) < 0) { - // Unfortunately, it is impossible to test any such initialization in - // a constructor (as virtual methods do not yet work). - // This -could- be solved by moving initialization to an outside - // call... - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Error " << saved_errno - << " in pipe(): " << strerror_r(saved_errno, buf, sizeof(buf)); - } - read_fd_ = pipe_fds[0]; - write_fd_ = pipe_fds[1]; - RegisterFD(read_fd_, wake_cb_.get(), EPOLLIN); -} - -void EpollServer::CleanupFDToCBMap() { - FDToCBMap::iterator cb_iter = cb_map_.begin(); - while (cb_iter != cb_map_.end()) { - int fd = cb_iter->fd; - CB* cb = cb_iter->cb; - - cb_iter->in_use = true; - if (cb) { - cb->OnShutdown(this, fd); - } - - cb_map_.erase(cb_iter); - cb_iter = cb_map_.begin(); - } -} - -void EpollServer::CleanupTimeToAlarmCBMap() { - TimeToAlarmCBMap::iterator erase_it; - - // Call OnShutdown() on alarms. Note that the structure of the loop - // is similar to the structure of loop in the function HandleAlarms() - for (TimeToAlarmCBMap::iterator i = alarm_map_.begin(); - i != alarm_map_.end(); - ) { - // Note that OnShutdown() can call UnregisterAlarm() on - // other iterators. OnShutdown() should not call UnregisterAlarm() - // on self because by definition the iterator is not valid any more. - i->second->OnShutdown(this); - erase_it = i; - ++i; - alarm_map_.erase(erase_it); - } -} - -EpollServer::~EpollServer() { - DCHECK_EQ(in_shutdown_, false); - in_shutdown_ = true; -#ifdef EPOLL_SERVER_EVENT_TRACING - LOG(INFO) << "\n" << event_recorder_; -#endif - VLOG(2) << "Shutting down epoll server "; - CleanupFDToCBMap(); - - LIST_INIT(&ready_list_); - LIST_INIT(&tmp_list_); - - CleanupTimeToAlarmCBMap(); - - close(read_fd_); - close(write_fd_); - close(epoll_fd_); -} - -// Whether a CBAandEventMask is on the ready list is determined by a non-NULL -// le_prev pointer (le_next being NULL indicates end of list). -inline void EpollServer::AddToReadyList(CBAndEventMask* cb_and_mask) { - if (cb_and_mask->entry.le_prev == NULL) { - LIST_INSERT_HEAD(&ready_list_, cb_and_mask, entry); - ++ready_list_size_; - } -} - -inline void EpollServer::RemoveFromReadyList( - const CBAndEventMask& cb_and_mask) { - if (cb_and_mask.entry.le_prev != NULL) { - LIST_REMOVE(&cb_and_mask, entry); - // Clean up all the ready list states. Don't bother with the other fields - // as they are initialized when the CBAandEventMask is added to the ready - // list. This saves a few cycles in the inner loop. - cb_and_mask.entry.le_prev = NULL; - --ready_list_size_; - if (ready_list_size_ == 0) { - DCHECK(ready_list_.lh_first == NULL); - DCHECK(tmp_list_.lh_first == NULL); - } - } -} - -void EpollServer::RegisterFD(int fd, CB* cb, int event_mask) { - CHECK(cb); - VLOG(3) << "RegisterFD fd=" << fd << " event_mask=" << event_mask; - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (cb_map_.end() != fd_i) { - // do we just abort, or do we just unregister the other guy? - // for now, lets just unregister the other guy. - - // unregister any callback that may already be registered for this FD. - CB* other_cb = fd_i->cb; - if (other_cb) { - // Must remove from the ready list before erasing. - RemoveFromReadyList(*fd_i); - other_cb->OnUnregistration(fd, true); - ModFD(fd, event_mask); - } else { - // already unregistered, so just recycle the node. - AddFD(fd, event_mask); - } - fd_i->cb = cb; - fd_i->event_mask = event_mask; - fd_i->events_to_fake = 0; - } else { - AddFD(fd, event_mask); - cb_map_.insert(CBAndEventMask(cb, event_mask, fd)); - } - - - // set the FD to be non-blocking. - SetNonblocking(fd); - - cb->OnRegistration(this, fd, event_mask); -} - -int EpollServer::GetFlags(int fd) { - return fcntl(fd, F_GETFL, 0); -} - -void EpollServer::SetNonblocking(int fd) { - int flags = GetFlags(fd); - if (flags == -1) { - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Error " << saved_errno - << " doing fcntl(" << fd << ", F_GETFL, 0): " - << strerror_r(saved_errno, buf, sizeof(buf)); - } - if (!(flags & O_NONBLOCK)) { - int saved_flags = flags; - flags = SetFlags(fd, flags | O_NONBLOCK); - if (flags == -1) { - // bad. - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Error " << saved_errno - << " doing fcntl(" << fd << ", F_SETFL, " << saved_flags << "): " - << strerror_r(saved_errno, buf, sizeof(buf)); - } - } -} - -int EpollServer::epoll_wait_impl(int epfd, - struct epoll_event* events, - int max_events, - int timeout_in_ms) { - return epoll_wait(epfd, events, max_events, timeout_in_ms); -} - -void EpollServer::RegisterFDForWrite(int fd, CB* cb) { - RegisterFD(fd, cb, EPOLLOUT); -} - -void EpollServer::RegisterFDForReadWrite(int fd, CB* cb) { - RegisterFD(fd, cb, EPOLLIN | EPOLLOUT); -} - -void EpollServer::RegisterFDForRead(int fd, CB* cb) { - RegisterFD(fd, cb, EPOLLIN); -} - -void EpollServer::UnregisterFD(int fd) { - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (cb_map_.end() == fd_i || fd_i->cb == NULL) { - // Doesn't exist in server, or has gone through UnregisterFD once and still - // inside the callchain of OnEvent. - return; - } -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordUnregistration(fd); -#endif - CB* cb = fd_i->cb; - // Since the links are embedded within the struct, we must remove it from the - // list before erasing it from the hash_set. - RemoveFromReadyList(*fd_i); - DelFD(fd); - cb->OnUnregistration(fd, false); - // fd_i->cb is NULL if that fd is unregistered inside the callchain of - // OnEvent. Since the EpollServer needs a valid CBAndEventMask after OnEvent - // returns in order to add it to the ready list, we cannot have UnregisterFD - // erase the entry if it is in use. Thus, a NULL fd_i->cb is used as a - // condition that tells the EpollServer that this entry is unused at a later - // point. - if (!fd_i->in_use) { - cb_map_.erase(fd_i); - } else { - // Remove all trace of the registration, and just keep the node alive long - // enough so the code that calls OnEvent doesn't have to worry about - // figuring out whether the CBAndEventMask is valid or not. - fd_i->cb = NULL; - fd_i->event_mask = 0; - fd_i->events_to_fake = 0; - } -} - -void EpollServer::ModifyCallback(int fd, int event_mask) { - ModifyFD(fd, ~0, event_mask); -} - -void EpollServer::StopRead(int fd) { - ModifyFD(fd, EPOLLIN, 0); -} - -void EpollServer::StartRead(int fd) { - ModifyFD(fd, 0, EPOLLIN); -} - -void EpollServer::StopWrite(int fd) { - ModifyFD(fd, EPOLLOUT, 0); -} - -void EpollServer::StartWrite(int fd) { - ModifyFD(fd, 0, EPOLLOUT); -} - -void EpollServer::HandleEvent(int fd, int event_mask) { -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordEpollEvent(fd, event_mask); -#endif - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (fd_i == cb_map_.end() || fd_i->cb == NULL) { - // Ignore the event. - // This could occur if epoll() returns a set of events, and - // while processing event A (earlier) we removed the callback - // for event B (and are now processing event B). - return; - } - fd_i->events_asserted = event_mask; - CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i); - AddToReadyList(cb_and_mask); -} - -class TrueFalseGuard { - public: - explicit TrueFalseGuard(bool* guarded_bool) : guarded_bool_(guarded_bool) { - DCHECK(guarded_bool_ != NULL); - DCHECK(*guarded_bool_ == false); - *guarded_bool_ = true; - } - ~TrueFalseGuard() { - *guarded_bool_ = false; - } - private: - bool* guarded_bool_; -}; - -void EpollServer::WaitForEventsAndExecuteCallbacks() { - if (in_wait_for_events_and_execute_callbacks_) { - LOG(DFATAL) << - "Attempting to call WaitForEventsAndExecuteCallbacks" - " when an ancestor to the current function is already" - " WaitForEventsAndExecuteCallbacks!"; - // The line below is actually tested, but in coverage mode, - // we never see it. - return; // COV_NF_LINE - } - TrueFalseGuard recursion_guard(&in_wait_for_events_and_execute_callbacks_); - if (alarm_map_.empty()) { - // no alarms, this is business as usual. - WaitForEventsAndCallHandleEvents(timeout_in_us_, - events_, - events_size_); - recorded_now_in_us_ = 0; - return; - } - - // store the 'now'. If we recomputed 'now' every iteration - // down below, then we might never exit that loop-- any - // long-running alarms might install other long-running - // alarms, etc. By storing it here now, we ensure that - // a more reasonable amount of work is done here. - int64 now_in_us = NowInUsec(); - - // Get the first timeout from the alarm_map where it is - // stored in absolute time. - int64 next_alarm_time_in_us = alarm_map_.begin()->first; - VLOG(4) << "next_alarm_time = " << next_alarm_time_in_us - << " now = " << now_in_us - << " timeout_in_us = " << timeout_in_us_; - - int64 wait_time_in_us; - int64 alarm_timeout_in_us = next_alarm_time_in_us - now_in_us; - - // If the next alarm is sooner than the default timeout, or if there is no - // timeout (timeout_in_us_ == -1), wake up when the alarm should fire. - // Otherwise use the default timeout. - if (alarm_timeout_in_us < timeout_in_us_ || timeout_in_us_ < 0) { - wait_time_in_us = std::max(alarm_timeout_in_us, static_cast<int64>(0)); - } else { - wait_time_in_us = timeout_in_us_; - } - - VLOG(4) << "wait_time_in_us = " << wait_time_in_us; - - // wait for events. - - WaitForEventsAndCallHandleEvents(wait_time_in_us, - events_, - events_size_); - CallAndReregisterAlarmEvents(); - recorded_now_in_us_ = 0; -} - -void EpollServer::SetFDReady(int fd, int events_to_fake) { - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (cb_map_.end() != fd_i && fd_i->cb != NULL) { - // This const_cast is necessary for LIST_HEAD_INSERT to work. Declaring - // entry mutable is insufficient because LIST_HEAD_INSERT assigns the - // forward pointer of the list head to the current cb_and_mask, and the - // compiler complains that it can't assign a const T* to a T*. - CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i); - // Note that there is no clearly correct behavior here when - // cb_and_mask->events_to_fake != 0 and this function is called. - // Of the two operations: - // cb_and_mask->events_to_fake = events_to_fake - // cb_and_mask->events_to_fake |= events_to_fake - // the first was picked because it discourages users from calling - // SetFDReady repeatedly to build up the correct event set as it is more - // efficient to call SetFDReady once with the correct, final mask. - cb_and_mask->events_to_fake = events_to_fake; - AddToReadyList(cb_and_mask); - } -} - -void EpollServer::SetFDNotReady(int fd) { - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (cb_map_.end() != fd_i) { - RemoveFromReadyList(*fd_i); - } -} - -bool EpollServer::IsFDReady(int fd) const { - FDToCBMap::const_iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - return (cb_map_.end() != fd_i && - fd_i->cb != NULL && - fd_i->entry.le_prev != NULL); -} - -void EpollServer::VerifyReadyList() const { - int count = 0; - CBAndEventMask* cur = ready_list_.lh_first; - for (; cur; cur = cur->entry.le_next) { - ++count; - } - for (cur = tmp_list_.lh_first; cur; cur = cur->entry.le_next) { - ++count; - } - CHECK_EQ(ready_list_size_, count) << "Ready list size does not match count"; -} - -void EpollServer::RegisterAlarm(int64 timeout_time_in_us, AlarmCB* ac) { - CHECK(ac); - if (ContainsAlarm(ac)) { - LOG(FATAL) << "Alarm already exists " << ac; - } - VLOG(4) << "RegisteringAlarm at : " << timeout_time_in_us; - - TimeToAlarmCBMap::iterator alarm_iter = - alarm_map_.insert(std::make_pair(timeout_time_in_us, ac)); - - all_alarms_.insert(ac); - // Pass the iterator to the EpollAlarmCallbackInterface. - ac->OnRegistration(alarm_iter, this); -} - -// Unregister a specific alarm callback: iterator_token must be a -// valid iterator. The caller must ensure the validity of the iterator. -void EpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) { - AlarmCB* cb = iterator_token->second; - alarm_map_.erase(iterator_token); - all_alarms_.erase(cb); - cb->OnUnregistration(); -} - -int EpollServer::NumFDsRegistered() const { - DCHECK_GE(cb_map_.size(), 1u); - // Omit the internal FD (read_fd_) - return cb_map_.size() - 1; -} - -void EpollServer::Wake() { - char data = 'd'; // 'd' is for data. It's good enough for me. - int rv = write(write_fd_, &data, 1); - DCHECK_EQ(rv, 1); -} - -int64 EpollServer::NowInUsec() const { - return base::Time::Now().ToInternalValue(); -} - -int64 EpollServer::ApproximateNowInUsec() const { - if (recorded_now_in_us_ != 0) { - return recorded_now_in_us_; - } - return this->NowInUsec(); -} - -std::string EpollServer::EventMaskToString(int event_mask) { - std::string s; - if (event_mask & EPOLLIN) s += "EPOLLIN "; - if (event_mask & EPOLLPRI) s += "EPOLLPRI "; - if (event_mask & EPOLLOUT) s += "EPOLLOUT "; - if (event_mask & EPOLLRDNORM) s += "EPOLLRDNORM "; - if (event_mask & EPOLLRDBAND) s += "EPOLLRDBAND "; - if (event_mask & EPOLLWRNORM) s += "EPOLLWRNORM "; - if (event_mask & EPOLLWRBAND) s += "EPOLLWRBAND "; - if (event_mask & EPOLLMSG) s += "EPOLLMSG "; - if (event_mask & EPOLLERR) s += "EPOLLERR "; - if (event_mask & EPOLLHUP) s += "EPOLLHUP "; - if (event_mask & EPOLLONESHOT) s += "EPOLLONESHOT "; - if (event_mask & EPOLLET) s += "EPOLLET "; - return s; -} - -void EpollServer::LogStateOnCrash() { - LOG(ERROR) << "----------------------Epoll Server---------------------------"; - LOG(ERROR) << "Epoll server " << this << " polling on fd " << epoll_fd_; - LOG(ERROR) << "timeout_in_us_: " << timeout_in_us_; - - // Log sessions with alarms. - LOG(ERROR) << alarm_map_.size() << " alarms registered."; - for (TimeToAlarmCBMap::iterator it = alarm_map_.begin(); - it != alarm_map_.end(); - ++it) { - const bool skipped = - alarms_reregistered_and_should_be_skipped_.find(it->second) - != alarms_reregistered_and_should_be_skipped_.end(); - LOG(ERROR) << "Alarm " << it->second << " registered at time " << it->first - << " and should be skipped = " << skipped; - } - - LOG(ERROR) << cb_map_.size() << " fd callbacks registered."; - for (FDToCBMap::iterator it = cb_map_.begin(); - it != cb_map_.end(); - ++it) { - LOG(ERROR) << "fd: " << it->fd << " with mask " << it->event_mask - << " registered with cb: " << it->cb; - } - LOG(ERROR) << "----------------------/Epoll Server--------------------------"; -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -void EpollServer::DelFD(int fd) const { - struct epoll_event ee; - memset(&ee, 0, sizeof(ee)); -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordFDMaskEvent(fd, 0, "DelFD"); -#endif - if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)) { - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Epoll set removal error for fd " << fd << ": " - << strerror_r(saved_errno, buf, sizeof(buf)); - } -} - -//////////////////////////////////////// - -void EpollServer::AddFD(int fd, int event_mask) const { - struct epoll_event ee; - memset(&ee, 0, sizeof(ee)); - ee.events = event_mask | EPOLLERR | EPOLLHUP; - ee.data.fd = fd; -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordFDMaskEvent(fd, ee.events, "AddFD"); -#endif - if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ee)) { - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Epoll set insertion error for fd " << fd << ": " - << strerror_r(saved_errno, buf, sizeof(buf)); - } -} - -//////////////////////////////////////// - -void EpollServer::ModFD(int fd, int event_mask) const { - struct epoll_event ee; - memset(&ee, 0, sizeof(ee)); - ee.events = event_mask | EPOLLERR | EPOLLHUP; - ee.data.fd = fd; -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordFDMaskEvent(fd, ee.events, "ModFD"); -#endif - VLOG(3) << "modifying fd= " << fd << " " - << EventMaskToString(ee.events); - if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &ee)) { - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Epoll set modification error for fd " << fd << ": " - << strerror_r(saved_errno, buf, sizeof(buf)); - } -} - -//////////////////////////////////////// - -void EpollServer::ModifyFD(int fd, int remove_event, int add_event) { - FDToCBMap::iterator fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd)); - if (cb_map_.end() == fd_i) { - VLOG(2) << "Didn't find the fd " << fd << "in internal structures"; - return; - } - - if (fd_i->cb != NULL) { - int & event_mask = fd_i->event_mask; - VLOG(3) << "fd= " << fd - << " event_mask before: " << EventMaskToString(event_mask); - event_mask &= ~remove_event; - event_mask |= add_event; - - VLOG(3) << " event_mask after: " << EventMaskToString(event_mask); - - ModFD(fd, event_mask); - - fd_i->cb->OnModification(fd, event_mask); - } -} - -void EpollServer::WaitForEventsAndCallHandleEvents(int64 timeout_in_us, - struct epoll_event events[], - int events_size) { - if (timeout_in_us == 0 || ready_list_.lh_first != NULL) { - // If ready list is not empty, then don't sleep at all. - timeout_in_us = 0; - } else if (timeout_in_us < 0) { - LOG(INFO) << "Negative epoll timeout: " << timeout_in_us - << "us; epoll will wait forever for events."; - // If timeout_in_us is < 0 we are supposed to Wait forever. This means we - // should set timeout_in_us to -1000 so we will - // Wait(-1000/1000) == Wait(-1) == Wait forever. - timeout_in_us = -1000; - } else { - // If timeout is specified, and the ready list is empty. - if (timeout_in_us < 1000) { - timeout_in_us = 1000; - } - } - const int timeout_in_ms = timeout_in_us / 1000; - int nfds = epoll_wait_impl(epoll_fd_, - events, - events_size, - timeout_in_ms); - VLOG(3) << "nfds=" << nfds; - -#ifdef EPOLL_SERVER_EVENT_TRACING - event_recorder_.RecordEpollWaitEvent(timeout_in_ms, nfds); -#endif - - // If you're wondering why the NowInUsec() is recorded here, the answer is - // simple: If we did it before the epoll_wait_impl, then the max error for - // the ApproximateNowInUs() call would be as large as the maximum length of - // epoll_wait, which can be arbitrarily long. Since this would make - // ApproximateNowInUs() worthless, we instead record the time -after- we've - // done epoll_wait, which guarantees that the maximum error is the amount of - // time it takes to process all the events generated by epoll_wait. - recorded_now_in_us_ = NowInUsec(); - if (nfds > 0) { - for (int i = 0; i < nfds; ++i) { - int event_mask = events[i].events; - int fd = events[i].data.fd; - HandleEvent(fd, event_mask); - } - } else if (nfds < 0) { - // Catch interrupted syscall and just ignore it and move on. - if (errno != EINTR && errno != 0) { - int saved_errno = errno; - char buf[kErrorBufferSize]; - LOG(FATAL) << "Error " << saved_errno << " in epoll_wait: " - << strerror_r(saved_errno, buf, sizeof(buf)); - } - } - - // Now run through the ready list. - if (ready_list_.lh_first) { - CallReadyListCallbacks(); - } -} - -void EpollServer::CallReadyListCallbacks() { - // Check pre-conditions. - DCHECK(tmp_list_.lh_first == NULL); - // Swap out the ready_list_ into the tmp_list_ before traversing the list to - // enable SetFDReady() to just push new items into the ready_list_. - std::swap(ready_list_.lh_first, tmp_list_.lh_first); - if (tmp_list_.lh_first) { - tmp_list_.lh_first->entry.le_prev = &tmp_list_.lh_first; - EpollEvent event(0, false); - while (tmp_list_.lh_first != NULL) { - DCHECK_GT(ready_list_size_, 0); - CBAndEventMask* cb_and_mask = tmp_list_.lh_first; - RemoveFromReadyList(*cb_and_mask); - - event.out_ready_mask = 0; - event.in_events = - cb_and_mask->events_asserted | cb_and_mask->events_to_fake; - // TODO(fenix): get rid of the two separate fields in cb_and_mask. - cb_and_mask->events_asserted = 0; - cb_and_mask->events_to_fake = 0; - { - // OnEvent() may call UnRegister, so we set in_use, here. Any - // UnRegister call will now simply set the cb to NULL instead of - // invalidating the cb_and_mask object (by deleting the object in the - // map to which cb_and_mask refers) - TrueFalseGuard in_use_guard(&(cb_and_mask->in_use)); - cb_and_mask->cb->OnEvent(cb_and_mask->fd, &event); - } - - // Since OnEvent may have called UnregisterFD, we must check here that - // the callback is still valid. If it isn't, then UnregisterFD *was* - // called, and we should now get rid of the object. - if (cb_and_mask->cb == NULL) { - cb_map_.erase(*cb_and_mask); - } else if (event.out_ready_mask != 0) { - cb_and_mask->events_to_fake = event.out_ready_mask; - AddToReadyList(cb_and_mask); - } - } - } - DCHECK(tmp_list_.lh_first == NULL); -} - -void EpollServer::CallAndReregisterAlarmEvents() { - int64 now_in_us = recorded_now_in_us_; - DCHECK_NE(0, recorded_now_in_us_); - - TimeToAlarmCBMap::iterator erase_it; - - // execute alarms. - for (TimeToAlarmCBMap::iterator i = alarm_map_.begin(); - i != alarm_map_.end(); - ) { - if (i->first > now_in_us) { - break; - } - AlarmCB* cb = i->second; - // Execute the OnAlarm() only if we did not register - // it in this loop itself. - const bool added_in_this_round = - alarms_reregistered_and_should_be_skipped_.find(cb) - != alarms_reregistered_and_should_be_skipped_.end(); - if (added_in_this_round) { - ++i; - continue; - } - all_alarms_.erase(cb); - const int64 new_timeout_time_in_us = cb->OnAlarm(); - - erase_it = i; - ++i; - alarm_map_.erase(erase_it); - - if (new_timeout_time_in_us > 0) { - // We add to hash_set only if the new timeout is <= now_in_us. - // if timeout is > now_in_us then we have no fear that this alarm - // can be reexecuted in this loop, and hence we do not need to - // worry about a recursive loop. - DVLOG(3) << "Reregistering alarm " - << " " << cb - << " " << new_timeout_time_in_us - << " " << now_in_us; - if (new_timeout_time_in_us <= now_in_us) { - alarms_reregistered_and_should_be_skipped_.insert(cb); - } - RegisterAlarm(new_timeout_time_in_us, cb); - } - } - alarms_reregistered_and_should_be_skipped_.clear(); -} - -EpollAlarm::EpollAlarm() : eps_(NULL), registered_(false) { -} - -EpollAlarm::~EpollAlarm() { - UnregisterIfRegistered(); -} - -int64 EpollAlarm::OnAlarm() { - registered_ = false; - return 0; -} - -void EpollAlarm::OnRegistration(const EpollServer::AlarmRegToken& token, - EpollServer* eps) { - DCHECK_EQ(false, registered_); - - token_ = token; - eps_ = eps; - registered_ = true; -} - -void EpollAlarm::OnUnregistration() { - registered_ = false; -} - -void EpollAlarm::OnShutdown(EpollServer* eps) { - registered_ = false; - eps_ = NULL; -} - -// If the alarm was registered, unregister it. -void EpollAlarm::UnregisterIfRegistered() { - if (!registered_) { - return; - } - eps_->UnregisterAlarm(token_); -} - -} // namespace net diff --git a/net/tools/flip_server/epoll_server.h b/net/tools/flip_server/epoll_server.h deleted file mode 100644 index 2fb7c62..0000000 --- a/net/tools/flip_server/epoll_server.h +++ /dev/null @@ -1,1054 +0,0 @@ -// 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. - -#ifndef NET_TOOLS_FLIP_SERVER_EPOLL_SERVER_H_ -#define NET_TOOLS_FLIP_SERVER_EPOLL_SERVER_H_ - -#include <fcntl.h> -#include <sys/queue.h> -#include <map> -#include <string> -#include <utility> -#include <set> -#include <vector> - -// #define EPOLL_SERVER_EVENT_TRACING 1 -// -// Defining EPOLL_SERVER_EVENT_TRACING -// causes code to exist which didn't before. -// This code tracks each event generated by the epollserver, -// as well as providing a per-fd-registered summary of -// events. Note that enabling this code vastly slows -// down operations, and uses substantially more -// memory. For these reasons, it should only be enabled when doing -// developer debugging at his/her workstation. -// -// A structure called 'EventRecorder' will exist when -// the macro is defined. See the EventRecorder class interface -// within the EpollServer class for more details. -#ifdef EPOLL_SERVER_EVENT_TRACING -#include <ostream> -#include "base/logging.h" -#endif - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/containers/hash_tables.h" -#include "base/memory/scoped_ptr.h" -#include <sys/epoll.h> - -namespace net { - -class EpollServer; -class EpollAlarmCallbackInterface; -class ReadPipeCallback; - -struct EpollEvent { - EpollEvent(int events, bool is_epoll_wait) - : in_events(events), - out_ready_mask(0) { - } - - int in_events; // incoming events - int out_ready_mask; // the new event mask for ready list (0 means don't - // get on the ready list). This field is always - // initialized to 0 when the event is passed to - // OnEvent. -}; - -// Callbacks which go into EpollServers are expected to derive from this class. -class EpollCallbackInterface { - public: - // Summary: - // Called when the callback is registered into a EpollServer. - // Args: - // eps - the poll server into which this callback was registered - // fd - the file descriptor which was registered - // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc) - // which was registered (and will initially be used - // in the epoll() calls) - virtual void OnRegistration(EpollServer* eps, int fd, int event_mask) = 0; - - // Summary: - // Called when the event_mask is modified (for a file-descriptor) - // Args: - // fd - the file descriptor which was registered - // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc) - // which was is now curren (and will be used - // in subsequent epoll() calls) - virtual void OnModification(int fd, int event_mask) = 0; - - // Summary: - // Called whenever an event occurs on the file-descriptor. - // This is where the bulk of processing is expected to occur. - // Args: - // fd - the file descriptor which was registered - // event - a struct that contains the event mask (composed of EPOLLIN, - // EPOLLOUT, etc), a flag that indicates whether this is a true - // epoll_wait event vs one from the ready list, and an output - // parameter for OnEvent to inform the EpollServer whether to put - // this fd on the ready list. - virtual void OnEvent(int fd, EpollEvent* event) = 0; - - // Summary: - // Called when the file-descriptor is unregistered from the poll-server. - // Args: - // fd - the file descriptor which was registered, and of this call, is now - // unregistered. - // replaced - If true, this callback is being replaced by another, otherwise - // it is simply being removed. - virtual void OnUnregistration(int fd, bool replaced) = 0; - - // Summary: - // Called when the epoll server is shutting down. This is different from - // OnUnregistration because the subclass may want to clean up memory. - // This is called in leiu of OnUnregistration. - // Args: - // fd - the file descriptor which was registered. - virtual void OnShutdown(EpollServer* eps, int fd) = 0; - - virtual ~EpollCallbackInterface() {} - - protected: - EpollCallbackInterface() {} -}; - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -class EpollServer { - public: - typedef EpollAlarmCallbackInterface AlarmCB; - typedef EpollCallbackInterface CB; - - typedef std::multimap<int64, AlarmCB*> TimeToAlarmCBMap; - typedef TimeToAlarmCBMap::iterator AlarmRegToken; - - // Summary: - // Constructor: - // By default, we don't wait any amount of time for events, and - // we suggest to the epoll-system that we're going to use on-the-order - // of 1024 FDs. - EpollServer(); - - //////////////////////////////////////// - - // Destructor - virtual ~EpollServer(); - - //////////////////////////////////////// - - // Summary - // Register a callback to be called whenever an event contained - // in the set of events included in event_mask occurs on the - // file-descriptor 'fd' - // - // Note that only one callback is allowed to be registered for - // any specific file-decriptor. - // - // If a callback is registered for a file-descriptor which has already - // been registered, then the previous callback is unregistered with - // the 'replaced' flag set to true. I.e. the previous callback's - // OnUnregistration() function is called like so: - // OnUnregistration(fd, true); - // - // The epoll server does NOT take on ownership of the callback: the callback - // creator is responsible for managing that memory. - // - // Args: - // fd - a valid file-descriptor - // cb - an instance of a subclass of EpollCallbackInterface - // event_mask - a combination of (EPOLLOUT, EPOLLIN.. etc) indicating - // the events for which the callback would like to be - // called. - virtual void RegisterFD(int fd, CB* cb, int event_mask); - - //////////////////////////////////////// - - // Summary: - // A shortcut for RegisterFD which sets things up such that the - // callback is called when 'fd' is available for writing. - // Args: - // fd - a valid file-descriptor - // cb - an instance of a subclass of EpollCallbackInterface - virtual void RegisterFDForWrite(int fd, CB* cb); - - //////////////////////////////////////// - - // Summary: - // A shortcut for RegisterFD which sets things up such that the - // callback is called when 'fd' is available for reading or writing. - // Args: - // fd - a valid file-descriptor - // cb - an instance of a subclass of EpollCallbackInterface - virtual void RegisterFDForReadWrite(int fd, CB* cb); - - //////////////////////////////////////// - - // Summary: - // A shortcut for RegisterFD which sets things up such that the - // callback is called when 'fd' is available for reading. - // Args: - // fd - a valid file-descriptor - // cb - an instance of a subclass of EpollCallbackInterface - virtual void RegisterFDForRead(int fd, CB* cb); - - //////////////////////////////////////// - - // Summary: - // Removes the FD and the associated callback from the pollserver. - // If the callback is registered with other FDs, they will continue - // to be processed using the callback without modification. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the file-descriptor which should no-longer be monitored. - virtual void UnregisterFD(int fd); - - //////////////////////////////////////// - - // Summary: - // Modifies the event mask for the file-descriptor, replacing - // the old event_mask with the new one specified here. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the fd whose event mask should be modified. - // event_mask - the new event mask. - virtual void ModifyCallback(int fd, int event_mask); - - //////////////////////////////////////// - - // Summary: - // Modifies the event mask for the file-descriptor such that we - // no longer request events when 'fd' is readable. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the fd whose event mask should be modified. - virtual void StopRead(int fd); - - //////////////////////////////////////// - - // Summary: - // Modifies the event mask for the file-descriptor such that we - // request events when 'fd' is readable. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the fd whose event mask should be modified. - virtual void StartRead(int fd); - - //////////////////////////////////////// - - // Summary: - // Modifies the event mask for the file-descriptor such that we - // no longer request events when 'fd' is writable. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the fd whose event mask should be modified. - virtual void StopWrite(int fd); - - //////////////////////////////////////// - - // Summary: - // Modifies the event mask for the file-descriptor such that we - // request events when 'fd' is writable. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the fd whose event mask should be modified. - virtual void StartWrite(int fd); - - //////////////////////////////////////// - - // Summary: - // Looks up the callback associated with the file-desriptor 'fd'. - // If a callback is associated with this file-descriptor, then - // it's OnEvent() method is called with the file-descriptor 'fd', - // and event_mask 'event_mask' - // - // If no callback is registered for this file-descriptor, nothing - // will happen as a result of this call. - // - // This function is used internally by the EpollServer, but is - // available publically so that events might be 'faked'. Calling - // this function with an fd and event_mask is equivalent (as far - // as the callback is concerned) to having a real event generated - // by epoll (except, of course, that read(), etc won't necessarily - // be able to read anything) - // Args: - // fd - the file-descriptor on which an event has occured. - // event_mask - a bitmask representing the events which have occured - // on/for this fd. This bitmask is composed of - // POLLIN, POLLOUT, etc. - // - void HandleEvent(int fd, int event_mask); - - // Summary: - // Call this when you want the pollserver to - // wait for events and execute the callbacks associated with - // the file-descriptors on which those events have occured. - // Depending on the value of timeout_in_us_, this may or may - // not return immediately. Please reference the set_timeout() - // function for the specific behaviour. - virtual void WaitForEventsAndExecuteCallbacks(); - - // Summary: - // When an fd is registered to use edge trigger notification, the ready - // list can be used to simulate level trigger semantics. Edge trigger - // registration doesn't send an initial event, and only rising edge (going - // from blocked to unblocked) events are sent. A callback can put itself on - // the ready list by calling SetFDReady() after calling RegisterFD(). The - // OnEvent method of all callbacks associated with the fds on the ready - // list will be called immediately after processing the events returned by - // epoll_wait(). The fd is removed from the ready list before the - // callback's OnEvent() method is invoked. To stay on the ready list, the - // OnEvent() (or some function in that call chain) must call SetFDReady - // again. When a fd is unregistered using UnregisterFD(), the fd is - // automatically removed from the ready list. - // - // When the callback for a edge triggered fd hits the falling edge (about - // to block, either because of it got an EAGAIN, or had a short read/write - // operation), it should remove itself from the ready list using - // SetFDNotReady() (since OnEvent cannot distinguish between invocation - // from the ready list vs from a normal epoll event). All four ready list - // methods are safe to be called within the context of the callbacks. - // - // Since the ready list invokes EpollCallbackInterface::OnEvent, only fds - // that are registered with the EpollServer will be put on the ready list. - // SetFDReady() and SetFDNotReady() will do nothing if the EpollServer - // doesn't know about the fd passed in. - // - // Since the ready list cannot reliably determine proper set of events - // which should be sent to the callback, SetFDReady() requests the caller - // to provide the ready list with the event mask, which will be used later - // when OnEvent() is invoked by the ready list. Hence, the event_mask - // passedto SetFDReady() does not affect the actual epoll registration of - // the fd with the kernel. If a fd is already put on the ready list, and - // SetFDReady() is called again for that fd with a different event_mask, - // the event_mask will be updated. - virtual void SetFDReady(int fd, int events_to_fake); - - virtual void SetFDNotReady(int fd); - - // Summary: - // IsFDReady(), ReadyListSize(), and VerifyReadyList are intended as - // debugging tools and for writing unit tests. - // ISFDReady() returns whether a fd is in the ready list. - // ReadyListSize() returns the number of fds on the ready list. - // VerifyReadyList() checks the consistency of internal data structure. It - // will CHECK if it finds an error. - virtual bool IsFDReady(int fd) const; - - size_t ReadyListSize() const { return ready_list_size_; } - - void VerifyReadyList() const; - - //////////////////////////////////////// - - // Summary: - // Registers an alarm 'ac' to go off at time 'timeout_time_in_us'. - // If the callback returns a positive number from its OnAlarm() function, - // then the callback will be re-registered at that time, else the alarm - // owner is responsible for freeing up memory. - // - // Important: A give AlarmCB* can not be registered again if it is already - // registered. If a user wants to register a callback again it should first - // unregister the previous callback before calling RegisterAlarm again. - // Args: - // timeout_time_in_us - the absolute time at which the alarm should go off - // ac - the alarm which will be called. - virtual void RegisterAlarm(int64 timeout_time_in_us, AlarmCB* ac); - - // Summary: - // Registers an alarm 'ac' to go off at time: (ApproximateNowInUs() + - // delta_in_us). While this is somewhat less accurate (see the description - // for ApproximateNowInUs() to see how 'approximate'), the error is never - // worse than the amount of time it takes to process all events in one - // WaitForEvents. As with 'RegisterAlarm()', if the callback returns a - // positive number from its OnAlarm() function, then the callback will be - // re-registered at that time, else the alarm owner is responsible for - // freeing up memory. - // Note that this function is purely a convienence. The - // same thing may be accomplished by using RegisterAlarm with - // ApproximateNowInUs() directly. - // - // Important: A give AlarmCB* can not be registered again if it is already - // registered. If a user wants to register a callback again it should first - // unregister the previous callback before calling RegisterAlarm again. - // Args: - // delta_in_us - the delta in microseconds from the ApproximateTimeInUs() at - // which point the alarm should go off. - // ac - the alarm which will be called. - void RegisterAlarmApproximateDelta(int64 delta_in_us, AlarmCB* ac) { - RegisterAlarm(ApproximateNowInUsec() + delta_in_us, ac); - } - - //////////////////////////////////////// - - // Summary: - // Unregister the alarm referred to by iterator_token; Callers should - // be warned that a token may have become already invalid when OnAlarm() - // is called, was unregistered, or OnShutdown was called on that alarm. - // Args: - // iterator_token - iterator to the alarm callback to unregister. - virtual void UnregisterAlarm( - const EpollServer::AlarmRegToken& iterator_token); - - //////////////////////////////////////// - - // Summary: - // returns the number of file-descriptors registered in this EpollServer. - // Returns: - // number of FDs registered (discounting the internal pipe used for Wake) - virtual int NumFDsRegistered() const; - - // Summary: - // Force the epoll server to wake up (by writing to an internal pipe). - virtual void Wake(); - - // Summary: - // Wrapper around WallTimer's NowInUsec. We do this so that we can test - // EpollServer without using the system clock (and can avoid the flakiness - // that would ensue) - // Returns: - // the current time as number of microseconds since the Unix epoch. - virtual int64 NowInUsec() const; - - // Summary: - // Since calling NowInUsec() many thousands of times per - // WaitForEventsAndExecuteCallbacks function call is, to say the least, - // inefficient, we allow users to use an approximate time instead. The - // time returned from this function is as accurate as NowInUsec() when - // WaitForEventsAndExecuteCallbacks is not an ancestor of the caller's - // callstack. - // However, when WaitForEventsAndExecuteCallbacks -is- an ancestor, then - // this function returns the time at which the - // WaitForEventsAndExecuteCallbacks function started to process events or - // alarms. - // - // Essentially, this function makes available a fast and mostly accurate - // mechanism for getting the time for any function handling an event or - // alarm. When functions which are not handling callbacks or alarms call - // this function, they get the slow and "absolutely" accurate time. - // - // Users should be encouraged to use this function. - // Returns: - // the "approximate" current time as number of microseconds since the Unix - // epoch. - virtual int64 ApproximateNowInUsec() const; - - static std::string EventMaskToString(int event_mask); - - // Summary: - // Logs the state of the epoll server with LOG(ERROR). - void LogStateOnCrash(); - - // Summary: - // Set the timeout to the value specified. - // If the timeout is set to a negative number, - // WaitForEventsAndExecuteCallbacks() will only return when an event has - // occured - // If the timeout is set to zero, - // WaitForEventsAndExecuteCallbacks() will return immediately - // If the timeout is set to a positive number, - // WaitForEventsAndExecuteCallbacks() will return when an event has - // occured, or when timeout_in_us microseconds has elapsed, whichever - // is first. - // Args: - // timeout_in_us - value specified depending on behaviour desired. - // See above. - void set_timeout_in_us(int64 timeout_in_us) { - timeout_in_us_ = timeout_in_us; - } - - //////////////////////////////////////// - - // Summary: - // Accessor for the current value of timeout_in_us. - int timeout_in_us() const { return timeout_in_us_; } - - // Summary: - // Returns true when the EpollServer() is being destroyed. - bool in_shutdown() const { return in_shutdown_; } - - bool ContainsAlarm(EpollAlarmCallbackInterface* alarm) const { - return all_alarms_.find(alarm) != all_alarms_.end(); - } - - // Summary: - // A function for implementing the ready list. It invokes OnEvent for each - // of the fd in the ready list, and takes care of adding them back to the - // ready list if the callback requests it (by checking that out_ready_mask - // is non-zero). - void CallReadyListCallbacks(); - - protected: - virtual int GetFlags(int fd); - inline int SetFlags(int fd, int flags) { - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); - } - - virtual void SetNonblocking(int fd); - - // This exists here so that we can override this function in unittests - // in order to make effective mock EpollServer objects. - virtual int epoll_wait_impl(int epfd, - struct epoll_event* events, - int max_events, - int timeout_in_ms); - - // this struct is used internally, and is never used by anything external - // to this class. Some of its members are declared mutable to get around the - // restriction imposed by hash_set. Since hash_set knows nothing about the - // objects it stores, it has to assume that every bit of the object is used - // in the hash function and equal_to comparison. Thus hash_set::iterator is a - // const iterator. In this case, the only thing that must stay constant is - // fd. Everything else are just along for the ride and changing them doesn't - // compromise the hash_set integrity. - struct CBAndEventMask { - CBAndEventMask() - : cb(NULL), - fd(-1), - event_mask(0), - events_asserted(0), - events_to_fake(0), - in_use(false) { - entry.le_next = NULL; - entry.le_prev = NULL; - } - - CBAndEventMask(EpollCallbackInterface* cb, - int event_mask, - int fd) - : cb(cb), fd(fd), event_mask(event_mask), events_asserted(0), - events_to_fake(0), in_use(false) { - entry.le_next = NULL; - entry.le_prev = NULL; - } - - // Required operator for hash_set. Normally operator== should be a free - // standing function. However, since CBAndEventMask is a protected type and - // it will never be a base class, it makes no difference. - bool operator==(const CBAndEventMask& cb_and_mask) const { - return fd == cb_and_mask.fd; - } - // A callback. If the fd is unregistered inside the callchain of OnEvent, - // the cb will be set to NULL. - mutable EpollCallbackInterface* cb; - - mutable LIST_ENTRY(CBAndEventMask) entry; - // file descriptor registered with the epoll server. - int fd; - // the current event_mask registered for this callback. - mutable int event_mask; - // the event_mask that was returned by epoll - mutable int events_asserted; - // the event_mask for the ready list to use to call OnEvent. - mutable int events_to_fake; - // toggle around calls to OnEvent to tell UnregisterFD to not erase the - // iterator because HandleEvent is using it. - mutable bool in_use; - }; - - // Custom hash function to be used by hash_set. - struct CBAndEventMaskHash { - size_t operator()(const CBAndEventMask& cb_and_eventmask) const { - return static_cast<size_t>(cb_and_eventmask.fd); - } - }; - - typedef base::hash_set<CBAndEventMask, CBAndEventMaskHash> FDToCBMap; - - // the following four functions are OS-specific, and are likely - // to be changed in a subclass if the poll/select method is changed - // from epoll. - - // Summary: - // Deletes a file-descriptor from the set of FDs that should be - // monitored with epoll. - // Note that this only deals with modifying data relating -directly- - // with the epoll call-- it does not modify any data within the - // epoll_server. - // Args: - // fd - the file descriptor to-be-removed from the monitoring set - virtual void DelFD(int fd) const; - - //////////////////////////////////////// - - // Summary: - // Adds a file-descriptor to the set of FDs that should be - // monitored with epoll. - // Note that this only deals with modifying data relating -directly- - // with the epoll call. - // Args: - // fd - the file descriptor to-be-added to the monitoring set - // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc - // OR'd together) which will be associated with this - // FD initially. - virtual void AddFD(int fd, int event_mask) const; - - //////////////////////////////////////// - - // Summary: - // Modifies a file-descriptor in the set of FDs that should be - // monitored with epoll. - // Note that this only deals with modifying data relating -directly- - // with the epoll call. - // Args: - // fd - the file descriptor to-be-added to the monitoring set - // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc - // OR'd together) which will be associated with this - // FD after this call. - virtual void ModFD(int fd, int event_mask) const; - - //////////////////////////////////////// - - // Summary: - // Modified the event mask associated with an FD in the set of - // data needed by epoll. - // Events are removed before they are added, thus, if ~0 is put - // in 'remove_event', whatever is put in 'add_event' will be - // the new event mask. - // If the file-descriptor specified is not registered in the - // epoll_server, then nothing happens as a result of this call. - // Args: - // fd - the file descriptor whose event mask is to be modified - // remove_event - the events which are to be removed from the current - // event_mask - // add_event - the events which are to be added to the current event_mask - // - // - virtual void ModifyFD(int fd, int remove_event, int add_event); - - //////////////////////////////////////// - - // Summary: - // Waits for events, and calls HandleEvents() for each - // fd, event pair discovered to possibly have an event. - // Note that a callback (B) may get a spurious event if - // another callback (A) has closed a file-descriptor N, and - // the callback (B) has a newly opened file-descriptor, which - // also happens to be N. - virtual void WaitForEventsAndCallHandleEvents(int64 timeout_in_us, - struct epoll_event events[], - int events_size); - - - - // Summary: - // An internal function for implementing the ready list. It adds a fd's - // CBAndEventMask to the ready list. If the fd is already on the ready - // list, it is a no-op. - void AddToReadyList(CBAndEventMask* cb_and_mask); - - // Summary: - // An internal function for implementing the ready list. It remove a fd's - // CBAndEventMask from the ready list. If the fd is not on the ready list, - // it is a no-op. - void RemoveFromReadyList(const CBAndEventMask& cb_and_mask); - - // Summary: - // Calls any pending alarms that should go off and reregisters them if they - // were recurring. - virtual void CallAndReregisterAlarmEvents(); - - // The file-descriptor created for epolling - int epoll_fd_; - - // The mapping of file-descriptor to CBAndEventMasks - FDToCBMap cb_map_; - - // Custom hash function to be used by hash_set. - struct AlarmCBHash { - size_t operator()(AlarmCB*const& p) const { - return reinterpret_cast<size_t>(p); - } - }; - - - // TOOD(sushantj): Having this hash_set is avoidable. We currently have it - // only so that we can enforce stringent checks that a caller can not register - // the same alarm twice. One option is to have an implementation in which - // this hash_set is used only in the debug mode. - typedef base::hash_set<AlarmCB*, AlarmCBHash> AlarmCBMap; - AlarmCBMap all_alarms_; - - TimeToAlarmCBMap alarm_map_; - - // The amount of time in microseconds that we'll wait before returning - // from the WaitForEventsAndExecuteCallbacks() function. - // If this is positive, wait that many microseconds. - // If this is negative, wait forever, or for the first event that occurs - // If this is zero, never wait for an event. - int64 timeout_in_us_; - - // This is nonzero only after the invocation of epoll_wait_impl within - // WaitForEventsAndCallHandleEvents and before the function - // WaitForEventsAndExecuteCallbacks returns. At all other times, this is - // zero. This enables us to have relatively accurate time returned from the - // ApproximateNowInUs() function. See that function for more details. - int64 recorded_now_in_us_; - - // This is used to implement CallAndReregisterAlarmEvents. This stores - // all alarms that were reregistered because OnAlarm() returned a - // value > 0 and the time at which they should be executed is less that - // the current time. By storing such alarms in this map we ensure - // that while calling CallAndReregisterAlarmEvents we do not call - // OnAlarm on any alarm in this set. This ensures that we do not - // go in an infinite loop. - AlarmCBMap alarms_reregistered_and_should_be_skipped_; - - LIST_HEAD(ReadyList, CBAndEventMask) ready_list_; - LIST_HEAD(TmpList, CBAndEventMask) tmp_list_; - int ready_list_size_; - // TODO(alyssar): make this into something that scales up. - static const int events_size_ = 256; - struct epoll_event events_[256]; - -#ifdef EPOLL_SERVER_EVENT_TRACING - struct EventRecorder { - public: - EventRecorder() : num_records_(0), record_threshold_(10000) {} - - ~EventRecorder() { - Clear(); - } - - // When a number of events equals the record threshold, - // the collected data summary for all FDs will be written - // to LOG(INFO). Note that this does not include the - // individual events (if you'reinterested in those, you'll - // have to get at them programmatically). - // After any such flushing to LOG(INFO) all events will - // be cleared. - // Note that the definition of an 'event' is a bit 'hazy', - // as it includes the 'Unregistration' event, and perhaps - // others. - void set_record_threshold(int64 new_threshold) { - record_threshold_ = new_threshold; - } - - void Clear() { - for (int i = 0; i < debug_events_.size(); ++i) { - delete debug_events_[i]; - } - debug_events_.clear(); - unregistered_fds_.clear(); - event_counts_.clear(); - } - - void MaybeRecordAndClear() { - ++num_records_; - if ((num_records_ > record_threshold_) && - (record_threshold_ > 0)) { - LOG(INFO) << "\n" << *this; - num_records_ = 0; - Clear(); - } - } - - void RecordFDMaskEvent(int fd, int mask, const char* function) { - FDMaskOutput* fdmo = new FDMaskOutput(fd, mask, function); - debug_events_.push_back(fdmo); - MaybeRecordAndClear(); - } - - void RecordEpollWaitEvent(int timeout_in_ms, - int num_events_generated) { - EpollWaitOutput* ewo = new EpollWaitOutput(timeout_in_ms, - num_events_generated); - debug_events_.push_back(ewo); - MaybeRecordAndClear(); - } - - void RecordEpollEvent(int fd, int event_mask) { - Events& events_for_fd = event_counts_[fd]; - events_for_fd.AssignFromMask(event_mask); - MaybeRecordAndClear(); - } - - friend ostream& operator<<(ostream& os, const EventRecorder& er) { - for (int i = 0; i < er.unregistered_fds_.size(); ++i) { - os << "fd: " << er.unregistered_fds_[i] << "\n"; - os << er.unregistered_fds_[i]; - } - for (EventCountsMap::const_iterator i = er.event_counts_.begin(); - i != er.event_counts_.end(); - ++i) { - os << "fd: " << i->first << "\n"; - os << i->second; - } - for (int i = 0; i < er.debug_events_.size(); ++i) { - os << *(er.debug_events_[i]) << "\n"; - } - return os; - } - - void RecordUnregistration(int fd) { - EventCountsMap::iterator i = event_counts_.find(fd); - if (i != event_counts_.end()) { - unregistered_fds_.push_back(i->second); - event_counts_.erase(i); - } - MaybeRecordAndClear(); - } - - protected: - class DebugOutput { - public: - friend ostream& operator<<(ostream& os, const DebugOutput& debug_output) { - debug_output.OutputToStream(os); - return os; - } - virtual void OutputToStream(ostream* os) const = 0; - virtual ~DebugOutput() {} - }; - - class FDMaskOutput : public DebugOutput { - public: - FDMaskOutput(int fd, int mask, const char* function) : - fd_(fd), mask_(mask), function_(function) {} - virtual void OutputToStream(ostream* os) const { - (*os) << "func: " << function_ - << "\tfd: " << fd_; - if (mask_ != 0) { - (*os) << "\tmask: " << EventMaskToString(mask_); - } - } - int fd_; - int mask_; - const char* function_; - }; - - class EpollWaitOutput : public DebugOutput { - public: - EpollWaitOutput(int timeout_in_ms, - int num_events_generated) : - timeout_in_ms_(timeout_in_ms), - num_events_generated_(num_events_generated) {} - virtual void OutputToStream(ostream* os) const { - (*os) << "timeout_in_ms: " << timeout_in_ms_ - << "\tnum_events_generated: " << num_events_generated_; - } - protected: - int timeout_in_ms_; - int num_events_generated_; - }; - - struct Events { - Events() : - epoll_in(0), - epoll_pri(0), - epoll_out(0), - epoll_rdnorm(0), - epoll_rdband(0), - epoll_wrnorm(0), - epoll_wrband(0), - epoll_msg(0), - epoll_err(0), - epoll_hup(0), - epoll_oneshot(0), - epoll_et(0) {} - - void AssignFromMask(int event_mask) { - if (event_mask & EPOLLIN) ++epoll_in; - if (event_mask & EPOLLPRI) ++epoll_pri; - if (event_mask & EPOLLOUT) ++epoll_out; - if (event_mask & EPOLLRDNORM) ++epoll_rdnorm; - if (event_mask & EPOLLRDBAND) ++epoll_rdband; - if (event_mask & EPOLLWRNORM) ++epoll_wrnorm; - if (event_mask & EPOLLWRBAND) ++epoll_wrband; - if (event_mask & EPOLLMSG) ++epoll_msg; - if (event_mask & EPOLLERR) ++epoll_err; - if (event_mask & EPOLLHUP) ++epoll_hup; - if (event_mask & EPOLLONESHOT) ++epoll_oneshot; - if (event_mask & EPOLLET) ++epoll_et; - }; - - friend ostream& operator<<(ostream& os, const Events& ev) { - if (ev.epoll_in) { - os << "\t EPOLLIN: " << ev.epoll_in << "\n"; - } - if (ev.epoll_pri) { - os << "\t EPOLLPRI: " << ev.epoll_pri << "\n"; - } - if (ev.epoll_out) { - os << "\t EPOLLOUT: " << ev.epoll_out << "\n"; - } - if (ev.epoll_rdnorm) { - os << "\t EPOLLRDNORM: " << ev.epoll_rdnorm << "\n"; - } - if (ev.epoll_rdband) { - os << "\t EPOLLRDBAND: " << ev.epoll_rdband << "\n"; - } - if (ev.epoll_wrnorm) { - os << "\t EPOLLWRNORM: " << ev.epoll_wrnorm << "\n"; - } - if (ev.epoll_wrband) { - os << "\t EPOLLWRBAND: " << ev.epoll_wrband << "\n"; - } - if (ev.epoll_msg) { - os << "\t EPOLLMSG: " << ev.epoll_msg << "\n"; - } - if (ev.epoll_err) { - os << "\t EPOLLERR: " << ev.epoll_err << "\n"; - } - if (ev.epoll_hup) { - os << "\t EPOLLHUP: " << ev.epoll_hup << "\n"; - } - if (ev.epoll_oneshot) { - os << "\t EPOLLONESHOT: " << ev.epoll_oneshot << "\n"; - } - if (ev.epoll_et) { - os << "\t EPOLLET: " << ev.epoll_et << "\n"; - } - return os; - } - - unsigned int epoll_in; - unsigned int epoll_pri; - unsigned int epoll_out; - unsigned int epoll_rdnorm; - unsigned int epoll_rdband; - unsigned int epoll_wrnorm; - unsigned int epoll_wrband; - unsigned int epoll_msg; - unsigned int epoll_err; - unsigned int epoll_hup; - unsigned int epoll_oneshot; - unsigned int epoll_et; - }; - - std::vector<DebugOutput*> debug_events_; - std::vector<Events> unregistered_fds_; - typedef base::hash_map<int, Events> EventCountsMap; - EventCountsMap event_counts_; - int64 num_records_; - int64 record_threshold_; - }; - - void ClearEventRecords() { - event_recorder_.Clear(); - } - void WriteEventRecords(ostream* os) const { - (*os) << event_recorder_; - } - - mutable EventRecorder event_recorder_; - -#endif - - private: - // Helper functions used in the destructor. - void CleanupFDToCBMap(); - void CleanupTimeToAlarmCBMap(); - - // The callback registered to the fds below. As the purpose of their - // registration is to wake the epoll server it just clears the pipe and - // returns. - scoped_ptr<ReadPipeCallback> wake_cb_; - - // A pipe owned by the epoll server. The server will be registered to listen - // on read_fd_ and can be woken by Wake() which writes to write_fd_. - int read_fd_; - int write_fd_; - - // This boolean is checked to see if it is false at the top of the - // WaitForEventsAndExecuteCallbacks function. If not, then it either returns - // without doing work, and logs to ERROR, or aborts the program (in - // DEBUG mode). If so, then it sets the bool to true, does work, and - // sets it back to false when done. This catches unwanted recursion. - bool in_wait_for_events_and_execute_callbacks_; - - // Returns true when the EpollServer() is being destroyed. - bool in_shutdown_; - - DISALLOW_COPY_AND_ASSIGN(EpollServer); -}; - -class EpollAlarmCallbackInterface { - public: - // Summary: - // Called when an alarm times out. Invalidates an AlarmRegToken. - // WARNING: If a token was saved to refer to an alarm callback, OnAlarm must - // delete it, as the reference is no longer valid. - // Returns: - // the unix time (in microseconds) at which this alarm should be signaled - // again, or 0 if the alarm should be removed. - virtual int64 OnAlarm() = 0; - - // Summary: - // Called when the an alarm is registered. Invalidates an AlarmRegToken. - // Args: - // token: the iterator to the the alarm registered in the alarm map. - // WARNING: this token becomes invalid when the alarm fires, is - // unregistered, or OnShutdown is called on that alarm. - // eps: the epoll server the alarm is registered with. - virtual void OnRegistration(const EpollServer::AlarmRegToken& token, - EpollServer* eps) = 0; - - // Summary: - // Called when the an alarm is unregistered. - // WARNING: It is not valid to unregister a callback and then use the token - // that was saved to refer to the callback. - virtual void OnUnregistration() = 0; - - // Summary: - // Called when the epoll server is shutting down. - // Invalidates the AlarmRegToken that was given when this alarm was - // registered. - virtual void OnShutdown(EpollServer* eps) = 0; - - virtual ~EpollAlarmCallbackInterface() {} - - protected: - EpollAlarmCallbackInterface() {} -}; - -// A simple alarm which unregisters itself on destruction. -// -// PLEASE NOTE: -// Any classes overriding these functions must either call the implementation -// of the parent class, or is must otherwise make sure that the 'registered_' -// boolean and the token, 'token_', are updated appropriately. -class EpollAlarm : public EpollAlarmCallbackInterface { - public: - EpollAlarm(); - - virtual ~EpollAlarm(); - - // Marks the alarm as unregistered and returns 0. The return value may be - // safely ignored by subclasses. - virtual int64 OnAlarm() OVERRIDE; - - // Marks the alarm as registered, and stores the token. - virtual void OnRegistration(const EpollServer::AlarmRegToken& token, - EpollServer* eps) OVERRIDE; - - // Marks the alarm as unregistered. - virtual void OnUnregistration() OVERRIDE; - - // Marks the alarm as unregistered. - virtual void OnShutdown(EpollServer* eps) OVERRIDE; - - // If the alarm was registered, unregister it. - void UnregisterIfRegistered(); - - bool registered() const { return registered_; } - - const EpollServer* eps() const { return eps_; } - - private: - EpollServer::AlarmRegToken token_; - EpollServer* eps_; - bool registered_; -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_EPOLL_SERVER_H_ - diff --git a/net/tools/flip_server/flip_in_mem_edsm_server.cc b/net/tools/flip_server/flip_in_mem_edsm_server.cc index ce7ffb8..79f3e5b 100644 --- a/net/tools/flip_server/flip_in_mem_edsm_server.cc +++ b/net/tools/flip_server/flip_in_mem_edsm_server.cc @@ -15,6 +15,7 @@ #include "base/logging.h" #include "base/synchronization/lock.h" #include "base/timer/timer.h" +#include "net/tools/balsa/split.h" #include "net/tools/flip_server/acceptor_thread.h" #include "net/tools/flip_server/constants.h" #include "net/tools/flip_server/flip_config.h" @@ -22,7 +23,6 @@ #include "net/tools/flip_server/sm_connection.h" #include "net/tools/flip_server/sm_interface.h" #include "net/tools/flip_server/spdy_interface.h" -#include "net/tools/flip_server/split.h" #include "net/tools/flip_server/streamer_interface.h" using std::cout; @@ -421,4 +421,3 @@ int main (int argc, char**argv) close(pidfile_fd); return 0; } - diff --git a/net/tools/flip_server/http_interface.cc b/net/tools/flip_server/http_interface.cc index 916ba51..78c42cc 100644 --- a/net/tools/flip_server/http_interface.cc +++ b/net/tools/flip_server/http_interface.cc @@ -4,8 +4,8 @@ #include "net/tools/flip_server/http_interface.h" +#include "net/tools/balsa/balsa_frame.h" #include "net/tools/dump_cache/url_utilities.h" -#include "net/tools/flip_server/balsa_frame.h" #include "net/tools/flip_server/flip_config.h" #include "net/tools/flip_server/sm_connection.h" #include "net/tools/flip_server/spdy_util.h" diff --git a/net/tools/flip_server/http_interface.h b/net/tools/flip_server/http_interface.h index 18e616d..867e342 100644 --- a/net/tools/flip_server/http_interface.h +++ b/net/tools/flip_server/http_interface.h @@ -8,8 +8,8 @@ #include <string> #include "base/compiler_specific.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" +#include "net/tools/balsa/balsa_headers.h" +#include "net/tools/balsa/balsa_visitor_interface.h" #include "net/tools/flip_server/output_ordering.h" #include "net/tools/flip_server/sm_connection.h" #include "net/tools/flip_server/sm_interface.h" diff --git a/net/tools/flip_server/http_interface_test.cc b/net/tools/flip_server/http_interface_test.cc index ba9b3aa..c6e3c64 100644 --- a/net/tools/flip_server/http_interface_test.cc +++ b/net/tools/flip_server/http_interface_test.cc @@ -9,9 +9,9 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/balsa_frame.h" -#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/balsa/balsa_enums.h" +#include "net/tools/balsa/balsa_frame.h" +#include "net/tools/balsa/balsa_headers.h" #include "net/tools/flip_server/flip_config.h" #include "net/tools/flip_server/flip_test_utils.h" #include "net/tools/flip_server/mem_cache.h" diff --git a/net/tools/flip_server/http_message_constants.h b/net/tools/flip_server/http_message_constants.h deleted file mode 100644 index de700cc..0000000 --- a/net/tools/flip_server/http_message_constants.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_HTTP_MESSAGE_CONSTANTS_H__ -#define NET_TOOLS_FLIP_SERVER_HTTP_MESSAGE_CONSTANTS_H__ - -namespace net { - -const char* get_http_status_message(int status_message); -extern const int http_status_codes[]; -extern const int http_status_code_count; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_HTTP_MESSAGE_CONSTANTS_H__ - diff --git a/net/tools/flip_server/mem_cache.cc b/net/tools/flip_server/mem_cache.cc index d1e0e58..42d9343 100644 --- a/net/tools/flip_server/mem_cache.cc +++ b/net/tools/flip_server/mem_cache.cc @@ -17,10 +17,10 @@ #include <string> #include "base/strings/string_util.h" +#include "net/tools/balsa/balsa_frame.h" +#include "net/tools/balsa/balsa_headers.h" #include "net/tools/dump_cache/url_to_filename_encoder.h" #include "net/tools/dump_cache/url_utilities.h" -#include "net/tools/flip_server/balsa_frame.h" -#include "net/tools/flip_server/balsa_headers.h" namespace { // The directory where cache locates); diff --git a/net/tools/flip_server/mem_cache.h b/net/tools/flip_server/mem_cache.h index 300c84a..b059f02 100644 --- a/net/tools/flip_server/mem_cache.h +++ b/net/tools/flip_server/mem_cache.h @@ -10,8 +10,8 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" +#include "net/tools/balsa/balsa_headers.h" +#include "net/tools/balsa/balsa_visitor_interface.h" #include "net/tools/flip_server/constants.h" namespace net { diff --git a/net/tools/flip_server/mem_cache_test.cc b/net/tools/flip_server/mem_cache_test.cc index d5601ac..59bc485 100644 --- a/net/tools/flip_server/mem_cache_test.cc +++ b/net/tools/flip_server/mem_cache_test.cc @@ -4,7 +4,7 @@ #include "net/tools/flip_server/mem_cache.h" -#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/balsa/balsa_headers.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { diff --git a/net/tools/flip_server/noop_balsa_visitor.h b/net/tools/flip_server/noop_balsa_visitor.h deleted file mode 100644 index 04f4aa6..0000000 --- a/net/tools/flip_server/noop_balsa_visitor.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2013 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. -// -// Provides empty BalsaVisitorInterface overrides for convenience. -// Intended to be used as a base class for BalsaVisitorInterface subclasses that -// only need to override a small number of methods. - -#ifndef NET_TOOLS_FLIP_SERVER_NOOP_BALSA_VISITOR_H_ -#define NET_TOOLS_FLIP_SERVER_NOOP_BALSA_VISITOR_H_ - -#include "net/tools/flip_server/balsa_visitor_interface.h" - -namespace net { - -// See file comment above. -class NoOpBalsaVisitor : public BalsaVisitorInterface { - public: - NoOpBalsaVisitor() { } - virtual ~NoOpBalsaVisitor() { } - - virtual void ProcessBodyInput(const char* input, size_t size) OVERRIDE { } - virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE { } - virtual void ProcessHeaderInput(const char* input, size_t size) OVERRIDE { } - virtual void ProcessTrailerInput(const char* input, size_t size) OVERRIDE { } - virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE { } - - virtual void ProcessRequestFirstLine(const char* line_input, - size_t line_length, - const char* method_input, - size_t method_length, - const char* request_uri_input, - size_t request_uri_length, - const char* version_input, - size_t version_length) OVERRIDE { } - virtual void ProcessResponseFirstLine(const char* line_input, - size_t line_length, - const char* version_input, - size_t version_length, - const char* status_input, - size_t status_length, - const char* reason_input, - size_t reason_length) OVERRIDE { } - virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE { } - virtual void ProcessChunkExtensions(const char* input, size_t size) OVERRIDE { - } - virtual void HeaderDone() OVERRIDE { } - virtual void MessageDone() OVERRIDE { } - virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE { } - virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE { } - virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE { } - virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE { } - - private: - DISALLOW_COPY_AND_ASSIGN(NoOpBalsaVisitor); -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_NOOP_BALSA_VISITOR_H_ diff --git a/net/tools/flip_server/output_ordering.h b/net/tools/flip_server/output_ordering.h index 922d03f..c1441f0 100644 --- a/net/tools/flip_server/output_ordering.h +++ b/net/tools/flip_server/output_ordering.h @@ -11,8 +11,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/flip_server/constants.h" -#include "net/tools/flip_server/epoll_server.h" #include "net/tools/flip_server/mem_cache.h" namespace net { diff --git a/net/tools/flip_server/ring_buffer.h b/net/tools/flip_server/ring_buffer.h index 03cbde7..ef660c3 100644 --- a/net/tools/flip_server/ring_buffer.h +++ b/net/tools/flip_server/ring_buffer.h @@ -7,7 +7,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "net/tools/flip_server/buffer_interface.h" +#include "net/tools/balsa/buffer_interface.h" namespace net { diff --git a/net/tools/flip_server/simple_buffer.cc b/net/tools/flip_server/simple_buffer.cc deleted file mode 100644 index d94153b..0000000 --- a/net/tools/flip_server/simple_buffer.cc +++ /dev/null @@ -1,208 +0,0 @@ -// 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/tools/flip_server/simple_buffer.h" -#include "base/logging.h" - -// Some of the following member functions are marked inlined, even though they -// are virtual. This may seem counter-intuitive, since virtual functions are -// generally not eligible for inlining. Profiling results indicate that these -// large amount of runtime is spent on virtual function dispatch on these -// simple functions. They are virtual because of the interface this class -// inherits from. However, it is very unlikely that anyone will sub-class -// SimpleBuffer and change their implementation. To get rid of this baggage, -// internal implementation (e.g., Write) explicitly use SimpleBuffer:: to -// qualify the method calls, thus disabling the virtual dispatch and enable -// inlining. - -namespace net { - -static const int kInitialSimpleBufferSize = 10; - -SimpleBuffer::SimpleBuffer() - : storage_(new char[kInitialSimpleBufferSize]), - write_idx_(0), - read_idx_(0), - storage_size_(kInitialSimpleBufferSize) { -} - -SimpleBuffer::SimpleBuffer(int size) - : write_idx_(0), - read_idx_(0), - storage_size_(size) { - // Callers may try to allocate overly large blocks, but negative sizes are - // obviously wrong. - CHECK_GE(size, 0); - storage_ = new char[size]; -} - -SimpleBuffer::~SimpleBuffer() { - delete[] storage_; -} - - -//////////////////////////////////////////////////////////////////////////////// - -int SimpleBuffer::ReadableBytes() const { - return write_idx_ - read_idx_; -} - -//////////////////////////////////////////////////////////////////////////////// - -std::string SimpleBuffer::str() const { - std::string s; - char * readable_ptr; - int readable_size; - GetReadablePtr(&readable_ptr, &readable_size); - s.append(readable_ptr, readable_ptr + readable_size); - return s; -} - -//////////////////////////////////////////////////////////////////////////////// - -int SimpleBuffer::BufferSize() const { - return storage_size_; -} - -//////////////////////////////////////////////////////////////////////////////// - -inline int SimpleBuffer::BytesFree() const { - return (storage_size_ - write_idx_); -} - -//////////////////////////////////////////////////////////////////////////////// - -bool SimpleBuffer::Empty() const { - return (read_idx_ == write_idx_); -} - -//////////////////////////////////////////////////////////////////////////////// - -bool SimpleBuffer::Full() const { - return ((write_idx_ == storage_size_) && (read_idx_ != write_idx_)); -} - -//////////////////////////////////////////////////////////////////////////////// - -// returns the number of characters written. -// appends up-to-'size' bytes to the simplebuffer. -int SimpleBuffer::Write(const char* bytes, int size) { - bool has_room = ((storage_size_ - write_idx_) >= size); - if (!has_room) { - (void)Reserve(size); - } - memcpy(storage_ + write_idx_, bytes, size); - SimpleBuffer::AdvanceWritablePtr(size); - return size; -} - -//////////////////////////////////////////////////////////////////////////////// - -// stores a pointer into the simple buffer in *ptr, -// and stores the number of characters which are allowed -// to be written in *size. -inline void SimpleBuffer::GetWritablePtr(char **ptr, int* size) const { - *ptr = storage_ + write_idx_; - *size = SimpleBuffer::BytesFree(); -} - -//////////////////////////////////////////////////////////////////////////////// - -// stores a pointer into the simple buffer in *ptr, -// and stores the number of characters which are allowed -// to be read in *size. -void SimpleBuffer::GetReadablePtr(char **ptr, int* size) const { - *ptr = storage_ + read_idx_; - *size = write_idx_ - read_idx_; -} - -//////////////////////////////////////////////////////////////////////////////// - -// returns the number of bytes read into 'bytes' -int SimpleBuffer::Read(char* bytes, int size) { - char * read_ptr = NULL; - int read_size = 0; - GetReadablePtr(&read_ptr, &read_size); - if (read_size > size) { - read_size = size; - } - memcpy(bytes, read_ptr, read_size); - AdvanceReadablePtr(read_size); - return read_size; -} - -//////////////////////////////////////////////////////////////////////////////// - -// removes all data from the simple buffer -void SimpleBuffer::Clear() { - read_idx_ = write_idx_ = 0; -} - -//////////////////////////////////////////////////////////////////////////////// - -// Attempts to reserve a contiguous block of buffer space by either reclaiming -// old data that is already read, and reallocate large storage as needed. -bool SimpleBuffer::Reserve(int size) { - if (size > 0 && BytesFree() < size) { - char * read_ptr = NULL; - int read_size = 0; - GetReadablePtr(&read_ptr, &read_size); - - if (read_size + size <= BufferSize()) { - // Can reclaim space from already read bytes by shifting - memmove(storage_, read_ptr, read_size); - read_idx_ = 0; - write_idx_ = read_size; - CHECK_GE(BytesFree(), size); - } else { - // what we need is to have at least size bytes available for writing. - // This implies that the buffer needs to be at least size bytes + - // read_size bytes long. Since we want linear time extensions in the case - // that we're extending this thing repeatedly, we should extend to twice - // the current size (if that is big enough), or the size + read_size - // bytes, whichever is larger. - int new_storage_size = 2 * storage_size_; - if (new_storage_size < size + read_size) { - new_storage_size = size + read_size; - } - - // have to extend the thing - char* new_storage = new char[new_storage_size]; - - // copy still useful info to the new buffer. - memcpy(new_storage, read_ptr, read_size); - // reset pointers. - read_idx_ = 0; - write_idx_ = read_size; - delete[] storage_; - storage_ = new_storage; - storage_size_ = new_storage_size; - } - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////// - -// removes the oldest 'amount_to_consume' characters. -void SimpleBuffer::AdvanceReadablePtr(int amount_to_advance) { - read_idx_ += amount_to_advance; - if (read_idx_ > storage_size_) { - read_idx_ = storage_size_; - } -} - -//////////////////////////////////////////////////////////////////////////////// - -// Moves the internal pointers around such that the -// amount of data specified here is expected to -// already be resident (as if it was Written) -inline void SimpleBuffer::AdvanceWritablePtr(int amount_to_advance) { - write_idx_ += amount_to_advance; - if (write_idx_ > storage_size_) { - write_idx_ = storage_size_; - } -} - -} // namespace net diff --git a/net/tools/flip_server/simple_buffer.h b/net/tools/flip_server/simple_buffer.h deleted file mode 100644 index 08f7d38..0000000 --- a/net/tools/flip_server/simple_buffer.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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. - -#ifndef NET_TOOLS_FLIP_SERVER_SIMPLE_BUFFER_H__ -#define NET_TOOLS_FLIP_SERVER_SIMPLE_BUFFER_H__ - -#include <string> - -#include "base/compiler_specific.h" -#include "net/tools/flip_server/buffer_interface.h" - -namespace net { - -class SimpleBuffer : public BufferInterface { - public: - SimpleBuffer(); - explicit SimpleBuffer(int size); - virtual ~SimpleBuffer(); - - std::string str() const; - - typedef char * iterator; - typedef const char * const_iterator; - - iterator begin() { return storage_ + read_idx_; } - const_iterator begin() const { return storage_ + read_idx_; } - - iterator end() { return storage_ + write_idx_; } - const_iterator end() const { return storage_ + write_idx_; } - - // The following functions all override pure virtual functions - // in BufferInterface. See buffer_interface.h for a description - // of what they do. - virtual int ReadableBytes() const OVERRIDE; - virtual int BufferSize() const OVERRIDE; - virtual int BytesFree() const OVERRIDE; - - virtual bool Empty() const OVERRIDE; - virtual bool Full() const OVERRIDE; - - virtual int Write(const char* bytes, int size) OVERRIDE; - - virtual void GetWritablePtr(char **ptr, int* size) const OVERRIDE; - - virtual void GetReadablePtr(char **ptr, int* size) const OVERRIDE; - - virtual int Read(char* bytes, int size) OVERRIDE; - - virtual void Clear() OVERRIDE; - - // This can be an expensive operation: costing a new/delete, and copying of - // all existing data. Even if the existing buffer does not need to be - // resized, unread data may still need to be non-destructively copied to - // consolidate fragmented free space. - virtual bool Reserve(int size) OVERRIDE; - - virtual void AdvanceReadablePtr(int amount_to_advance) OVERRIDE; - - virtual void AdvanceWritablePtr(int amount_to_advance) OVERRIDE; - - void Swap(SimpleBuffer* other) { - char* tmp = storage_; - storage_ = other->storage_; - other->storage_ = tmp; - - int tmp_int = write_idx_; - write_idx_ = other->write_idx_; - other->write_idx_ = tmp_int; - - tmp_int = read_idx_; - read_idx_ = other->read_idx_; - other->read_idx_ = tmp_int; - - tmp_int = storage_size_; - storage_size_ = other->storage_size_; - other->storage_size_ = tmp_int; - } - - protected: - char* storage_; - int write_idx_; - int read_idx_; - int storage_size_; - - private: - //DISALLOW_COPY_AND_ASSIGN(SimpleBuffer); -}; - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_SIMPLE_BUFFER_H__ diff --git a/net/tools/flip_server/sm_connection.h b/net/tools/flip_server/sm_connection.h index 3e21772..a79dca9 100644 --- a/net/tools/flip_server/sm_connection.h +++ b/net/tools/flip_server/sm_connection.h @@ -12,8 +12,8 @@ #include <string> #include "base/compiler_specific.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/flip_server/create_listener.h" -#include "net/tools/flip_server/epoll_server.h" #include "net/tools/flip_server/mem_cache.h" #include "net/tools/flip_server/ring_buffer.h" #include "net/tools/flip_server/sm_interface.h" diff --git a/net/tools/flip_server/sm_interface.h b/net/tools/flip_server/sm_interface.h index 5bc942f..28e5bc6 100644 --- a/net/tools/flip_server/sm_interface.h +++ b/net/tools/flip_server/sm_interface.h @@ -9,7 +9,7 @@ #include <string> -#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/balsa/balsa_headers.h" namespace net { diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index 3ce1f3b..3b2e513 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -12,8 +12,8 @@ #include "base/compiler_specific.h" #include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_protocol.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" +#include "net/tools/balsa/balsa_headers.h" +#include "net/tools/balsa/balsa_visitor_interface.h" #include "net/tools/flip_server/output_ordering.h" #include "net/tools/flip_server/sm_connection.h" #include "net/tools/flip_server/sm_interface.h" diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc index 7a1c6e9..4dbfea8 100644 --- a/net/tools/flip_server/spdy_interface_test.cc +++ b/net/tools/flip_server/spdy_interface_test.cc @@ -9,8 +9,8 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "net/spdy/buffered_spdy_framer.h" -#include "net/tools/flip_server/balsa_enums.h" -#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/balsa/balsa_enums.h" +#include "net/tools/balsa/balsa_headers.h" #include "net/tools/flip_server/flip_config.h" #include "net/tools/flip_server/flip_test_utils.h" #include "net/tools/flip_server/mem_cache.h" diff --git a/net/tools/flip_server/split.cc b/net/tools/flip_server/split.cc deleted file mode 100644 index 7900513..0000000 --- a/net/tools/flip_server/split.cc +++ /dev/null @@ -1,72 +0,0 @@ -// 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/tools/flip_server/split.h" - -#include <string.h> - -#include <vector> - -#include "base/strings/string_piece.h" - -namespace net { - -// Yea, this could be done with less code duplication using -// template magic, I know. -void SplitStringPieceToVector(const base::StringPiece& full, - const char* delim, - std::vector<base::StringPiece>* vec, - bool omit_empty_strings) { - vec->clear(); - if (full.empty() || delim[0] == '\0') - return; - - if (delim[1] == '\0') { - base::StringPiece::const_iterator s = full.begin(); - base::StringPiece::const_iterator e = s; - for (;e != full.end(); ++e) { - if (*e == delim[0]) { - if (e != s || !omit_empty_strings) { - vec->push_back(base::StringPiece(s, e - s)); - } - s = e; - ++s; - } - } - if (s != e) { - --e; - if (e != s || !omit_empty_strings) { - vec->push_back(base::StringPiece(s, e - s)); - } - } - } else { - base::StringPiece::const_iterator s = full.begin(); - base::StringPiece::const_iterator e = s; - for (;e != full.end(); ++e) { - bool one_matched = false; - for (const char *d = delim; *d != '\0'; ++d) { - if (*d == *e) { - one_matched = true; - break; - } - } - if (one_matched) { - if (e != s || !omit_empty_strings) { - vec->push_back(base::StringPiece(s, e - s)); - } - s = e; - ++s; - } - } - if (s != e) { - --e; - if (e != s || !omit_empty_strings) { - vec->push_back(base::StringPiece(s, e - s)); - } - } - } -} - -} // namespace net - diff --git a/net/tools/flip_server/split.h b/net/tools/flip_server/split.h deleted file mode 100644 index aae20b0..0000000 --- a/net/tools/flip_server/split.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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_TOOLS_FLIP_SERVER_SPLIT_H_ -#define NET_TOOLS_FLIP_SERVER_SPLIT_H_ - -#include <vector> -#include "base/strings/string_piece.h" - -namespace net { - -// Yea, this could be done with less code duplication using -// template magic, I know. -void SplitStringPieceToVector(const base::StringPiece& full, - const char* delim, - std::vector<base::StringPiece>* vec, - bool omit_empty_strings); - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_SPLIT_H_ - diff --git a/net/tools/flip_server/streamer_interface.cc b/net/tools/flip_server/streamer_interface.cc index b1612e7..8c2e0e7 100644 --- a/net/tools/flip_server/streamer_interface.cc +++ b/net/tools/flip_server/streamer_interface.cc @@ -6,7 +6,7 @@ #include <string> -#include "net/tools/flip_server/balsa_frame.h" +#include "net/tools/balsa/balsa_frame.h" #include "net/tools/flip_server/constants.h" #include "net/tools/flip_server/flip_config.h" #include "net/tools/flip_server/sm_connection.h" diff --git a/net/tools/flip_server/streamer_interface.h b/net/tools/flip_server/streamer_interface.h index 4f09b45..efaee6b 100644 --- a/net/tools/flip_server/streamer_interface.h +++ b/net/tools/flip_server/streamer_interface.h @@ -8,8 +8,8 @@ #include <string> #include "base/compiler_specific.h" -#include "net/tools/flip_server/balsa_headers.h" -#include "net/tools/flip_server/balsa_visitor_interface.h" +#include "net/tools/balsa/balsa_headers.h" +#include "net/tools/balsa/balsa_visitor_interface.h" #include "net/tools/flip_server/sm_interface.h" namespace net { diff --git a/net/tools/flip_server/string_piece_utils.h b/net/tools/flip_server/string_piece_utils.h deleted file mode 100644 index 3c46dd9..0000000 --- a/net/tools/flip_server/string_piece_utils.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2010 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_TOOLS_FLIP_SERVER_STRING_PIECE_UTILS_H_ -#define NET_TOOLS_FLIP_SERVER_STRING_PIECE_UTILS_H_ - -#include <ctype.h> - -#include "base/port.h" -#include "base/strings/string_piece.h" - -namespace net { - -struct StringPieceCaseHash { - size_t operator()(const base::StringPiece& sp) const { - // based on __stl_string_hash in http://www.sgi.com/tech/stl/string - size_t hash_val = 0; - for (base::StringPiece::const_iterator it = sp.begin(); - it != sp.end(); ++it) { - hash_val = 5 * hash_val + tolower(*it); - } - return hash_val; - } -}; - -struct StringPieceUtils { - static bool EqualIgnoreCase(const base::StringPiece& piece1, - const base::StringPiece& piece2) { - base::StringPiece::const_iterator p1i = piece1.begin(); - base::StringPiece::const_iterator p2i = piece2.begin(); - if (piece1.empty() && piece2.empty()) { - return true; - } else if (piece1.size() != piece2.size()) { - return false; - } - while (p1i != piece1.end() && p2i != piece2.end()) { - if (tolower(*p1i) != tolower(*p2i)) - return false; - ++p1i; - ++p2i; - } - return true; - } - - static void RemoveWhitespaceContext(base::StringPiece* piece1) { - base::StringPiece::const_iterator c = piece1->begin(); - base::StringPiece::const_iterator e = piece1->end(); - while (c != e && isspace(*c)) { - ++c; - } - if (c == e) { - *piece1 = base::StringPiece(c, e-c); - return; - } - --e; - while (c != e &&isspace(*e)) { - --e; - } - ++e; - *piece1 = base::StringPiece(c, e-c); - } - - static bool StartsWithIgnoreCase(const base::StringPiece& text, - const base::StringPiece& starts_with) { - if (text.size() < starts_with.size()) - return false; - return EqualIgnoreCase(text.substr(0, starts_with.size()), starts_with); - } -}; -struct StringPieceCaseEqual { - bool operator()(const base::StringPiece& piece1, - const base::StringPiece& piece2) const { - return StringPieceUtils::EqualIgnoreCase(piece1, piece2); - } -}; - - - -} // namespace net - -#endif // NET_TOOLS_FLIP_SERVER_STRING_PIECE_UTILS_H_ - |