diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/tools | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net/tools')
109 files changed, 4927 insertions, 2753 deletions
diff --git a/chromium/net/tools/DEPS b/chromium/net/tools/DEPS index 1ea12e4c3b9..4648a84aacb 100644 --- a/chromium/net/tools/DEPS +++ b/chromium/net/tools/DEPS @@ -1,3 +1,7 @@ +include_rules = [ + "+base/i18n", +] + skip_child_includes = [ "balsa", "flip_server", diff --git a/chromium/net/tools/balsa/balsa_enums.h b/chromium/net/tools/balsa/balsa_enums.h index a59dcda687c..2a49abada92 100644 --- a/chromium/net/tools/balsa/balsa_enums.h +++ b/chromium/net/tools/balsa/balsa_enums.h @@ -24,6 +24,11 @@ struct BalsaFrameEnums { }; enum ErrorCode { +#if defined(_WIN32) + // On Windows, <WinError.h> defines the NO_ERROR macro as 0L, which + // breaks the compilation of the "NO_ERROR = 0" line. +#undef NO_ERROR +#endif NO_ERROR = 0, // A sentinel value for convenience, none of the callbacks // should ever see this error code. // Header parsing errors diff --git a/chromium/net/tools/balsa/balsa_frame.cc b/chromium/net/tools/balsa/balsa_frame.cc index 96e91935597..463a678dead 100644 --- a/chromium/net/tools/balsa/balsa_frame.cc +++ b/chromium/net/tools/balsa/balsa_frame.cc @@ -8,7 +8,6 @@ #if __SSE2__ #include <emmintrin.h> #endif // __SSE2__ -#include <strings.h> #include <limits> #include <string> @@ -26,6 +25,13 @@ #include "net/tools/balsa/split.h" #include "net/tools/balsa/string_piece_utils.h" +#if defined(COMPILER_MSVC) +#include <string.h> +#define strncasecmp _strnicmp +#else +#include <strings.h> +#endif + namespace net { // Constants holding some header names for headers which can affect the way the @@ -426,7 +432,7 @@ void BalsaFrame::ProcessFirstLine(const char* begin, const char* end) { } if (is_request_) { - int version_length = + size_t version_length = headers_->whitespace_4_idx_ - headers_->non_whitespace_3_idx_; visitor_->ProcessRequestFirstLine( begin + headers_->non_whitespace_1_idx_, @@ -1556,9 +1562,4 @@ size_t BalsaFrame::ProcessInput(const char* input, size_t size) { return current - input; } -const uint32 BalsaFrame::kValidTerm1; -const uint32 BalsaFrame::kValidTerm1Mask; -const uint32 BalsaFrame::kValidTerm2; -const uint32 BalsaFrame::kValidTerm2Mask; - } // namespace net diff --git a/chromium/net/tools/balsa/balsa_frame.h b/chromium/net/tools/balsa/balsa_frame.h index 8e1240c86e9..6178d795d90 100644 --- a/chromium/net/tools/balsa/balsa_frame.h +++ b/chromium/net/tools/balsa/balsa_frame.h @@ -5,8 +5,6 @@ #ifndef NET_TOOLS_BALSA_BALSA_FRAME_H_ #define NET_TOOLS_BALSA_BALSA_FRAME_H_ -#include <strings.h> - #include <utility> #include <vector> diff --git a/chromium/net/tools/balsa/balsa_headers.cc b/chromium/net/tools/balsa/balsa_headers.cc index 27bfd24a514..a77b75b89e5 100644 --- a/chromium/net/tools/balsa/balsa_headers.cc +++ b/chromium/net/tools/balsa/balsa_headers.cc @@ -6,11 +6,11 @@ #include <stdio.h> #include <algorithm> -#include <ext/hash_set> #include <string> #include <utility> #include <vector> +#include "base/containers/hash_tables.h" #include "base/logging.h" #include "base/port.h" #include "base/strings/string_piece.h" @@ -20,15 +20,28 @@ #include "net/tools/balsa/simple_buffer.h" #include "third_party/tcmalloc/chromium/src/base/googleinit.h" +#if defined(COMPILER_MSVC) +#include <string.h> +#define snprintf _snprintf +#define strncasecmp _strnicmp +#else +#include <strings.h> +#endif + 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; +#if defined(COMPILER_MSVC) +base::hash_set<base::StringPiece, + net::StringPieceCaseCompare> g_multivalued_headers; +#else +base::hash_set<base::StringPiece, + net::StringPieceCaseHash, + net::StringPieceCaseEqual> g_multivalued_headers; +#endif void InitMultivaluedHeaders() { g_multivalued_headers.insert("accept"); @@ -66,8 +79,6 @@ const int kFastToBufferSize = 32; // I think 22 is adequate, but anyway.. 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) @@ -542,7 +553,7 @@ const base::StringPiece BalsaHeaders::GetHeader( const HeaderLines::const_iterator begin = header_lines_.begin(); HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin); if (i == end) { - return base::StringPiece(NULL, 0); + return base::StringPiece(); } return GetValueFromHeaderLineDescription(*i); } diff --git a/chromium/net/tools/balsa/balsa_headers.h b/chromium/net/tools/balsa/balsa_headers.h index 47cf388b8ab..840aae5e72e 100644 --- a/chromium/net/tools/balsa/balsa_headers.h +++ b/chromium/net/tools/balsa/balsa_headers.h @@ -938,7 +938,7 @@ class BalsaHeaders { return transfer_encoding_is_chunked_; } - static bool ResponseCodeImpliesNoBody(int code) { + static bool ResponseCodeImpliesNoBody(size_t 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); diff --git a/chromium/net/tools/balsa/string_piece_utils.h b/chromium/net/tools/balsa/string_piece_utils.h index eb0eaf365c8..12fc69be85f 100644 --- a/chromium/net/tools/balsa/string_piece_utils.h +++ b/chromium/net/tools/balsa/string_piece_utils.h @@ -12,6 +12,34 @@ namespace net { +#if defined(COMPILER_MSVC) +struct StringPieceCaseCompare { + static const size_t bucket_size = 4; + + 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; + } + + bool operator()(const base::StringPiece& sp1, + const base::StringPiece& sp2) const { + size_t len1 = sp1.length(); + size_t len2 = sp2.length(); + bool sp1_shorter = len1 < len2; + size_t len = sp1_shorter ? len1 : len2; + int rv = _memicmp(sp1.data(), sp2.data(), len); + if (rv == 0) { + return sp1_shorter; + } + return rv < 0; + } +}; +#else // COMPILER_MSVC struct StringPieceCaseHash { size_t operator()(const base::StringPiece& sp) const { // based on __stl_string_hash in http://www.sgi.com/tech/stl/string @@ -23,6 +51,7 @@ struct StringPieceCaseHash { return hash_val; } }; +#endif // COMPILER_MSVC struct StringPieceUtils { static bool EqualIgnoreCase(const base::StringPiece& piece1, diff --git a/chromium/net/tools/crash_cache/crash_cache.cc b/chromium/net/tools/crash_cache/crash_cache.cc index 32934a6a870..aeeafbe56bf 100644 --- a/chromium/net/tools/crash_cache/crash_cache.cc +++ b/chromium/net/tools/crash_cache/crash_cache.cc @@ -25,10 +25,10 @@ #include "net/base/net_errors.h" #include "net/base/net_export.h" #include "net/base/test_completion_callback.h" -#include "net/disk_cache/backend_impl.h" +#include "net/disk_cache/blockfile/backend_impl.h" +#include "net/disk_cache/blockfile/rankings.h" #include "net/disk_cache/disk_cache.h" #include "net/disk_cache/disk_cache_test_util.h" -#include "net/disk_cache/rankings.h" using base::Time; @@ -47,7 +47,7 @@ int RunSlave(RankCrashes action) { base::FilePath exe; PathService::Get(base::FILE_EXE, &exe); - CommandLine cmdline(exe); + base::CommandLine cmdline(exe); cmdline.AppendArg(base::IntToString(action)); base::ProcessHandle handle; diff --git a/chromium/net/tools/crl_set_dump/crl_set_dump.cc b/chromium/net/tools/crl_set_dump/crl_set_dump.cc index 6c3bb54bab5..ce6f5e5570d 100644 --- a/chromium/net/tools/crl_set_dump/crl_set_dump.cc +++ b/chromium/net/tools/crl_set_dump/crl_set_dump.cc @@ -62,8 +62,7 @@ int main(int argc, char** argv) { if (!output_filename.empty()) { const std::string out = final_crl_set->Serialize(); - if (file_util::WriteFile(output_filename, out.data(), - out.size()) == -1) { + if (base::WriteFile(output_filename, out.data(), out.size()) == -1) { fprintf(stderr, "Failed to write resulting CRL set\n"); return 1; } diff --git a/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc b/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc index aafd2caf216..f129ef22984 100644 --- a/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc +++ b/chromium/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc @@ -267,8 +267,9 @@ bool ParseAndStoreSpec(const std::string& spec_str, bool Main(int argc, char** argv) { base::AtExitManager at_exit_manager; base::MessageLoopForIO message_loop; - CommandLine::Init(argc, argv); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch("help")) { PrintUsage(&std::cout); return true; diff --git a/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc b/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc index f9caa79df62..d77abeee881 100644 --- a/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc +++ b/chromium/net/tools/dns_fuzz_stub/dns_fuzz_stub.cc @@ -58,7 +58,7 @@ bool ReadTestCase(const char* filename, return false; } - scoped_ptr<Value> value(base::JSONReader::Read(json)); + scoped_ptr<base::Value> value(base::JSONReader::Read(json)); if (!value.get()) { LOG(ERROR) << filename << ": couldn't parse JSON."; return false; diff --git a/chromium/net/tools/dump_cache/cache_dumper.cc b/chromium/net/tools/dump_cache/cache_dumper.cc index 7d29a5c1785..28981644282 100644 --- a/chromium/net/tools/dump_cache/cache_dumper.cc +++ b/chromium/net/tools/dump_cache/cache_dumper.cc @@ -8,7 +8,7 @@ #include "base/strings/utf_string_conversions.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" -#include "net/disk_cache/entry_impl.h" +#include "net/disk_cache/blockfile/entry_impl.h" #include "net/http/http_cache.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" diff --git a/chromium/net/tools/dump_cache/cache_dumper.h b/chromium/net/tools/dump_cache/cache_dumper.h index 4bdf4a1bd2f..9c5837ce566 100644 --- a/chromium/net/tools/dump_cache/cache_dumper.h +++ b/chromium/net/tools/dump_cache/cache_dumper.h @@ -8,7 +8,7 @@ #include <string> #include "base/files/file_path.h" -#include "net/disk_cache/backend_impl.h" +#include "net/disk_cache/blockfile/backend_impl.h" #ifdef WIN32 // Dumping the cache often creates very large filenames, which are tricky diff --git a/chromium/net/tools/dump_cache/dump_cache.cc b/chromium/net/tools/dump_cache/dump_cache.cc index d4ccc294448..b3970e6053f 100644 --- a/chromium/net/tools/dump_cache/dump_cache.cc +++ b/chromium/net/tools/dump_cache/dump_cache.cc @@ -13,7 +13,7 @@ #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "net/disk_cache/disk_format.h" +#include "net/disk_cache/blockfile/disk_format.h" #include "net/tools/dump_cache/dump_files.h" #include "net/tools/dump_cache/simple_cache_dumper.h" @@ -96,7 +96,7 @@ int LaunchSlave(CommandLine command_line, if (!base::LaunchProcess(command_line, base::LaunchOptions(), NULL)) { printf("Unable to launch the needed version of this tool: %ls\n", command_line.GetProgram().value().c_str()); - printf(kUpgradeHelp); + printf("%s", kUpgradeHelp); return TOOL_NOT_FOUND; } return ALL_GOOD; @@ -110,9 +110,10 @@ int main(int argc, const char* argv[]) { // Setup an AtExitManager so Singleton objects will be destroyed. base::AtExitManager at_exit_manager; - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); base::FilePath input_path = command_line.GetSwitchValuePath(kInputPath); if (input_path.empty()) return Help(); diff --git a/chromium/net/tools/dump_cache/dump_files.cc b/chromium/net/tools/dump_cache/dump_files.cc index 7f4fb58343d..796f205e818 100644 --- a/chromium/net/tools/dump_cache/dump_files.cc +++ b/chromium/net/tools/dump_cache/dump_files.cc @@ -14,16 +14,16 @@ #include <string> #include "base/file_util.h" +#include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/format_macros.h" #include "base/message_loop/message_loop.h" -#include "net/base/file_stream.h" -#include "net/disk_cache/block_files.h" -#include "net/disk_cache/disk_format.h" -#include "net/disk_cache/mapped_file.h" -#include "net/disk_cache/stats.h" -#include "net/disk_cache/storage_block-inl.h" -#include "net/disk_cache/storage_block.h" +#include "net/disk_cache/blockfile/block_files.h" +#include "net/disk_cache/blockfile/disk_format.h" +#include "net/disk_cache/blockfile/mapped_file.h" +#include "net/disk_cache/blockfile/stats.h" +#include "net/disk_cache/blockfile/storage_block-inl.h" +#include "net/disk_cache/blockfile/storage_block.h" namespace { @@ -31,14 +31,13 @@ const base::FilePath::CharType kIndexName[] = FILE_PATH_LITERAL("index"); // Reads the |header_size| bytes from the beginning of file |name|. bool ReadHeader(const base::FilePath& name, char* header, int header_size) { - net::FileStream file(NULL); - file.OpenSync(name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); - if (!file.IsOpen()) { + base::File file(name, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) { printf("Unable to open file %s\n", name.MaybeAsASCII().c_str()); return false; } - int read = file.ReadSync(header, header_size); + int read = file.Read(0, header, header_size); if (read != header_size) { printf("Unable to read file %s\n", name.MaybeAsASCII().c_str()); return false; @@ -57,7 +56,7 @@ int GetMajorVersionFromFile(const base::FilePath& name) { // Dumps the contents of the Stats record. void DumpStats(const base::FilePath& path, disk_cache::CacheAddr addr) { // We need a message loop, although we really don't run any task. - base::MessageLoop loop(base::MessageLoop::TYPE_IO); + base::MessageLoopForIO loop; disk_cache::BlockFiles block_files(path); if (!block_files.Init(false)) { @@ -355,7 +354,7 @@ int DumpContents(const base::FilePath& input_path) { DumpHeaders(input_path); // We need a message loop, although we really don't run any task. - base::MessageLoop loop(base::MessageLoop::TYPE_IO); + base::MessageLoopForIO loop; CacheDumper dumper(input_path); if (!dumper.Init()) return -1; diff --git a/chromium/net/tools/dump_cache/upgrade_win.cc b/chromium/net/tools/dump_cache/upgrade_win.cc index 5135592429b..dfb9e5c32fc 100644 --- a/chromium/net/tools/dump_cache/upgrade_win.cc +++ b/chromium/net/tools/dump_cache/upgrade_win.cc @@ -17,8 +17,8 @@ #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" -#include "net/disk_cache/backend_impl.h" -#include "net/disk_cache/entry_impl.h" +#include "net/disk_cache/blockfile/backend_impl.h" +#include "net/disk_cache/blockfile/entry_impl.h" #include "net/http/http_cache.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" @@ -893,7 +893,7 @@ HANDLE CreateServer(base::string16* pipe_number) { // This is the controller process for an upgrade operation. int UpgradeCache(const base::FilePath& output_path, HANDLE pipe) { - base::MessageLoop loop(base::MessageLoop::TYPE_IO); + base::MessageLoopForIO loop; MasterSM master(output_path, pipe); if (!master.DoInit()) { @@ -908,7 +908,7 @@ int UpgradeCache(const base::FilePath& output_path, HANDLE pipe) { // This process will only execute commands from the controller. int RunSlave(const base::FilePath& input_path, const base::string16& pipe_number) { - base::MessageLoop loop(base::MessageLoop::TYPE_IO); + base::MessageLoopForIO loop; base::win::ScopedHandle pipe(OpenServer(pipe_number)); if (!pipe.IsValid()) { diff --git a/chromium/net/tools/fetch/fetch_client.cc b/chromium/net/tools/fetch/fetch_client.cc deleted file mode 100644 index 12fae24399a..00000000000 --- a/chromium/net/tools/fetch/fetch_client.cc +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) 2012 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 "build/build_config.h" - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/command_line.h" -#include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/stats_counters.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "net/base/completion_callback.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/request_priority.h" -#include "net/cert/cert_verifier.h" -#include "net/dns/host_resolver.h" -#include "net/http/http_auth_handler_factory.h" -#include "net/http/http_cache.h" -#include "net/http/http_network_layer.h" -#include "net/http/http_network_session.h" -#include "net/http/http_request_info.h" -#include "net/http/http_server_properties_impl.h" -#include "net/http/http_stream_factory.h" -#include "net/http/http_transaction.h" -#include "net/http/transport_security_state.h" -#include "net/proxy/proxy_service.h" -#include "net/ssl/ssl_config_service_defaults.h" - -void usage(const char* program_name) { - printf("usage: %s --url=<url> [--n=<clients>] [--stats] [--use_cache]\n", - program_name); - exit(1); -} - -// Test Driver -class Driver { - public: - Driver() - : clients_(0) {} - - void ClientStarted() { clients_++; } - void ClientStopped() { - if (!--clients_) { - base::MessageLoop::current()->Quit(); - } - } - - private: - int clients_; -}; - -static base::LazyInstance<Driver> g_driver = LAZY_INSTANCE_INITIALIZER; - -// A network client -class Client { - public: - Client(net::HttpTransactionFactory* factory, const std::string& url) : - url_(url), - buffer_(new net::IOBuffer(kBufferSize)) { - int rv = factory->CreateTransaction( - net::DEFAULT_PRIORITY, &transaction_, NULL); - DCHECK_EQ(net::OK, rv); - buffer_->AddRef(); - g_driver.Get().ClientStarted(); - request_info_.url = url_; - request_info_.method = "GET"; - int state = transaction_->Start( - &request_info_, - base::Bind(&Client::OnConnectComplete, base::Unretained(this)), - net::BoundNetLog()); - DCHECK(state == net::ERR_IO_PENDING); - }; - - private: - void OnConnectComplete(int result) { - // Do work here. - int state = transaction_->Read( - buffer_.get(), kBufferSize, - base::Bind(&Client::OnReadComplete, base::Unretained(this))); - if (state == net::ERR_IO_PENDING) - return; // IO has started. - if (state < 0) - return; // ERROR! - OnReadComplete(state); - } - - void OnReadComplete(int result) { - if (result == 0) { - OnRequestComplete(result); - return; - } - - // Deal with received data here. - base::StatsCounter bytes_read("FetchClient.bytes_read"); - bytes_read.Add(result); - - // Issue a read for more data. - int state = transaction_->Read( - buffer_.get(), kBufferSize, - base::Bind(&Client::OnReadComplete, base::Unretained(this))); - if (state == net::ERR_IO_PENDING) - return; // IO has started. - if (state < 0) - return; // ERROR! - OnReadComplete(state); - } - - void OnRequestComplete(int result) { - base::StatsCounter requests("FetchClient.requests"); - requests.Increment(); - g_driver.Get().ClientStopped(); - printf("."); - } - - static const int kBufferSize = (16 * 1024); - GURL url_; - net::HttpRequestInfo request_info_; - scoped_ptr<net::HttpTransaction> transaction_; - scoped_refptr<net::IOBuffer> buffer_; -}; - -int main(int argc, char** argv) { - base::AtExitManager exit; - base::StatsTable table("fetchclient", 50, 1000); - table.set_current(&table); - - CommandLine::Init(argc, argv); - const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); - std::string url = parsed_command_line.GetSwitchValueASCII("url"); - if (!url.length()) - usage(argv[0]); - int client_limit = 1; - if (parsed_command_line.HasSwitch("n")) { - base::StringToInt(parsed_command_line.GetSwitchValueASCII("n"), - &client_limit); - } - bool use_cache = parsed_command_line.HasSwitch("use-cache"); - - // Do work here. - base::MessageLoop loop(base::MessageLoop::TYPE_IO); - - net::HttpStreamFactory::EnableNpnHttp2Draft04(); - - scoped_ptr<net::HostResolver> host_resolver( - net::HostResolver::CreateDefaultResolver(NULL)); - scoped_ptr<net::CertVerifier> cert_verifier( - net::CertVerifier::CreateDefault()); - scoped_ptr<net::TransportSecurityState> transport_security_state( - new net::TransportSecurityState); - scoped_ptr<net::ProxyService> proxy_service( - net::ProxyService::CreateDirect()); - scoped_refptr<net::SSLConfigService> ssl_config_service( - new net::SSLConfigServiceDefaults); - net::HttpTransactionFactory* factory = NULL; - scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory( - net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); - net::HttpServerPropertiesImpl http_server_properties; - - net::HttpNetworkSession::Params session_params; - session_params.host_resolver = host_resolver.get(); - session_params.cert_verifier = cert_verifier.get(); - session_params.transport_security_state = transport_security_state.get(); - session_params.proxy_service = proxy_service.get(); - session_params.http_auth_handler_factory = http_auth_handler_factory.get(); - session_params.http_server_properties = http_server_properties.GetWeakPtr(); - session_params.ssl_config_service = ssl_config_service.get(); - - scoped_refptr<net::HttpNetworkSession> network_session( - new net::HttpNetworkSession(session_params)); - if (use_cache) { - factory = new net::HttpCache(network_session.get(), - net::HttpCache::DefaultBackend::InMemory(0)); - } else { - factory = new net::HttpNetworkLayer(network_session.get()); - } - - { - base::StatsCounterTimer driver_time("FetchClient.total_time"); - base::StatsScope<base::StatsCounterTimer> scope(driver_time); - - Client** clients = new Client*[client_limit]; - for (int i = 0; i < client_limit; i++) - clients[i] = new Client(factory, url); - - base::MessageLoop::current()->Run(); - } - - // Print Statistics here. - int num_clients = table.GetCounterValue("c:FetchClient.requests"); - int test_time = table.GetCounterValue("t:FetchClient.total_time"); - int bytes_read = table.GetCounterValue("c:FetchClient.bytes_read"); - - printf("\n"); - printf("Clients : %d\n", num_clients); - printf("Time : %dms\n", test_time); - printf("Bytes Read : %d\n", bytes_read); - if (test_time > 0) { - const char *units = "bps"; - double bps = static_cast<float>(bytes_read * 8) / - (static_cast<float>(test_time) / 1000.0); - - if (bps > (1024*1024)) { - bps /= (1024*1024); - units = "Mbps"; - } else if (bps > 1024) { - bps /= 1024; - units = "Kbps"; - } - printf("Bandwidth : %.2f%s\n", bps, units); - } - - if (parsed_command_line.HasSwitch("stats")) { - // Dump the stats table. - printf("<stats>\n"); - int counter_max = table.GetMaxCounters(); - for (int index = 0; index < counter_max; index++) { - std::string name(table.GetRowName(index)); - if (name.length() > 0) { - int value = table.GetRowValue(index); - printf("%s:\t%d\n", name.c_str(), value); - } - } - printf("</stats>\n"); - } - return 0; -} diff --git a/chromium/net/tools/fetch/fetch_server.cc b/chromium/net/tools/fetch/fetch_server.cc deleted file mode 100644 index a2ffc5b5708..00000000000 --- a/chromium/net/tools/fetch/fetch_server.cc +++ /dev/null @@ -1,57 +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 "base/at_exit.h" -#include "base/command_line.h" -#include "base/memory/singleton.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/stats_counters.h" -#include "net/base/completion_callback.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/winsock_init.h" -#include "net/http/http_network_layer.h" -#include "net/http/http_request_info.h" -#include "net/http/http_transaction.h" -#include "net/proxy/proxy_service.h" -#include "net/tools/fetch/http_server.h" - -void usage(const char* program_name) { - printf("usage: %s\n", program_name); - exit(-1); -} - -int main(int argc, char**argv) { - base::AtExitManager exit; - base::StatsTable table("fetchserver", 50, 1000); - table.set_current(&table); - -#if defined(OS_WIN) - net::EnsureWinsockInit(); -#endif // defined(OS_WIN) - - CommandLine::Init(0, NULL); - const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); - - // Do work here. - base::MessageLoop loop; - HttpServer server(std::string(), - 80); // TODO(mbelshe): make port configurable - base::MessageLoop::current()->Run(); - - if (parsed_command_line.HasSwitch("stats")) { - // Dump the stats table. - printf("<stats>\n"); - int counter_max = table.GetMaxCounters(); - for (int index=0; index < counter_max; index++) { - std::string name(table.GetRowName(index)); - if (name.length() > 0) { - int value = table.GetRowValue(index); - printf("%s:\t%d\n", name.c_str(), value); - } - } - printf("</stats>\n"); - } - -} diff --git a/chromium/net/tools/fetch/http_listen_socket.cc b/chromium/net/tools/fetch/http_listen_socket.cc deleted file mode 100644 index 410a0ba55e5..00000000000 --- a/chromium/net/tools/fetch/http_listen_socket.cc +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) 2012 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/fetch/http_listen_socket.h" - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "net/tools/fetch/http_server_request_info.h" -#include "net/tools/fetch/http_server_response_info.h" - -HttpListenSocket::HttpListenSocket(net::SocketDescriptor s, - HttpListenSocket::Delegate* delegate) - : net::TCPListenSocket(s, this), - delegate_(delegate) { -} - -HttpListenSocket::~HttpListenSocket() { - STLDeleteElements(&connections_); -} - -void HttpListenSocket::Accept() { - net::SocketDescriptor conn = net::TCPListenSocket::AcceptSocket(); - DCHECK_NE(conn, net::kInvalidSocket); - if (conn == net::kInvalidSocket) { - // TODO - } else { - scoped_ptr<StreamListenSocket> sock( - new HttpListenSocket(conn, delegate_)); - DidAccept(this, sock.Pass()); - } -} - -// static -scoped_ptr<HttpListenSocket> HttpListenSocket::CreateAndListen( - const std::string& ip, - int port, - HttpListenSocket::Delegate* delegate) { - net::SocketDescriptor s = net::TCPListenSocket::CreateAndBind(ip, port); - if (s == net::kInvalidSocket) { - // TODO (ibrar): error handling. - } else { - scoped_ptr<HttpListenSocket> serv(new HttpListenSocket(s, delegate)); - serv->Listen(); - return serv.Pass(); - } - return scoped_ptr<HttpListenSocket>(); -} - -// -// HTTP Request Parser -// This HTTP request parser uses a simple state machine to quickly parse -// through the headers. The parser is not 100% complete, as it is designed -// for use in this simple test driver. -// -// Known issues: -// - does not handle whitespace on first HTTP line correctly. Expects -// a single space between the method/url and url/protocol. - -// Input character types. -enum header_parse_inputs { - INPUT_SPACE, - INPUT_CR, - INPUT_LF, - INPUT_COLON, - INPUT_DEFAULT, - MAX_INPUTS -}; - -// Parser states. -enum header_parse_states { - ST_METHOD, // Receiving the method. - ST_URL, // Receiving the URL. - ST_PROTO, // Receiving the protocol. - ST_HEADER, // Starting a Request Header. - ST_NAME, // Receiving a request header name. - ST_SEPARATOR, // Receiving the separator between header name and value. - ST_VALUE, // Receiving a request header value. - ST_DONE, // Parsing is complete and successful. - ST_ERR, // Parsing encountered invalid syntax. - MAX_STATES -}; - -// State transition table. -int parser_state[MAX_STATES][MAX_INPUTS] = { -/* METHOD */ { ST_URL, ST_ERR, ST_ERR, ST_ERR, ST_METHOD }, -/* URL */ { ST_PROTO, ST_ERR, ST_ERR, ST_URL, ST_URL }, -/* PROTOCOL */ { ST_ERR, ST_HEADER, ST_NAME, ST_ERR, ST_PROTO }, -/* HEADER */ { ST_ERR, ST_ERR, ST_NAME, ST_ERR, ST_ERR }, -/* NAME */ { ST_SEPARATOR, ST_DONE, ST_ERR, ST_SEPARATOR, ST_NAME }, -/* SEPARATOR */ { ST_SEPARATOR, ST_ERR, ST_ERR, ST_SEPARATOR, ST_VALUE }, -/* VALUE */ { ST_VALUE, ST_HEADER, ST_NAME, ST_VALUE, ST_VALUE }, -/* DONE */ { ST_DONE, ST_DONE, ST_DONE, ST_DONE, ST_DONE }, -/* ERR */ { ST_ERR, ST_ERR, ST_ERR, ST_ERR, ST_ERR } -}; - -// Convert an input character to the parser's input token. -int charToInput(char ch) { - switch(ch) { - case ' ': - return INPUT_SPACE; - case '\r': - return INPUT_CR; - case '\n': - return INPUT_LF; - case ':': - return INPUT_COLON; - } - return INPUT_DEFAULT; -} - -HttpServerRequestInfo* HttpListenSocket::ParseHeaders() { - int pos = 0; - int data_len = recv_data_.length(); - int state = ST_METHOD; - HttpServerRequestInfo* info = new HttpServerRequestInfo(); - std::string buffer; - std::string header_name; - std::string header_value; - while (pos < data_len) { - char ch = recv_data_[pos++]; - int input = charToInput(ch); - int next_state = parser_state[state][input]; - - bool transition = (next_state != state); - if (transition) { - // Do any actions based on state transitions. - switch (state) { - case ST_METHOD: - info->method = buffer; - buffer.clear(); - break; - case ST_URL: - info->url = GURL(buffer); - buffer.clear(); - break; - case ST_PROTO: - // TODO(mbelshe): Deal better with parsing protocol. - DCHECK(buffer == "HTTP/1.1"); - buffer.clear(); - break; - case ST_NAME: - header_name = buffer; - buffer.clear(); - break; - case ST_VALUE: - header_value = buffer; - // TODO(mbelshe): Deal better with duplicate headers. - DCHECK(info->headers.find(header_name) == info->headers.end()); - info->headers[header_name] = header_value; - buffer.clear(); - break; - } - state = next_state; - } else { - // Do any actions based on current state. - switch (state) { - case ST_METHOD: - case ST_URL: - case ST_PROTO: - case ST_VALUE: - case ST_NAME: - buffer.append(&ch, 1); - break; - case ST_DONE: - recv_data_ = recv_data_.substr(pos); - return info; - case ST_ERR: - delete info; - return NULL; - } - } - } - // No more characters, but we haven't finished parsing yet. - delete info; - return NULL; -} - -void HttpListenSocket::DidAccept( - net::StreamListenSocket* server, - scoped_ptr<net::StreamListenSocket> connection) { - connections_.insert(connection.release()); -} - -void HttpListenSocket::DidRead(net::StreamListenSocket* connection, - const char* data, - int len) { - recv_data_.append(data, len); - while (recv_data_.length()) { - HttpServerRequestInfo* request = ParseHeaders(); - if (!request) - break; - delegate_->OnRequest(this, request); - delete request; - } -} - -void HttpListenSocket::DidClose(net::StreamListenSocket* sock) { - size_t count = connections_.erase(sock); - DCHECK_EQ(1u, count); - delete sock; -} - -// Convert the numeric status code to a string. -// e.g. 200 -> "200 OK". -std::string ServerStatus(int code) { - switch(code) { - case 200: - return std::string("200 OK"); - // TODO(mbelshe): handle other codes. - } - NOTREACHED(); - return std::string(); -} - -void HttpListenSocket::Respond(HttpServerResponseInfo* info, - std::string& data) { - std::string response; - - // Status line. - response = info->protocol + " "; - response += ServerStatus(info->status); - response += "\r\n"; - - // Standard headers. - if (info->content_type.length()) - response += "Content-type: " + info->content_type + "\r\n"; - - if (info->content_length > 0) - response += "Content-length: " + base::IntToString(info->content_length) + - "\r\n"; - - if (info->connection_close) - response += "Connection: close\r\n"; - - // TODO(mbelshe): support additional headers. - - // End of headers. - response += "\r\n"; - - // Add data. - response += data; - - // Write it all out. - this->Send(response, false); -} diff --git a/chromium/net/tools/fetch/http_listen_socket.h b/chromium/net/tools/fetch/http_listen_socket.h deleted file mode 100644 index e0a58c03e2c..00000000000 --- a/chromium/net/tools/fetch/http_listen_socket.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012 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_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_ -#define NET_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_ - -#include <set> - -#include "base/message_loop/message_loop.h" -#include "net/socket/stream_listen_socket.h" -#include "net/socket/tcp_listen_socket.h" - -class HttpServerRequestInfo; -class HttpServerResponseInfo; - -// Implements a simple HTTP listen socket on top of the raw socket interface. -class HttpListenSocket : public net::TCPListenSocket, - public net::StreamListenSocket::Delegate { - public: - class Delegate { - public: - virtual void OnRequest(HttpListenSocket* connection, - HttpServerRequestInfo* info) = 0; - - protected: - virtual ~Delegate() {} - }; - - virtual ~HttpListenSocket(); - - static scoped_ptr<HttpListenSocket> CreateAndListen( - const std::string& ip, int port, HttpListenSocket::Delegate* delegate); - - // Send a server response. - // TODO(mbelshe): make this capable of non-ascii data. - void Respond(HttpServerResponseInfo* info, std::string& data); - - // StreamListenSocket::Delegate. - virtual void DidAccept( - net::StreamListenSocket* server, - scoped_ptr<net::StreamListenSocket> connection) OVERRIDE; - virtual void DidRead(net::StreamListenSocket* connection, - const char* data, int len) OVERRIDE; - virtual void DidClose(net::StreamListenSocket* sock) OVERRIDE; - - protected: - // Overrides TCPListenSocket::Accept(). - virtual void Accept() OVERRIDE; - - private: - static const int kReadBufSize = 16 * 1024; - - // Must run in the IO thread. - HttpListenSocket(net::SocketDescriptor s, HttpListenSocket::Delegate* del); - - // Expects the raw data to be stored in recv_data_. If parsing is successful, - // will remove the data parsed from recv_data_, leaving only the unused - // recv data. - HttpServerRequestInfo* ParseHeaders(); - - HttpListenSocket::Delegate* const delegate_; - std::string recv_data_; - - std::set<StreamListenSocket*> connections_; - - DISALLOW_COPY_AND_ASSIGN(HttpListenSocket); -}; - -#endif // NET_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_ diff --git a/chromium/net/tools/fetch/http_server.cc b/chromium/net/tools/fetch/http_server.cc deleted file mode 100644 index 71afb67bc30..00000000000 --- a/chromium/net/tools/fetch/http_server.cc +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2012 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/fetch/http_server.h" - -HttpServer::HttpServer(std::string ip, int port) - : session_(new HttpSession(ip, port)) { -} - -HttpServer::~HttpServer() { -} diff --git a/chromium/net/tools/fetch/http_server.h b/chromium/net/tools/fetch/http_server.h deleted file mode 100644 index 691413078e5..00000000000 --- a/chromium/net/tools/fetch/http_server.h +++ /dev/null @@ -1,26 +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_BASE_TOOLS_HTTP_SERVER_H_ -#define NET_BASE_TOOLS_HTTP_SERVER_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/tools/fetch/http_session.h" - -// Implements a simple, single-threaded HttpServer. -// Right now, this class implements no functionality above and beyond -// the HttpSession. -class HttpServer { -public: - HttpServer(std::string ip, int port); - ~HttpServer(); - -private: - scoped_ptr<HttpSession> session_; - DISALLOW_COPY_AND_ASSIGN(HttpServer); -}; - -#endif // NET_BASE_TOOLS_HTTP_SERVER_H_ - diff --git a/chromium/net/tools/fetch/http_server_request_info.cc b/chromium/net/tools/fetch/http_server_request_info.cc deleted file mode 100644 index 52b6860e0a4..00000000000 --- a/chromium/net/tools/fetch/http_server_request_info.cc +++ /dev/null @@ -1,11 +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/fetch/http_server_request_info.h" - -HttpServerRequestInfo::HttpServerRequestInfo() - : net::HttpRequestInfo() { -} - -HttpServerRequestInfo::~HttpServerRequestInfo() {} diff --git a/chromium/net/tools/fetch/http_server_request_info.h b/chromium/net/tools/fetch/http_server_request_info.h deleted file mode 100644 index 9b0fae18d16..00000000000 --- a/chromium/net/tools/fetch/http_server_request_info.h +++ /dev/null @@ -1,26 +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_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_ -#define NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_ - -#include <map> -#include <string> - -#include "net/http/http_request_info.h" - -// Meta information about an HTTP request. -// This is geared toward servers in that it keeps a map of the headers and -// values rather than just a list of header strings (which net::HttpRequestInfo -// does). -class HttpServerRequestInfo : public net::HttpRequestInfo { - public: - HttpServerRequestInfo(); - virtual ~HttpServerRequestInfo(); - - // A map of the names -> values for HTTP headers. - std::map<std::string, std::string> headers; -}; - -#endif // NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_ diff --git a/chromium/net/tools/fetch/http_server_response_info.cc b/chromium/net/tools/fetch/http_server_response_info.cc deleted file mode 100644 index d9ca02a6d0d..00000000000 --- a/chromium/net/tools/fetch/http_server_response_info.cc +++ /dev/null @@ -1,11 +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/fetch/http_server_response_info.h" - -HttpServerResponseInfo::HttpServerResponseInfo() - : status(200), content_length(0), connection_close(false) { -} - -HttpServerResponseInfo::~HttpServerResponseInfo() {} diff --git a/chromium/net/tools/fetch/http_server_response_info.h b/chromium/net/tools/fetch/http_server_response_info.h deleted file mode 100644 index e01f7300d97..00000000000 --- a/chromium/net/tools/fetch/http_server_response_info.h +++ /dev/null @@ -1,39 +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_HTTP_HTTP_RESPONSE_INFO_H_ -#define NET_HTTP_HTTP_RESPONSE_INFO_H_ - -#include <map> -#include <string> - -// Meta information about a server response. -class HttpServerResponseInfo { - public: - HttpServerResponseInfo(); - ~HttpServerResponseInfo(); - - // The response protocol. - std::string protocol; - - // The status code. - int status; - - // The server identifier. - std::string server_name; - - // The content type. - std::string content_type; - - // The content length. - int content_length; - - // Should we close the connection. - bool connection_close; - - // Additional response headers. - std::map<std::string, std::string> headers; -}; - -#endif // NET_HTTP_HTTP_RESPONSE_INFO_H_ diff --git a/chromium/net/tools/fetch/http_session.cc b/chromium/net/tools/fetch/http_session.cc deleted file mode 100644 index d9e991b798a..00000000000 --- a/chromium/net/tools/fetch/http_session.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012 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/fetch/http_session.h" -#include "net/tools/fetch/http_server_response_info.h" - -HttpSession::HttpSession(const std::string& ip, int port) - : socket_(HttpListenSocket::CreateAndListen(ip, port, this)) { -} - -HttpSession::~HttpSession() { -} - -void HttpSession::OnRequest(HttpListenSocket* connection, - HttpServerRequestInfo* info) { - // TODO(mbelshe): Make this function more interesting. - - // Generate a 10KB sequence of data. - CR_DEFINE_STATIC_LOCAL(std::string, data, ()); - if (data.length() == 0) { - while (data.length() < (10 * 1024)) - data += 'a' + (rand() % 26); - } - - HttpServerResponseInfo response_info; - response_info.protocol = "HTTP/1.1"; - response_info.status = 200; - response_info.content_type = "text/plain"; - response_info.content_length = data.length(); - - connection->Respond(&response_info, data); -} diff --git a/chromium/net/tools/fetch/http_session.h b/chromium/net/tools/fetch/http_session.h deleted file mode 100644 index b0266f2a9f7..00000000000 --- a/chromium/net/tools/fetch/http_session.h +++ /dev/null @@ -1,27 +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_BASE_TOOLS_HTTP_SESSION_H_ -#define NET_BASE_TOOLS_HTTP_SESSION_H_ - -#include "base/basictypes.h" -#include "net/http/http_request_info.h" -#include "net/tools/fetch/http_listen_socket.h" - -// An HttpSession encapsulates a server-side HTTP listen socket. -class HttpSession : HttpListenSocket::Delegate { - public: - HttpSession(const std::string& ip, int port); - virtual ~HttpSession(); - - virtual void OnRequest(HttpListenSocket* connection, - HttpServerRequestInfo* info) OVERRIDE; - - private: - scoped_ptr<HttpListenSocket> socket_; - DISALLOW_COPY_AND_ASSIGN(HttpSession); -}; - -#endif // NET_BASE_TOOLS_HTTP_SESSION_H_ - diff --git a/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc b/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc index ef1cefd6125..a8459b63c97 100644 --- a/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc +++ b/chromium/net/tools/flip_server/flip_in_mem_edsm_server.cc @@ -161,8 +161,8 @@ int main(int argc, char** argv) { signal(SIGINT, SignalHandler); signal(SIGHUP, SignalHandler); - CommandLine::Init(argc, argv); - CommandLine cl(argc, argv); + base::CommandLine::Init(argc, argv); + base::CommandLine cl(argc, argv); if (cl.HasSwitch("help") || argc < 2) { printf("%s <options>\n", argv[0]); diff --git a/chromium/net/tools/flip_server/loadtime_measurement.h b/chromium/net/tools/flip_server/loadtime_measurement.h index ccbb2e53234..59fcc0bfcdc 100644 --- a/chromium/net/tools/flip_server/loadtime_measurement.h +++ b/chromium/net/tools/flip_server/loadtime_measurement.h @@ -2,8 +2,8 @@ // 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_LOADTIME_MEASUREMENT_H__ -#define NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H__ +#ifndef NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_ +#define NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_ #include <errno.h> #include <fcntl.h> @@ -15,6 +15,9 @@ #include <string> #include <vector> +#include "base/file_util.h" +#include "base/strings/string_split.h" + // Class to handle loadtime measure related urls, which all start with testing // The in memory server has a singleton object of this class. It includes a // html file containing javascript to go through a list of urls and upload the @@ -26,8 +29,8 @@ class LoadtimeMeasurement { const std::string& pageload_html_file) : num_urls_(0), pageload_html_file_(pageload_html_file) { std::string urls_string; - read_file_to_string(urls_file.c_str(), &urls_string); - split_string(urls_string, '\n', &urls_); + base::ReadFileToString(urls_file, &urls_string); + base::SplitString(urls_string, '\n', &urls_); num_urls_ = urls_.size(); } @@ -39,7 +42,7 @@ class LoadtimeMeasurement { // remove "/testing/" from uri to get the action std::string action = uri.substr(9); if (pageload_html_file_.find(action) != std::string::npos) { - read_file_to_string(pageload_html_file_.c_str(), &output); + base::ReadFileToString(pageload_html_file_, &output); return; } if (action.find("get_total_iteration") == 0) { @@ -70,13 +73,13 @@ class LoadtimeMeasurement { } if (action.find("record_page_load") == 0) { std::vector<std::string> query; - split_string(action, '?', &query); + base::SplitString(action, '?', &query); std::vector<std::string> params; - split_string(query[1], '&', ¶ms); + base::SplitString(query[1], '&', ¶ms); std::vector<std::string> url; std::vector<std::string> loadtime; - split_string(params[1], '=', &url); - split_string(params[2], '=', &loadtime); + base::SplitString(params[1], '=', &url); + base::SplitString(params[2], '=', &loadtime); loadtimes_[url[1]] = atoi(loadtime[1].c_str()); output.append("OK"); return; @@ -84,41 +87,10 @@ class LoadtimeMeasurement { } private: - void read_file_to_string(const char* filename, std::string* output) { - output->clear(); - int fd = open(filename, 0, "r"); - if (fd == -1) - return; - char buffer[4096]; - ssize_t read_status = read(fd, buffer, sizeof(buffer)); - while (read_status > 0) { - output->append(buffer, static_cast<size_t>(read_status)); - do { - read_status = read(fd, buffer, sizeof(buffer)); - } while (read_status <= 0 && errno == EINTR); - } - close(fd); - } - - void split_string(const std::string& str, - char sepa, - std::vector<std::string>* sub_strs) { - size_t b = 0; - size_t e = str.find_first_of(sepa, b); - while (e != std::string::npos && e > b) { - sub_strs->push_back(str.substr(b, e - b)); - b = e + 1; - e = str.find_first_of(sepa, b); - } - if (b < str.size()) { - sub_strs->push_back(str.substr(b)); - } - } - int num_urls_; std::vector<std::string> urls_; std::map<std::string, int> loadtimes_; const std::string pageload_html_file_; }; -#endif // NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H__ +#endif // NET_TOOLS_FLIP_SERVER_LOADTIME_MEASUREMENT_H_ diff --git a/chromium/net/tools/flip_server/sm_connection.cc b/chromium/net/tools/flip_server/sm_connection.cc index 158cd30ff4f..4acdea44adf 100644 --- a/chromium/net/tools/flip_server/sm_connection.cc +++ b/chromium/net/tools/flip_server/sm_connection.cc @@ -391,7 +391,9 @@ bool SMConnection::SetupProtocolInterfaces() { VLOG(2) << log_prefix_ << ACCEPTOR_CLIENT_IDENT << (sm_spdy_interface_ ? "Creating" : "Reusing") << " SPDY interface."; - if (!sm_spdy_interface_) + if (sm_spdy_interface_) + sm_spdy_interface_->CreateFramer(version); + else sm_spdy_interface_ = new SpdySM( this, NULL, epoll_server_, memory_cache_, acceptor_, version); sm_interface_ = sm_spdy_interface_; diff --git a/chromium/net/tools/flip_server/sm_connection.h b/chromium/net/tools/flip_server/sm_connection.h index 93ad42e7e5a..2fe3228c0a1 100644 --- a/chromium/net/tools/flip_server/sm_connection.h +++ b/chromium/net/tools/flip_server/sm_connection.h @@ -25,6 +25,7 @@ namespace net { class FlipAcceptor; class MemoryCache; struct SSLState; +class SpdySM; // A frame of data to be sent. class DataFrame { @@ -146,7 +147,7 @@ class SMConnection : public SMConnectionInterface, RingBuffer read_buffer_; OutputList output_list_; - SMInterface* sm_spdy_interface_; + SpdySM* sm_spdy_interface_; SMInterface* sm_http_interface_; SMInterface* sm_streamer_interface_; SMInterface* sm_interface_; diff --git a/chromium/net/tools/flip_server/spdy_interface.cc b/chromium/net/tools/flip_server/spdy_interface.cc index 41ad43025e2..73025dd5407 100644 --- a/chromium/net/tools/flip_server/spdy_interface.cc +++ b/chromium/net/tools/flip_server/spdy_interface.cc @@ -51,7 +51,7 @@ SpdySM::SpdySM(SMConnection* connection, buffered_spdy_framer_->set_visitor(this); } -SpdySM::~SpdySM() { delete buffered_spdy_framer_; } +SpdySM::~SpdySM() { } void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool, SMInterface* sm_interface, @@ -130,7 +130,6 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id, VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn(" << stream_id << ")"; VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: " << headers.size(); - SpdyHeaderBlock supplement; SpdyHeaderBlock::const_iterator method = headers.end(); SpdyHeaderBlock::const_iterator host = headers.end(); SpdyHeaderBlock::const_iterator path = headers.end(); @@ -138,6 +137,8 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id, SpdyHeaderBlock::const_iterator version = headers.end(); SpdyHeaderBlock::const_iterator url = headers.end(); + std::string path_string, host_string, version_string; + if (spdy_version() == SPDY2) { url = headers.find("url"); method = headers.find("method"); @@ -153,22 +154,23 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id, // path contains a query string with a http:// in one of its values, // UrlUtilities::GetUrlPath will fail and always return a / breaking // the request. GetUrlPath assumes the absolute URL is being passed in. - std::string path_string = UrlUtilities::GetUrlPath(url->second); - std::string host_string = UrlUtilities::GetUrlHost(url->second); - path = supplement.insert(std::make_pair(":path", path_string)).first; - host = supplement.insert(std::make_pair(":host", host_string)).first; + path_string = UrlUtilities::GetUrlPath(url->second); + host_string = UrlUtilities::GetUrlHost(url->second); + version_string = version->second; } else { method = headers.find(":method"); host = headers.find(":host"); path = headers.find(":path"); scheme = headers.find(":scheme"); - version = supplement.insert(std::make_pair(":version", "HTTP/1.1")).first; if (method == headers.end() || host == headers.end() || path == headers.end() || scheme == headers.end()) { VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is " << "missing. Not creating stream"; return 0; } + host_string = host->second; + path_string = path->second; + version_string = "HTTP/1.1"; } if (scheme->second.compare("https") == 0) { @@ -177,16 +179,16 @@ int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id, if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) { VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second - << " " << path->second; - std::string filename = EncodeURL(path->second, - host->second, + << " " << path_string; + std::string filename = EncodeURL(path_string, + host_string, method->second); NewStream(stream_id, priority, filename); } else { http_data += - method->second + " " + path->second + " " + version->second + "\r\n"; + method->second + " " + path_string + " " + version_string + "\r\n"; VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " " - << path->second << " " << version->second; + << path_string << " " << version_string; http_data += "Host: " + (*is_https_scheme ? acceptor_->https_server_ip_ : acceptor_->http_server_ip_) + "\r\n"; @@ -241,7 +243,6 @@ void SpdySM::OnStreamFrameData(SpdyStreamId stream_id, void SpdySM::OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, - uint8 credential_slot, bool fin, bool unidirectional, const SpdyHeaderBlock& headers) { @@ -296,21 +297,25 @@ void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) { } size_t SpdySM::ProcessReadInput(const char* data, size_t len) { + DCHECK(buffered_spdy_framer_); return buffered_spdy_framer_->ProcessInput(data, len); } size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; } bool SpdySM::MessageFullyRead() const { + DCHECK(buffered_spdy_framer_); return buffered_spdy_framer_->MessageFullyRead(); } bool SpdySM::Error() const { + DCHECK(buffered_spdy_framer_); return close_on_error_ || buffered_spdy_framer_->HasError(); } const char* SpdySM::ErrorAsString() const { DCHECK(Error()); + DCHECK(buffered_spdy_framer_); return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code()); } @@ -322,9 +327,7 @@ void SpdySM::ResetForNewInterface(int32 server_idx) { void SpdySM::ResetForNewConnection() { // seq_num is not cleared, intentionally. - delete buffered_spdy_framer_; - buffered_spdy_framer_ = new BufferedSpdyFramer(SPDY2, true); - buffered_spdy_framer_->set_visitor(this); + buffered_spdy_framer_.reset(); valid_spdy_session_ = false; client_output_ordering_.Reset(); next_outgoing_stream_id_ = 2; @@ -332,6 +335,8 @@ void SpdySM::ResetForNewConnection() { // Send a settings frame int SpdySM::PostAcceptHook() { + // We should have buffered_spdy_framer_ set after reuse + DCHECK(buffered_spdy_framer_); SettingsMap settings; settings[SETTINGS_MAX_CONCURRENT_STREAMS] = SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100); @@ -469,8 +474,9 @@ size_t SpdySM::SendSynStreamImpl(uint32 stream_id, } } + DCHECK(buffered_spdy_framer_); SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream( - stream_id, 0, 0, 0, CONTROL_FLAG_NONE, &block); + stream_id, 0, 0, CONTROL_FLAG_NONE, &block); size_t df_size = fsrcf->size(); EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); @@ -492,6 +498,7 @@ size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { block[":version"] = headers.response_version().as_string(); } + DCHECK(buffered_spdy_framer_); SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply( stream_id, CONTROL_FLAG_NONE, &block); size_t df_size = fsrcf->size(); @@ -507,6 +514,7 @@ void SpdySM::SendDataFrameImpl(uint32 stream_id, int64 len, SpdyDataFlags flags, bool compress) { + DCHECK(buffered_spdy_framer_); // TODO(mbelshe): We can't compress here - before going into the // priority queue. Compression needs to be done // with late binding. @@ -607,4 +615,10 @@ void SpdySM::GetOutput() { } } +void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) { + DCHECK(!buffered_spdy_framer_); + buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true)); + buffered_spdy_framer_->set_visitor(this); +} + } // namespace net diff --git a/chromium/net/tools/flip_server/spdy_interface.h b/chromium/net/tools/flip_server/spdy_interface.h index 6e5ad0b0a8f..e472cf2ac59 100644 --- a/chromium/net/tools/flip_server/spdy_interface.h +++ b/chromium/net/tools/flip_server/spdy_interface.h @@ -10,6 +10,7 @@ #include <vector> #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_protocol.h" #include "net/tools/balsa/balsa_headers.h" @@ -45,6 +46,9 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { std::string remote_ip, bool use_ssl) OVERRIDE; + // Create new SPDY framer after reusing SpdySM and negotiating new version + void CreateFramer(SpdyMajorVersion spdy_version); + private: virtual void set_is_request() OVERRIDE {} SMInterface* NewConnectionInterface(); @@ -66,7 +70,6 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { virtual void OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, - uint8 credential_slot, bool fin, bool unidirectional, const SpdyHeaderBlock& headers) OVERRIDE; @@ -108,7 +111,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { uint32 value) OVERRIDE {} // Called when a PING frame has been parsed. - virtual void OnPing(uint32 unique_id) OVERRIDE {} + virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {} // Called when a RST_STREAM frame has been parsed. virtual void OnRstStream(SpdyStreamId stream_id, @@ -124,7 +127,8 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { // Called when a PUSH_PROMISE frame has been parsed. virtual void OnPushPromise(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id) OVERRIDE {} + SpdyStreamId promised_stream_id, + const SpdyHeaderBlock& headers) OVERRIDE {} public: virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE; @@ -159,7 +163,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { int64 len, uint32 flags, bool compress) OVERRIDE; - BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_; } + BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_.get(); } const OutputOrdering& output_ordering() const { return client_output_ordering_; @@ -170,6 +174,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { forward_ip_header_ = value; } SpdyMajorVersion spdy_version() const { + DCHECK(buffered_spdy_framer_); return buffered_spdy_framer_->protocol_version(); } @@ -189,7 +194,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface { virtual void GetOutput() OVERRIDE; private: - BufferedSpdyFramer* buffered_spdy_framer_; + scoped_ptr<BufferedSpdyFramer> buffered_spdy_framer_; bool valid_spdy_session_; // True if we have seen valid data on this session. // Use this to fail fast when junk is sent to our // port. diff --git a/chromium/net/tools/flip_server/spdy_interface_test.cc b/chromium/net/tools/flip_server/spdy_interface_test.cc index 3b988520158..7e2e5245cbb 100644 --- a/chromium/net/tools/flip_server/spdy_interface_test.cc +++ b/chromium/net/tools/flip_server/spdy_interface_test.cc @@ -44,15 +44,13 @@ class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface { virtual ~SpdyFramerVisitor() {} MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError)); MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&)); - MOCK_METHOD7(OnSynStream, + MOCK_METHOD6(OnSynStream, void(SpdyStreamId, SpdyStreamId, SpdyPriority, - uint8, bool, bool, const SpdyHeaderBlock&)); - MOCK_METHOD3(OnSynStream, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool)); @@ -62,11 +60,12 @@ class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface { bool)); MOCK_METHOD1(OnSettings, void(bool clear_persisted)); MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32)); - MOCK_METHOD1(OnPing, void(uint32 unique_id)); + MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus)); MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus)); MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32)); - MOCK_METHOD2(OnPushPromise, void(SpdyStreamId, SpdyStreamId)); + MOCK_METHOD3(OnPushPromise, + void(SpdyStreamId, SpdyStreamId, const SpdyHeaderBlock&)); }; class FakeSMConnection : public SMConnection { @@ -243,7 +242,7 @@ TEST_P(SpdySMProxyTest, OnSynStream_SPDY2) { InvokeWithoutArgs(&saver, &StringSaver::Save), Return(0))); } - visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + visitor->OnSynStream(stream_id, associated_id, 0, false, false, block); ASSERT_EQ(expected, saver.string); } @@ -278,7 +277,7 @@ TEST_P(SpdySMProxyTest, OnSynStream) { InvokeWithoutArgs(&saver, &StringSaver::Save), Return(0))); } - visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + visitor->OnSynStream(stream_id, associated_id, 0, false, false, block); ASSERT_EQ(expected, saver.string); } @@ -294,7 +293,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) { SpdyHeaderBlock block; testing::MockFunction<void(int)> checkpoint; // NOLINT - scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12)); + scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false)); block["method"] = "GET"; block["url"] = "http://www.example.com/path"; block["scheme"] = "http"; @@ -310,7 +309,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) { ProcessWriteInput(frame->data(), frame->size())).Times(1); } - visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + visitor->OnSynStream(stream_id, associated_id, 0, false, false, block); checkpoint.Call(0); visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true); } @@ -327,7 +326,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData) { SpdyHeaderBlock block; testing::MockFunction<void(int)> checkpoint; // NOLINT - scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12)); + scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false)); block[":method"] = "GET"; block[":host"] = "www.example.com"; block[":path"] = "/path"; @@ -345,7 +344,7 @@ TEST_P(SpdySMProxyTest, OnStreamFrameData) { ProcessWriteInput(frame->data(), frame->size())).Times(1); } - visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + visitor->OnSynStream(stream_id, associated_id, 0, false, false, block); checkpoint.Call(0); visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true); } @@ -397,7 +396,19 @@ TEST_P(SpdySMProxyTest, ResetForNewConnection) { interface_->ResetForNewConnection(); ASSERT_FALSE(HasStream(stream_id)); - ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state()); + ASSERT_TRUE(interface_->spdy_framer() == NULL); +} + +TEST_P(SpdySMProxyTest, CreateFramer) { + interface_->ResetForNewConnection(); + interface_->CreateFramer(SPDY2); + ASSERT_TRUE(interface_->spdy_framer() != NULL); + ASSERT_EQ(interface_->spdy_version(), SPDY2); + + interface_->ResetForNewConnection(); + interface_->CreateFramer(SPDY3); + ASSERT_TRUE(interface_->spdy_framer() != NULL); + ASSERT_EQ(interface_->spdy_version(), SPDY3); } TEST_P(SpdySMProxyTest, PostAcceptHook) { @@ -455,8 +466,13 @@ TEST_P(SpdySMProxyTest, SendErrorNotFound_SPDY2) { { InSequence s; - EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + if (GetParam() < SPDY4) { + EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } else { + EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_, OnDataFrameHeader(stream_id, _, true)); @@ -499,9 +515,15 @@ TEST_P(SpdySMProxyTest, SendErrorNotFound) { { InSequence s; - EXPECT_CALL(*spdy_framer_visitor_, - OnSynReply(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + if (GetParam() < SPDY4) { + EXPECT_CALL(*spdy_framer_visitor_, + OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } else { + EXPECT_CALL(*spdy_framer_visitor_, + OnHeaders(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_, OnDataFrameHeader(stream_id, _, true)); @@ -547,8 +569,8 @@ TEST_P(SpdySMProxyTest, SendSynStream_SPDY2) { { InSequence s; EXPECT_CALL(*spdy_framer_visitor_, - OnSynStream(stream_id, 0, _, _, false, false, _)) - .WillOnce(SaveArg<6>(&actual_header_block)); + OnSynStream(stream_id, 0, _, false, false, _)) + .WillOnce(SaveArg<5>(&actual_header_block)); } spdy_framer_->ProcessInput(df->data, df->size); @@ -581,8 +603,8 @@ TEST_P(SpdySMProxyTest, SendSynStream) { { InSequence s; EXPECT_CALL(*spdy_framer_visitor_, - OnSynStream(stream_id, 0, _, _, false, false, _)) - .WillOnce(SaveArg<6>(&actual_header_block)); + OnSynStream(stream_id, 0, _, false, false, _)) + .WillOnce(SaveArg<5>(&actual_header_block)); } spdy_framer_->ProcessInput(df->data, df->size); @@ -614,8 +636,13 @@ TEST_P(SpdySMProxyTest, SendSynReply_SPDY2) { { InSequence s; - EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + if (GetParam() < SPDY4) { + EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } else { + EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } } spdy_framer_->ProcessInput(df->data, df->size); @@ -645,8 +672,13 @@ TEST_P(SpdySMProxyTest, SendSynReply) { { InSequence s; - EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + if (GetParam() < SPDY4) { + EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } else { + EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } } spdy_framer_->ProcessInput(df->data, df->size); @@ -787,7 +819,7 @@ TEST_P(SpdySMServerTest, OnSynStream) { BalsaHeaders headers; memory_cache_->InsertFile(&headers, "GET_/path", ""); } - visitor->OnSynStream(stream_id, 0, 0, 0, true, true, spdy_headers); + visitor->OnSynStream(stream_id, 0, 0, true, true, spdy_headers); ASSERT_TRUE(HasStream(stream_id)); } @@ -817,8 +849,13 @@ TEST_P(SpdySMServerTest, NewStreamError) { { InSequence s; - EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + if (GetParam() < SPDY4) { + EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } else { + EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_, OnDataFrameHeader(stream_id, _, true)); diff --git a/chromium/net/tools/flip_server/spdy_ssl.cc b/chromium/net/tools/flip_server/spdy_ssl.cc index 1f18a0b30ff..2a5cb0e4953 100644 --- a/chromium/net/tools/flip_server/spdy_ssl.cc +++ b/chromium/net/tools/flip_server/spdy_ssl.cc @@ -42,9 +42,7 @@ void InitSSL(SSLState* state, PrintSslError(); state->ssl_method = SSLv23_method(); - // TODO(joth): Remove const_cast when the openssl 1.0.0 upgrade is complete. - // (See http://codereview.chromium.org/9254031). - state->ssl_ctx = SSL_CTX_new(const_cast<SSL_METHOD*>(state->ssl_method)); + state->ssl_ctx = SSL_CTX_new(state->ssl_method); if (!state->ssl_ctx) { PrintSslError(); LOG(FATAL) << "Unable to create SSL context"; diff --git a/chromium/net/tools/gdig/file_net_log.cc b/chromium/net/tools/gdig/file_net_log.cc index 7c755c855fe..5020f6de647 100644 --- a/chromium/net/tools/gdig/file_net_log.cc +++ b/chromium/net/tools/gdig/file_net_log.cc @@ -27,7 +27,7 @@ void FileNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { const char* source = NetLog::SourceTypeToString(entry.source().type); const char* type = NetLog::EventTypeToString(entry.type()); - scoped_ptr<Value> param_value(entry.ParametersToValue()); + scoped_ptr<base::Value> param_value(entry.ParametersToValue()); std::string params; if (param_value.get() != NULL) { JSONStringValueSerializer serializer(¶ms); diff --git a/chromium/net/tools/gdig/gdig.cc b/chromium/net/tools/gdig/gdig.cc index db0704c5a84..fbee8860ad6 100644 --- a/chromium/net/tools/gdig/gdig.cc +++ b/chromium/net/tools/gdig/gdig.cc @@ -16,6 +16,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "net/base/address_list.h" #include "net/base/ip_endpoint.h" @@ -275,8 +276,9 @@ GDig::Result GDig::Main(int argc, const char* argv[]) { } bool GDig::ParseCommandLine(int argc, const char* argv[]) { - CommandLine::Init(argc, argv); - const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + const base::CommandLine& parsed_command_line = + *base::CommandLine::ForCurrentProcess(); if (parsed_command_line.HasSwitch("config_timeout")) { int timeout_seconds = 0; @@ -299,7 +301,6 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) { std::map<std::string, NetLog::LogLevel> log_levels; log_levels["all"] = NetLog::LOG_ALL; log_levels["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES; - log_levels["basic"] = NetLog::LOG_BASIC; if (log_levels.find(log_param) != log_levels.end()) { level = log_levels[log_param]; @@ -362,7 +363,7 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) { ReplayLogEntry entry; entry.start_time = base::TimeDelta(); #if defined(OS_WIN) - entry.domain_name = WideToASCII(parsed_command_line.GetArgs()[0]); + entry.domain_name = base::UTF16ToASCII(parsed_command_line.GetArgs()[0]); #else entry.domain_name = parsed_command_line.GetArgs()[0]; #endif @@ -420,12 +421,11 @@ void GDig::OnDnsConfig(const DnsConfig& dns_config_const) { scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL)); dns_client->SetConfig(dns_config); + HostResolver::Options options; + options.max_concurrent_resolves = parallellism_; + options.max_retry_attempts = 1u; scoped_ptr<HostResolverImpl> resolver( - new HostResolverImpl( - HostCache::CreateDefaultCache(), - PrioritizedDispatcher::Limits(NUM_PRIORITIES, parallellism_), - HostResolverImpl::ProcTaskParams(NULL, 1), - log_.get())); + new HostResolverImpl(options, log_.get())); resolver->SetDnsClient(dns_client.Pass()); resolver_ = resolver.Pass(); diff --git a/chromium/net/tools/get_server_time/get_server_time.cc b/chromium/net/tools/get_server_time/get_server_time.cc index 19a2010d83e..ddc08b2f5c1 100644 --- a/chromium/net/tools/get_server_time/get_server_time.cc +++ b/chromium/net/tools/get_server_time/get_server_time.cc @@ -48,6 +48,8 @@ #include "net/proxy/proxy_config_service_fixed.h" #endif +using base::UTF16ToUTF8; + namespace { // base::TimeTicks::Now() is documented to have a resolution of @@ -148,32 +150,6 @@ BuildURLRequestContext(net::NetLog* net_log) { return context.Pass(); } -class SingleThreadRequestContextGetter : public net::URLRequestContextGetter { - public: - // Since there's only a single thread, there's no need to worry - // about when |context_| gets created. - SingleThreadRequestContextGetter( - net::NetLog* net_log, - const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner) - : context_(BuildURLRequestContext(net_log)), - main_task_runner_(main_task_runner) {} - - virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { - return context_.get(); - } - - virtual scoped_refptr<base::SingleThreadTaskRunner> - GetNetworkTaskRunner() const OVERRIDE { - return main_task_runner_; - } - - private: - virtual ~SingleThreadRequestContextGetter() {} - - const scoped_ptr<net::URLRequestContext> context_; - const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; -}; - // Assuming that the time |server_time| was received from a server, // that the request for the server was started on |start_ticks|, and // that it ended on |end_ticks|, fills |server_now| with an estimate @@ -218,12 +194,13 @@ int main(int argc, char* argv[]) { #endif base::AtExitManager exit_manager; - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; logging::InitLogging(settings); - const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& parsed_command_line = + *base::CommandLine::ForCurrentProcess(); GURL url(parsed_command_line.GetSwitchValueASCII("url")); if (!url.is_valid() || (url.scheme() != "http" && url.scheme() != "https")) { @@ -249,15 +226,19 @@ int main(int argc, char* argv[]) { net::NetLog net_log; PrintingLogObserver printing_log_observer; net_log.AddThreadSafeObserver(&printing_log_observer, net::NetLog::LOG_ALL); - scoped_refptr<SingleThreadRequestContextGetter> context_getter( - new SingleThreadRequestContextGetter(&net_log, - main_loop.message_loop_proxy())); QuitDelegate delegate; scoped_ptr<net::URLFetcher> fetcher( net::URLFetcher::Create(url, net::URLFetcher::HEAD, &delegate)); - fetcher->SetRequestContext(context_getter.get()); - + scoped_ptr<net::URLRequestContext> url_request_context( + BuildURLRequestContext(&net_log)); + fetcher->SetRequestContext( + // Since there's only a single thread, there's no need to worry + // about when the URLRequestContext gets created. + // The URLFetcher will take a reference on the object, and hence + // implicitly take ownership. + new net::TrivialURLRequestContextGetter(url_request_context.get(), + main_loop.message_loop_proxy())); const base::Time start_time = base::Time::Now(); const base::TimeTicks start_ticks = base::TimeTicks::Now(); diff --git a/chromium/net/tools/net_watcher/net_watcher.cc b/chromium/net/tools/net_watcher/net_watcher.cc index 67eb124f8ac..421e6951271 100644 --- a/chromium/net/tools/net_watcher/net_watcher.cc +++ b/chromium/net/tools/net_watcher/net_watcher.cc @@ -21,7 +21,7 @@ #include "net/proxy/proxy_config_service.h" #include "net/proxy/proxy_service.h" -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS) +#if defined(USE_GLIB) && !defined(OS_CHROMEOS) #include <glib-object.h> #endif @@ -50,6 +50,8 @@ const char* ConnectionTypeToString( return "CONNECTION_4G"; case net::NetworkChangeNotifier::CONNECTION_NONE: return "CONNECTION_NONE"; + case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH: + return "CONNECTION_BLUETOOTH"; default: return "CONNECTION_UNEXPECTED"; } @@ -131,7 +133,7 @@ int main(int argc, char* argv[]) { #if defined(OS_MACOSX) base::mac::ScopedNSAutoreleasePool pool; #endif -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS) +#if defined(USE_GLIB) && !defined(OS_CHROMEOS) // g_type_init will be deprecated in 2.36. 2.35 is the development // version for 2.36, hence do not call g_type_init starting 2.35. // http://developer.gnome.org/gobject/unstable/gobject-Type-Information.html#g-type-init @@ -140,9 +142,9 @@ int main(int argc, char* argv[]) { // Normally handled by BrowserMainLoop::InitializeToolkit(). g_type_init(); #endif -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS) +#endif // defined(USE_GLIB) && !defined(OS_CHROMEOS) base::AtExitManager exit_manager; - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; logging::InitLogging(settings); diff --git a/chromium/net/tools/quic/end_to_end_test.cc b/chromium/net/tools/quic/end_to_end_test.cc index 2b94b0f2fd1..a21dc4082a3 100644 --- a/chromium/net/tools/quic/end_to_end_test.cc +++ b/chromium/net/tools/quic/end_to_end_test.cc @@ -4,30 +4,39 @@ #include <stddef.h> #include <string> +#include <sys/epoll.h> #include <vector> +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/time/time.h" #include "net/base/ip_endpoint.h" #include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_sent_packet_manager.h" +#include "net/quic/quic_server_id.h" #include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_flow_controller_peer.h" #include "net/quic/test_tools/quic_session_peer.h" -#include "net/quic/test_tools/quic_test_writer.h" +#include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" +#include "net/test/gtest_util.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_in_memory_cache.h" +#include "net/tools/quic/quic_packet_writer_wrapper.h" #include "net/tools/quic/quic_server.h" #include "net/tools/quic/quic_socket_utils.h" #include "net/tools/quic/quic_spdy_client_stream.h" -#include "net/tools/quic/test_tools/http_message_test_utils.h" +#include "net/tools/quic/test_tools/http_message.h" #include "net/tools/quic/test_tools/packet_dropping_test_writer.h" #include "net/tools/quic/test_tools/quic_client_peer.h" #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" @@ -39,10 +48,14 @@ using base::StringPiece; using base::WaitableEvent; +using net::EpollServer; +using net::test::GenerateBody; using net::test::QuicConnectionPeer; +using net::test::QuicFlowControllerPeer; using net::test::QuicSessionPeer; -using net::test::QuicTestWriter; using net::test::ReliableQuicStreamPeer; +using net::test::ValueRestore; +using net::test::kClientDataStreamId1; using net::tools::test::PacketDroppingTestWriter; using net::tools::test::QuicDispatcherPeer; using net::tools::test::QuicServerPeer; @@ -58,14 +71,6 @@ namespace { const char* kFooResponseBody = "Artichoke hearts make me happy."; const char* kBarResponseBody = "Palm hearts are pretty delicious, also."; -void GenerateBody(string* body, int length) { - body->clear(); - body->reserve(length); - for (int i = 0; i < length; ++i) { - body->append(1, static_cast<char>(32 + i % (126 - 32))); - } -} - // Run all tests with the cross products of all versions. struct TestParams { TestParams(const QuicVersionVector& client_supported_versions, @@ -98,7 +103,6 @@ struct TestParams { vector<TestParams> GetTestParams() { vector<TestParams> params; QuicVersionVector all_supported_versions = QuicSupportedVersions(); - for (int use_pacing = 0; use_pacing < 2; ++use_pacing) { // Add an entry for server and client supporting all versions. params.push_back(TestParams(all_supported_versions, @@ -106,19 +110,6 @@ vector<TestParams> GetTestParams() { all_supported_versions[0], use_pacing != 0)); - // Test client supporting 1 version and server supporting all versions. - // Simulate an old client and exercise version downgrade in the server. - // No protocol negotiation should occur. Skip the i = 0 case because it - // is essentially the same as the default case. - for (size_t i = 1; i < all_supported_versions.size(); ++i) { - QuicVersionVector client_supported_versions; - client_supported_versions.push_back(all_supported_versions[i]); - params.push_back(TestParams(client_supported_versions, - all_supported_versions, - client_supported_versions[0], - use_pacing != 0)); - } - // Test client supporting all versions and server supporting 1 version. // Simulate an old server and exercise version downgrade in the client. // Protocol negotiation should occur. Skip the i = 0 case because it is @@ -126,6 +117,12 @@ vector<TestParams> GetTestParams() { for (size_t i = 1; i < all_supported_versions.size(); ++i) { QuicVersionVector server_supported_versions; server_supported_versions.push_back(all_supported_versions[i]); + if (all_supported_versions[i] >= QUIC_VERSION_17) { + // Until flow control is globally rolled out and we remove + // QUIC_VERSION_16, the server MUST support at least one QUIC version + // that does not use flow control. + server_supported_versions.push_back(QUIC_VERSION_16); + } params.push_back(TestParams(all_supported_versions, server_supported_versions, server_supported_versions[0], @@ -135,6 +132,28 @@ vector<TestParams> GetTestParams() { return params; } +class ServerDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ServerDelegate(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) {} + virtual ~ServerDelegate() {} + virtual void OnCanWrite() OVERRIDE { dispatcher_->OnCanWrite(); } + private: + QuicDispatcher* dispatcher_; +}; + +class ClientDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ClientDelegate(QuicClient* client) : client_(client) {} + virtual ~ClientDelegate() {} + virtual void OnCanWrite() OVERRIDE { + EpollEvent event(EPOLLOUT, false); + client_->OnEvent(client_->fd(), &event); + } + private: + QuicClient* client_; +}; + class EndToEndTest : public ::testing::TestWithParam<TestParams> { protected: EndToEndTest() @@ -148,14 +167,32 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { client_supported_versions_ = GetParam().client_supported_versions; server_supported_versions_ = GetParam().server_supported_versions; negotiated_version_ = GetParam().negotiated_version; - FLAGS_limit_rto_increase_for_tests = true; FLAGS_enable_quic_pacing = GetParam().use_pacing; - LOG(INFO) << "Using Configuration: " << GetParam(); + + if (negotiated_version_ >= QUIC_VERSION_17) { + FLAGS_enable_quic_stream_flow_control_2 = true; + } + if (negotiated_version_ >= QUIC_VERSION_19) { + FLAGS_enable_quic_connection_flow_control_2 = true; + } + VLOG(1) << "Using Configuration: " << GetParam(); client_config_.SetDefaults(); server_config_.SetDefaults(); - server_config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, - 0); + + // Use different flow control windows for client/server. + client_config_.SetInitialFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + client_config_.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + client_config_.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialFlowControlWindowToSend( + 3 * kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialStreamFlowControlWindowToSend( + 3 * kInitialStreamFlowControlWindowForTest); + server_config_.SetInitialSessionFlowControlWindowToSend( + 3 * kInitialSessionFlowControlWindowForTest); QuicInMemoryCachePeer::ResetForTests(); AddToCache("GET", "https://www.google.com/foo", @@ -170,54 +207,99 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { QuicInMemoryCachePeer::ResetForTests(); } - virtual QuicTestClient* CreateQuicClient(QuicTestWriter* writer) { - QuicTestClient* client = new QuicTestClient(server_address_, - server_hostname_, - false, // not secure - client_config_, - client_supported_versions_); + QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) { + QuicTestClient* client = new QuicTestClient( + server_address_, + server_hostname_, + false, // not secure + client_config_, + client_supported_versions_); client->UseWriter(writer); client->Connect(); return client; } - virtual bool Initialize() { + void set_client_initial_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DVLOG(1) << "Setting client initial flow control window: " << window; + client_config_.SetInitialFlowControlWindowToSend(window); + } + + void set_client_initial_stream_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DLOG(INFO) << "Setting client initial stream flow control window: " + << window; + client_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_client_initial_session_flow_control_receive_window(uint32 window) { + CHECK(client_.get() == NULL); + DLOG(INFO) << "Setting client initial session flow control window: " + << window; + client_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + void set_server_initial_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DVLOG(1) << "Setting server initial flow control window: " << window; + server_config_.SetInitialFlowControlWindowToSend(window); + } + + void set_server_initial_stream_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DLOG(INFO) << "Setting server initial stream flow control window: " + << window; + server_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_server_initial_session_flow_control_receive_window(uint32 window) { + CHECK(server_thread_.get() == NULL); + DLOG(INFO) << "Setting server initial session flow control window: " + << window; + server_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + bool Initialize() { // Start the server first, because CreateQuicClient() attempts // to connect to the server. StartServer(); client_.reset(CreateQuicClient(client_writer_)); - QuicEpollConnectionHelper* helper = + static EpollEvent event(EPOLLOUT, false); + client_writer_->Initialize( reinterpret_cast<QuicEpollConnectionHelper*>( QuicConnectionPeer::GetHelper( - client_->client()->session()->connection())); - client_writer_->SetConnectionHelper(helper); + client_->client()->session()->connection())), + new ClientDelegate(client_->client())); return client_->client()->connected(); } - virtual void SetUp() { - // The ownership of these gets transferred to the QuicTestWriter and - // QuicDispatcher when Initialize() is executed. + virtual void SetUp() OVERRIDE { + // The ownership of these gets transferred to the QuicPacketWriterWrapper + // and QuicDispatcher when Initialize() is executed. client_writer_ = new PacketDroppingTestWriter(); server_writer_ = new PacketDroppingTestWriter(); } - virtual void TearDown() { + virtual void TearDown() OVERRIDE { StopServer(); } void StartServer() { - server_thread_.reset(new ServerThread(server_address_, server_config_, - server_supported_versions_, - strike_register_no_startup_period_)); - server_thread_->Start(); - server_thread_->WaitForServerStartup(); + server_thread_.reset( + new ServerThread( + new QuicServer(server_config_, server_supported_versions_), + server_address_, + strike_register_no_startup_period_)); + server_thread_->Initialize(); server_address_ = IPEndPoint(server_address_.address(), server_thread_->GetPort()); QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server_thread_->server()); - server_writer_->SetConnectionHelper( - QuicDispatcherPeer::GetHelper(dispatcher)); QuicDispatcherPeer::UseWriter(dispatcher, server_writer_); + server_writer_->Initialize( + QuicDispatcherPeer::GetHelper(dispatcher), + new ServerDelegate(dispatcher)); + server_thread_->Start(); server_started_ = true; } @@ -243,8 +325,10 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { void SetPacketLossPercentage(int32 loss) { // TODO(rtenneti): enable when we can do random packet loss tests in // chrome's tree. - // client_writer_->set_fake_packet_loss_percentage(loss); - // server_writer_->set_fake_packet_loss_percentage(loss); + if (loss != 0 && loss != 100) + return; + client_writer_->set_fake_packet_loss_percentage(loss); + server_writer_->set_fake_packet_loss_percentage(loss); } void SetPacketSendDelay(QuicTime::Delta delay) { @@ -261,6 +345,36 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { // server_writer_->set_fake_reorder_percentage(reorder); } + // Verifies that the client and server connections were both free of packets + // being discarded, based on connection stats. + // Calls server_thread_ Pause() and Resume(), which may only be called once + // per test. + void VerifyCleanConnection(bool had_packet_loss) { + QuicConnectionStats client_stats = + client_->client()->session()->connection()->GetStats(); + if (!had_packet_loss) { + EXPECT_EQ(0u, client_stats.packets_lost); + } + EXPECT_EQ(0u, client_stats.packets_discarded); + EXPECT_EQ(0u, client_stats.packets_dropped); + EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed); + + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + QuicConnectionStats server_stats = session->connection()->GetStats(); + if (!had_packet_loss) { + EXPECT_EQ(0u, server_stats.packets_lost); + } + EXPECT_EQ(0u, server_stats.packets_discarded); + // TODO(ianswett): Restore the check for packets_dropped equals 0. + // The expect for packets received is equal to packets processed fails + // due to version negotiation packets. + server_thread_->Resume(); + } + IPEndPoint server_address_; string server_hostname_; scoped_ptr<ServerThread> server_thread_; @@ -398,12 +512,14 @@ TEST_P(EndToEndTest, PostMissingBytes) { EXPECT_EQ(500u, client_->response_headers()->parsed_response_code()); } -TEST_P(EndToEndTest, LargePostNoPacketLoss) { +// TODO(rtenneti): DISABLED_LargePostNoPacketLoss seems to be flaky. +// http://crbug.com/297040. +TEST_P(EndToEndTest, DISABLED_LargePostNoPacketLoss) { ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); - // 1 Mb body. + // 1 MB body. string body; GenerateBody(&body, 1024 * 1024); @@ -412,6 +528,25 @@ TEST_P(EndToEndTest, LargePostNoPacketLoss) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) { + ASSERT_TRUE(Initialize()); + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000)); + + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // 100 KB body. + string body; + GenerateBody(&body, 100 * 1024); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddBody(body, true); + + EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(false); } TEST_P(EndToEndTest, LargePostWithPacketLoss) { @@ -424,7 +559,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { client_->client()->WaitForCryptoHandshakeConfirmed(); SetPacketLossPercentage(30); - // 10 Kb body. + // 10 KB body. string body; GenerateBody(&body, 1024 * 10); @@ -433,19 +568,23 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(true); } -TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { +TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) { + // Connect with lower fake packet loss than we'd like to test. Until + // b/10126687 is fixed, losing handshake packets is pretty brutal. + SetPacketLossPercentage(5); ASSERT_TRUE(Initialize()); + // Wait for the server SHLO before upping the packet loss. client_->client()->WaitForCryptoHandshakeConfirmed(); - // Both of these must be called when the writer is not actively used. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); + SetPacketLossPercentage(10); + client_writer_->set_fake_blocked_socket_percentage(10); - // 1 Mb body. + // 10 KB body. string body; - GenerateBody(&body, 1024 * 1024); + GenerateBody(&body, 1024 * 10); HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); @@ -454,20 +593,17 @@ TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); } -TEST_P(EndToEndTest, LargePostWithPacketLossAndBlocketSocket) { - // Connect with lower fake packet loss than we'd like to test. Until - // b/10126687 is fixed, losing handshake packets is pretty brutal. - SetPacketLossPercentage(5); +TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { ASSERT_TRUE(Initialize()); - // Wait for the server SHLO before upping the packet loss. client_->client()->WaitForCryptoHandshakeConfirmed(); - SetPacketLossPercentage(30); - client_writer_->set_fake_blocked_socket_percentage(10); + // Both of these must be called when the writer is not actively used. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); - // 10 Kb body. + // 1 MB body. string body; - GenerateBody(&body, 1024 * 10); + GenerateBody(&body, 1024 * 1024); HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); @@ -500,6 +636,14 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); + if (client_supported_versions_[0] >= QUIC_VERSION_17 && + negotiated_version_ < QUIC_VERSION_17) { + // If the version negotiation has resulted in a downgrade, then the client + // must wait for the handshake to complete before sending any data. + // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a + // DFATAL when they are serialized after the downgrade. + client_->client()->WaitForCryptoHandshakeConfirmed(); + } client_->WaitForResponseForMs(-1); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); @@ -513,16 +657,35 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { StartServer(); client_->Connect(); + if (client_supported_versions_[0] >= QUIC_VERSION_17 && + negotiated_version_ < QUIC_VERSION_17) { + // If the version negotiation has resulted in a downgrade, then the client + // must wait for the handshake to complete before sending any data. + // Otherwise it may have queued QUIC_VERSION_17 frames which will trigger a + // DFATAL when they are serialized after the downgrade. + client_->client()->WaitForCryptoHandshakeConfirmed(); + } ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); EXPECT_EQ(2, client_->client()->session()->GetNumSentClientHellos()); + VerifyCleanConnection(false); } -// TODO(ianswett): Enable once b/9295090 is fixed. -TEST_P(EndToEndTest, DISABLED_LargePostFEC) { - SetPacketLossPercentage(30); +TEST_P(EndToEndTest, LargePostFEC) { + // Connect without packet loss to avoid issues with losing handshake packets, + // and then up the packet loss rate (b/10126687). ASSERT_TRUE(Initialize()); - client_->options()->max_packets_per_fec_group = 6; + + // Wait for the server SHLO before upping the packet loss. + client_->client()->WaitForCryptoHandshakeConfirmed(); + SetPacketLossPercentage(30); + + // Enable FEC protection. + QuicPacketCreator* creator = QuicConnectionPeer::GetPacketCreator( + client_->client()->session()->connection()); + creator->set_max_packets_per_fec_group(3); + // Set FecPolicy to always protect data on all streams. + client_->SetFecPolicy(FEC_PROTECT_ALWAYS); string body; GenerateBody(&body, 10240); @@ -530,21 +693,23 @@ TEST_P(EndToEndTest, DISABLED_LargePostFEC) { HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/foo"); request.AddBody(body, true); - EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + VerifyCleanConnection(true); } -TEST_P(EndToEndTest, LargePostLargeBuffer) { +// TODO(shess): This is flaky on ChromiumOS bots. +// http://crbug.com/374871 +TEST_P(EndToEndTest, DISABLED_LargePostSmallBandwidthLargeBuffer) { ASSERT_TRUE(Initialize()); SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1)); - // 1Mbit per second with a 128k buffer from server to client. Wireless + // 256KB per second with a 256KB buffer from server to client. Wireless // clients commonly have larger buffers, but our max CWND is 200. server_writer_->set_max_bandwidth_and_buffer_size( - QuicBandwidth::FromBytesPerSecond(256 * 1024), 128 * 1024); + QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024); client_->client()->WaitForCryptoHandshakeConfirmed(); - // 1 Mb body. + // 1 MB body. string body; GenerateBody(&body, 1024 * 1024); @@ -553,6 +718,52 @@ TEST_P(EndToEndTest, LargePostLargeBuffer) { request.AddBody(body, true); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); + // This connection will not drop packets, because the buffer size is larger + // than the default receive window. + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, DoNotSetResumeWriteAlarmIfConnectionFlowControlBlocked) { + // Regression test for b/14677858. + // Test that the resume write alarm is not set in QuicConnection::OnCanWrite + // if currently connection level flow control blocked. If set, this results in + // an infinite loop in the EpollServer, as the alarm fires and is immediately + // rescheduled. + ASSERT_TRUE(Initialize()); + if (negotiated_version_ < QUIC_VERSION_19) { + return; + } + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Ensure both stream and connection level are flow control blocked by setting + // the send window offset to 0. + const uint64 kFlowControlWindow = + server_config_.GetInitialFlowControlWindowToSend(); + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + QuicSession* session = client_->client()->session(); + QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0); + QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0); + EXPECT_TRUE(stream->flow_controller()->IsBlocked()); + EXPECT_TRUE(session->flow_controller()->IsBlocked()); + + // Make sure that the stream has data pending so that it will be marked as + // write blocked when it receives a stream level WINDOW_UPDATE. + stream->SendBody("hello", false); + + // The stream now attempts to write, fails because it is still connection + // level flow control blocked, and is added to the write blocked list. + QuicWindowUpdateFrame window_update(stream->id(), 2 * kFlowControlWindow); + stream->OnWindowUpdateFrame(window_update); + + // Prior to fixing b/14677858 this call would result in an infinite loop in + // Chromium. As a proxy for detecting this, we now check whether the + // resume_writes_alarm is set after OnCanWrite. It should not be, as the + // connection is still flow control blocked. + session->connection()->OnCanWrite(); + + QuicAlarm* resume_writes_alarm = + QuicConnectionPeer::GetResumeWritesAlarm(session->connection()); + EXPECT_FALSE(resume_writes_alarm->IsSet()); } TEST_P(EndToEndTest, InvalidStream) { @@ -570,7 +781,7 @@ TEST_P(EndToEndTest, InvalidStream) { QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2); client_->SendCustomSynchronousRequest(request); -// EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + // EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); EXPECT_EQ(QUIC_PACKET_FOR_NONEXISTENT_STREAM, client_->connection_error()); } @@ -598,23 +809,7 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) { ReliableQuicStreamPeer::SetWriteSideClosed( false, client_->GetOrCreateStream()); -#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) -#if !defined(DCHECK_ALWAYS_ON) - EXPECT_DEBUG_DEATH({ - client_->SendData("eep", true); - client_->WaitForResponse(); - EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error()); - }, - "Check failed: !fin_buffered_"); -#else - EXPECT_DEATH({ - client_->SendData("eep", true); - client_->WaitForResponse(); - EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error()); - }, - "Check failed: !fin_buffered_"); -#endif -#endif + EXPECT_DFATAL(client_->SendData("eep", true), "Fin already buffered"); } TEST_P(EndToEndTest, Timeout) { @@ -629,6 +824,30 @@ TEST_P(EndToEndTest, Timeout) { } } +TEST_P(EndToEndTest, NegotiateMaxOpenStreams) { + // Negotiate 1 max open stream. + client_config_.set_max_streams_per_connection(1, 1); + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Make the client misbehave after negotiation. + QuicSessionPeer::SetMaxOpenStreams(client_->client()->session(), 10); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddHeader("content-length", "3"); + request.set_has_complete_message(false); + + // Open two simultaneous streams. + client_->SendMessage(request); + client_->SendMessage(request); + client_->WaitForResponse(); + + EXPECT_FALSE(client_->connected()); + EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_TOO_MANY_OPEN_STREAMS, client_->connection_error()); +} + TEST_P(EndToEndTest, LimitMaxOpenStreams) { // Server limits the number of max streams to 2. server_config_.set_max_streams_per_connection(2, 2); @@ -644,13 +863,10 @@ TEST_P(EndToEndTest, LimitMaxOpenStreams) { // TODO(rtenneti): DISABLED_LimitCongestionWindowAndRTT seems to be flaky. // http://crbug.com/321870. TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { - server_config_.set_server_initial_congestion_window(kMaxInitialWindow, - kDefaultInitialWindow); - // Client tries to negotiate twice the server's max and negotiation settles - // on the max. - client_config_.set_server_initial_congestion_window(2 * kMaxInitialWindow, - kDefaultInitialWindow); - client_config_.set_initial_round_trip_time_us(1, 1); + // Client tries to request twice the server's max initial window, and the + // server limits it to the max. + client_config_.SetInitialCongestionWindowToSend(2 * kMaxInitialWindow); + client_config_.SetInitialRoundTripTimeUsToSend(1); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -662,17 +878,11 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); QuicSession* session = dispatcher->session_map().begin()->second; - QuicConfig* client_negotiated_config = client_->client()->session()->config(); - QuicConfig* server_negotiated_config = session->config(); const QuicSentPacketManager& client_sent_packet_manager = client_->client()->session()->connection()->sent_packet_manager(); const QuicSentPacketManager& server_sent_packet_manager = session->connection()->sent_packet_manager(); - EXPECT_EQ(kMaxInitialWindow, - client_negotiated_config->server_initial_congestion_window()); - EXPECT_EQ(kMaxInitialWindow, - server_negotiated_config->server_initial_congestion_window()); // The client shouldn't set it's initial window based on the negotiated value. EXPECT_EQ(kDefaultInitialWindow * kDefaultTCPMSS, client_sent_packet_manager.GetCongestionWindow()); @@ -684,13 +894,14 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { EXPECT_EQ(FLAGS_enable_quic_pacing, client_sent_packet_manager.using_pacing()); - EXPECT_EQ(1u, client_negotiated_config->initial_round_trip_time_us()); - EXPECT_EQ(1u, server_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(100000u, + client_sent_packet_manager.GetRttStats()->initial_rtt_us()); + EXPECT_EQ(1u, server_sent_packet_manager.GetRttStats()->initial_rtt_us()); // Now use the negotiated limits with packet loss. SetPacketLossPercentage(30); - // 10 Kb body. + // 10 KB body. string body; GenerateBody(&body, 1024 * 10); @@ -703,11 +914,42 @@ TEST_P(EndToEndTest, DISABLED_LimitCongestionWindowAndRTT) { EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); } -TEST_P(EndToEndTest, InitialRTT) { - // Client tries to negotiate twice the server's max and negotiation settles - // on the max. - client_config_.set_initial_round_trip_time_us(2 * kMaxInitialRoundTripTimeUs, - 0); +TEST_P(EndToEndTest, MaxInitialRTT) { + // Client tries to suggest twice the server's max initial rtt and the server + // uses the max. + client_config_.SetInitialRoundTripTimeUsToSend( + 2 * kMaxInitialRoundTripTimeUs); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->session()->connection()->sent_packet_manager(); + const QuicSentPacketManager& server_sent_packet_manager = + session->connection()->sent_packet_manager(); + + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite()); + EXPECT_EQ(static_cast<int64>(kMaxInitialRoundTripTimeUs), + server_sent_packet_manager.GetRttStats()->initial_rtt_us()); + EXPECT_GE( + static_cast<int64>(kMaxInitialRoundTripTimeUs), + server_sent_packet_manager.GetRttStats()->SmoothedRtt().ToMicroseconds()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, MinInitialRTT) { + // Client tries to suggest 0 and the server uses the default. + client_config_.SetInitialRoundTripTimeUsToSend(0); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -719,22 +961,22 @@ TEST_P(EndToEndTest, InitialRTT) { QuicServerPeer::GetDispatcher(server_thread_->server()); ASSERT_EQ(1u, dispatcher->session_map().size()); QuicSession* session = dispatcher->session_map().begin()->second; - QuicConfig* client_negotiated_config = client_->client()->session()->config(); - QuicConfig* server_negotiated_config = session->config(); const QuicSentPacketManager& client_sent_packet_manager = client_->client()->session()->connection()->sent_packet_manager(); const QuicSentPacketManager& server_sent_packet_manager = session->connection()->sent_packet_manager(); - EXPECT_EQ(kMaxInitialRoundTripTimeUs, - client_negotiated_config->initial_round_trip_time_us()); - EXPECT_EQ(kMaxInitialRoundTripTimeUs, - server_negotiated_config->initial_round_trip_time_us()); // Now that acks have been exchanged, the RTT estimate has decreased on the // server and is not infinite on the client. - EXPECT_FALSE(client_sent_packet_manager.SmoothedRtt().IsInfinite()); - EXPECT_GE(static_cast<int64>(kMaxInitialRoundTripTimeUs), - server_sent_packet_manager.SmoothedRtt().ToMicroseconds()); + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->SmoothedRtt().IsInfinite()); + // Expect the default rtt of 100ms. + EXPECT_EQ(static_cast<int64>(100 * base::Time::kMicrosecondsPerMillisecond), + server_sent_packet_manager.GetRttStats()->initial_rtt_us()); + // Ensure the bandwidth is valid. + client_sent_packet_manager.BandwidthEstimate(); + server_sent_packet_manager.BandwidthEstimate(); + server_thread_->Resume(); } TEST_P(EndToEndTest, ResetConnection) { @@ -770,7 +1012,35 @@ TEST_P(EndToEndTest, MaxStreamsUberTest) { } } -class WrongAddressWriter : public QuicTestWriter { +TEST_P(EndToEndTest, StreamCancelErrorTest) { + ASSERT_TRUE(Initialize()); + string small_body; + GenerateBody(&small_body, 256); + + AddToCache("GET", "/small_response", "HTTP/1.1", "200", "OK", small_body); + + client_->client()->WaitForCryptoHandshakeConfirmed(); + + QuicSession* session = client_->client()->session(); + // Lose the request. + SetPacketLossPercentage(100); + EXPECT_LT(0, client_->SendRequest("/small_response")); + client_->client()->WaitForEvents(); + // Transmit the cancel, and ensure the connection is torn down properly. + SetPacketLossPercentage(0); + QuicStreamId stream_id = kClientDataStreamId1; + session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0); + + // WaitForEvents waits 50ms and returns true if there are outstanding + // requests. + while (client_->client()->WaitForEvents() == true) { + } + // It should be completely fine to RST a stream before any data has been + // received for that stream. + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +class WrongAddressWriter : public QuicPacketWriterWrapper { public: WrongAddressWriter() { IPAddressNumber ip; @@ -779,12 +1049,13 @@ class WrongAddressWriter : public QuicTestWriter { } virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, + const char* buffer, + size_t buf_len, const IPAddressNumber& real_self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE { - return writer()->WritePacket(buffer, buf_len, self_address_.address(), - peer_address, blocked_writer); + const IPEndPoint& peer_address) OVERRIDE { + // Use wrong address! + return QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_address_.address(), peer_address); } virtual bool IsWriteBlockedDataBuffered() const OVERRIDE { @@ -794,7 +1065,10 @@ class WrongAddressWriter : public QuicTestWriter { IPEndPoint self_address_; }; -TEST_P(EndToEndTest, ConnectionMigration) { +TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) { + // Tests that the client's IP can not change during an established QUIC + // connection. If it changes, the connection is closed by the server as we do + // not yet support IP migration. ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -802,8 +1076,7 @@ TEST_P(EndToEndTest, ConnectionMigration) { scoped_ptr<WrongAddressWriter> writer(new WrongAddressWriter()); - writer->set_writer(new QuicDefaultPacketWriter( - QuicClientPeer::GetFd(client_->client()))); + writer->set_writer(new QuicDefaultPacketWriter(client_->client()->fd())); QuicConnectionPeer::SetWriter(client_->client()->session()->connection(), writer.get()); @@ -813,6 +1086,157 @@ TEST_P(EndToEndTest, ConnectionMigration) { EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error()); } +TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { + // Tests that the client's port can change during an established QUIC + // connection, and that doing so does not result in the connection being + // closed by the server. + FLAGS_quic_allow_port_migration = true; + + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Store the client address which was used to send the first request. + IPEndPoint old_address = client_->client()->client_address(); + + // Stop listening on the old FD. + EpollServer* eps = client_->epoll_server(); + int old_fd = client_->client()->fd(); + eps->UnregisterFD(old_fd); + // Create a new socket before closing the old one, which will result in a new + // ephemeral port. + QuicClientPeer::CreateUDPSocket(client_->client()); + close(old_fd); + + // The packet writer needs to be updated to use the new FD. + client_->client()->CreateQuicPacketWriter(); + + // Change the internal state of the client and connection to use the new port, + // this is done because in a real NAT rebinding the client wouldn't see any + // port change, and so expects no change to incoming port. + // This is kind of ugly, but needed as we are simply swapping out the client + // FD rather than any more complex NAT rebinding simulation. + int new_port = client_->client()->client_address().port(); + QuicClientPeer::SetClientPort(client_->client(), new_port); + QuicConnectionPeer::SetSelfAddress( + client_->client()->session()->connection(), + IPEndPoint( + client_->client()->session()->connection()->self_address().address(), + new_port)); + + // Register the new FD for epoll events. + int new_fd = client_->client()->fd(); + eps->RegisterFD(new_fd, client_->client(), EPOLLIN | EPOLLOUT | EPOLLET); + + // Send a second request, using the new FD. + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Verify that the client's ephemeral port is different. + IPEndPoint new_address = client_->client()->client_address(); + EXPECT_EQ(old_address.address(), new_address.address()); + EXPECT_NE(old_address.port(), new_address.port()); +} + + +TEST_P(EndToEndTest, DifferentFlowControlWindowsQ019) { + // TODO(rjshade): Remove this test when removing QUIC_VERSION_19. + // Client and server can set different initial flow control receive windows. + // These are sent in CHLO/SHLO. Tests that these values are exchanged properly + // in the crypto handshake. + + const uint32 kClientIFCW = 123456; + set_client_initial_flow_control_receive_window(kClientIFCW); + + const uint32 kServerIFCW = 654321; + set_server_initial_flow_control_receive_window(kServerIFCW); + + ASSERT_TRUE(Initialize()); + if (negotiated_version_ > QUIC_VERSION_19) { + return; + } + + // Values are exchanged during crypto handshake, so wait for that to finish. + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Client should have the right value for server's receive window. + EXPECT_EQ(kServerIFCW, client_->client() + ->session() + ->config() + ->ReceivedInitialFlowControlWindowBytes()); + + // Server should have the right value for client's receive window. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(kClientIFCW, + session->config()->ReceivedInitialFlowControlWindowBytes()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, DifferentFlowControlWindowsQ020) { + // TODO(rjshade): Rename to DifferentFlowControlWindows when removing + // QUIC_VERSION_19. + // Client and server can set different initial flow control receive windows. + // These are sent in CHLO/SHLO. Tests that these values are exchanged properly + // in the crypto handshake. + const uint32 kClientStreamIFCW = 123456; + const uint32 kClientSessionIFCW = 234567; + set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); + set_client_initial_session_flow_control_receive_window(kClientSessionIFCW); + + const uint32 kServerStreamIFCW = 654321; + const uint32 kServerSessionIFCW = 765432; + set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW); + set_server_initial_session_flow_control_receive_window(kServerSessionIFCW); + + ASSERT_TRUE(Initialize()); + if (negotiated_version_ <= QUIC_VERSION_19) { + return; + } + + // Values are exchanged during crypto handshake, so wait for that to finish. + client_->client()->WaitForCryptoHandshakeConfirmed(); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Open a data stream to make sure the stream level flow control is updated. + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + stream->SendBody("hello", false); + + // Client should have the right values for server's receive window. + EXPECT_EQ(kServerStreamIFCW, + client_->client() + ->session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kServerSessionIFCW, + client_->client() + ->session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( + stream->flow_controller())); + EXPECT_EQ(kServerSessionIFCW, + QuicFlowControllerPeer::SendWindowOffset( + client_->client()->session()->flow_controller())); + + // Server should have the right values for client's receive window. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(kClientStreamIFCW, + session->config()->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, + session->config()->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( + session->flow_controller())); + server_thread_->Resume(); +} + } // namespace } // namespace test } // namespace tools diff --git a/chromium/net/tools/quic/quic_client.cc b/chromium/net/tools/quic/quic_client.cc index dcf9612a38f..19e98013af2 100644 --- a/chromium/net/tools/quic/quic_client.cc +++ b/chromium/net/tools/quic/quic_client.cc @@ -12,11 +12,14 @@ #include <unistd.h> #include "base/logging.h" +#include "net/quic/congestion_control/tcp_receiver.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_id.h" #include "net/tools/balsa/balsa_headers.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_socket_utils.h" #include "net/tools/quic/quic_spdy_client_stream.h" @@ -31,12 +34,14 @@ namespace tools { const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET; QuicClient::QuicClient(IPEndPoint server_address, - const string& server_hostname, + const QuicServerId& server_id, const QuicVersionVector& supported_versions, - bool print_response) + bool print_response, + EpollServer* epoll_server) : server_address_(server_address), - server_hostname_(server_hostname), + server_id_(server_id), local_port_(0), + epoll_server_(epoll_server), fd_(-1), helper_(CreateQuicConnectionHelper()), initialized_(false), @@ -48,20 +53,23 @@ QuicClient::QuicClient(IPEndPoint server_address, } QuicClient::QuicClient(IPEndPoint server_address, - const string& server_hostname, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + bool print_response, const QuicConfig& config, - const QuicVersionVector& supported_versions) + EpollServer* epoll_server) : server_address_(server_address), - server_hostname_(server_hostname), + server_id_(server_id), config_(config), local_port_(0), + epoll_server_(epoll_server), fd_(-1), helper_(CreateQuicConnectionHelper()), initialized_(false), packets_dropped_(0), overflow_supported_(false), supported_versions_(supported_versions), - print_response_(false) { + print_response_(print_response) { } QuicClient::~QuicClient() { @@ -69,14 +77,27 @@ QuicClient::~QuicClient() { session()->connection()->SendConnectionClosePacket( QUIC_PEER_GOING_AWAY, ""); } + if (fd_ > 0) { + epoll_server_->UnregisterFD(fd_); + } } bool QuicClient::Initialize() { DCHECK(!initialized_); - epoll_server_.set_timeout_in_us(50 * 1000); + epoll_server_->set_timeout_in_us(50 * 1000); crypto_config_.SetDefaults(); + if (!CreateUDPSocket()) { + return false; + } + + epoll_server_->RegisterFD(fd_, this, kEpollFlags); + initialized_ = true; + return true; +} + +bool QuicClient::CreateUDPSocket() { int address_family = server_address_.GetSockAddrFamily(); fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); if (fd_ < 0) { @@ -93,6 +114,16 @@ bool QuicClient::Initialize() { overflow_supported_ = true; } + if (!QuicSocketUtils::SetReceiveBufferSize(fd_, + TcpReceiver::kReceiveWindowTCP)) { + return false; + } + + if (!QuicSocketUtils::SetSendBufferSize(fd_, + TcpReceiver::kReceiveWindowTCP)) { + return false; + } + int get_local_ip = 1; if (address_family == AF_INET) { rc = setsockopt(fd_, IPPROTO_IP, IP_PKTINFO, @@ -137,8 +168,6 @@ bool QuicClient::Initialize() { LOG(ERROR) << "Unable to get self address. Error: " << strerror(errno); } - epoll_server_.RegisterFD(fd_, this, kEpollFlags); - initialized_ = true; return true; } @@ -153,7 +182,8 @@ bool QuicClient::Connect() { } bool QuicClient::StartConnect() { - DCHECK(!connected() && initialized_); + DCHECK(initialized_); + DCHECK(!connected()); QuicPacketWriter* writer = CreateQuicPacketWriter(); if (writer_.get() != writer) { @@ -161,9 +191,9 @@ bool QuicClient::StartConnect() { } session_.reset(new QuicClientSession( - server_hostname_, + server_id_, config_, - new QuicConnection(GenerateGuid(), server_address_, helper_.get(), + new QuicConnection(GenerateConnectionId(), server_address_, helper_.get(), writer_.get(), false, supported_versions_), &crypto_config_)); return session_->CryptoConnect(); @@ -180,23 +210,24 @@ void QuicClient::Disconnect() { if (connected()) { session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY); } - epoll_server_.UnregisterFD(fd_); + epoll_server_->UnregisterFD(fd_); close(fd_); fd_ = -1; initialized_ = false; } void QuicClient::SendRequestsAndWaitForResponse( - const CommandLine::StringVector& args) { + const base::CommandLine::StringVector& args) { for (size_t i = 0; i < args.size(); ++i) { BalsaHeaders headers; headers.SetRequestFirstlineFromStringPieces("GET", args[i], "HTTP/1.1"); QuicSpdyClientStream* stream = CreateReliableClientStream(); + DCHECK(stream != NULL); stream->SendRequest(headers, "", true); stream->set_visitor(this); } - while (WaitForEvents()) { } + while (WaitForEvents()) {} } QuicSpdyClientStream* QuicClient::CreateReliableClientStream() { @@ -210,23 +241,23 @@ QuicSpdyClientStream* QuicClient::CreateReliableClientStream() { void QuicClient::WaitForStreamToClose(QuicStreamId id) { DCHECK(connected()); - while (!session_->IsClosedStream(id)) { - epoll_server_.WaitForEventsAndExecuteCallbacks(); + while (connected() && !session_->IsClosedStream(id)) { + epoll_server_->WaitForEventsAndExecuteCallbacks(); } } void QuicClient::WaitForCryptoHandshakeConfirmed() { DCHECK(connected()); - while (!session_->IsCryptoHandshakeConfirmed()) { - epoll_server_.WaitForEventsAndExecuteCallbacks(); + while (connected() && !session_->IsCryptoHandshakeConfirmed()) { + epoll_server_->WaitForEventsAndExecuteCallbacks(); } } bool QuicClient::WaitForEvents() { DCHECK(connected()); - epoll_server_.WaitForEventsAndExecuteCallbacks(); + epoll_server_->WaitForEventsAndExecuteCallbacks(); return session_->num_active_requests() != 0; } @@ -238,20 +269,26 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) { } } if (connected() && (event->in_events & EPOLLOUT)) { + writer_->SetWritable(); session_->connection()->OnCanWrite(); } if (event->in_events & EPOLLERR) { - DLOG(INFO) << "Epollerr"; + DVLOG(1) << "Epollerr"; } } void QuicClient::OnClose(QuicDataStream* stream) { + QuicSpdyClientStream* client_stream = + static_cast<QuicSpdyClientStream*>(stream); + if (response_listener_.get() != NULL) { + response_listener_->OnCompleteResponse( + stream->id(), client_stream->headers(), client_stream->data()); + } + if (!print_response_) { return; } - QuicSpdyClientStream* client_stream = - static_cast<QuicSpdyClientStream*>(stream); const BalsaHeaders& headers = client_stream->headers(); printf("%s\n", headers.first_line().as_string().c_str()); for (BalsaHeaders::const_header_lines_iterator i = @@ -263,30 +300,32 @@ void QuicClient::OnClose(QuicDataStream* stream) { printf("%s\n", client_stream->data().c_str()); } -QuicPacketCreator::Options* QuicClient::options() { - if (session() == NULL) { - return NULL; - } - return session_->options(); -} - bool QuicClient::connected() const { return session_.get() && session_->connection() && session_->connection()->connected(); } -QuicGuid QuicClient::GenerateGuid() { +QuicConnectionId QuicClient::GenerateConnectionId() { return QuicRandom::GetInstance()->RandUint64(); } QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() { - return new QuicEpollConnectionHelper(&epoll_server_); + return new QuicEpollConnectionHelper(epoll_server_); } QuicPacketWriter* QuicClient::CreateQuicPacketWriter() { return new QuicDefaultPacketWriter(fd_); } +int QuicClient::ReadPacket(char* buffer, + int buffer_len, + IPEndPoint* server_address, + IPAddressNumber* client_ip) { + return QuicSocketUtils::ReadPacket( + fd_, buffer, buffer_len, overflow_supported_ ? &packets_dropped_ : NULL, + client_ip, server_address); +} + bool QuicClient::ReadAndProcessPacket() { // Allocate some extra space so we can send an error if the server goes over // the limit. @@ -295,27 +334,13 @@ bool QuicClient::ReadAndProcessPacket() { IPEndPoint server_address; IPAddressNumber client_ip; - int bytes_read = QuicSocketUtils::ReadPacket( - fd_, buf, arraysize(buf), overflow_supported_ ? &packets_dropped_ : NULL, - &client_ip, &server_address); + int bytes_read = ReadPacket(buf, arraysize(buf), &server_address, &client_ip); if (bytes_read < 0) { return false; } QuicEncryptedPacket packet(buf, bytes_read, false); - QuicGuid our_guid = session_->connection()->guid(); - QuicGuid packet_guid; - - if (!QuicFramer::ReadGuidFromPacket(packet, &packet_guid)) { - DLOG(INFO) << "Could not read GUID from packet"; - return true; - } - if (packet_guid != our_guid) { - DLOG(INFO) << "Ignoring packet from unexpected GUID: " - << packet_guid << " instead of " << our_guid; - return true; - } IPEndPoint client_address(client_ip, client_address_.port()); session_->connection()->ProcessUdpPacket( diff --git a/chromium/net/tools/quic/quic_client.h b/chromium/net/tools/quic/quic_client.h index 02f45e6c56a..13aff636ada 100644 --- a/chromium/net/tools/quic/quic_client.h +++ b/chromium/net/tools/quic/quic_client.h @@ -10,6 +10,7 @@ #include <string> +#include "base/basictypes.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" @@ -24,6 +25,7 @@ namespace net { class ProofVerifier; +class QuicServerId; namespace tools { @@ -36,14 +38,28 @@ class QuicClientPeer; class QuicClient : public EpollCallbackInterface, public QuicDataStream::Visitor { public: + class ResponseListener { + public: + ResponseListener() {} + virtual ~ResponseListener() {} + virtual void OnCompleteResponse(QuicStreamId id, + const BalsaHeaders& response_headers, + const string& response_body) = 0; + }; + + // Create a quic client, which will have events managed by an externally owned + // EpollServer. QuicClient(IPEndPoint server_address, - const string& server_hostname, + const QuicServerId& server_id, const QuicVersionVector& supported_versions, - bool print_response); + bool print_response, + EpollServer* epoll_server); QuicClient(IPEndPoint server_address, - const std::string& server_hostname, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + bool print_response, const QuicConfig& config, - const QuicVersionVector& supported_versions); + EpollServer* epoll_server); virtual ~QuicClient(); @@ -69,11 +85,12 @@ class QuicClient : public EpollCallbackInterface, // Disconnects from the QUIC server. void Disconnect(); - // Sends a request simple GET for each URL in arg, and then waits for + // Sends a request simple GET for each URL in |args|, and then waits for // each to complete. - void SendRequestsAndWaitForResponse(const CommandLine::StringVector& args); + void SendRequestsAndWaitForResponse(const + base::CommandLine::StringVector& args); - // Returns a newly created CreateReliableClientStream, owned by the + // Returns a newly created QuicSpdyClientStream, owned by the // QuicClient. QuicSpdyClientStream* CreateReliableClientStream(); @@ -88,8 +105,9 @@ class QuicClient : public EpollCallbackInterface, bool WaitForEvents(); // From EpollCallbackInterface - virtual void OnRegistration( - EpollServer* eps, int fd, int event_mask) OVERRIDE {} + virtual void OnRegistration(EpollServer* eps, + int fd, + int event_mask) OVERRIDE {} virtual void OnModification(int fd, int event_mask) OVERRIDE {} virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE; // |fd_| can be unregistered without the client being disconnected. This @@ -101,8 +119,6 @@ class QuicClient : public EpollCallbackInterface, // QuicDataStream::Visitor virtual void OnClose(QuicDataStream* stream) OVERRIDE; - QuicPacketCreator::Options* options(); - QuicClientSession* session() { return session_.get(); } bool connected() const; @@ -119,13 +135,19 @@ class QuicClient : public EpollCallbackInterface, const IPEndPoint& client_address() const { return client_address_; } - EpollServer* epoll_server() { return &epoll_server_; } + EpollServer* epoll_server() { return epoll_server_; } int fd() { return fd_; } + const QuicServerId& server_id() const { return server_id_; } + // This should only be set before the initial Connect() - void set_server_hostname(const string& hostname) { - server_hostname_ = hostname; + void set_server_id(const QuicServerId& server_id) { + server_id_ = server_id; + } + + void SetUserAgentID(const string& user_agent_id) { + crypto_config_.set_user_agent_id(user_agent_id); } // SetProofVerifier sets the ProofVerifier that will be used to verify the @@ -135,29 +157,48 @@ class QuicClient : public EpollCallbackInterface, crypto_config_.SetProofVerifier(verifier); } - // SetChannelIDSigner sets a ChannelIDSigner that will be called when the - // server supports channel IDs to sign a message proving possession of the - // given ChannelID. This object takes ownership of |signer|. - void SetChannelIDSigner(ChannelIDSigner* signer) { - crypto_config_.SetChannelIDSigner(signer); + // SetChannelIDSource sets a ChannelIDSource that will be called, when the + // server supports channel IDs, to obtain a channel ID for signing a message + // proving possession of the channel ID. This object takes ownership of + // |source|. + void SetChannelIDSource(ChannelIDSource* source) { + crypto_config_.SetChannelIDSource(source); + } + + void SetSupportedVersions(const QuicVersionVector& versions) { + supported_versions_ = versions; + } + + // Takes ownership of the listener. + void set_response_listener(ResponseListener* listener) { + response_listener_.reset(listener); } protected: - virtual QuicGuid GenerateGuid(); + virtual QuicConnectionId GenerateConnectionId(); virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper(); virtual QuicPacketWriter* CreateQuicPacketWriter(); + virtual int ReadPacket(char* buffer, + int buffer_len, + IPEndPoint* server_address, + IPAddressNumber* client_ip); + private: friend class net::tools::test::QuicClientPeer; + // Used during initialization: creates the UDP socket FD, sets socket options, + // and binds the socket to our address. + bool CreateUDPSocket(); + // Read a UDP packet and hand it to the framer. bool ReadAndProcessPacket(); // Address of the server. const IPEndPoint server_address_; - // Hostname of the server. This may be a DNS name or an IP address literal. - std::string server_hostname_; + // |server_id_| is a tuple (hostname, port, is_https) of the server. + QuicServerId server_id_; // config_ and crypto_config_ contain configuration and cached state about // servers. @@ -175,13 +216,16 @@ class QuicClient : public EpollCallbackInterface, // Session which manages streams. scoped_ptr<QuicClientSession> session_; // Listens for events on the client socket. - EpollServer epoll_server_; + EpollServer* epoll_server_; // UDP socket. int fd_; // Helper to be used by created connections. scoped_ptr<QuicEpollConnectionHelper> helper_; + // Listens for full responses. + scoped_ptr<ResponseListener> response_listener_; + // Writer used to actually send packets to the wire. scoped_ptr<QuicPacketWriter> writer_; @@ -191,7 +235,7 @@ class QuicClient : public EpollCallbackInterface, // If overflow_supported_ is true, this will be the number of packets dropped // during the lifetime of the server. This may overflow if enough packets // are dropped. - int packets_dropped_; + uint32 packets_dropped_; // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped // because the socket would otherwise overflow. diff --git a/chromium/net/tools/quic/quic_client_bin.cc b/chromium/net/tools/quic/quic_client_bin.cc index 2f8cfac1736..f743a7dc127 100644 --- a/chromium/net/tools/quic/quic_client_bin.cc +++ b/chromium/net/tools/quic/quic_client_bin.cc @@ -4,6 +4,11 @@ // A binary wrapper for QuicClient. Connects to --hostname via --address // on --port and requests URLs specified on the command line. +// Pass --secure to check the certificates using proof verifier. +// Pass --initial_stream_flow_control_window to specify the size of the initial +// stream flow control receive window to advertise to server. +// Pass --initial_session_flow_control_window to specify the size of the initial +// session flow control receive window to advertise to server. // // For example: // quic_client --address=127.0.0.1 --port=6122 --hostname=www.google.com @@ -16,16 +21,34 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "net/base/ip_endpoint.h" +#include "net/base/privacy_mode.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_id.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client.h" +// The port the quic client will connect to. int32 FLAGS_port = 6121; std::string FLAGS_address = "127.0.0.1"; +// The hostname the quic client will connect to. std::string FLAGS_hostname = "localhost"; +// Size of the initial stream flow control receive window to advertise to +// server. +int32 FLAGS_initial_stream_flow_control_window = 100 * net::kMaxPacketSize; +// Size of the initial session flow control receive window to advertise to +// server. +int32 FLAGS_initial_session_flow_control_window = 200 * net::kMaxPacketSize; +// Check the certificates using proof verifier. +bool FLAGS_secure = false; int main(int argc, char *argv[]) { - CommandLine::Init(argc, argv); - CommandLine* line = CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + base::CommandLine* line = base::CommandLine::ForCurrentProcess(); + + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + CHECK(logging::InitLogging(settings)); + if (line->HasSwitch("h") || line->HasSwitch("help")) { const char* help_str = "Usage: quic_client [options]\n" @@ -34,7 +57,8 @@ int main(int argc, char *argv[]) { "-h, --help show this help message and exit\n" "--port=<port> specify the port to connect to\n" "--address=<address> specify the IP address to connect to\n" - "--host=<host> specify the SNI hostname to use\n"; + "--host=<host> specify the SNI hostname to use\n" + "--secure check certificates\n"; std::cout << help_str; exit(0); } @@ -50,18 +74,35 @@ int main(int argc, char *argv[]) { if (line->HasSwitch("hostname")) { FLAGS_hostname = line->GetSwitchValueASCII("hostname"); } - LOG(INFO) << "server port: " << FLAGS_port - << " address: " << FLAGS_address - << " hostname: " << FLAGS_hostname; + if (line->HasSwitch("secure")) { + FLAGS_secure = true; + } + VLOG(1) << "server port: " << FLAGS_port + << " address: " << FLAGS_address + << " hostname: " << FLAGS_hostname + << " secure: " << FLAGS_secure; base::AtExitManager exit_manager; net::IPAddressNumber addr; CHECK(net::ParseIPLiteralToNumber(FLAGS_address, &addr)); + + net::QuicConfig config; + config.SetDefaults(); + config.SetInitialFlowControlWindowToSend( + FLAGS_initial_session_flow_control_window); + config.SetInitialStreamFlowControlWindowToSend( + FLAGS_initial_stream_flow_control_window); + config.SetInitialSessionFlowControlWindowToSend( + FLAGS_initial_session_flow_control_window); + // TODO(rjshade): Set version on command line. + net::EpollServer epoll_server; net::tools::QuicClient client( - net::IPEndPoint(addr, FLAGS_port), FLAGS_hostname, - net::QuicSupportedVersions(), true); + net::IPEndPoint(addr, FLAGS_port), + net::QuicServerId(FLAGS_hostname, FLAGS_port, FLAGS_secure, + net::PRIVACY_MODE_DISABLED), + net::QuicSupportedVersions(), true, config, &epoll_server); client.Initialize(); diff --git a/chromium/net/tools/quic/quic_client_session.cc b/chromium/net/tools/quic/quic_client_session.cc index 5bcf6f201f4..aca5418ac37 100644 --- a/chromium/net/tools/quic/quic_client_session.cc +++ b/chromium/net/tools/quic/quic_client_session.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_server_id.h" #include "net/tools/quic/quic_spdy_client_stream.h" using std::string; @@ -14,30 +15,38 @@ namespace net { namespace tools { QuicClientSession::QuicClientSession( - const string& server_hostname, + const QuicServerId& server_id, const QuicConfig& config, QuicConnection* connection, QuicCryptoClientConfig* crypto_config) - : QuicSession(connection, config), - crypto_stream_(server_hostname, this, crypto_config) { + : QuicClientSessionBase(connection, config), + crypto_stream_(server_id, this, NULL, crypto_config) { } QuicClientSession::~QuicClientSession() { } +void QuicClientSession::OnProofValid( + const QuicCryptoClientConfig::CachedState& /*cached*/) { +} + +void QuicClientSession::OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& /*verify_details*/) { +} + QuicSpdyClientStream* QuicClientSession::CreateOutgoingDataStream() { if (!crypto_stream_.encryption_established()) { - DLOG(INFO) << "Encryption not active so no outgoing stream created."; + DVLOG(1) << "Encryption not active so no outgoing stream created."; return NULL; } if (GetNumOpenStreams() >= get_max_open_streams()) { - DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already " << GetNumOpenStreams() << " open."; + DVLOG(1) << "Failed to create a new outgoing stream. " + << "Already " << GetNumOpenStreams() << " open."; return NULL; } if (goaway_received()) { - DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already received goaway."; + DVLOG(1) << "Failed to create a new outgoing stream. " + << "Already received goaway."; return NULL; } QuicSpdyClientStream* stream diff --git a/chromium/net/tools/quic/quic_client_session.h b/chromium/net/tools/quic/quic_client_session.h index dcee15e4275..3aad445fcd2 100644 --- a/chromium/net/tools/quic/quic_client_session.h +++ b/chromium/net/tools/quic/quic_client_session.h @@ -9,26 +9,34 @@ #include <string> +#include "base/basictypes.h" +#include "net/quic/quic_client_session_base.h" #include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_session.h" #include "net/tools/quic/quic_spdy_client_stream.h" namespace net { class QuicConnection; +class QuicServerId; class ReliableQuicStream; namespace tools { -class QuicClientSession : public QuicSession { +class QuicClientSession : public QuicClientSessionBase { public: - QuicClientSession(const std::string& server_hostname, + QuicClientSession(const QuicServerId& server_id, const QuicConfig& config, QuicConnection* connection, QuicCryptoClientConfig* crypto_config); virtual ~QuicClientSession(); + // QuicClientSessionBase methods: + virtual void OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) OVERRIDE; + virtual void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) OVERRIDE; + // QuicSession methods: virtual QuicSpdyClientStream* CreateOutgoingDataStream() OVERRIDE; virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE; diff --git a/chromium/net/tools/quic/quic_client_session_test.cc b/chromium/net/tools/quic/quic_client_session_test.cc index c893c20b5e3..7b50f42ab39 100644 --- a/chromium/net/tools/quic/quic_client_session_test.cc +++ b/chromium/net/tools/quic/quic_client_session_test.cc @@ -16,6 +16,7 @@ using net::test::CryptoTestUtils; using net::test::DefaultQuicConfig; using net::test::PacketSavingConnection; +using net::test::SupportedVersions; using testing::_; namespace net { @@ -24,14 +25,20 @@ namespace test { namespace { const char kServerHostname[] = "www.example.com"; +const uint16 kPort = 80; -class ToolsQuicClientSessionTest : public ::testing::Test { +class ToolsQuicClientSessionTest + : public ::testing::TestWithParam<QuicVersion> { protected: ToolsQuicClientSessionTest() - : connection_(new PacketSavingConnection(false)) { + : connection_(new PacketSavingConnection(false, + SupportedVersions(GetParam()))) { crypto_config_.SetDefaults(); - session_.reset(new QuicClientSession(kServerHostname, DefaultQuicConfig(), - connection_, &crypto_config_)); + session_.reset(new QuicClientSession( + QuicServerId(kServerHostname, kPort, false, PRIVACY_MODE_DISABLED), + DefaultQuicConfig(), + connection_, + &crypto_config_)); session_->config()->SetDefaults(); } @@ -46,11 +53,14 @@ class ToolsQuicClientSessionTest : public ::testing::Test { QuicCryptoClientConfig crypto_config_; }; -TEST_F(ToolsQuicClientSessionTest, CryptoConnect) { +INSTANTIATE_TEST_CASE_P(Tests, ToolsQuicClientSessionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(ToolsQuicClientSessionTest, CryptoConnect) { CompleteCryptoHandshake(); } -TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) { +TEST_P(ToolsQuicClientSessionTest, MaxNumStreams) { session_->config()->set_max_streams_per_connection(1, 1); // FLAGS_max_streams_per_connection = 1; // Initialize crypto before the client session will create a stream. @@ -67,7 +77,7 @@ TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) { EXPECT_TRUE(stream); } -TEST_F(ToolsQuicClientSessionTest, GoAwayReceived) { +TEST_P(ToolsQuicClientSessionTest, GoAwayReceived) { CompleteCryptoHandshake(); // After receiving a GoAway, I should no longer be able to create outgoing diff --git a/chromium/net/tools/quic/quic_default_packet_writer.cc b/chromium/net/tools/quic/quic_default_packet_writer.cc index 9d3e30831c1..84f118aee00 100644 --- a/chromium/net/tools/quic/quic_default_packet_writer.cc +++ b/chromium/net/tools/quic/quic_default_packet_writer.cc @@ -9,22 +9,37 @@ namespace net { namespace tools { -QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd) : fd_(fd) {} +QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd) + : fd_(fd), + write_blocked_(false) {} QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {} WriteResult QuicDefaultPacketWriter::WritePacket( - const char* buffer, size_t buf_len, - const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) { - return QuicSocketUtils::WritePacket(fd_, buffer, buf_len, - self_address, peer_address); + const char* buffer, + size_t buf_len, + const IPAddressNumber& self_address, + const IPEndPoint& peer_address) { + DCHECK(!IsWriteBlocked()); + WriteResult result = QuicSocketUtils::WritePacket( + fd_, buffer, buf_len, self_address, peer_address); + if (result.status == WRITE_STATUS_BLOCKED) { + write_blocked_ = true; + } + return result; } bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const { return false; } +bool QuicDefaultPacketWriter::IsWriteBlocked() const { + return write_blocked_; +} + +void QuicDefaultPacketWriter::SetWritable() { + write_blocked_ = false; +} + } // namespace tools } // namespace net diff --git a/chromium/net/tools/quic/quic_default_packet_writer.h b/chromium/net/tools/quic/quic_default_packet_writer.h index 20f5fb0db61..7b5a36bdce1 100644 --- a/chromium/net/tools/quic/quic_default_packet_writer.h +++ b/chromium/net/tools/quic/quic_default_packet_writer.h @@ -11,7 +11,6 @@ namespace net { -class QuicBlockedWriterInterface; struct WriteResult; namespace tools { @@ -23,15 +22,27 @@ class QuicDefaultPacketWriter : public QuicPacketWriter { virtual ~QuicDefaultPacketWriter(); // QuicPacketWriter - virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, - const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE; + virtual WriteResult WritePacket(const char* buffer, + size_t buf_len, + const IPAddressNumber& self_address, + const IPEndPoint& peer_address) OVERRIDE; virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual bool IsWriteBlocked() const OVERRIDE; + virtual void SetWritable() OVERRIDE; + + void set_fd(int fd) { fd_ = fd; } + + protected: + void set_write_blocked(bool is_blocked) { + write_blocked_ = is_blocked; + } + int fd() { return fd_; } private: int fd_; + bool write_blocked_; + + DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter); }; } // namespace tools diff --git a/chromium/net/tools/quic/quic_dispatcher.cc b/chromium/net/tools/quic/quic_dispatcher.cc index 297234d9755..b873f98618a 100644 --- a/chromium/net/tools/quic/quic_dispatcher.cc +++ b/chromium/net/tools/quic/quic_dispatcher.cc @@ -6,17 +6,23 @@ #include <errno.h> +#include "base/debug/stack_trace.h" #include "base/logging.h" #include "base/stl_util.h" #include "net/quic/quic_blocked_writer_interface.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_default_packet_writer.h" #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_socket_utils.h" +#include "net/tools/quic/quic_time_wait_list_manager.h" namespace net { + namespace tools { +using base::StringPiece; using std::make_pair; class DeleteSessionsAlarm : public EpollAlarm { @@ -35,22 +41,140 @@ class DeleteSessionsAlarm : public EpollAlarm { QuicDispatcher* dispatcher_; }; +class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface { + public: + explicit QuicFramerVisitor(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher), + connection_id_(0) {} + + // QuicFramerVisitorInterface implementation + virtual void OnPacket() OVERRIDE {} + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE { + connection_id_ = header.connection_id; + return dispatcher_->OnUnauthenticatedPublicHeader(header); + } + virtual bool OnUnauthenticatedHeader( + const QuicPacketHeader& header) OVERRIDE { + dispatcher_->OnUnauthenticatedHeader(header); + return false; + } + virtual void OnError(QuicFramer* framer) OVERRIDE { + DVLOG(1) << QuicUtils::ErrorToString(framer->error()); + } + + virtual bool OnProtocolVersionMismatch( + QuicVersion /*received_version*/) OVERRIDE { + if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait( + connection_id_)) { + // Keep processing after protocol mismatch - this will be dealt with by + // the TimeWaitListManager. + return true; + } else { + DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_ + << ") not in time wait list."; + return false; + } + } + + // The following methods should never get called because we always return + // false from OnUnauthenticatedHeader(). As a result, we never process the + // payload of the packet. + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& /*packet*/) OVERRIDE { + DCHECK(false); + } + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE { + DCHECK(false); + } + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE { + DCHECK(false); + } + virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual void OnRevivedPacket() OVERRIDE { + DCHECK(false); + } + virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE { + DCHECK(false); + } + virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnStopWaitingFrame( + const QuicStopWaitingFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnConnectionCloseFrame( + const QuicConnectionCloseFrame & /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/) + OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE { + DCHECK(false); + return false; + } + virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE { + DCHECK(false); + } + virtual void OnPacketComplete() OVERRIDE { + DCHECK(false); + } + + private: + QuicDispatcher* dispatcher_; + + // Latched in OnUnauthenticatedPublicHeader for use later. + QuicConnectionId connection_id_; +}; + QuicDispatcher::QuicDispatcher(const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, const QuicVersionVector& supported_versions, - int fd, EpollServer* epoll_server) : config_(config), crypto_config_(crypto_config), - time_wait_list_manager_( - new QuicTimeWaitListManager(this, epoll_server, supported_versions)), delete_sessions_alarm_(new DeleteSessionsAlarm(this)), epoll_server_(epoll_server), - fd_(fd), - write_blocked_(false), helper_(new QuicEpollConnectionHelper(epoll_server_)), - writer_(new QuicDefaultPacketWriter(fd)), - supported_versions_(supported_versions) { + supported_versions_(supported_versions), + supported_versions_no_flow_control_(supported_versions), + supported_versions_no_connection_flow_control_(supported_versions), + current_packet_(NULL), + framer_(supported_versions, /*unused*/ QuicTime::Zero(), true), + framer_visitor_(new QuicFramerVisitor(this)) { + framer_.set_visitor(framer_visitor_.get()); } QuicDispatcher::~QuicDispatcher() { @@ -58,89 +182,121 @@ QuicDispatcher::~QuicDispatcher() { STLDeleteElements(&closed_session_list_); } -void QuicDispatcher::set_fd(int fd) { - fd_ = fd; - writer_.reset(new QuicDefaultPacketWriter(fd)); -} +void QuicDispatcher::Initialize(int fd) { + DCHECK(writer_ == NULL); + writer_.reset(CreateWriter(fd)); + time_wait_list_manager_.reset(CreateQuicTimeWaitListManager()); -WriteResult QuicDispatcher::WritePacket(const char* buffer, size_t buf_len, - const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* writer) { - if (write_blocked_) { - write_blocked_list_.insert(make_pair(writer, true)); - return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN); + // Remove all versions > QUIC_VERSION_16 from the + // supported_versions_no_flow_control_ vector. + QuicVersionVector::iterator it = + find(supported_versions_no_flow_control_.begin(), + supported_versions_no_flow_control_.end(), QUIC_VERSION_17); + if (it != supported_versions_no_flow_control_.end()) { + supported_versions_no_flow_control_.erase( + supported_versions_no_flow_control_.begin(), it + 1); } + CHECK(!supported_versions_no_flow_control_.empty()); - WriteResult result = - writer_->WritePacket(buffer, buf_len, self_address, peer_address, writer); - if (result.status == WRITE_STATUS_BLOCKED) { - write_blocked_list_.insert(make_pair(writer, true)); - write_blocked_ = true; + // Remove all versions > QUIC_VERSION_18 from the + // supported_versions_no_connection_flow_control_ vector. + QuicVersionVector::iterator connection_it = find( + supported_versions_no_connection_flow_control_.begin(), + supported_versions_no_connection_flow_control_.end(), QUIC_VERSION_19); + if (connection_it != supported_versions_no_connection_flow_control_.end()) { + supported_versions_no_connection_flow_control_.erase( + supported_versions_no_connection_flow_control_.begin(), + connection_it + 1); } - return result; -} - -bool QuicDispatcher::IsWriteBlockedDataBuffered() const { - return writer_->IsWriteBlockedDataBuffered(); + CHECK(!supported_versions_no_connection_flow_control_.empty()); } void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, - bool has_version_flag, const QuicEncryptedPacket& packet) { + current_server_address_ = server_address; + current_client_address_ = client_address; + current_packet_ = &packet; + // ProcessPacket will cause the packet to be dispatched in + // OnUnauthenticatedPublicHeader, or sent to the time wait list manager + // in OnAuthenticatedHeader. + framer_.ProcessPacket(packet); + // TODO(rjshade): Return a status describing if/why a packet was dropped, + // and log somehow. Maybe expose as a varz. +} + +bool QuicDispatcher::OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) { QuicSession* session = NULL; - SessionMap::iterator it = session_map_.find(guid); + QuicConnectionId connection_id = header.connection_id; + SessionMap::iterator it = session_map_.find(connection_id); if (it == session_map_.end()) { - if (time_wait_list_manager_->IsGuidInTimeWait(guid)) { - time_wait_list_manager_->ProcessPacket(server_address, - client_address, - guid, - packet); - return; + if (header.reset_flag) { + return false; + } + if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) { + return HandlePacketForTimeWait(header); } // Ensure the packet has a version negotiation bit set before creating a new // session for it. All initial packets for a new connection are required to // have the flag set. Otherwise it may be a stray packet. - if (has_version_flag) { - session = CreateQuicSession(guid, server_address, client_address); + if (header.version_flag) { + session = CreateQuicSession(connection_id, current_server_address_, + current_client_address_); } if (session == NULL) { - DLOG(INFO) << "Failed to create session for " << guid; - // Add this guid fo the time-wait state, to safely reject future packets. - // We don't know the version here, so assume latest. - // TODO(ianswett): Produce a no-version version negotiation packet. - time_wait_list_manager_->AddGuidToTimeWait(guid, - supported_versions_.front(), - NULL); - time_wait_list_manager_->ProcessPacket(server_address, - client_address, - guid, - packet); - return; + DVLOG(1) << "Failed to create session for " << connection_id; + // Add this connection_id fo the time-wait state, to safely reject future + // packets. + + if (header.version_flag && + !framer_.IsSupportedVersion(header.versions.front())) { + // TODO(ianswett): Produce a no-version version negotiation packet. + return false; + } + + // Use the version in the packet if possible, otherwise assume the latest. + QuicVersion version = header.version_flag ? header.versions.front() : + supported_versions_.front(); + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id, version, NULL); + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + return HandlePacketForTimeWait(header); } - DLOG(INFO) << "Created new session for " << guid; - session_map_.insert(make_pair(guid, session)); + DVLOG(1) << "Created new session for " << connection_id; + session_map_.insert(make_pair(connection_id, session)); } else { session = it->second; } session->connection()->ProcessUdpPacket( - server_address, client_address, packet); + current_server_address_, current_client_address_, *current_packet_); + + // Do not parse the packet further. The session will process it completely. + return false; +} + +void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) { + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( + header.public_header.connection_id)); + time_wait_list_manager_->ProcessPacket(current_server_address_, + current_client_address_, + header.public_header.connection_id, + header.packet_sequence_number, + *current_packet_); } void QuicDispatcher::CleanUpSession(SessionMap::iterator it) { QuicConnection* connection = it->second->connection(); QuicEncryptedPacket* connection_close_packet = - connection->ReleaseConnectionClosePacket(); + connection->ReleaseConnectionClosePacket(); write_blocked_list_.erase(connection); - time_wait_list_manager_->AddGuidToTimeWait(it->first, - connection->version(), - connection_close_packet); + time_wait_list_manager_->AddConnectionIdToTimeWait(it->first, + connection->version(), + connection_close_packet); session_map_.erase(it); } @@ -148,37 +304,29 @@ void QuicDispatcher::DeleteSessions() { STLDeleteElements(&closed_session_list_); } -void QuicDispatcher::UseWriter(QuicPacketWriter* writer) { - writer_.reset(writer); -} - -bool QuicDispatcher::OnCanWrite() { +void QuicDispatcher::OnCanWrite() { // We got an EPOLLOUT: the socket should not be blocked. - write_blocked_ = false; + writer_->SetWritable(); // Give each writer one attempt to write. int num_writers = write_blocked_list_.size(); for (int i = 0; i < num_writers; ++i) { if (write_blocked_list_.empty()) { - break; + return; } - QuicBlockedWriterInterface* writer = write_blocked_list_.begin()->first; + QuicBlockedWriterInterface* blocked_writer = + write_blocked_list_.begin()->first; write_blocked_list_.erase(write_blocked_list_.begin()); - bool can_write_more = writer->OnCanWrite(); - if (write_blocked_) { - // We were unable to write. Wait for the next EPOLLOUT. - // In this case, the session would have been added to the blocked list - // up in WritePacket. - return false; - } - // The socket is not blocked but the writer has ceded work. Add it to the - // end of the list. - if (can_write_more) { - write_blocked_list_.insert(make_pair(writer, true)); + blocked_writer->OnCanWrite(); + if (writer_->IsWriteBlocked()) { + // We were unable to write. Wait for the next EPOLLOUT. The writer is + // responsible for adding itself to the blocked list via OnWriteBlocked(). + return; } } +} - // We're not write blocked. Return true if there's more work to do. +bool QuicDispatcher::HasPendingWrites() const { return !write_blocked_list_.empty(); } @@ -192,15 +340,19 @@ void QuicDispatcher::Shutdown() { DeleteSessions(); } -void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) { - SessionMap::iterator it = session_map_.find(guid); +void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) { + SessionMap::iterator it = session_map_.find(connection_id); if (it == session_map_.end()) { - LOG(DFATAL) << "GUID " << guid << " does not exist in the session map. " + LOG(DFATAL) << "ConnectionId " << connection_id + << " does not exist in the session map. " << "Error: " << QuicUtils::ErrorToString(error); + LOG(DFATAL) << base::debug::StackTrace().ToString(); return; } - DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection (" << guid + DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection (" + << connection_id << ") due to error: " << QuicUtils::ErrorToString(error); @@ -212,16 +364,75 @@ void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) { CleanUpSession(it); } +void QuicDispatcher::OnWriteBlocked(QuicBlockedWriterInterface* writer) { + DCHECK(writer_->IsWriteBlocked()); + write_blocked_list_.insert(make_pair(writer, true)); +} + +QuicPacketWriter* QuicDispatcher::CreateWriter(int fd) { + return new QuicDefaultPacketWriter(fd); +} + QuicSession* QuicDispatcher::CreateQuicSession( - QuicGuid guid, + QuicConnectionId connection_id, const IPEndPoint& server_address, const IPEndPoint& client_address) { QuicServerSession* session = new QuicServerSession( - config_, new QuicConnection(guid, client_address, helper_.get(), this, - true, supported_versions_), this); + config_, + CreateQuicConnection(connection_id, server_address, client_address), + this); session->InitializeSession(crypto_config_); return session; } +QuicConnection* QuicDispatcher::CreateQuicConnection( + QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address) { + if (FLAGS_enable_quic_stream_flow_control_2 && + FLAGS_enable_quic_connection_flow_control_2) { + DLOG(INFO) << "Creating QuicDispatcher with all versions."; + return new QuicConnection(connection_id, client_address, helper_.get(), + writer_.get(), true, supported_versions_); + } + + if (FLAGS_enable_quic_stream_flow_control_2 && + !FLAGS_enable_quic_connection_flow_control_2) { + DLOG(INFO) << "Connection flow control disabled, creating QuicDispatcher " + << "WITHOUT version 19 or higher."; + return new QuicConnection(connection_id, client_address, helper_.get(), + writer_.get(), true, + supported_versions_no_connection_flow_control_); + } + + DLOG(INFO) << "Flow control disabled, creating QuicDispatcher WITHOUT " + << "version 17 or higher."; + return new QuicConnection(connection_id, client_address, helper_.get(), + writer_.get(), true, + supported_versions_no_flow_control_); +} + +QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { + return new QuicTimeWaitListManager( + writer_.get(), this, epoll_server(), supported_versions()); +} + +bool QuicDispatcher::HandlePacketForTimeWait( + const QuicPacketPublicHeader& header) { + if (header.reset_flag) { + // Public reset packets do not have sequence numbers, so ignore the packet. + return false; + } + + // Switch the framer to the correct version, so that the sequence number can + // be parsed correctly. + framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId( + header.connection_id)); + + // Continue parsing the packet to extract the sequence number. Then + // send it to the time wait manager in OnUnathenticatedHeader. + return true; +} + } // namespace tools } // namespace net diff --git a/chromium/net/tools/quic/quic_dispatcher.h b/chromium/net/tools/quic/quic_dispatcher.h index 58a4c0c1423..d60ba69883d 100644 --- a/chromium/net/tools/quic/quic_dispatcher.h +++ b/chromium/net/tools/quic/quic_dispatcher.h @@ -10,14 +10,13 @@ #include <list> +#include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" #include "net/base/linked_hash_map.h" #include "net/quic/quic_blocked_writer_interface.h" -#include "net/quic/quic_packet_writer.h" #include "net/quic/quic_protocol.h" -#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_server_session.h" #include "net/tools/quic/quic_time_wait_list_manager.h" @@ -42,6 +41,8 @@ class QuicSession; namespace tools { +class QuicPacketWriterWrapper; + namespace test { class QuicDispatcherPeer; } // namespace test @@ -49,7 +50,7 @@ class QuicDispatcherPeer; class DeleteSessionsAlarm; class QuicEpollConnectionHelper; -class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner { +class QuicDispatcher : public QuicServerSessionVisitor { public: // Ideally we'd have a linked_hash_set: the boolean is unused. typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList; @@ -60,87 +61,134 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner { QuicDispatcher(const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, const QuicVersionVector& supported_versions, - int fd, EpollServer* epoll_server); + virtual ~QuicDispatcher(); - // QuicPacketWriter - virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, - const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* writer) OVERRIDE; - virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual void Initialize(int fd); // Process the incoming packet by creating a new session, passing it to // an existing session, or passing it to the TimeWaitListManager. virtual void ProcessPacket(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, - bool has_version_flag, const QuicEncryptedPacket& packet); - // Called when the underyling connection becomes writable to allow - // queued writes to happen. - // - // Returns true if more writes are possible, false otherwise. - virtual bool OnCanWrite(); + // Called when the socket becomes writable to allow queued writes to happen. + virtual void OnCanWrite(); + + // Returns true if there's anything in the blocked writer list. + virtual bool HasPendingWrites() const; // Sends ConnectionClose frames to all connected clients. void Shutdown(); + // QuicServerSessionVisitor interface implementation: // Ensure that the closed connection is cleaned up asynchronously. - virtual void OnConnectionClosed(QuicGuid guid, QuicErrorCode error) OVERRIDE; + virtual void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) OVERRIDE; - // Sets the fd and creates a default packet writer with that fd. - void set_fd(int fd); + // Queues the blocked writer for later resumption. + virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) OVERRIDE; - typedef base::hash_map<QuicGuid, QuicSession*> SessionMap; - - virtual QuicSession* CreateQuicSession( - QuicGuid guid, - const IPEndPoint& server_address, - const IPEndPoint& client_address); + typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap; // Deletes all sessions on the closed session list and clears the list. void DeleteSessions(); const SessionMap& session_map() const { return session_map_; } - // Uses the specified |writer| instead of QuicSocketUtils and takes ownership - // of writer. - void UseWriter(QuicPacketWriter* writer); - WriteBlockedList* write_blocked_list() { return &write_blocked_list_; } protected: - const QuicConfig& config_; - const QuicCryptoServerConfig& crypto_config_; + // Instantiates a new low-level packet writer. Caller takes ownership of the + // returned object. + virtual QuicPacketWriter* CreateWriter(int fd); + + virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address); + + virtual QuicConnection* CreateQuicConnection( + QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address); + + // Called by |framer_visitor_| when the public header has been parsed. + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header); + + // Create and return the time wait list manager for this dispatcher, which + // will be owned by the dispatcher as time_wait_list_manager_ + virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager(); + + // Replaces the packet writer with |writer|. Takes ownership of |writer|. + void set_writer(QuicPacketWriter* writer) { + writer_.reset(writer); + } QuicTimeWaitListManager* time_wait_list_manager() { return time_wait_list_manager_.get(); } - QuicEpollConnectionHelper* helper() { return helper_.get(); } EpollServer* epoll_server() { return epoll_server_; } const QuicVersionVector& supported_versions() const { return supported_versions_; } + const QuicVersionVector& supported_versions_no_flow_control() const { + return supported_versions_no_flow_control_; + } + + const QuicVersionVector& supported_versions_no_connection_flow_control() + const { + return supported_versions_no_connection_flow_control_; + } + + const IPEndPoint& current_server_address() { + return current_server_address_; + } + const IPEndPoint& current_client_address() { + return current_client_address_; + } + const QuicEncryptedPacket& current_packet() { + return *current_packet_; + } + + const QuicConfig& config() const { return config_; } + + const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; } + + QuicFramer* framer() { return &framer_; } + + QuicEpollConnectionHelper* helper() { return helper_.get(); } + + QuicPacketWriter* writer() { return writer_.get(); } + private: + class QuicFramerVisitor; friend class net::tools::test::QuicDispatcherPeer; + // Called by |framer_visitor_| when the private header has been parsed + // of a data packet that is destined for the time wait manager. + void OnUnauthenticatedHeader(const QuicPacketHeader& header); + // Removes the session from the session map and write blocked list, and - // adds the GUID to the time-wait list. + // adds the ConnectionId to the time-wait list. void CleanUpSession(SessionMap::iterator it); + bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header); + + const QuicConfig& config_; + + const QuicCryptoServerConfig& crypto_config_; + // The list of connections waiting to write. WriteBlockedList write_blocked_list_; SessionMap session_map_; - // Entity that manages guids in time wait state. + // Entity that manages connection_ids in time wait state. scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_; // An alarm which deletes closed sessions. @@ -151,13 +199,6 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner { EpollServer* epoll_server_; // Owned by the server. - // The connection for client-server communication - int fd_; - - // True if the session is write blocked due to the socket returning EAGAIN. - // False if we have gotten a call to OnCanWrite after the last failed write. - bool write_blocked_; - // The helper used for all connections. scoped_ptr<QuicEpollConnectionHelper> helper_; @@ -170,6 +211,28 @@ class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner { // skipped as necessary). const QuicVersionVector supported_versions_; + // Versions which do not support flow control (introduced in QUIC_VERSION_17). + // This is used to construct new QuicConnections when flow control is disabled + // via flag. + // TODO(rjshade): Remove this when + // FLAGS_enable_quic_stream_flow_control_2 is removed. + QuicVersionVector supported_versions_no_flow_control_; + // Versions which do not support *connection* flow control (introduced in + // QUIC_VERSION_19). + // This is used to construct new QuicConnections when connection flow control + // is disabled via flag. + // TODO(rjshade): Remove this when + // FLAGS_enable_quic_connection_flow_control_2 is removed. + QuicVersionVector supported_versions_no_connection_flow_control_; + + // Information about the packet currently being handled. + IPEndPoint current_client_address_; + IPEndPoint current_server_address_; + const QuicEncryptedPacket* current_packet_; + + QuicFramer framer_; + scoped_ptr<QuicFramerVisitor> framer_visitor_; + DISALLOW_COPY_AND_ASSIGN(QuicDispatcher); }; diff --git a/chromium/net/tools/quic/quic_dispatcher_test.cc b/chromium/net/tools/quic/quic_dispatcher_test.cc index 74cab69e6a7..41c3c27946a 100644 --- a/chromium/net/tools/quic/quic_dispatcher_test.cc +++ b/chromium/net/tools/quic/quic_dispatcher_test.cc @@ -11,8 +11,11 @@ #include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_crypto_stream.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/epoll_server/epoll_server.h" +#include "net/tools/quic/quic_packet_writer_wrapper.h" #include "net/tools/quic/quic_time_wait_list_manager.h" #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" #include "net/tools/quic/test_tools/quic_test_utils.h" @@ -21,15 +24,16 @@ using base::StringPiece; using net::EpollServer; +using net::test::ConstructEncryptedPacket; using net::test::MockSession; +using net::test::ValueRestore; using net::tools::test::MockConnection; using std::make_pair; -using testing::_; using testing::DoAll; -using testing::Invoke; using testing::InSequence; -using testing::Return; +using testing::Invoke; using testing::WithoutArgs; +using testing::_; namespace net { namespace tools { @@ -41,14 +45,20 @@ class TestDispatcher : public QuicDispatcher { explicit TestDispatcher(const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, EpollServer* eps) - : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), 1, eps) { + : QuicDispatcher(config, + crypto_config, + QuicSupportedVersions(), + eps) { } MOCK_METHOD3(CreateQuicSession, QuicSession*( - QuicGuid guid, + QuicConnectionId connection_id, const IPEndPoint& server_address, const IPEndPoint& client_address)); using QuicDispatcher::write_blocked_list; + + using QuicDispatcher::current_server_address; + using QuicDispatcher::current_client_address; }; // A Connection class which unregisters the session from the dispatcher @@ -57,30 +67,31 @@ class TestDispatcher : public QuicDispatcher { // involve a lot more mocking. class MockServerConnection : public MockConnection { public: - MockServerConnection(QuicGuid guid, + MockServerConnection(QuicConnectionId connection_id, QuicDispatcher* dispatcher) - : MockConnection(guid, true), - dispatcher_(dispatcher) { - } + : MockConnection(connection_id, true), + dispatcher_(dispatcher) {} + void UnregisterOnConnectionClosed() { - LOG(ERROR) << "Unregistering " << guid(); - dispatcher_->OnConnectionClosed(guid(), QUIC_NO_ERROR); + LOG(ERROR) << "Unregistering " << connection_id(); + dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR); } private: QuicDispatcher* dispatcher_; }; QuicSession* CreateSession(QuicDispatcher* dispatcher, - QuicGuid guid, - const IPEndPoint& addr, + QuicConnectionId connection_id, + const IPEndPoint& client_address, MockSession** session) { - MockServerConnection* connection = new MockServerConnection(guid, dispatcher); + MockServerConnection* connection = + new MockServerConnection(connection_id, dispatcher); *session = new MockSession(connection); ON_CALL(*connection, SendConnectionClose(_)).WillByDefault( WithoutArgs(Invoke( connection, &MockServerConnection::UnregisterOnConnectionClosed))); EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()), - ProcessUdpPacket(_, addr, _)); + ProcessUdpPacket(_, client_address, _)); return *session; } @@ -93,6 +104,7 @@ class QuicDispatcherTest : public ::testing::Test { dispatcher_(config_, crypto_config_, &eps_), session1_(NULL), session2_(NULL) { + dispatcher_.Initialize(1); } virtual ~QuicDispatcherTest() {} @@ -105,22 +117,25 @@ class QuicDispatcherTest : public ::testing::Test { return reinterpret_cast<MockConnection*>(session2_->connection()); } - void ProcessPacket(IPEndPoint addr, - QuicGuid guid, + void ProcessPacket(IPEndPoint client_address, + QuicConnectionId connection_id, bool has_version_flag, const string& data) { - dispatcher_.ProcessPacket( - IPEndPoint(), addr, guid, has_version_flag, - QuicEncryptedPacket(data.data(), data.length())); + scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + connection_id, has_version_flag, false, 1, data)); + data_ = string(packet->data(), packet->length()); + dispatcher_.ProcessPacket(server_address_, client_address, *packet); } void ValidatePacket(const QuicEncryptedPacket& packet) { - EXPECT_TRUE(packet.AsStringPiece().find(data_) != StringPiece::npos); + EXPECT_EQ(data_.length(), packet.AsStringPiece().length()); + EXPECT_EQ(data_, packet.AsStringPiece()); } EpollServer eps_; QuicConfig config_; QuicCryptoServerConfig crypto_config_; + IPEndPoint server_address_; TestDispatcher dispatcher_; MockSession* session1_; MockSession* session2_; @@ -128,34 +143,39 @@ class QuicDispatcherTest : public ::testing::Test { }; TEST_F(QuicDispatcherTest, ProcessPackets) { - IPEndPoint addr(net::test::Loopback4(), 1); + IPEndPoint client_address(net::test::Loopback4(), 1); + IPAddressNumber any4; + CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4)); + server_address_ = IPEndPoint(any4, 5); - EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, addr)) + EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, 1, addr, &session1_))); - ProcessPacket(addr, 1, true, "foo"); + &dispatcher_, 1, client_address, &session1_))); + ProcessPacket(client_address, 1, true, "foo"); + EXPECT_EQ(client_address, dispatcher_.current_client_address()); + EXPECT_EQ(server_address_, dispatcher_.current_server_address()); - EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, addr)) + + EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, 2, addr, &session2_))); - ProcessPacket(addr, 2, true, "bar"); + &dispatcher_, 2, client_address, &session2_))); + ProcessPacket(client_address, 2, true, "bar"); - data_ = "eep"; EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), ProcessUdpPacket(_, _, _)).Times(1). WillOnce(testing::WithArgs<2>(Invoke( this, &QuicDispatcherTest::ValidatePacket))); - ProcessPacket(addr, 1, false, "eep"); + ProcessPacket(client_address, 1, false, "eep"); } TEST_F(QuicDispatcherTest, Shutdown) { - IPEndPoint addr(net::test::Loopback4(), 1); + IPEndPoint client_address(net::test::Loopback4(), 1); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr)) + EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, 1, addr, &session1_))); + &dispatcher_, 1, client_address, &session1_))); - ProcessPacket(addr, 1, true, "foo"); + ProcessPacket(client_address, 1, true, "foo"); EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), SendConnectionClose(QUIC_PEER_GOING_AWAY)); @@ -166,34 +186,36 @@ TEST_F(QuicDispatcherTest, Shutdown) { class MockTimeWaitListManager : public QuicTimeWaitListManager { public: MockTimeWaitListManager(QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, EpollServer* eps) - : QuicTimeWaitListManager(writer, eps, QuicSupportedVersions()) { + : QuicTimeWaitListManager(writer, visitor, eps, QuicSupportedVersions()) { } - MOCK_METHOD4(ProcessPacket, void(const IPEndPoint& server_address, + MOCK_METHOD5(ProcessPacket, void(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, const QuicEncryptedPacket& packet)); }; TEST_F(QuicDispatcherTest, TimeWaitListManager) { MockTimeWaitListManager* time_wait_list_manager = new MockTimeWaitListManager( - QuicDispatcherPeer::GetWriter(&dispatcher_), &eps_); + QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &eps_); // dispatcher takes the ownership of time_wait_list_manager. QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_, time_wait_list_manager); // Create a new session. - IPEndPoint addr(net::test::Loopback4(), 1); - QuicGuid guid = 1; - EXPECT_CALL(dispatcher_, CreateQuicSession(guid, _, addr)) + IPEndPoint client_address(net::test::Loopback4(), 1); + QuicConnectionId connection_id = 1; + EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, guid, addr, &session1_))); - ProcessPacket(addr, guid, true, "foo"); + &dispatcher_, connection_id, client_address, &session1_))); + ProcessPacket(client_address, connection_id, true, "foo"); // Close the connection by sending public reset packet. QuicPublicResetPacket packet; - packet.public_header.guid = guid; + packet.public_header.connection_id = connection_id; packet.public_header.reset_flag = true; packet.public_header.version_flag = false; packet.rejected_sequence_number = 19191; @@ -209,47 +231,117 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) { .WillOnce(Invoke( reinterpret_cast<MockConnection*>(session1_->connection()), &MockConnection::ReallyProcessUdpPacket)); - dispatcher_.ProcessPacket(IPEndPoint(), addr, guid, true, *encrypted); - EXPECT_TRUE(time_wait_list_manager->IsGuidInTimeWait(guid)); - - // Dispatcher forwards subsequent packets for this guid to the time wait list - // manager. - EXPECT_CALL(*time_wait_list_manager, ProcessPacket(_, _, guid, _)).Times(1); - ProcessPacket(addr, guid, true, "foo"); + dispatcher_.ProcessPacket(IPEndPoint(), client_address, *encrypted); + EXPECT_TRUE(time_wait_list_manager->IsConnectionIdInTimeWait(connection_id)); + + // Dispatcher forwards subsequent packets for this connection_id to the time + // wait list manager. + EXPECT_CALL(*time_wait_list_manager, + ProcessPacket(_, _, connection_id, _, _)).Times(1); + ProcessPacket(client_address, connection_id, true, "foo"); } TEST_F(QuicDispatcherTest, StrayPacketToTimeWaitListManager) { MockTimeWaitListManager* time_wait_list_manager = new MockTimeWaitListManager( - QuicDispatcherPeer::GetWriter(&dispatcher_), &eps_); + QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &eps_); // dispatcher takes the ownership of time_wait_list_manager. QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_, time_wait_list_manager); - IPEndPoint addr(net::test::Loopback4(), 1); - QuicGuid guid = 1; - // Dispatcher forwards all packets for this guid to the time wait list - // manager. + IPEndPoint client_address(net::test::Loopback4(), 1); + QuicConnectionId connection_id = 1; + // Dispatcher forwards all packets for this connection_id to the time wait + // list manager. EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0); - EXPECT_CALL(*time_wait_list_manager, ProcessPacket(_, _, guid, _)).Times(1); + EXPECT_CALL(*time_wait_list_manager, + ProcessPacket(_, _, connection_id, _, _)).Times(1); string data = "foo"; - ProcessPacket(addr, guid, false, "foo"); + ProcessPacket(client_address, connection_id, false, "foo"); +} + +TEST(QuicDispatcherFlowControlTest, NoNewVersion17ConnectionsIfFlagDisabled) { + // If FLAGS_enable_quic_stream_flow_control_2 is disabled + // then the dispatcher should stop creating connections that support + // QUIC_VERSION_17 (existing connections will stay alive). + // TODO(rjshade): Remove once + // FLAGS_enable_quic_stream_flow_control_2 is removed. + + EpollServer eps; + QuicConfig config; + QuicCryptoServerConfig server_config(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance()); + IPEndPoint client(net::test::Loopback4(), 1); + IPEndPoint server(net::test::Loopback4(), 1); + QuicConnectionId kCID = 1234; + + QuicVersion kTestQuicVersions[] = {QUIC_VERSION_17, + QUIC_VERSION_16, + QUIC_VERSION_15}; + QuicVersionVector kTestVersions; + for (size_t i = 0; i < arraysize(kTestQuicVersions); ++i) { + kTestVersions.push_back(kTestQuicVersions[i]); + } + + QuicDispatcher dispatcher(config, server_config, kTestVersions, &eps); + dispatcher.Initialize(0); + + // When flag is enabled, new connections should support QUIC_VERSION_17. + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + scoped_ptr<QuicConnection> connection_1( + QuicDispatcherPeer::CreateQuicConnection(&dispatcher, kCID, client, + server)); + EXPECT_EQ(QUIC_VERSION_17, connection_1->version()); + + + // When flag is disabled, new connections should not support QUIC_VERSION_17. + FLAGS_enable_quic_stream_flow_control_2 = false; + scoped_ptr<QuicConnection> connection_2( + QuicDispatcherPeer::CreateQuicConnection(&dispatcher, kCID, client, + server)); + EXPECT_EQ(QUIC_VERSION_16, connection_2->version()); } -class QuicWriteBlockedListTest : public QuicDispatcherTest { +class BlockingWriter : public QuicPacketWriterWrapper { + public: + BlockingWriter() : write_blocked_(false) {} + + virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; } + virtual void SetWritable() OVERRIDE { write_blocked_ = false; } + + virtual WriteResult WritePacket( + const char* buffer, + size_t buf_len, + const IPAddressNumber& self_client_address, + const IPEndPoint& peer_client_address) OVERRIDE { + if (write_blocked_) { + return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN); + } else { + return QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_client_address, peer_client_address); + } + } + + bool write_blocked_; +}; + +class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest { public: virtual void SetUp() { - IPEndPoint addr(net::test::Loopback4(), 1); + writer_ = new BlockingWriter; + QuicDispatcherPeer::UseWriter(&dispatcher_, writer_); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr)) + IPEndPoint client_address(net::test::Loopback4(), 1); + + EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, 1, addr, &session1_))); - ProcessPacket(addr, 1, true, "foo"); + &dispatcher_, 1, client_address, &session1_))); + ProcessPacket(client_address, 1, true, "foo"); - EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, addr)) + EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) .WillOnce(testing::Return(CreateSession( - &dispatcher_, 2, addr, &session2_))); - ProcessPacket(addr, 2, true, "bar"); + &dispatcher_, 2, client_address, &session2_))); + ProcessPacket(client_address, 2, true, "bar"); blocked_list_ = dispatcher_.write_blocked_list(); } @@ -260,90 +352,105 @@ class QuicWriteBlockedListTest : public QuicDispatcherTest { dispatcher_.Shutdown(); } - bool SetBlocked() { - QuicDispatcherPeer::SetWriteBlocked(&dispatcher_); - return true; + void SetBlocked() { + writer_->write_blocked_ = true; + } + + void BlockConnection2() { + writer_->write_blocked_ = true; + dispatcher_.OnWriteBlocked(connection2()); } protected: + BlockingWriter* writer_; QuicDispatcher::WriteBlockedList* blocked_list_; }; -TEST_F(QuicWriteBlockedListTest, BasicOnCanWrite) { +TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) { // No OnCanWrite calls because no connections are blocked. dispatcher_.OnCanWrite(); - // Register connection 1 for events, and make sure it's nofitied. - blocked_list_->insert(make_pair(connection1(), true)); + // Register connection 1 for events, and make sure it's notified. + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()); dispatcher_.OnCanWrite(); // It should get only one notification. EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); - EXPECT_FALSE(dispatcher_.OnCanWrite()); + dispatcher_.OnCanWrite(); + EXPECT_FALSE(dispatcher_.HasPendingWrites()); } -TEST_F(QuicWriteBlockedListTest, OnCanWriteOrder) { +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) { // Make sure we handle events in order. InSequence s; - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection2(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()); EXPECT_CALL(*connection2(), OnCanWrite()); dispatcher_.OnCanWrite(); // Check the other ordering. - blocked_list_->insert(make_pair(connection2(), true)); - blocked_list_->insert(make_pair(connection1(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection2()); + dispatcher_.OnWriteBlocked(connection1()); EXPECT_CALL(*connection2(), OnCanWrite()); EXPECT_CALL(*connection1(), OnCanWrite()); dispatcher_.OnCanWrite(); } -TEST_F(QuicWriteBlockedListTest, OnCanWriteRemove) { +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) { // Add and remove one connction. - blocked_list_->insert(make_pair(connection1(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); dispatcher_.OnCanWrite(); // Add and remove one connction and make sure it doesn't affect others. - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection2(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection2()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection2(), OnCanWrite()); dispatcher_.OnCanWrite(); // Add it, remove it, and add it back and make sure things are OK. - blocked_list_->insert(make_pair(connection1(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); - blocked_list_->insert(make_pair(connection1(), true)); + dispatcher_.OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); dispatcher_.OnCanWrite(); } -TEST_F(QuicWriteBlockedListTest, DoubleAdd) { +TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) { // Make sure a double add does not necessitate a double remove. - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection1(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection1()); blocked_list_->erase(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); dispatcher_.OnCanWrite(); // Make sure a double add does not result in two OnCanWrite calls. - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection1(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection1()); EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); dispatcher_.OnCanWrite(); } -TEST_F(QuicWriteBlockedListTest, OnCanWriteHandleBlock) { +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlock) { // Finally make sure if we write block on a write call, we stop calling. InSequence s; - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection2(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce( - Invoke(this, &QuicWriteBlockedListTest::SetBlocked)); + Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked)); EXPECT_CALL(*connection2(), OnCanWrite()).Times(0); dispatcher_.OnCanWrite(); @@ -352,34 +459,41 @@ TEST_F(QuicWriteBlockedListTest, OnCanWriteHandleBlock) { dispatcher_.OnCanWrite(); } -TEST_F(QuicWriteBlockedListTest, LimitedWrites) { +TEST_F(QuicDispatcherWriteBlockedListTest, LimitedWrites) { // Make sure we call both writers. The first will register for more writing // but should not be immediately called due to limits. InSequence s; - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection2(), true)); - EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(Return(true)); - EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce(Return(false)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection2()); + EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce( + Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2)); dispatcher_.OnCanWrite(); + EXPECT_TRUE(dispatcher_.HasPendingWrites()); // Now call OnCanWrite again, and connection1 should get its second chance - EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()); dispatcher_.OnCanWrite(); + EXPECT_FALSE(dispatcher_.HasPendingWrites()); } -TEST_F(QuicWriteBlockedListTest, TestWriteLimits) { +TEST_F(QuicDispatcherWriteBlockedListTest, TestWriteLimits) { // Finally make sure if we write block on a write call, we stop calling. InSequence s; - blocked_list_->insert(make_pair(connection1(), true)); - blocked_list_->insert(make_pair(connection2(), true)); + SetBlocked(); + dispatcher_.OnWriteBlocked(connection1()); + dispatcher_.OnWriteBlocked(connection2()); EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce( - Invoke(this, &QuicWriteBlockedListTest::SetBlocked)); + Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked)); EXPECT_CALL(*connection2(), OnCanWrite()).Times(0); dispatcher_.OnCanWrite(); + EXPECT_TRUE(dispatcher_.HasPendingWrites()); // And we'll resume where we left off when we get another call. EXPECT_CALL(*connection2(), OnCanWrite()); dispatcher_.OnCanWrite(); + EXPECT_FALSE(dispatcher_.HasPendingWrites()); } } // namespace diff --git a/chromium/net/tools/quic/quic_epoll_clock.h b/chromium/net/tools/quic/quic_epoll_clock.h index fb21354a9e0..d96bff64389 100644 --- a/chromium/net/tools/quic/quic_epoll_clock.h +++ b/chromium/net/tools/quic/quic_epoll_clock.h @@ -5,6 +5,7 @@ #ifndef NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_ #define NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_ +#include "base/basictypes.h" #include "base/compiler_specific.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_time.h" @@ -31,6 +32,9 @@ class QuicEpollClock : public QuicClock { protected: EpollServer* epoll_server_; + + private: + DISALLOW_COPY_AND_ASSIGN(QuicEpollClock); }; } // namespace tools diff --git a/chromium/net/tools/quic/quic_in_memory_cache.cc b/chromium/net/tools/quic/quic_in_memory_cache.cc index c7c7cd04ebf..af46ca37daa 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache.cc +++ b/chromium/net/tools/quic/quic_in_memory_cache.cc @@ -99,7 +99,7 @@ void QuicInMemoryCache::AddSimpleResponse(StringPiece method, void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, const BalsaHeaders& response_headers, StringPiece response_body) { - LOG(INFO) << "Adding response for: " << GetKey(request_headers); + VLOG(1) << "Adding response for: " << GetKey(request_headers); if (ContainsKey(responses_, GetKey(request_headers))) { LOG(DFATAL) << "Response for given request already exists!"; return; @@ -110,6 +110,18 @@ void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, responses_[GetKey(request_headers)] = new_response; } +void QuicInMemoryCache::AddSpecialResponse(StringPiece method, + StringPiece path, + StringPiece version, + SpecialResponseType response_type) { + BalsaHeaders request_headers, response_headers; + request_headers.SetRequestFirstlineFromStringPieces(method, + path, + version); + AddResponse(request_headers, response_headers, ""); + responses_[GetKey(request_headers)]->response_type_ = response_type; +} + QuicInMemoryCache::QuicInMemoryCache() { Initialize(); } @@ -122,11 +134,11 @@ void QuicInMemoryCache::ResetForTests() { void QuicInMemoryCache::Initialize() { // If there's no defined cache dir, we have no initialization to do. if (FLAGS_quic_in_memory_cache_dir.empty()) { - LOG(WARNING) << "No cache directory found. Skipping initialization."; + VLOG(1) << "No cache directory found. Skipping initialization."; return; } - LOG(INFO) << "Attempting to initialize QuicInMemoryCache from directory: " - << FLAGS_quic_in_memory_cache_dir; + VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " + << FLAGS_quic_in_memory_cache_dir; FilePath directory(FLAGS_quic_in_memory_cache_dir); base::FileEnumerator file_list(directory, @@ -199,8 +211,8 @@ void QuicInMemoryCache::Initialize() { "HTTP/1.1"); request_headers.ReplaceOrAppendHeader("host", host); - LOG(INFO) << "Inserting 'http://" << GetKey(request_headers) - << "' into QuicInMemoryCache."; + VLOG(1) << "Inserting 'http://" << GetKey(request_headers) + << "' into QuicInMemoryCache."; AddResponse(request_headers, response_headers, caching_visitor.body()); @@ -214,6 +226,9 @@ QuicInMemoryCache::~QuicInMemoryCache() { string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const { StringPiece uri = request_headers.request_uri(); + if (uri.size() == 0) { + return ""; + } StringPiece host; if (uri[0] == '/') { host = request_headers.GetHeader("host"); diff --git a/chromium/net/tools/quic/quic_in_memory_cache.h b/chromium/net/tools/quic/quic_in_memory_cache.h index 3be25a6cac1..be091d5579e 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache.h +++ b/chromium/net/tools/quic/quic_in_memory_cache.h @@ -32,12 +32,19 @@ class QuicServer; // `wget -p --save_headers <url>` class QuicInMemoryCache { public: + enum SpecialResponseType { + REGULAR_RESPONSE, // Send the headers and body like a server should. + CLOSE_CONNECTION, // Close the connection (sending the close packet). + IGNORE_REQUEST, // Do nothing, expect the client to time out. + }; + // Container for response header/body pairs. class Response { public: - Response() {} + Response() : response_type_(REGULAR_RESPONSE) {} ~Response() {} + const SpecialResponseType response_type() const { return response_type_; } const BalsaHeaders& headers() const { return headers_; } const base::StringPiece body() const { return base::StringPiece(body_); } @@ -51,6 +58,7 @@ class QuicInMemoryCache { body.CopyToString(&body_); } + SpecialResponseType response_type_; BalsaHeaders headers_; std::string body_; @@ -79,6 +87,12 @@ class QuicInMemoryCache { const BalsaHeaders& response_headers, base::StringPiece response_body); + // Simulate a special behavior at a particular path. + void AddSpecialResponse(base::StringPiece method, + base::StringPiece path, + base::StringPiece version, + SpecialResponseType response_type); + private: typedef base::hash_map<std::string, Response*> ResponseMap; friend struct DefaultSingletonTraits<QuicInMemoryCache>; diff --git a/chromium/net/tools/quic/quic_in_memory_cache_test.cc b/chromium/net/tools/quic/quic_in_memory_cache_test.cc index adcb59917c5..1d4d24cff9f 100644 --- a/chromium/net/tools/quic/quic_in_memory_cache_test.cc +++ b/chromium/net/tools/quic/quic_in_memory_cache_test.cc @@ -39,7 +39,7 @@ class QuicInMemoryCacheTest : public ::testing::Test { headers->ReplaceOrAppendHeader("host", host); } - virtual void SetUp() { + virtual void SetUp() OVERRIDE { QuicInMemoryCachePeer::ResetForTests(); } diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.cc b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc new file mode 100644 index 00000000000..3d1b03d48e9 --- /dev/null +++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.cc @@ -0,0 +1,48 @@ +// Copyright 2014 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/quic/quic_packet_writer_wrapper.h" + +#include "net/quic/quic_types.h" + +namespace net { +namespace tools { + +QuicPacketWriterWrapper::QuicPacketWriterWrapper() {} + +QuicPacketWriterWrapper::QuicPacketWriterWrapper(QuicPacketWriter* writer) + : writer_(writer) {} + +QuicPacketWriterWrapper::~QuicPacketWriterWrapper() {} + +WriteResult QuicPacketWriterWrapper::WritePacket( + const char* buffer, + size_t buf_len, + const net::IPAddressNumber& self_address, + const net::IPEndPoint& peer_address) { + return writer_->WritePacket(buffer, buf_len, self_address, peer_address); +} + +bool QuicPacketWriterWrapper::IsWriteBlockedDataBuffered() const { + return writer_->IsWriteBlockedDataBuffered(); +} + +bool QuicPacketWriterWrapper::IsWriteBlocked() const { + return writer_->IsWriteBlocked(); +} + +void QuicPacketWriterWrapper::SetWritable() { + writer_->SetWritable(); +} + +void QuicPacketWriterWrapper::set_writer(QuicPacketWriter* writer) { + writer_.reset(writer); +} + +QuicPacketWriter* QuicPacketWriterWrapper::release_writer() { + return writer_.release(); +} + +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/quic_packet_writer_wrapper.h b/chromium/net/tools/quic/quic_packet_writer_wrapper.h new file mode 100644 index 00000000000..9dafe776e24 --- /dev/null +++ b/chromium/net/tools/quic/quic_packet_writer_wrapper.h @@ -0,0 +1,50 @@ +// Copyright 2014 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_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_ +#define NET_TOOLS_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_ + +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_packet_writer.h" + +namespace net { + +namespace tools { + +// Wraps a writer object to allow dynamically extending functionality. Use +// cases: replace writer while dispatcher and connections hold on to the +// wrapper; mix in monitoring in internal server; mix in mocks in unit tests. +class QuicPacketWriterWrapper : public net::QuicPacketWriter { + public: + QuicPacketWriterWrapper(); + explicit QuicPacketWriterWrapper(QuicPacketWriter* writer); + virtual ~QuicPacketWriterWrapper(); + + // Default implementation of the QuicPacketWriter interface. Passes everything + // to |writer_|. + virtual WriteResult WritePacket( + const char* buffer, + size_t buf_len, + const IPAddressNumber& self_address, + const IPEndPoint& peer_address) OVERRIDE; + virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual bool IsWriteBlocked() const OVERRIDE; + virtual void SetWritable() OVERRIDE; + + // Takes ownership of |writer|. + void set_writer(QuicPacketWriter* writer); + + // Releases ownership of |writer_|. + QuicPacketWriter* release_writer(); + + private: + scoped_ptr<QuicPacketWriter> writer_; + + DISALLOW_COPY_AND_ASSIGN(QuicPacketWriterWrapper); +}; + +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_QUIC_PACKET_WRITER_WRAPPER_H_ diff --git a/chromium/net/tools/quic/quic_server.cc b/chromium/net/tools/quic/quic_server.cc index a62cfbbdf15..be3d9fbd128 100644 --- a/chromium/net/tools/quic/quic_server.cc +++ b/chromium/net/tools/quic/quic_server.cc @@ -12,6 +12,7 @@ #include <sys/socket.h> #include "net/base/ip_endpoint.h" +#include "net/quic/congestion_control/tcp_receiver.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_clock.h" @@ -29,6 +30,7 @@ const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET; static const char kSourceAddressTokenSecret[] = "secret"; +const uint32 kServerInitialFlowControlWindow = 100 * net::kMaxPacketSize; namespace net { namespace tools { @@ -43,9 +45,6 @@ QuicServer::QuicServer() supported_versions_(QuicSupportedVersions()) { // Use hardcoded crypto parameters for now. config_.SetDefaults(); - config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); - config_.set_server_initial_congestion_window(kMaxInitialWindow, - kDefaultInitialWindow); Initialize(); } @@ -76,6 +75,9 @@ void QuicServer::Initialize() { crypto_config_.AddDefaultConfig( QuicRandom::GetInstance(), &clock, QuicCryptoServerConfig::ConfigOptions())); + + // Set flow control options in the config. + config_.SetInitialCongestionWindowToSend(kServerInitialFlowControlWindow); } QuicServer::~QuicServer() { @@ -107,6 +109,19 @@ bool QuicServer::Listen(const IPEndPoint& address) { overflow_supported_ = true; } + // These send and receive buffer sizes are sized for a single connection, + // because the default usage of QuicServer is as a test server with one or + // two clients. Adjust higher for use with many clients. + if (!QuicSocketUtils::SetReceiveBufferSize(fd_, + TcpReceiver::kReceiveWindowTCP)) { + return false; + } + + if (!QuicSocketUtils::SetSendBufferSize(fd_, + TcpReceiver::kReceiveWindowTCP)) { + return false; + } + // Enable the socket option that allows the local address to be // returned if the socket is bound to more than on address. int get_local_ip = 1; @@ -133,7 +148,7 @@ bool QuicServer::Listen(const IPEndPoint& address) { return false; } - LOG(INFO) << "Listening on " << address.ToString(); + DVLOG(1) << "Listening on " << address.ToString(); if (port_ == 0) { SockaddrStorage storage; IPEndPoint server_address; @@ -143,17 +158,24 @@ bool QuicServer::Listen(const IPEndPoint& address) { return false; } port_ = server_address.port(); - LOG(INFO) << "Kernel assigned port is " << port_; + DVLOG(1) << "Kernel assigned port is " << port_; } epoll_server_.RegisterFD(fd_, this, kEpollFlags); - dispatcher_.reset(new QuicDispatcher(config_, crypto_config_, - supported_versions_, - fd_, &epoll_server_)); + dispatcher_.reset(CreateQuicDispatcher()); + dispatcher_->Initialize(fd_); return true; } +QuicDispatcher* QuicServer::CreateQuicDispatcher() { + return new QuicDispatcher( + config_, + crypto_config_, + supported_versions_, + &epoll_server_); +} + void QuicServer::WaitForEvents() { epoll_server_.WaitForEventsAndExecuteCallbacks(); } @@ -172,7 +194,7 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) { event->out_ready_mask = 0; if (event->in_events & EPOLLIN) { - LOG(ERROR) << "EPOLLIN"; + DVLOG(1) << "EPOLLIN"; bool read = true; while (read) { read = ReadAndDispatchSinglePacket( @@ -181,8 +203,8 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) { } } if (event->in_events & EPOLLOUT) { - bool can_write_more = dispatcher_->OnCanWrite(); - if (can_write_more) { + dispatcher_->OnCanWrite(); + if (dispatcher_->HasPendingWrites()) { event->out_ready_mask |= EPOLLOUT; } } @@ -191,25 +213,10 @@ void QuicServer::OnEvent(int fd, EpollEvent* event) { } /* static */ -void QuicServer::MaybeDispatchPacket(QuicDispatcher* dispatcher, - const QuicEncryptedPacket& packet, - const IPEndPoint& server_address, - const IPEndPoint& client_address) { - QuicGuid guid; - if (!QuicFramer::ReadGuidFromPacket(packet, &guid)) { - return; - } - - bool has_version_flag = QuicFramer::HasVersionFlag(packet); - - dispatcher->ProcessPacket( - server_address, client_address, guid, has_version_flag, packet); -} - bool QuicServer::ReadAndDispatchSinglePacket(int fd, int port, QuicDispatcher* dispatcher, - int* packets_dropped) { + uint32* packets_dropped) { // Allocate some extra space so we can send an error if the client goes over // the limit. char buf[2 * kMaxPacketSize]; @@ -228,7 +235,7 @@ bool QuicServer::ReadAndDispatchSinglePacket(int fd, QuicEncryptedPacket packet(buf, bytes_read, false); IPEndPoint server_address(server_ip, port); - MaybeDispatchPacket(dispatcher, packet, server_address, client_address); + dispatcher->ProcessPacket(server_address, client_address, packet); return true; } diff --git a/chromium/net/tools/quic/quic_server.h b/chromium/net/tools/quic/quic_server.h index 7c8e4054f53..6285fd769d9 100644 --- a/chromium/net/tools/quic/quic_server.h +++ b/chromium/net/tools/quic/quic_server.h @@ -8,6 +8,7 @@ #ifndef NET_TOOLS_QUIC_QUIC_SERVER_H_ #define NET_TOOLS_QUIC_QUIC_SERVER_H_ +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" #include "net/quic/crypto/quic_crypto_server_config.h" @@ -44,8 +45,9 @@ class QuicServer : public EpollCallbackInterface { void Shutdown(); // From EpollCallbackInterface - virtual void OnRegistration( - EpollServer* eps, int fd, int event_mask) OVERRIDE {} + virtual void OnRegistration(EpollServer* eps, + int fd, + int event_mask) OVERRIDE {} virtual void OnModification(int fd, int event_mask) OVERRIDE {} virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE; virtual void OnUnregistration(int fd, bool replaced) OVERRIDE {} @@ -58,28 +60,38 @@ class QuicServer : public EpollCallbackInterface { // dropped packets. static bool ReadAndDispatchSinglePacket(int fd, int port, QuicDispatcher* dispatcher, - int* packets_dropped); + uint32* packets_dropped); virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE {} - // Dispatches the given packet only if it looks like a valid QUIC packet. - // TODO(rjshade): Return a status describing why a packet was dropped, and log - // somehow. Maybe expose as a varz. - static void MaybeDispatchPacket(QuicDispatcher* dispatcher, - const QuicEncryptedPacket& packet, - const IPEndPoint& server_address, - const IPEndPoint& client_address); - void SetStrikeRegisterNoStartupPeriod() { crypto_config_.set_strike_register_no_startup_period(); } + // SetProofSource sets the ProofSource that will be used to verify the + // server's certificate, and takes ownership of |source|. + void SetProofSource(ProofSource* source) { + crypto_config_.SetProofSource(source); + } + bool overflow_supported() { return overflow_supported_; } - int packets_dropped() { return packets_dropped_; } + uint32 packets_dropped() { return packets_dropped_; } int port() { return port_; } + protected: + virtual QuicDispatcher* CreateQuicDispatcher(); + + const QuicConfig& config() const { return config_; } + const QuicCryptoServerConfig& crypto_config() const { + return crypto_config_; + } + const QuicVersionVector& supported_versions() const { + return supported_versions_; + } + EpollServer* epoll_server() { return &epoll_server_; } + private: friend class net::tools::test::QuicServerPeer; @@ -100,7 +112,7 @@ class QuicServer : public EpollCallbackInterface { // If overflow_supported_ is true this will be the number of packets dropped // during the lifetime of the server. This may overflow if enough packets // are dropped. - int packets_dropped_; + uint32 packets_dropped_; // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped // because the socket would otherwise overflow. @@ -121,6 +133,10 @@ class QuicServer : public EpollCallbackInterface { // skipped as necessary). QuicVersionVector supported_versions_; + // Size of flow control receive window to advertise to clients on new + // connections. + uint32 server_initial_flow_control_receive_window_; + DISALLOW_COPY_AND_ASSIGN(QuicServer); }; diff --git a/chromium/net/tools/quic/quic_server_bin.cc b/chromium/net/tools/quic/quic_server_bin.cc index a71cbcf6218..2d349ac4792 100644 --- a/chromium/net/tools/quic/quic_server_bin.cc +++ b/chromium/net/tools/quic/quic_server_bin.cc @@ -10,6 +10,7 @@ #include "base/at_exit.h" #include "base/basictypes.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "net/base/ip_endpoint.h" #include "net/tools/quic/quic_in_memory_cache.h" @@ -20,8 +21,13 @@ int32 FLAGS_port = 6121; int main(int argc, char *argv[]) { - CommandLine::Init(argc, argv); - CommandLine* line = CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + base::CommandLine* line = base::CommandLine::ForCurrentProcess(); + + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + CHECK(logging::InitLogging(settings)); + if (line->HasSwitch("h") || line->HasSwitch("help")) { const char* help_str = "Usage: quic_server [options]\n" diff --git a/chromium/net/tools/quic/quic_server_session.cc b/chromium/net/tools/quic/quic_server_session.cc index dcb112e0204..d7bc3b84e70 100644 --- a/chromium/net/tools/quic/quic_server_session.cc +++ b/chromium/net/tools/quic/quic_server_session.cc @@ -12,16 +12,13 @@ namespace net { namespace tools { -QuicServerSession::QuicServerSession( - const QuicConfig& config, - QuicConnection* connection, - QuicSessionOwner* owner) +QuicServerSession::QuicServerSession(const QuicConfig& config, + QuicConnection* connection, + QuicServerSessionVisitor* visitor) : QuicSession(connection, config), - owner_(owner) { -} + visitor_(visitor) {} -QuicServerSession::~QuicServerSession() { -} +QuicServerSession::~QuicServerSession() {} void QuicServerSession::InitializeSession( const QuicCryptoServerConfig& crypto_config) { @@ -36,18 +33,28 @@ QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream( void QuicServerSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { QuicSession::OnConnectionClosed(error, from_peer); - owner_->OnConnectionClosed(connection()->guid(), error); + // In the unlikely event we get a connection close while doing an asynchronous + // crypto event, make sure we cancel the callback. + if (crypto_stream_.get() != NULL) { + crypto_stream_->CancelOutstandingCallbacks(); + } + visitor_->OnConnectionClosed(connection()->connection_id(), error); +} + +void QuicServerSession::OnWriteBlocked() { + QuicSession::OnWriteBlocked(); + visitor_->OnWriteBlocked(connection()); } bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) { if (id % 2 == 0) { - DLOG(INFO) << "Invalid incoming even stream_id:" << id; + DVLOG(1) << "Invalid incoming even stream_id:" << id; connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID); return false; } if (GetNumOpenStreams() >= get_max_open_streams()) { - DLOG(INFO) << "Failed to create a new incoming stream with id:" << id - << " Already " << GetNumOpenStreams() << " open."; + DVLOG(1) << "Failed to create a new incoming stream with id:" << id + << " Already " << GetNumOpenStreams() << " open."; connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS); return false; } diff --git a/chromium/net/tools/quic/quic_server_session.h b/chromium/net/tools/quic/quic_server_session.h index f4705519791..e05ba161d4f 100644 --- a/chromium/net/tools/quic/quic_server_session.h +++ b/chromium/net/tools/quic/quic_server_session.h @@ -10,6 +10,7 @@ #include <set> #include <vector> +#include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "net/quic/quic_crypto_server_stream.h" @@ -18,6 +19,7 @@ namespace net { +class QuicBlockedWriterInterface; class QuicConfig; class QuicConnection; class QuicCryptoServerConfig; @@ -31,28 +33,33 @@ class QuicServerSessionPeer; // An interface from the session to the entity owning the session. // This lets the session notify its owner (the Dispatcher) when the connection -// is closed. -class QuicSessionOwner { +// is closed or blocked. +class QuicServerSessionVisitor { public: - virtual ~QuicSessionOwner() {} + virtual ~QuicServerSessionVisitor() {} - virtual void OnConnectionClosed(QuicGuid guid, QuicErrorCode error) = 0; + virtual void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) = 0; + virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) = 0; }; class QuicServerSession : public QuicSession { public: QuicServerSession(const QuicConfig& config, - QuicConnection *connection, - QuicSessionOwner* owner); + QuicConnection* connection, + QuicServerSessionVisitor* visitor); // Override the base class to notify the owner of the connection close. virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; + virtual void OnWriteBlocked() OVERRIDE; virtual ~QuicServerSession(); virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config); - const QuicCryptoServerStream* crypto_stream() { return crypto_stream_.get(); } + const QuicCryptoServerStream* crypto_stream() const { + return crypto_stream_.get(); + } protected: // QuicSession methods: @@ -66,13 +73,13 @@ class QuicServerSession : public QuicSession { virtual bool ShouldCreateIncomingDataStream(QuicStreamId id); virtual QuicCryptoServerStream* CreateQuicCryptoServerStream( - const QuicCryptoServerConfig& crypto_config); + const QuicCryptoServerConfig& crypto_config); private: friend class test::QuicServerSessionPeer; scoped_ptr<QuicCryptoServerStream> crypto_stream_; - QuicSessionOwner* owner_; + QuicServerSessionVisitor* visitor_; DISALLOW_COPY_AND_ASSIGN(QuicServerSession); }; diff --git a/chromium/net/tools/quic/quic_server_session_test.cc b/chromium/net/tools/quic/quic_server_session_test.cc index a3dd9968285..1e04a7fd768 100644 --- a/chromium/net/tools/quic/quic_server_session_test.cc +++ b/chromium/net/tools/quic/quic_server_session_test.cc @@ -7,10 +7,10 @@ #include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_data_stream_peer.h" #include "net/quic/test_tools/quic_test_utils.h" -#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_spdy_server_stream.h" #include "net/tools/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -20,8 +20,13 @@ using __gnu_cxx::vector; using net::test::MockConnection; using net::test::QuicConnectionPeer; using net::test::QuicDataStreamPeer; -using testing::_; +using net::test::SupportedVersions; +using net::test::kClientDataStreamId1; +using net::test::kClientDataStreamId2; +using net::test::kClientDataStreamId3; +using net::test::kClientDataStreamId4; using testing::StrictMock; +using testing::_; namespace net { namespace tools { @@ -29,206 +34,152 @@ namespace test { class QuicServerSessionPeer { public: - static QuicDataStream* GetIncomingReliableStream( + static QuicDataStream* GetIncomingDataStream( QuicServerSession* s, QuicStreamId id) { - return s->GetIncomingReliableStream(id); + return s->GetIncomingDataStream(id); } static QuicDataStream* GetDataStream(QuicServerSession* s, QuicStreamId id) { return s->GetDataStream(id); } }; -class CloseOnDataStream : public QuicDataStream { - public: - CloseOnDataStream(QuicStreamId id, QuicSession* session) - : QuicDataStream(id, session) { - } - - virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE { - session()->MarkDecompressionBlocked(1, id()); - session()->CloseStream(id()); - return true; - } - - virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE { - return 0; - } -}; - -class TestQuicQuicServerSession : public QuicServerSession { - public: - TestQuicQuicServerSession(const QuicConfig& config, - QuicConnection* connection, - QuicSessionOwner* owner) - : QuicServerSession(config, connection, owner), - close_stream_on_data_(false) { - } - - virtual QuicDataStream* CreateIncomingDataStream( - QuicStreamId id) OVERRIDE { - if (!ShouldCreateIncomingDataStream(id)) { - return NULL; - } - if (close_stream_on_data_) { - return new CloseOnDataStream(id, this); - } else { - return new QuicSpdyServerStream(id, this); - } - } - - void CloseStreamOnData() { - close_stream_on_data_ = true; - } - - private: - bool close_stream_on_data_; -}; - namespace { -class QuicServerSessionTest : public ::testing::Test { +class QuicServerSessionTest : public ::testing::TestWithParam<QuicVersion> { protected: QuicServerSessionTest() : crypto_config_(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance()) { config_.SetDefaults(); config_.set_max_streams_per_connection(3, 3); - - connection_ = new MockConnection(true); - session_.reset(new TestQuicQuicServerSession( - config_, connection_, &owner_)); + config_.SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + config_.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + config_.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + + connection_ = + new StrictMock<MockConnection>(true, SupportedVersions(GetParam())); + session_.reset(new QuicServerSession(config_, connection_, &owner_)); session_->InitializeSession(crypto_config_); visitor_ = QuicConnectionPeer::GetVisitor(connection_); } - void MarkHeadersReadForStream(QuicStreamId id) { - QuicDataStream* stream = QuicServerSessionPeer::GetDataStream( - session_.get(), id); - ASSERT_TRUE(stream != NULL); - QuicDataStreamPeer::SetHeadersDecompressed(stream, true); - } + QuicVersion version() const { return connection_->version(); } - StrictMock<MockQuicSessionOwner> owner_; - MockConnection* connection_; + StrictMock<MockQuicServerSessionVisitor> owner_; + StrictMock<MockConnection>* connection_; QuicConfig config_; QuicCryptoServerConfig crypto_config_; - scoped_ptr<TestQuicQuicServerSession> session_; + scoped_ptr<QuicServerSession> session_; QuicConnectionVisitorInterface* visitor_; }; -TEST_F(QuicServerSessionTest, CloseStreamDueToReset) { +INSTANTIATE_TEST_CASE_P(Tests, QuicServerSessionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicServerSessionTest, CloseStreamDueToReset) { // Open a stream, then reset it. // Send two bytes of payload to open it. - QuicStreamFrame data1(3, false, 0, MakeIOVector("HT")); + QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); vector<QuicStreamFrame> frames; frames.push_back(data1); - EXPECT_TRUE(visitor_->OnStreamFrames(frames)); + session_->OnStreamFrames(frames); EXPECT_EQ(1u, session_->GetNumOpenStreams()); - // Pretend we got full headers, so we won't trigger the 'unrecoverable - // compression context' state. - MarkHeadersReadForStream(3); - - // Send a reset. - QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR); + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); + EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, + QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0)); visitor_->OnRstStream(rst1); EXPECT_EQ(0u, session_->GetNumOpenStreams()); // Send the same two bytes of payload in a new packet. - EXPECT_TRUE(visitor_->OnStreamFrames(frames)); + visitor_->OnStreamFrames(frames); // The stream should not be re-opened. EXPECT_EQ(0u, session_->GetNumOpenStreams()); + EXPECT_TRUE(connection_->connected()); } -TEST_F(QuicServerSessionTest, NeverOpenStreamDueToReset) { - // Send a reset. - QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR); +TEST_P(QuicServerSessionTest, NeverOpenStreamDueToReset) { + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); + EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, + QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0)); visitor_->OnRstStream(rst1); EXPECT_EQ(0u, session_->GetNumOpenStreams()); // Send two bytes of payload. - QuicStreamFrame data1(3, false, 0, MakeIOVector("HT")); + QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); vector<QuicStreamFrame> frames; frames.push_back(data1); - - // When we get data for the closed stream, it implies the far side has - // compressed some headers. As a result we're going to bail due to - // unrecoverable compression context state. - EXPECT_CALL(*connection_, SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED)); - EXPECT_FALSE(visitor_->OnStreamFrames(frames)); + visitor_->OnStreamFrames(frames); // The stream should never be opened, now that the reset is received. EXPECT_EQ(0u, session_->GetNumOpenStreams()); + EXPECT_TRUE(connection_->connected()); } -TEST_F(QuicServerSessionTest, GoOverPrematureClosedStreamLimit) { - QuicStreamFrame data1(3, false, 0, MakeIOVector("H")); - vector<QuicStreamFrame> frames; - frames.push_back(data1); - - // Set up the stream such that it's open in OnPacket, but closes half way - // through while on the decompression blocked list. - session_->CloseStreamOnData(); - - EXPECT_CALL(*connection_, SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED)); - EXPECT_FALSE(visitor_->OnStreamFrames(frames)); -} - -TEST_F(QuicServerSessionTest, AcceptClosedStream) { +TEST_P(QuicServerSessionTest, AcceptClosedStream) { vector<QuicStreamFrame> frames; // Send (empty) compressed headers followed by two bytes of data. - frames.push_back( - QuicStreamFrame(3, false, 0, MakeIOVector("\1\0\0\0\0\0\0\0HT"))); - frames.push_back( - QuicStreamFrame(5, false, 0, MakeIOVector("\2\0\0\0\0\0\0\0HT"))); - EXPECT_TRUE(visitor_->OnStreamFrames(frames)); - - // Pretend we got full headers, so we won't trigger the 'unercoverable - // compression context' state. - MarkHeadersReadForStream(3); - - // Send a reset. - QuicRstStreamFrame rst(3, QUIC_STREAM_NO_ERROR); + frames.push_back(QuicStreamFrame(kClientDataStreamId1, false, 0, + MakeIOVector("\1\0\0\0\0\0\0\0HT"))); + frames.push_back(QuicStreamFrame(kClientDataStreamId2, false, 0, + MakeIOVector("\2\0\0\0\0\0\0\0HT"))); + visitor_->OnStreamFrames(frames); + EXPECT_EQ(2u, session_->GetNumOpenStreams()); + + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); + EXPECT_CALL(*connection_, SendRstStream(kClientDataStreamId1, + QUIC_RST_FLOW_CONTROL_ACCOUNTING, 0)); visitor_->OnRstStream(rst); // If we were tracking, we'd probably want to reject this because it's data // past the reset point of stream 3. As it's a closed stream we just drop the // data on the floor, but accept the packet because it has data for stream 5. frames.clear(); - frames.push_back(QuicStreamFrame(3, false, 2, MakeIOVector("TP"))); - frames.push_back(QuicStreamFrame(5, false, 2, MakeIOVector("TP"))); - EXPECT_TRUE(visitor_->OnStreamFrames(frames)); + frames.push_back( + QuicStreamFrame(kClientDataStreamId1, false, 2, MakeIOVector("TP"))); + frames.push_back( + QuicStreamFrame(kClientDataStreamId2, false, 2, MakeIOVector("TP"))); + visitor_->OnStreamFrames(frames); + // The stream should never be opened, now that the reset is received. + EXPECT_EQ(1u, session_->GetNumOpenStreams()); + EXPECT_TRUE(connection_->connected()); } -TEST_F(QuicServerSessionTest, MaxNumConnections) { +TEST_P(QuicServerSessionTest, MaxNumConnections) { EXPECT_EQ(0u, session_->GetNumOpenStreams()); - EXPECT_TRUE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 3)); - EXPECT_TRUE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 5)); - EXPECT_TRUE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 7)); - EXPECT_FALSE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 9)); + EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId1)); + EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId2)); + EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId3)); + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS)); + EXPECT_FALSE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId4)); } -TEST_F(QuicServerSessionTest, MaxNumConnectionsImplicit) { +TEST_P(QuicServerSessionTest, MaxNumConnectionsImplicit) { EXPECT_EQ(0u, session_->GetNumOpenStreams()); - EXPECT_TRUE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 3)); - // Implicitly opens two more streams before 9. - EXPECT_FALSE( - QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 9)); + EXPECT_TRUE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId1)); + // Implicitly opens two more streams. + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS)); + EXPECT_FALSE(QuicServerSessionPeer::GetIncomingDataStream( + session_.get(), kClientDataStreamId4)); } -TEST_F(QuicServerSessionTest, GetEvenIncomingError) { +TEST_P(QuicServerSessionTest, GetEvenIncomingError) { // Incoming streams on the server session must be odd. + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)); EXPECT_EQ(NULL, - QuicServerSessionPeer::GetIncomingReliableStream( - session_.get(), 2)); + QuicServerSessionPeer::GetIncomingDataStream(session_.get(), 4)); } } // namespace diff --git a/chromium/net/tools/quic/quic_server_test.cc b/chromium/net/tools/quic/quic_server_test.cc index 7d24edc28bb..e2d7c4f3288 100644 --- a/chromium/net/tools/quic/quic_server_test.cc +++ b/chromium/net/tools/quic/quic_server_test.cc @@ -21,13 +21,13 @@ class QuicServerDispatchPacketTest : public ::testing::Test { public: QuicServerDispatchPacketTest() : crypto_config_("blah", QuicRandom::GetInstance()), - dispatcher_(config_, crypto_config_, 1234, &eps_) {} - + dispatcher_(config_, crypto_config_, &eps_) { + dispatcher_.Initialize(1234); + } - void MaybeDispatchPacket(const QuicEncryptedPacket& packet) { + void DispatchPacket(const QuicEncryptedPacket& packet) { IPEndPoint client_addr, server_addr; - QuicServer::MaybeDispatchPacket(&dispatcher_, packet, - client_addr, server_addr); + dispatcher_.ProcessPacket(server_addr, client_addr, packet); } protected: @@ -37,23 +37,11 @@ class QuicServerDispatchPacketTest : public ::testing::Test { MockQuicDispatcher dispatcher_; }; -TEST_F(QuicServerDispatchPacketTest, DoNotDispatchPacketWithoutGUID) { - // Packet too short to be considered valid. - unsigned char invalid_packet[] = { 0x00 }; - QuicEncryptedPacket encrypted_invalid_packet( - QuicUtils::AsChars(invalid_packet), arraysize(invalid_packet), false); - - // We expect the invalid packet to be dropped, and ProcessPacket should never - // be called. - EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _, _)).Times(0); - MaybeDispatchPacket(encrypted_invalid_packet); -} - -TEST_F(QuicServerDispatchPacketTest, DispatchValidPacket) { +TEST_F(QuicServerDispatchPacketTest, DispatchPacket) { unsigned char valid_packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -64,8 +52,8 @@ TEST_F(QuicServerDispatchPacketTest, DispatchValidPacket) { QuicEncryptedPacket encrypted_valid_packet(QuicUtils::AsChars(valid_packet), arraysize(valid_packet), false); - EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _, _)).Times(1); - MaybeDispatchPacket(encrypted_valid_packet); + EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1); + DispatchPacket(encrypted_valid_packet); } } // namespace diff --git a/chromium/net/tools/quic/quic_socket_utils.cc b/chromium/net/tools/quic/quic_socket_utils.cc index 87071a681ae..3202150917b 100644 --- a/chromium/net/tools/quic/quic_socket_utils.cc +++ b/chromium/net/tools/quic/quic_socket_utils.cc @@ -11,6 +11,7 @@ #include <sys/uio.h> #include <string> +#include "base/basictypes.h" #include "base/logging.h" #include "net/quic/quic_protocol.h" @@ -51,7 +52,7 @@ IPAddressNumber QuicSocketUtils::GetAddressFromMsghdr(struct msghdr *hdr) { // static bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr *hdr, - int *dropped_packets) { + uint32 *dropped_packets) { if (hdr->msg_controllen > 0) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(hdr); @@ -79,8 +80,26 @@ int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) { } // static +bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) { + LOG(ERROR) << "Failed to set socket send size"; + return false; + } + return true; +} + +// static +bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) { + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) { + LOG(ERROR) << "Failed to set socket recv size"; + return false; + } + return true; +} + +// static int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len, - int* dropped_packets, + uint32* dropped_packets, IPAddressNumber* self_address, IPEndPoint* peer_address) { CHECK(peer_address != NULL); @@ -135,6 +154,28 @@ int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len, return bytes_read; } +size_t QuicSocketUtils::SetIpInfoInCmsg(const IPAddressNumber& self_address, + cmsghdr* cmsg) { + if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) { + cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in_pktinfo)); + pktinfo->ipi_ifindex = 0; + memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size()); + return sizeof(in_pktinfo); + } else { + cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in6_pktinfo)); + memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size()); + return sizeof(in6_pktinfo); + } +} + // static WriteResult QuicSocketUtils::WritePacket(int fd, const char* buffer, @@ -164,30 +205,11 @@ WriteResult QuicSocketUtils::WritePacket(int fd, if (self_address.empty()) { hdr.msg_control = 0; hdr.msg_controllen = 0; - } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) { - hdr.msg_control = cbuf; - hdr.msg_controllen = kSpaceForIp; - cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); - - cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); - memset(pktinfo, 0, sizeof(in_pktinfo)); - pktinfo->ipi_ifindex = 0; - memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size()); - hdr.msg_controllen = cmsg->cmsg_len; } else { hdr.msg_control = cbuf; hdr.msg_controllen = kSpaceForIp; - cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); - - cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); - memset(pktinfo, 0, sizeof(in6_pktinfo)); - memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size()); + cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + SetIpInfoInCmsg(self_address, cmsg); hdr.msg_controllen = cmsg->cmsg_len; } diff --git a/chromium/net/tools/quic/quic_socket_utils.h b/chromium/net/tools/quic/quic_socket_utils.h index 8f0feffced2..03b95e5179e 100644 --- a/chromium/net/tools/quic/quic_socket_utils.h +++ b/chromium/net/tools/quic/quic_socket_utils.h @@ -11,8 +11,9 @@ #include <sys/socket.h> #include <string> +#include "base/basictypes.h" #include "net/base/ip_endpoint.h" -#include "net/quic/quic_protocol.h" +#include "net/quic/quic_types.h" namespace net { namespace tools { @@ -26,12 +27,19 @@ class QuicSocketUtils { // If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets // to the correct value and return true. Otherwise it will return false. - static bool GetOverflowFromMsghdr(struct msghdr *hdr, int *dropped_packets); + static bool GetOverflowFromMsghdr(struct msghdr *hdr, + uint32 *dropped_packets); // Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on // address_family. Returns the return code from setsockopt. static int SetGetAddressInfo(int fd, int address_family); + // Sets the send buffer size to |size| and returns false if it fails. + static bool SetSendBufferSize(int fd, size_t size); + + // Sets the receive buffer size to |size| and returns false if it fails. + static bool SetReceiveBufferSize(int fd, size_t size); + // Reads buf_len from the socket. If reading is successful, returns bytes // read and sets peer_address to the peer address. Otherwise returns -1. // @@ -41,8 +49,10 @@ class QuicSocketUtils { // // If self_address is non-null, it will be set to the address the peer sent // packets to, assuming a packet was read. - static int ReadPacket(int fd, char* buffer, size_t buf_len, - int* dropped_packets, + static int ReadPacket(int fd, + char* buffer, + size_t buf_len, + uint32* dropped_packets, IPAddressNumber* self_address, IPEndPoint* peer_address); @@ -50,9 +60,20 @@ class QuicSocketUtils { // status to WRITE_STATUS_OK and sets bytes_written. Otherwise sets the // result's status to WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and sets // error_code to errno. - static WriteResult WritePacket(int fd, const char* buffer, size_t buf_len, + static WriteResult WritePacket(int fd, + const char* buffer, + size_t buf_len, const IPAddressNumber& self_address, const IPEndPoint& peer_address); + + // A helper for WritePacket which fills in the cmsg with the supplied self + // address. + // Returns the length of the packet info structure used. + static size_t SetIpInfoInCmsg(const IPAddressNumber& self_address, + cmsghdr* cmsg); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicSocketUtils); }; } // namespace tools diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.cc b/chromium/net/tools/quic/quic_spdy_client_stream.cc index 1956c7940db..8423295a1ac 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream.cc +++ b/chromium/net/tools/quic/quic_spdy_client_stream.cc @@ -20,7 +20,9 @@ QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id, QuicClientSession* session) : QuicDataStream(id, session), read_buf_(new GrowableIOBuffer()), - response_headers_received_(false) { + response_headers_received_(false), + header_bytes_read_(0), + header_bytes_written_(0) { } QuicSpdyClientStream::~QuicSpdyClientStream() { @@ -28,29 +30,37 @@ QuicSpdyClientStream::~QuicSpdyClientStream() { bool QuicSpdyClientStream::OnStreamFrame(const QuicStreamFrame& frame) { if (!write_side_closed()) { - DLOG(INFO) << "Got a response before the request was complete. " - << "Aborting request."; + DVLOG(1) << "Got a response before the request was complete. " + << "Aborting request."; CloseWriteSide(); } return QuicDataStream::OnStreamFrame(frame); } -uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 length) { - uint32 total_bytes_processed = 0; +void QuicSpdyClientStream::OnStreamHeadersComplete(bool fin, + size_t frame_len) { + header_bytes_read_ = frame_len; + QuicDataStream::OnStreamHeadersComplete(fin, frame_len); +} + +uint32 QuicSpdyClientStream::ProcessData(const char* data, + uint32 data_len) { + int total_bytes_processed = 0; // Are we still reading the response headers. if (!response_headers_received_) { // Grow the read buffer if necessary. - if (read_buf_->RemainingCapacity() < (int)length) { + if (read_buf_->RemainingCapacity() < (int)data_len) { read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize); } - memcpy(read_buf_->data(), data, length); - read_buf_->set_offset(read_buf_->offset() + length); + memcpy(read_buf_->data(), data, data_len); + read_buf_->set_offset(read_buf_->offset() + data_len); ParseResponseHeaders(); } else { - data_.append(data + total_bytes_processed, length - total_bytes_processed); + data_.append(data + total_bytes_processed, + data_len - total_bytes_processed); } - return length; + return data_len; } void QuicSpdyClientStream::OnFinRead() { @@ -71,15 +81,16 @@ ssize_t QuicSpdyClientStream::SendRequest(const BalsaHeaders& headers, SpdyUtils::RequestHeadersToSpdyHeaders(headers); bool send_fin_with_headers = fin && body.empty(); - string headers_string = session()->compressor()->CompressHeadersWithPriority( - priority(), header_block); - WriteOrBufferData(headers_string, send_fin_with_headers); + size_t bytes_sent = body.size(); + header_bytes_written_ = WriteHeaders( + header_block, send_fin_with_headers, NULL); + bytes_sent += header_bytes_written_; if (!body.empty()) { - WriteOrBufferData(body, fin); + WriteOrBufferData(body, fin, NULL); } - return headers_string.size() + body.size(); + return bytes_sent; } int QuicSpdyClientStream::ParseResponseHeaders() { @@ -109,7 +120,7 @@ int QuicSpdyClientStream::ParseResponseHeaders() { // Sends body data to the server and returns the number of bytes sent. void QuicSpdyClientStream::SendBody(const string& data, bool fin) { - return WriteOrBufferData(data, fin); + WriteOrBufferData(data, fin, NULL); } } // namespace tools diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.h b/chromium/net/tools/quic/quic_spdy_client_stream.h index 2b17205f53e..1292e197603 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream.h +++ b/chromium/net/tools/quic/quic_spdy_client_stream.h @@ -8,6 +8,7 @@ #include <sys/types.h> #include <string> +#include "base/basictypes.h" #include "base/strings/string_piece.h" #include "net/base/io_buffer.h" #include "net/quic/quic_data_stream.h" @@ -33,6 +34,9 @@ class QuicSpdyClientStream : public QuicDataStream { // SPDY/HTTP does not support bidirectional streaming. virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; + // Override the base class to store the size of the headers. + virtual void OnStreamHeadersComplete(bool fin, size_t frame_len) OVERRIDE; + // ReliableQuicStream implementation called by the session when there's // data for us. virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE; @@ -54,6 +58,10 @@ class QuicSpdyClientStream : public QuicDataStream { // Returns whatever headers have been received for this stream. const BalsaHeaders& headers() { return headers_; } + size_t header_bytes_read() const { return header_bytes_read_; } + + size_t header_bytes_written() const { return header_bytes_written_; } + // While the server's set_priority shouldn't be called externally, the creator // of client-side streams should be able to set the priority. using QuicDataStream::set_priority; @@ -66,6 +74,10 @@ class QuicSpdyClientStream : public QuicDataStream { scoped_refptr<GrowableIOBuffer> read_buf_; bool response_headers_received_; + size_t header_bytes_read_; + size_t header_bytes_written_; + + DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientStream); }; } // namespace tools diff --git a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc index 44c640cbdea..475f832b1c5 100644 --- a/chromium/net/tools/quic/quic_spdy_client_stream_test.cc +++ b/chromium/net/tools/quic/quic_spdy_client_stream_test.cc @@ -7,7 +7,6 @@ #include "base/strings/string_number_conversions.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" -#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client_session.h" #include "net/tools/quic/quic_spdy_client_stream.h" #include "net/tools/quic/spdy_utils.h" @@ -16,6 +15,8 @@ #include "testing/gtest/include/gtest/gtest.h" using net::test::DefaultQuicConfig; +using net::test::SupportedVersions; +using testing::StrictMock; using testing::TestWithParam; namespace net { @@ -23,23 +24,35 @@ namespace tools { namespace test { namespace { -class QuicSpdyClientStreamTest : public ::testing::Test { +class QuicSpdyClientStreamTest : public TestWithParam<QuicVersion> { public: QuicSpdyClientStreamTest() - : session_("example.com", DefaultQuicConfig(), - new MockConnection(false), + : connection_(new StrictMock<MockConnection>( + false, SupportedVersions(GetParam()))), + session_(QuicServerId("example.com", 80, false, PRIVACY_MODE_DISABLED), + DefaultQuicConfig(), + connection_, &crypto_config_), body_("hello world") { - session_.config()->SetDefaults(); crypto_config_.SetDefaults(); headers_.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "Ok"); headers_.ReplaceOrAppendHeader("content-length", "11"); headers_string_ = SpdyUtils::SerializeResponseHeaders(headers_); + + // New streams rely on having the peer's flow control receive window + // negotiated in the config. + session_.config()->SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session_.config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session_.config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); stream_.reset(new QuicSpdyClientStream(3, &session_)); } + StrictMock<MockConnection>* connection_; QuicClientSession session_; scoped_ptr<QuicSpdyClientStream> stream_; BalsaHeaders headers_; @@ -48,7 +61,10 @@ class QuicSpdyClientStreamTest : public ::testing::Test { QuicCryptoClientConfig crypto_config_; }; -TEST_F(QuicSpdyClientStreamTest, TestFraming) { +INSTANTIATE_TEST_CASE_P(Tests, QuicSpdyClientStreamTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicSpdyClientStreamTest, TestFraming) { EXPECT_EQ(headers_string_.size(), stream_->ProcessData( headers_string_.c_str(), headers_string_.size())); EXPECT_EQ(body_.size(), @@ -57,7 +73,7 @@ TEST_F(QuicSpdyClientStreamTest, TestFraming) { EXPECT_EQ(body_, stream_->data()); } -TEST_F(QuicSpdyClientStreamTest, TestFramingOnePacket) { +TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) { string message = headers_string_ + body_; EXPECT_EQ(message.size(), stream_->ProcessData( @@ -66,7 +82,7 @@ TEST_F(QuicSpdyClientStreamTest, TestFramingOnePacket) { EXPECT_EQ(body_, stream_->data()); } -TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) { +TEST_P(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) { string large_body = "hello world!!!!!!"; EXPECT_EQ(headers_string_.size(), stream_->ProcessData( @@ -75,12 +91,14 @@ TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) { EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); EXPECT_EQ(200u, stream_->headers().parsed_response_code()); + EXPECT_CALL(*connection_, + SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0)); stream_->ProcessData(large_body.c_str(), large_body.size()); EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error()); } -TEST_F(QuicSpdyClientStreamTest, TestNoBidirectionalStreaming) { +TEST_P(QuicSpdyClientStreamTest, TestNoBidirectionalStreaming) { QuicStreamFrame frame(3, false, 3, MakeIOVector("asd")); EXPECT_FALSE(stream_->write_side_closed()); diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.cc b/chromium/net/tools/quic/quic_spdy_server_stream.cc index c1a9cf15e18..7ae20d02e97 100644 --- a/chromium/net/tools/quic/quic_spdy_server_stream.cc +++ b/chromium/net/tools/quic/quic_spdy_server_stream.cc @@ -28,22 +28,23 @@ QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id, QuicSpdyServerStream::~QuicSpdyServerStream() { } -uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 length) { +uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) { uint32 total_bytes_processed = 0; // Are we still reading the request headers. if (!request_headers_received_) { // Grow the read buffer if necessary. - if (read_buf_->RemainingCapacity() < (int)length) { + if (read_buf_->RemainingCapacity() < (int)data_len) { read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize); } - memcpy(read_buf_->data(), data, length); - read_buf_->set_offset(read_buf_->offset() + length); + memcpy(read_buf_->data(), data, data_len); + read_buf_->set_offset(read_buf_->offset() + data_len); ParseRequestHeaders(); } else { - body_.append(data + total_bytes_processed, length - total_bytes_processed); + body_.append(data + total_bytes_processed, + data_len - total_bytes_processed); } - return length; + return data_len; } void QuicSpdyServerStream::OnFinRead() { @@ -55,7 +56,7 @@ void QuicSpdyServerStream::OnFinRead() { if (!request_headers_received_) { SendErrorResponse(); // We're not done reading headers. } else if ((headers_.content_length_status() == - BalsaHeadersEnums::VALID_CONTENT_LENGTH) && + BalsaHeadersEnums::VALID_CONTENT_LENGTH) && body_.size() != headers_.content_length()) { SendErrorResponse(); // Invalid content length } else { @@ -97,12 +98,23 @@ void QuicSpdyServerStream::SendResponse() { return; } - DLOG(INFO) << "Sending response for stream " << id(); + if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) { + DVLOG(1) << "Special response: closing connection."; + CloseConnection(QUIC_NO_ERROR); + return; + } + + if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) { + DVLOG(1) << "Special response: ignoring request."; + return; + } + + DVLOG(1) << "Sending response for stream " << id(); SendHeadersAndBody(response->headers(), response->body()); } void QuicSpdyServerStream::SendErrorResponse() { - DLOG(INFO) << "Sending error response for stream " << id(); + DVLOG(1) << "Sending error response for stream " << id(); BalsaHeaders headers; headers.SetResponseFirstlineFromStringPieces( "HTTP/1.1", "500", "Server Error"); @@ -110,7 +122,7 @@ void QuicSpdyServerStream::SendErrorResponse() { SendHeadersAndBody(headers, "bad"); } -void QuicSpdyServerStream:: SendHeadersAndBody( +void QuicSpdyServerStream::SendHeadersAndBody( const BalsaHeaders& response_headers, StringPiece body) { // We only support SPDY and HTTP, and neither handles bidirectional streaming. @@ -121,12 +133,10 @@ void QuicSpdyServerStream:: SendHeadersAndBody( SpdyHeaderBlock header_block = SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers); - string headers_string = - session()->compressor()->CompressHeaders(header_block); - WriteOrBufferData(headers_string, body.empty()); + WriteHeaders(header_block, body.empty(), NULL); if (!body.empty()) { - WriteOrBufferData(body, true); + WriteOrBufferData(body, true, NULL); } } diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.h b/chromium/net/tools/quic/quic_spdy_server_stream.h index 574ef76f33d..741f7867331 100644 --- a/chromium/net/tools/quic/quic_spdy_server_stream.h +++ b/chromium/net/tools/quic/quic_spdy_server_stream.h @@ -7,6 +7,7 @@ #include <string> +#include "base/basictypes.h" #include "net/base/io_buffer.h" #include "net/quic/quic_data_stream.h" #include "net/quic/quic_protocol.h" @@ -56,6 +57,8 @@ class QuicSpdyServerStream : public QuicDataStream { // Buffer into which response header data is read. scoped_refptr<GrowableIOBuffer> read_buf_; bool request_headers_received_; + + DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream); }; } // namespace tools diff --git a/chromium/net/tools/quic/quic_spdy_server_stream_test.cc b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc index c0ec9449707..e01569ede79 100644 --- a/chromium/net/tools/quic/quic_spdy_server_stream_test.cc +++ b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc @@ -8,7 +8,6 @@ #include "base/strings/string_piece.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_spdy_compressor.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/epoll_server/epoll_server.h" @@ -20,8 +19,9 @@ #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; -using net::tools::test::MockConnection; using net::test::MockSession; +using net::test::SupportedVersions; +using net::tools::test::MockConnection; using std::string; using testing::_; using testing::AnyNumber; @@ -29,7 +29,6 @@ using testing::Invoke; using testing::InvokeArgument; using testing::InSequence; using testing::Return; -using testing::StrEq; using testing::StrictMock; using testing::WithArgs; @@ -46,25 +45,35 @@ class QuicSpdyServerStreamPeer : public QuicSpdyServerStream { using QuicSpdyServerStream::SendResponse; using QuicSpdyServerStream::SendErrorResponse; - const string& body() { - return body_; + BalsaHeaders* mutable_headers() { + return &headers_; } - const BalsaHeaders& headers() { - return headers_; + static void SendResponse(QuicSpdyServerStream* stream) { + stream->SendResponse(); } - BalsaHeaders* mutable_headers() { - return &headers_; + static void SendErrorResponse(QuicSpdyServerStream* stream) { + stream->SendResponse(); + } + + static const string& body(QuicSpdyServerStream* stream) { + return stream->body_; + } + + static const BalsaHeaders& headers(QuicSpdyServerStream* stream) { + return stream->headers_; } }; namespace { -class QuicSpdyServerStreamTest : public ::testing::Test { +class QuicSpdyServerStreamTest : public ::testing::TestWithParam<QuicVersion> { public: QuicSpdyServerStreamTest() - : session_(new MockConnection(true)), + : connection_(new StrictMock<MockConnection>( + true, SupportedVersions(GetParam()))), + session_(connection_), body_("hello world") { BalsaHeaders request_headers; request_headers.SetRequestFirstlineFromStringPieces( @@ -72,35 +81,23 @@ class QuicSpdyServerStreamTest : public ::testing::Test { request_headers.ReplaceOrAppendHeader("content-length", "11"); headers_string_ = SpdyUtils::SerializeRequestHeaders(request_headers); - stream_.reset(new QuicSpdyServerStreamPeer(3, &session_)); - } - QuicConsumedData ValidateHeaders(const struct iovec* iov) { - StringPiece headers = - StringPiece(static_cast<const char*>(iov[0].iov_base), iov[0].iov_len); - headers_string_ = SpdyUtils::SerializeResponseHeaders( - response_headers_); - QuicSpdyDecompressor decompressor; - TestDecompressorVisitor visitor; - - // First the header id, then the compressed data. - EXPECT_EQ(1, headers[0]); - EXPECT_EQ(0, headers[1]); - EXPECT_EQ(0, headers[2]); - EXPECT_EQ(0, headers[3]); - EXPECT_EQ(static_cast<size_t>(headers.length() - 4), - decompressor.DecompressData(headers.substr(4), &visitor)); - - EXPECT_EQ(headers_string_, visitor.data()); - - return QuicConsumedData(headers.size(), false); + // New streams rely on having the peer's flow control receive window + // negotiated in the config. + session_.config()->SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session_.config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session_.config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + stream_.reset(new QuicSpdyServerStreamPeer(3, &session_)); } static void SetUpTestCase() { QuicInMemoryCachePeer::ResetForTests(); } - virtual void SetUp() { + virtual void SetUp() OVERRIDE { QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); BalsaHeaders request_headers, response_headers; @@ -130,8 +127,17 @@ class QuicSpdyServerStreamTest : public ::testing::Test { cache->AddResponse(request_headers, response_headers, body); } + const string& StreamBody() { + return QuicSpdyServerStreamPeer::body(stream_.get()); + } + + const BalsaHeaders& StreamHeaders() { + return QuicSpdyServerStreamPeer::headers(stream_.get()); + } + BalsaHeaders response_headers_; EpollServer eps_; + StrictMock<MockConnection>* connection_; StrictMock<MockSession> session_; scoped_ptr<QuicSpdyServerStreamPeer> stream_; string headers_string_; @@ -140,32 +146,31 @@ class QuicSpdyServerStreamTest : public ::testing::Test { QuicConsumedData ConsumeAllData( QuicStreamId id, - const struct iovec* iov, - int iov_count, + const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection /*fec_protection_*/, QuicAckNotifier::DelegateInterface* /*ack_notifier_delegate*/) { - ssize_t consumed_length = 0; - for (int i = 0; i < iov_count; ++i) { - consumed_length += iov[i].iov_len; - } - return QuicConsumedData(consumed_length, fin); + return QuicConsumedData(data.TotalBufferSize(), fin); } -TEST_F(QuicSpdyServerStreamTest, TestFraming) { +INSTANTIATE_TEST_CASE_P(Tests, QuicSpdyServerStreamTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicSpdyServerStreamTest, TestFraming) { EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()). WillRepeatedly(Invoke(ConsumeAllData)); EXPECT_EQ(headers_string_.size(), stream_->ProcessData( headers_string_.c_str(), headers_string_.size())); EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size())); - EXPECT_EQ(11u, stream_->headers().content_length()); - EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri()); - EXPECT_EQ("POST", stream_->headers().request_method()); - EXPECT_EQ(body_, stream_->body()); + EXPECT_EQ(11u, StreamHeaders().content_length()); + EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri()); + EXPECT_EQ("POST", StreamHeaders().request_method()); + EXPECT_EQ(body_, StreamBody()); } -TEST_F(QuicSpdyServerStreamTest, TestFramingOnePacket) { +TEST_P(QuicSpdyServerStreamTest, TestFramingOnePacket) { EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber()). WillRepeatedly(Invoke(ConsumeAllData)); @@ -173,14 +178,13 @@ TEST_F(QuicSpdyServerStreamTest, TestFramingOnePacket) { EXPECT_EQ(message.size(), stream_->ProcessData( message.c_str(), message.size())); - EXPECT_EQ(11u, stream_->headers().content_length()); - EXPECT_EQ("https://www.google.com/", - stream_->headers().request_uri()); - EXPECT_EQ("POST", stream_->headers().request_method()); - EXPECT_EQ(body_, stream_->body()); + EXPECT_EQ(11u, StreamHeaders().content_length()); + EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri()); + EXPECT_EQ("POST", StreamHeaders().request_method()); + EXPECT_EQ(body_, StreamBody()); } -TEST_F(QuicSpdyServerStreamTest, TestFramingExtraData) { +TEST_P(QuicSpdyServerStreamTest, TestFramingExtraData) { string large_body = "hello world!!!!!!"; // We'll automatically write out an error (headers + body) @@ -192,12 +196,12 @@ TEST_F(QuicSpdyServerStreamTest, TestFramingExtraData) { // Content length is still 11. This will register as an error and we won't // accept the bytes. stream_->ProcessData(large_body.c_str(), large_body.size()); - EXPECT_EQ(11u, stream_->headers().content_length()); - EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri()); - EXPECT_EQ("POST", stream_->headers().request_method()); + EXPECT_EQ(11u, StreamHeaders().content_length()); + EXPECT_EQ("https://www.google.com/", StreamHeaders().request_uri()); + EXPECT_EQ("POST", StreamHeaders().request_method()); } -TEST_F(QuicSpdyServerStreamTest, TestSendResponse) { +TEST_P(QuicSpdyServerStreamTest, TestSendResponse) { BalsaHeaders* request_headers = stream_->mutable_headers(); request_headers->SetRequestFirstlineFromStringPieces( "GET", @@ -209,40 +213,34 @@ TEST_F(QuicSpdyServerStreamTest, TestSendResponse) { response_headers_.ReplaceOrAppendHeader("content-length", "3"); InSequence s; - EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1) - .WillOnce(WithArgs<1>(Invoke( - this, &QuicSpdyServerStreamTest::ValidateHeaders))); - - EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1). + EXPECT_CALL(session_, + WritevData(kHeadersStreamId, _, 0, false, _, NULL)); + EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(1). WillOnce(Return(QuicConsumedData(3, true))); - stream_->SendResponse(); + QuicSpdyServerStreamPeer::SendResponse(stream_.get()); EXPECT_TRUE(stream_->read_side_closed()); EXPECT_TRUE(stream_->write_side_closed()); } -TEST_F(QuicSpdyServerStreamTest, TestSendErrorResponse) { +TEST_P(QuicSpdyServerStreamTest, TestSendErrorResponse) { response_headers_.SetResponseFirstlineFromStringPieces( "HTTP/1.1", "500", "Server Error"); response_headers_.ReplaceOrAppendHeader("content-length", "3"); InSequence s; - EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1) - .WillOnce(WithArgs<1>(Invoke( - this, &QuicSpdyServerStreamTest::ValidateHeaders))); - - EXPECT_CALL(session_, WritevData(_, _, 1, _, _, _)).Times(1). + EXPECT_CALL(session_, + WritevData(kHeadersStreamId, _, 0, false, _, NULL)); + EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)).Times(1). WillOnce(Return(QuicConsumedData(3, true))); - stream_->SendErrorResponse(); + QuicSpdyServerStreamPeer::SendErrorResponse(stream_.get()); EXPECT_TRUE(stream_->read_side_closed()); EXPECT_TRUE(stream_->write_side_closed()); } -TEST_F(QuicSpdyServerStreamTest, InvalidHeadersWithFin) { +TEST_P(QuicSpdyServerStreamTest, InvalidHeadersWithFin) { char arr[] = { - 0x00, 0x00, 0x00, 0x05, // .... - 0x00, 0x00, 0x00, 0x05, // .... 0x3a, 0x68, 0x6f, 0x73, // :hos 0x74, 0x00, 0x00, 0x00, // t... 0x00, 0x00, 0x00, 0x00, // .... @@ -265,8 +263,8 @@ TEST_F(QuicSpdyServerStreamTest, InvalidHeadersWithFin) { 0x54, 0x54, 0x50, 0x2f, // TTP/ 0x31, 0x2e, 0x31, // 1.1 }; - QuicStreamFrame frame( - stream_->id(), true, 0, MakeIOVector(StringPiece(arr, arraysize(arr)))); + StringPiece data(arr, arraysize(arr)); + QuicStreamFrame frame(stream_->id(), true, 0, MakeIOVector(data)); // Verify that we don't crash when we get a invalid headers in stream frame. stream_->OnStreamFrame(frame); } diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.cc b/chromium/net/tools/quic/quic_time_wait_list_manager.cc index df9e378e722..9744ff89e53 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager.cc +++ b/chromium/net/tools/quic/quic_time_wait_list_manager.cc @@ -17,6 +17,8 @@ #include "net/quic/quic_framer.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" +#include "net/tools/epoll_server/epoll_server.h" +#include "net/tools/quic/quic_server_session.h" using base::StringPiece; using std::make_pair; @@ -26,23 +28,24 @@ namespace tools { namespace { -// Time period for which the guid should live in time wait state.. +// Time period for which the connection_id should live in time wait state.. const int kTimeWaitSeconds = 5; } // namespace // A very simple alarm that just informs the QuicTimeWaitListManager to clean -// up old guids. This alarm should be unregistered and deleted before the -// QuicTimeWaitListManager is deleted. -class GuidCleanUpAlarm : public EpollAlarm { +// up old connection_ids. This alarm should be unregistered and deleted before +// the QuicTimeWaitListManager is deleted. +class ConnectionIdCleanUpAlarm : public EpollAlarm { public: - explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager) + explicit ConnectionIdCleanUpAlarm( + QuicTimeWaitListManager* time_wait_list_manager) : time_wait_list_manager_(time_wait_list_manager) { } virtual int64 OnAlarm() OVERRIDE { EpollAlarm::OnAlarm(); - time_wait_list_manager_->CleanUpOldGuids(); + time_wait_list_manager_->CleanUpOldConnectionIds(); // Let the time wait manager register the alarm at appropriate time. return 0; } @@ -52,19 +55,9 @@ class GuidCleanUpAlarm : public EpollAlarm { QuicTimeWaitListManager* time_wait_list_manager_; }; -struct QuicTimeWaitListManager::GuidAddTime { - GuidAddTime(QuicGuid guid, const QuicTime& time) - : guid(guid), - time_added(time) { - } - - QuicGuid guid; - QuicTime time_added; -}; - // This class stores pending public reset packets to be sent to clients. // server_address - server address on which a packet what was received for -// a guid in time wait state. +// a connection_id in time wait state. // client_address - address of the client that sent that packet. Needed to send // the public reset packet back to the client. // packet - the pending public reset packet that is to be sent to the client. @@ -93,174 +86,103 @@ class QuicTimeWaitListManager::QueuedPacket { QuicTimeWaitListManager::QuicTimeWaitListManager( QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, EpollServer* epoll_server, const QuicVersionVector& supported_versions) - : framer_(supported_versions, - QuicTime::Zero(), // unused - true), - epoll_server_(epoll_server), + : epoll_server_(epoll_server), kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), - guid_clean_up_alarm_(new GuidCleanUpAlarm(this)), - clock_(epoll_server), + connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)), + clock_(epoll_server_), writer_(writer), - is_write_blocked_(false) { - framer_.set_visitor(this); - SetGuidCleanUpAlarm(); + visitor_(visitor) { + SetConnectionIdCleanUpAlarm(); } QuicTimeWaitListManager::~QuicTimeWaitListManager() { - guid_clean_up_alarm_->UnregisterIfRegistered(); - STLDeleteElements(&time_ordered_guid_list_); + connection_id_clean_up_alarm_->UnregisterIfRegistered(); STLDeleteElements(&pending_packets_queue_); - for (GuidMapIterator it = guid_map_.begin(); it != guid_map_.end(); ++it) { + for (ConnectionIdMap::iterator it = connection_id_map_.begin(); + it != connection_id_map_.end(); + ++it) { delete it->second.close_packet; } } -void QuicTimeWaitListManager::AddGuidToTimeWait( - QuicGuid guid, +void QuicTimeWaitListManager::AddConnectionIdToTimeWait( + QuicConnectionId connection_id, QuicVersion version, QuicEncryptedPacket* close_packet) { - DCHECK(!IsGuidInTimeWait(guid)); - // Initialize the guid with 0 packets received. - GuidData data(0, version, close_packet); - guid_map_.insert(make_pair(guid, data)); - time_ordered_guid_list_.push_back(new GuidAddTime(guid, - clock_.ApproximateNow())); -} - -bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const { - return guid_map_.find(guid) != guid_map_.end(); + DVLOG(1) << "Adding " << connection_id << " to the time wait list."; + int num_packets = 0; + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + if (it != connection_id_map_.end()) { // Replace record if it is reinserted. + num_packets = it->second.num_packets; + delete it->second.close_packet; + connection_id_map_.erase(it); + } + ConnectionIdData data(num_packets, version, clock_.ApproximateNow(), + close_packet); + connection_id_map_.insert(make_pair(connection_id, data)); } -void QuicTimeWaitListManager::ProcessPacket( - const IPEndPoint& server_address, - const IPEndPoint& client_address, - QuicGuid guid, - const QuicEncryptedPacket& packet) { - DCHECK(IsGuidInTimeWait(guid)); - server_address_ = server_address; - client_address_ = client_address; - - // Set the framer to the appropriate version for this GUID, before processing. - QuicVersion version = GetQuicVersionFromGuid(guid); - framer_.set_version(version); - - framer_.ProcessPacket(packet); +bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( + QuicConnectionId connection_id) const { + return ContainsKey(connection_id_map_, connection_id); } -QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) { - GuidMapIterator it = guid_map_.find(guid); - DCHECK(it != guid_map_.end()); +QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId( + QuicConnectionId connection_id) { + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); return (it->second).version; } -bool QuicTimeWaitListManager::OnCanWrite() { - is_write_blocked_ = false; - while (!is_write_blocked_ && !pending_packets_queue_.empty()) { +void QuicTimeWaitListManager::OnCanWrite() { + while (!pending_packets_queue_.empty()) { QueuedPacket* queued_packet = pending_packets_queue_.front(); - WriteToWire(queued_packet); - if (!is_write_blocked_) { - pending_packets_queue_.pop_front(); - delete queued_packet; + if (!WriteToWire(queued_packet)) { + return; } + pending_packets_queue_.pop_front(); + delete queued_packet; } - - return !is_write_blocked_; } -void QuicTimeWaitListManager::OnError(QuicFramer* framer) { - DLOG(INFO) << QuicUtils::ErrorToString(framer->error()); -} - -bool QuicTimeWaitListManager::OnProtocolVersionMismatch( - QuicVersion received_version) { - // Drop such packets whose version don't match. - return false; -} - -bool QuicTimeWaitListManager::OnUnauthenticatedHeader( - const QuicPacketHeader& header) { +void QuicTimeWaitListManager::ProcessPacket( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, + const QuicEncryptedPacket& /*packet*/) { + DCHECK(IsConnectionIdInTimeWait(connection_id)); + DVLOG(1) << "Processing " << connection_id << " in time wait state."; // TODO(satyamshekhar): Think about handling packets from different client // addresses. - GuidMapIterator it = guid_map_.find(header.public_header.guid); - DCHECK(it != guid_map_.end()); + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); // Increment the received packet count. ++((it->second).num_packets); if (!ShouldSendResponse((it->second).num_packets)) { - return false; + return; } if (it->second.close_packet) { QueuedPacket* queued_packet = - new QueuedPacket(server_address_, - client_address_, + new QueuedPacket(server_address, + client_address, it->second.close_packet->Clone()); // Takes ownership of the packet. SendOrQueuePacket(queued_packet); } else { - // We don't need the packet anymore. Just tell the client what sequence - // number we rejected. - SendPublicReset(server_address_, - client_address_, - header.public_header.guid, - header.packet_sequence_number); + SendPublicReset(server_address, + client_address, + connection_id, + sequence_number); } - // Never process the body of the packet in time wait state. - return false; -} - -bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) { - DCHECK(false); - return false; -} - -void QuicTimeWaitListManager::OnRevivedPacket() { - DCHECK(false); -} - -void QuicTimeWaitListManager::OnFecProtectedPayload(StringPiece /*payload*/) { - DCHECK(false); -} - -bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& /*frame*/) { - DCHECK(false); - return false; -} - -bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& /*frame*/) { - DCHECK(false); - return false; -} - -bool QuicTimeWaitListManager::OnCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& /*frame*/) { - DCHECK(false); - return false; } -bool QuicTimeWaitListManager::OnRstStreamFrame( - const QuicRstStreamFrame& /*frame*/) { - DCHECK(false); - return false; -} - -bool QuicTimeWaitListManager::OnConnectionCloseFrame( - const QuicConnectionCloseFrame & /*frame*/) { - DCHECK(false); - return false; -} - -bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) { - DCHECK(false); - return false; -} - -void QuicTimeWaitListManager::OnFecData(const QuicFecData& /*fec*/) { - DCHECK(false); -} - -// Returns true if the number of packets received for this guid is a power of 2 -// to throttle the number of public reset packets we send to a client. +// Returns true if the number of packets received for this connection_id is a +// power of 2 to throttle the number of public reset packets we send to a +// client. bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { return (received_packet_count & (received_packet_count - 1)) == 0; } @@ -268,95 +190,100 @@ bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { void QuicTimeWaitListManager::SendPublicReset( const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, + QuicConnectionId connection_id, QuicPacketSequenceNumber rejected_sequence_number) { QuicPublicResetPacket packet; - packet.public_header.guid = guid; + packet.public_header.connection_id = connection_id; packet.public_header.reset_flag = true; packet.public_header.version_flag = false; packet.rejected_sequence_number = rejected_sequence_number; - // TODO(satyamshekhar): generate a valid nonce for this guid. + // TODO(satyamshekhar): generate a valid nonce for this connection_id. packet.nonce_proof = 1010101; + packet.client_address = client_address; QueuedPacket* queued_packet = new QueuedPacket( server_address, client_address, - QuicFramer::BuildPublicResetPacket(packet)); + BuildPublicReset(packet)); // Takes ownership of the packet. SendOrQueuePacket(queued_packet); } +QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset( + const QuicPublicResetPacket& packet) { + return QuicFramer::BuildPublicResetPacket(packet); +} + // Either sends the packet and deletes it or makes pending queue the // owner of the packet. void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) { - if (!is_write_blocked_) { - // TODO(satyamshekhar): Handle packets that fail due to error other than - // EAGAIN or EWOULDBLOCK. - WriteToWire(packet); - } - - if (is_write_blocked_) { + if (WriteToWire(packet)) { + delete packet; + } else { // pending_packets_queue takes the ownership of the queued packet. pending_packets_queue_.push_back(packet); - } else { - delete packet; } } -void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { - DCHECK(!is_write_blocked_); +bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(this); + return false; + } WriteResult result = writer_->WritePacket( queued_packet->packet()->data(), queued_packet->packet()->length(), queued_packet->server_address().address(), - queued_packet->client_address(), - this); - + queued_packet->client_address()); if (result.status == WRITE_STATUS_BLOCKED) { - is_write_blocked_ = true; + // If blocked and unbuffered, return false to retry sending. + DCHECK(writer_->IsWriteBlocked()); + visitor_->OnWriteBlocked(this); + return writer_->IsWriteBlockedDataBuffered(); } else if (result.status == WRITE_STATUS_ERROR) { LOG(WARNING) << "Received unknown error while sending reset packet to " << queued_packet->client_address().ToString() << ": " << strerror(result.error_code); } + return true; } -void QuicTimeWaitListManager::SetGuidCleanUpAlarm() { - guid_clean_up_alarm_->UnregisterIfRegistered(); +void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { + connection_id_clean_up_alarm_->UnregisterIfRegistered(); int64 next_alarm_interval; - if (!time_ordered_guid_list_.empty()) { - GuidAddTime* oldest_guid = time_ordered_guid_list_.front(); + if (!connection_id_map_.empty()) { + QuicTime oldest_connection_id = + connection_id_map_.begin()->second.time_added; QuicTime now = clock_.ApproximateNow(); - DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_); - next_alarm_interval = oldest_guid->time_added - .Add(kTimeWaitPeriod_) - .Subtract(now) - .ToMicroseconds(); + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { + next_alarm_interval = oldest_connection_id.Add(kTimeWaitPeriod_) + .Subtract(now) + .ToMicroseconds(); + } else { + LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod"; + next_alarm_interval = 0; + } } else { - // No guids added so none will expire before kTimeWaitPeriod_. + // No connection_ids added so none will expire before kTimeWaitPeriod_. next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds(); } - epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval, - guid_clean_up_alarm_.get()); + epoll_server_->RegisterAlarmApproximateDelta( + next_alarm_interval, connection_id_clean_up_alarm_.get()); } -void QuicTimeWaitListManager::CleanUpOldGuids() { +void QuicTimeWaitListManager::CleanUpOldConnectionIds() { QuicTime now = clock_.ApproximateNow(); - while (time_ordered_guid_list_.size() > 0) { - DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size()); - GuidAddTime* oldest_guid = time_ordered_guid_list_.front(); - if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) { + while (!connection_id_map_.empty()) { + ConnectionIdMap::iterator it = connection_id_map_.begin(); + QuicTime oldest_connection_id = it->second.time_added; + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { break; } - // This guid has lived its age, retire it now. - GuidMapIterator it = guid_map_.find(oldest_guid->guid); - DCHECK(it != guid_map_.end()); + // This connection_id has lived its age, retire it now. delete it->second.close_packet; - guid_map_.erase(oldest_guid->guid); - time_ordered_guid_list_.pop_front(); - delete oldest_guid; + connection_id_map_.erase(it); } - SetGuidCleanUpAlarm(); + SetConnectionIdCleanUpAlarm(); } } // namespace tools diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.h b/chromium/net/tools/quic/quic_time_wait_list_manager.h index bb24f144a02..426f09f0872 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager.h +++ b/chromium/net/tools/quic/quic_time_wait_list_manager.h @@ -2,195 +2,175 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Handles packets for guids in time wait state by discarding the packet and -// sending the clients a public reset packet with exponential backoff. +// Handles packets for connection_ids in time wait state by discarding the +// packet and sending the clients a public reset packet with exponential +// backoff. #ifndef NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_ #define NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_ #include <deque> +#include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/strings/string_piece.h" +#include "net/base/linked_hash_map.h" #include "net/quic/quic_blocked_writer_interface.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_writer.h" #include "net/quic/quic_protocol.h" -#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_epoll_clock.h" namespace net { + +class EpollServer; + namespace tools { -class GuidCleanUpAlarm; +class ConnectionIdCleanUpAlarm; +class QuicServerSessionVisitor; namespace test { class QuicTimeWaitListManagerPeer; } // namespace test -// Maintains a list of all guids that have been recently closed. A guid lives in -// this state for kTimeWaitPeriod. All packets received for guids in this state -// are handed over to the QuicTimeWaitListManager by the QuicDispatcher. -// Decides whether to send a public reset packet, a copy of the previously sent -// connection close packet, or nothing to the client which sent a packet -// with the guid in time wait state. After the guid expires its time wait -// period, a new connection/session will be created if a packet is received -// for this guid. -class QuicTimeWaitListManager : public QuicBlockedWriterInterface, - public QuicFramerVisitorInterface { +// Maintains a list of all connection_ids that have been recently closed. A +// connection_id lives in this state for kTimeWaitPeriod. All packets received +// for connection_ids in this state are handed over to the +// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a +// public reset packet, a copy of the previously sent connection close packet, +// or nothing to the client which sent a packet with the connection_id in time +// wait state. After the connection_id expires its time wait period, a new +// connection/session will be created if a packet is received for this +// connection_id. +class QuicTimeWaitListManager : public QuicBlockedWriterInterface { public: // writer - the entity that writes to the socket. (Owned by the dispatcher) + // visitor - the entity that manages blocked writers. (The dispatcher) // epoll_server - used to run clean up alarms. (Owned by the dispatcher) QuicTimeWaitListManager(QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, EpollServer* epoll_server, const QuicVersionVector& supported_versions); virtual ~QuicTimeWaitListManager(); - // Adds the given guid to time wait state for kTimeWaitPeriod. Henceforth, - // any packet bearing this guid should not be processed while the guid remains - // in this list. If a non-NULL |close_packet| is provided, it is sent again - // when packets are received for added guids. If NULL, a public reset packet - // is sent with the specified |version|. DCHECKs that guid is not already on - // the list. - void AddGuidToTimeWait(QuicGuid guid, - QuicVersion version, - QuicEncryptedPacket* close_packet); // Owned. - - // Returns true if the guid is in time wait state, false otherwise. Packets - // received for this guid should not lead to creation of new QuicSessions. - bool IsGuidInTimeWait(QuicGuid guid) const; - - // Called when a packet is received for a guid that is in time wait state. - // Sends a public reset packet to the client which sent this guid. Sending - // of the public reset packet is throttled by using exponential back off. - // DCHECKs for the guid to be in time wait state. - // virtual to override in tests. + // Adds the given connection_id to time wait state for kTimeWaitPeriod. + // Henceforth, any packet bearing this connection_id should not be processed + // while the connection_id remains in this list. If a non-NULL |close_packet| + // is provided, it is sent again when packets are received for added + // connection_ids. If NULL, a public reset packet is sent with the specified + // |version|. DCHECKs that connection_id is not already on the list. + void AddConnectionIdToTimeWait(QuicConnectionId connection_id, + QuicVersion version, + QuicEncryptedPacket* close_packet); // Owned. + + // Returns true if the connection_id is in time wait state, false otherwise. + // Packets received for this connection_id should not lead to creation of new + // QuicSessions. + bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const; + + // Called when a packet is received for a connection_id that is in time wait + // state. Sends a public reset packet to the client which sent this + // connection_id. Sending of the public reset packet is throttled by using + // exponential back off. DCHECKs for the connection_id to be in time wait + // state. virtual to override in tests. virtual void ProcessPacket(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, const QuicEncryptedPacket& packet); // Called by the dispatcher when the underlying socket becomes writable again, // since we might need to send pending public reset packets which we didn't // send because the underlying socket was write blocked. - virtual bool OnCanWrite() OVERRIDE; - - // Used to delete guid entries that have outlived their time wait period. - void CleanUpOldGuids(); - - // QuicFramerVisitorInterface - virtual void OnError(QuicFramer* framer) OVERRIDE; - virtual bool OnProtocolVersionMismatch(QuicVersion received_version) OVERRIDE; - virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE; - virtual void OnPacket() OVERRIDE {} - virtual void OnPublicResetPacket( - const QuicPublicResetPacket& /*packet*/) OVERRIDE {} - virtual void OnVersionNegotiationPacket( - const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {} - - virtual void OnPacketComplete() OVERRIDE {} - // The following methods should never get called because we always return - // false from OnUnauthenticatedHeader(). We never process the encrypted bytes. - virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; - virtual void OnRevivedPacket() OVERRIDE; - virtual void OnFecProtectedPayload(base::StringPiece /*payload*/) OVERRIDE; - virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE; - virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE; - virtual bool OnCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE; - virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE; - virtual bool OnConnectionCloseFrame( - const QuicConnectionCloseFrame & /*frame*/) OVERRIDE; - virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE; - virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE; + virtual void OnCanWrite() OVERRIDE; + + // Used to delete connection_id entries that have outlived their time wait + // period. + void CleanUpOldConnectionIds(); + + // Given a ConnectionId that exists in the time wait list, returns the + // QuicVersion associated with it. + QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id); + + protected: + virtual QuicEncryptedPacket* BuildPublicReset( + const QuicPublicResetPacket& packet); private: friend class test::QuicTimeWaitListManagerPeer; - // Stores the guid and the time it was added to time wait state. - struct GuidAddTime; // Internal structure to store pending public reset packets. class QueuedPacket; - // Decides if a packet should be sent for this guid based on the number of - // received packets. + // Decides if a packet should be sent for this connection_id based on the + // number of received packets. bool ShouldSendResponse(int received_packet_count); - // Given a GUID that exists in the time wait list, returns the QuicVersion - // associated with it. Used internally to set the framer version before - // writing the public reset packet. - QuicVersion GetQuicVersionFromGuid(QuicGuid guid); - // Creates a public reset packet and sends it or queues it to be sent later. void SendPublicReset(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, + QuicConnectionId connection_id, QuicPacketSequenceNumber rejected_sequence_number); // Either sends the packet and deletes it or makes pending_packets_queue_ the // owner of the packet. void SendOrQueuePacket(QueuedPacket* packet); - // Should only be called when write_blocked_ == false. We only care if the - // writing was unsuccessful because the socket got blocked, which can be - // tested using write_blocked_ == true. In case of all other errors we drop - // the packet. Hence, we return void. - void WriteToWire(QueuedPacket* packet); + // Sends the packet out. Returns true if the packet was successfully consumed. + // If the writer got blocked and did not buffer the packet, we'll need to keep + // the packet and retry sending. In case of all other errors we drop the + // packet. + bool WriteToWire(QueuedPacket* packet); // Register the alarm with the epoll server to wake up at appropriate time. - void SetGuidCleanUpAlarm(); - - // A map from a recently closed guid to the number of packets received after - // the termination of the connection bound to the guid. - struct GuidData { - GuidData(int num_packets_, - QuicVersion version_, - QuicEncryptedPacket* close_packet) + void SetConnectionIdCleanUpAlarm(); + + // A map from a recently closed connection_id to the number of packets + // received after the termination of the connection bound to the + // connection_id. + struct ConnectionIdData { + ConnectionIdData(int num_packets_, + QuicVersion version_, + QuicTime time_added_, + QuicEncryptedPacket* close_packet) : num_packets(num_packets_), version(version_), + time_added(time_added_), close_packet(close_packet) {} int num_packets; QuicVersion version; + QuicTime time_added; QuicEncryptedPacket* close_packet; }; - base::hash_map<QuicGuid, GuidData> guid_map_; - typedef base::hash_map<QuicGuid, GuidData>::iterator GuidMapIterator; - // Maintains a list of GuidAddTime elements which it owns, in the - // order they should be deleted. - std::deque<GuidAddTime*> time_ordered_guid_list_; + // linked_hash_map allows lookup by ConnectionId and traversal in add order. + typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap; + ConnectionIdMap connection_id_map_; // Pending public reset packets that need to be sent out to the client // when we are given a chance to write by the dispatcher. std::deque<QueuedPacket*> pending_packets_queue_; - // Used to parse incoming packets. - QuicFramer framer_; - - // Server and client address of the last packet processed. - IPEndPoint server_address_; - IPEndPoint client_address_; - - // Used to schedule alarms to delete old guids which have been in the list for - // too long. Owned by the dispatcher. + // Used to schedule alarms to delete old connection_ids which have been in the + // list for too long. EpollServer* epoll_server_; - // Time period for which guids should remain in time wait state. + // Time period for which connection_ids should remain in time wait state. const QuicTime::Delta kTimeWaitPeriod_; - // Alarm registered with the epoll server to clean up guids that have out - // lived their duration in time wait state. - scoped_ptr<GuidCleanUpAlarm> guid_clean_up_alarm_; + // Alarm registered with the epoll server to clean up connection_ids that have + // out lived their duration in time wait state. + scoped_ptr<ConnectionIdCleanUpAlarm> connection_id_clean_up_alarm_; // Clock to efficiently measure approximate time from the epoll server. QuicEpollClock clock_; - // Interface that writes given buffer to the socket. Owned by the dispatcher. + // Interface that writes given buffer to the socket. QuicPacketWriter* writer_; - // True if the underlying udp socket is write blocked, i.e will return EAGAIN - // on sendmsg. - bool is_write_blocked_; + // Interface that manages blocked writers. + QuicServerSessionVisitor* visitor_; DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager); }; diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc index 22fd8ef4d06..67abd5928f7 100644 --- a/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc +++ b/chromium/net/tools/quic/quic_time_wait_list_manager_test.cc @@ -14,37 +14,56 @@ #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_writer.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/test_tools/mock_epoll_server.h" #include "net/tools/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using net::test::FramerVisitorCapturingPublicReset; -using testing::_; +using net::test::BuildUnsizedDataPacket; +using net::test::NoOpFramerVisitor; +using net::test::QuicVersionMax; +using net::test::QuicVersionMin; using testing::Args; +using testing::Assign; +using testing::DoAll; using testing::Matcher; using testing::MatcherInterface; +using testing::NiceMock; using testing::Return; +using testing::ReturnPointee; using testing::SetArgPointee; +using testing::StrictMock; using testing::Truly; +using testing::_; namespace net { namespace tools { namespace test { -class QuicTimeWaitListManagerPeer { +class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { public: - static QuicVersion version(QuicTimeWaitListManager* manager) { - return manager->framer_.version(); + FramerVisitorCapturingPublicReset() {} + virtual ~FramerVisitorCapturingPublicReset() OVERRIDE {} + + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& public_reset) OVERRIDE { + public_reset_packet_ = public_reset; } - static bool is_write_blocked(QuicTimeWaitListManager* manager) { - return manager->is_write_blocked_; + const QuicPublicResetPacket public_reset_packet() { + return public_reset_packet_; } + private: + QuicPublicResetPacket public_reset_packet_; +}; + +class QuicTimeWaitListManagerPeer { + public: static bool ShouldSendResponse(QuicTimeWaitListManager* manager, - int received_packet_count) { + int received_packet_count) { return manager->ShouldSendResponse(received_packet_count); } @@ -52,67 +71,72 @@ class QuicTimeWaitListManagerPeer { return manager->kTimeWaitPeriod_; } - static QuicVersion GetQuicVersionFromGuid(QuicTimeWaitListManager* manager, - QuicGuid guid) { - return manager->GetQuicVersionFromGuid(guid); + static QuicVersion GetQuicVersionFromConnectionId( + QuicTimeWaitListManager* manager, + QuicConnectionId connection_id) { + return manager->GetQuicVersionFromConnectionId(connection_id); } }; namespace { -class TestTimeWaitListManager : public QuicTimeWaitListManager { - public: - TestTimeWaitListManager(QuicPacketWriter* writer, - EpollServer* epoll_server) - : QuicTimeWaitListManager(writer, epoll_server, QuicSupportedVersions()) { - } -}; - class MockFakeTimeEpollServer : public FakeTimeEpollServer { public: MOCK_METHOD2(RegisterAlarm, void(int64 timeout_in_us, EpollAlarmCallbackInterface* alarm)); }; -class QuicTimeWaitListManagerTest : public testing::Test { +class QuicTimeWaitListManagerTest : public ::testing::Test { protected: QuicTimeWaitListManagerTest() - : time_wait_list_manager_( - &writer_, &epoll_server_, QuicSupportedVersions()), + : time_wait_list_manager_(&writer_, &visitor_, + &epoll_server_, QuicSupportedVersions()), framer_(QuicSupportedVersions(), QuicTime::Zero(), true), - guid_(45) { - } + connection_id_(45), + client_address_(net::test::TestPeerIPAddress(), kTestPort), + writer_is_blocked_(false) {} - virtual ~QuicTimeWaitListManagerTest() {} + virtual ~QuicTimeWaitListManagerTest() OVERRIDE {} - void AddGuid(QuicGuid guid) { - AddGuid(guid, net::test::QuicVersionMax(), NULL); + virtual void SetUp() OVERRIDE { + EXPECT_CALL(writer_, IsWriteBlocked()) + .WillRepeatedly(ReturnPointee(&writer_is_blocked_)); + EXPECT_CALL(writer_, IsWriteBlockedDataBuffered()) + .WillRepeatedly(Return(false)); } - void AddGuid(QuicGuid guid, - QuicVersion version, - QuicEncryptedPacket* packet) { - time_wait_list_manager_.AddGuidToTimeWait(guid, version, packet); + void AddConnectionId(QuicConnectionId connection_id) { + AddConnectionId(connection_id, QuicVersionMax(), NULL); } - bool IsGuidInTimeWait(QuicGuid guid) { - return time_wait_list_manager_.IsGuidInTimeWait(guid); + void AddConnectionId(QuicConnectionId connection_id, + QuicVersion version, + QuicEncryptedPacket* packet) { + time_wait_list_manager_.AddConnectionIdToTimeWait( + connection_id, version, packet); } - void ProcessPacket(QuicGuid guid, const QuicEncryptedPacket& packet) { + bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) { + return time_wait_list_manager_.IsConnectionIdInTimeWait(connection_id); + } + + void ProcessPacket(QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number) { + QuicEncryptedPacket packet(NULL, 0); time_wait_list_manager_.ProcessPacket(server_address_, client_address_, - guid, + connection_id, + sequence_number, packet); } QuicEncryptedPacket* ConstructEncryptedPacket( EncryptionLevel level, - QuicGuid guid, + QuicConnectionId connection_id, QuicPacketSequenceNumber sequence_number) { QuicPacketHeader header; - header.public_header.guid = guid; - header.public_header.guid_length = PACKET_8BYTE_GUID; + header.public_header.connection_id = connection_id; + header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID; header.public_header.version_flag = false; header.public_header.reset_flag = false; header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; @@ -127,7 +151,7 @@ class QuicTimeWaitListManagerTest : public testing::Test { QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + BuildUnsizedDataPacket(&framer_, header, frames).packet); EXPECT_TRUE(packet != NULL); QuicEncryptedPacket* encrypted = framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, @@ -136,26 +160,28 @@ class QuicTimeWaitListManagerTest : public testing::Test { return encrypted; } - MockFakeTimeEpollServer epoll_server_; - MockPacketWriter writer_; + NiceMock<MockFakeTimeEpollServer> epoll_server_; + StrictMock<MockPacketWriter> writer_; + StrictMock<MockQuicServerSessionVisitor> visitor_; QuicTimeWaitListManager time_wait_list_manager_; QuicFramer framer_; - QuicGuid guid_; + QuicConnectionId connection_id_; IPEndPoint server_address_; IPEndPoint client_address_; + bool writer_is_blocked_; }; class ValidatePublicResetPacketPredicate : public MatcherInterface<const std::tr1::tuple<const char*, int> > { public: - explicit ValidatePublicResetPacketPredicate(QuicGuid guid, + explicit ValidatePublicResetPacketPredicate(QuicConnectionId connection_id, QuicPacketSequenceNumber number) - : guid_(guid), sequence_number_(number) { + : connection_id_(connection_id), sequence_number_(number) { } virtual bool MatchAndExplain( const std::tr1::tuple<const char*, int> packet_buffer, - testing::MatchResultListener* /* listener */) const { + testing::MatchResultListener* /* listener */) const OVERRIDE { FramerVisitorCapturingPublicReset visitor; QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), @@ -165,119 +191,73 @@ class ValidatePublicResetPacketPredicate std::tr1::get<1>(packet_buffer)); framer.ProcessPacket(encrypted); QuicPublicResetPacket packet = visitor.public_reset_packet(); - return guid_ == packet.public_header.guid && + return connection_id_ == packet.public_header.connection_id && packet.public_header.reset_flag && !packet.public_header.version_flag && - sequence_number_ == packet.rejected_sequence_number; + sequence_number_ == packet.rejected_sequence_number && + net::test::TestPeerIPAddress() == packet.client_address.address() && + kTestPort == packet.client_address.port(); } - virtual void DescribeTo(::std::ostream* os) const { } + virtual void DescribeTo(::std::ostream* os) const OVERRIDE {} - virtual void DescribeNegationTo(::std::ostream* os) const { } + virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE {} private: - QuicGuid guid_; + QuicConnectionId connection_id_; QuicPacketSequenceNumber sequence_number_; }; Matcher<const std::tr1::tuple<const char*, int> > PublicResetPacketEq( - QuicGuid guid, + QuicConnectionId connection_id, QuicPacketSequenceNumber sequence_number) { - return MakeMatcher(new ValidatePublicResetPacketPredicate(guid, + return MakeMatcher(new ValidatePublicResetPacketPredicate(connection_id, sequence_number)); } -TEST_F(QuicTimeWaitListManagerTest, CheckGuidInTimeWait) { - EXPECT_FALSE(IsGuidInTimeWait(guid_)); - AddGuid(guid_); - EXPECT_TRUE(IsGuidInTimeWait(guid_)); +TEST_F(QuicTimeWaitListManagerTest, CheckConnectionIdInTimeWait) { + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_)); + AddConnectionId(connection_id_); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); } TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) { size_t kConnectionCloseLength = 100; - AddGuid(guid_, - net::test::QuicVersionMax(), - new QuicEncryptedPacket( - new char[kConnectionCloseLength], kConnectionCloseLength, true)); + AddConnectionId( + connection_id_, + QuicVersionMax(), + new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true)); const int kRandomSequenceNumber = 1; - scoped_ptr<QuicEncryptedPacket> packet( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber)); EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, server_address_.address(), - client_address_, - &time_wait_list_manager_)) + client_address_)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); - ProcessPacket(guid_, *packet); + ProcessPacket(connection_id_, kRandomSequenceNumber); } TEST_F(QuicTimeWaitListManagerTest, SendPublicReset) { - AddGuid(guid_); - const int kRandomSequenceNumber = 1; - scoped_ptr<QuicEncryptedPacket> packet( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber)); - EXPECT_CALL(writer_, WritePacket(_, _, - server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(guid_, - kRandomSequenceNumber))) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); - - ProcessPacket(guid_, *packet); -} - -TEST_F(QuicTimeWaitListManagerTest, SendPublicResetUndecryptable) { - AddGuid(guid_); + AddConnectionId(connection_id_); const int kRandomSequenceNumber = 1; - scoped_ptr<QuicEncryptedPacket> packet( - ConstructEncryptedPacket( - ENCRYPTION_INITIAL, guid_, kRandomSequenceNumber)); EXPECT_CALL(writer_, WritePacket(_, _, server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(guid_, + client_address_)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id_, kRandomSequenceNumber))) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); - - ProcessPacket(guid_, *packet); -} - -TEST_F(QuicTimeWaitListManagerTest, DropInvalidPacket) { - AddGuid(guid_); - const char buffer[] = "invalid"; - QuicEncryptedPacket packet(buffer, arraysize(buffer)); - // Will get called for a valid packet since received packet count = 1 (2 ^ 0). - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0); - ProcessPacket(guid_, packet); -} + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); -TEST_F(QuicTimeWaitListManagerTest, DropPublicResetPacket) { - AddGuid(guid_); - QuicPublicResetPacket packet; - packet.public_header.guid = guid_; - packet.public_header.version_flag = false; - packet.public_header.reset_flag = true; - packet.rejected_sequence_number = 239191; - packet.nonce_proof = 1010101; - scoped_ptr<QuicEncryptedPacket> public_reset_packet( - QuicFramer::BuildPublicResetPacket(packet)); - // Will get called for a data packet since received packet count = 1 (2 ^ 0). - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0); - ProcessPacket(guid_, *public_reset_packet); + ProcessPacket(connection_id_, kRandomSequenceNumber); } TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) { - AddGuid(guid_); + AddConnectionId(connection_id_); for (int sequence_number = 1; sequence_number < 101; ++sequence_number) { - scoped_ptr<QuicEncryptedPacket> packet( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, sequence_number)); if ((sequence_number & (sequence_number - 1)) == 0) { - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)) + EXPECT_CALL(writer_, WritePacket(_, _, _, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); } - ProcessPacket(guid_, *packet); + ProcessPacket(connection_id_, sequence_number); // Send public reset with exponential back off. if ((sequence_number & (sequence_number - 1)) == 0) { EXPECT_TRUE(QuicTimeWaitListManagerPeer::ShouldSendResponse( @@ -289,157 +269,192 @@ TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) { } } -TEST_F(QuicTimeWaitListManagerTest, CleanUpOldGuids) { - const int kGuidCount = 100; - const int kOldGuidCount = 31; +TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) { + const int kConnectionIdCount = 100; + const int kOldConnectionIdCount = 31; - // Add guids such that their expiry time is kTimeWaitPeriod_. + // Add connection_ids such that their expiry time is kTimeWaitPeriod_. epoll_server_.set_now_in_usec(0); - for (int guid = 1; guid <= kOldGuidCount; ++guid) { - AddGuid(guid); + for (int connection_id = 1; + connection_id <= kOldConnectionIdCount; + ++connection_id) { + AddConnectionId(connection_id); } - // Add remaining guids such that their add time is 2 * kTimeWaitPeriod. + // Add remaining connection_ids such that their add time is + // 2 * kTimeWaitPeriod. const QuicTime::Delta time_wait_period = QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds()); - for (int guid = kOldGuidCount + 1; guid <= kGuidCount; ++guid) { - AddGuid(guid); + for (int connection_id = kOldConnectionIdCount + 1; + connection_id <= kConnectionIdCount; + ++connection_id) { + AddConnectionId(connection_id); } QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); // Now set the current time as time_wait_period + offset usecs. epoll_server_.set_now_in_usec(time_wait_period.Add(offset).ToMicroseconds()); - // After all the old guids are cleaned up, check the next alarm interval. + // After all the old connection_ids are cleaned up, check the next alarm + // interval. int64 next_alarm_time = epoll_server_.ApproximateNowInUsec() + time_wait_period.Subtract(offset).ToMicroseconds(); EXPECT_CALL(epoll_server_, RegisterAlarm(next_alarm_time, _)); - time_wait_list_manager_.CleanUpOldGuids(); - for (int guid = 1; guid <= kGuidCount; ++guid) { - EXPECT_EQ(guid > kOldGuidCount, IsGuidInTimeWait(guid)) - << "kOldGuidCount: " << kOldGuidCount - << " guid: " << guid; + time_wait_list_manager_.CleanUpOldConnectionIds(); + for (int connection_id = 1; + connection_id <= kConnectionIdCount; + ++connection_id) { + EXPECT_EQ(connection_id > kOldConnectionIdCount, + IsConnectionIdInTimeWait(connection_id)) + << "kOldConnectionIdCount: " << kOldConnectionIdCount + << " connection_id: " << connection_id; } } TEST_F(QuicTimeWaitListManagerTest, SendQueuedPackets) { - QuicGuid guid = 1; - AddGuid(guid); + QuicConnectionId connection_id = 1; + AddConnectionId(connection_id); QuicPacketSequenceNumber sequence_number = 234; - scoped_ptr<QuicEncryptedPacket> packet( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid, sequence_number)); + scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + ENCRYPTION_NONE, connection_id, sequence_number)); // Let first write through. EXPECT_CALL(writer_, WritePacket(_, _, server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(guid, + client_address_)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id, sequence_number))) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); - ProcessPacket(guid, *packet); - EXPECT_FALSE( - QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_)); + ProcessPacket(connection_id, sequence_number); // write block for the next packet. EXPECT_CALL(writer_, WritePacket(_, _, server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(guid, + client_address_)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id, sequence_number))) - .WillOnce(Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN))); - ProcessPacket(guid, *packet); + .WillOnce(DoAll( + Assign(&writer_is_blocked_, true), + Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN)))); + EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_)); + ProcessPacket(connection_id, sequence_number); // 3rd packet. No public reset should be sent; - ProcessPacket(guid, *packet); - EXPECT_TRUE( - QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_)); + ProcessPacket(connection_id, sequence_number); - // write packet should not be called since already write blocked but the + // write packet should not be called since we are write blocked but the // should be queued. - QuicGuid other_guid = 2; - AddGuid(other_guid); + QuicConnectionId other_connection_id = 2; + AddConnectionId(other_connection_id); QuicPacketSequenceNumber other_sequence_number = 23423; scoped_ptr<QuicEncryptedPacket> other_packet( ConstructEncryptedPacket( - ENCRYPTION_NONE, other_guid, other_sequence_number)); - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)) + ENCRYPTION_NONE, other_connection_id, other_sequence_number)); + EXPECT_CALL(writer_, WritePacket(_, _, _, _)) .Times(0); - ProcessPacket(other_guid, *other_packet); + EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_)); + ProcessPacket(other_connection_id, other_sequence_number); // Now expect all the write blocked public reset packets to be sent again. + writer_is_blocked_ = false; EXPECT_CALL(writer_, WritePacket(_, _, server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(guid, + client_address_)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id, sequence_number))) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); EXPECT_CALL(writer_, WritePacket(_, _, server_address_.address(), - client_address_, - &time_wait_list_manager_)) - .With(Args<0, 1>(PublicResetPacketEq(other_guid, + client_address_)) + .With(Args<0, 1>(PublicResetPacketEq(other_connection_id, other_sequence_number))) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, other_packet->length()))); time_wait_list_manager_.OnCanWrite(); - EXPECT_FALSE( - QuicTimeWaitListManagerPeer::is_write_blocked(&time_wait_list_manager_)); } -TEST_F(QuicTimeWaitListManagerTest, MakeSureFramerUsesCorrectVersion) { +TEST_F(QuicTimeWaitListManagerTest, GetQuicVersionFromMap) { + const int kConnectionId1 = 123; + const int kConnectionId2 = 456; + const int kConnectionId3 = 789; + + AddConnectionId(kConnectionId1, QuicVersionMin(), NULL); + AddConnectionId(kConnectionId2, QuicVersionMax(), NULL); + AddConnectionId(kConnectionId3, QuicVersionMax(), NULL); + + EXPECT_EQ(QuicVersionMin(), + QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId( + &time_wait_list_manager_, kConnectionId1)); + EXPECT_EQ(QuicVersionMax(), + QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId( + &time_wait_list_manager_, kConnectionId2)); + EXPECT_EQ(QuicVersionMax(), + QuicTimeWaitListManagerPeer::GetQuicVersionFromConnectionId( + &time_wait_list_manager_, kConnectionId3)); +} + +TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) { + // Add connection_ids such that their expiry time is kTimeWaitPeriod_. + epoll_server_.set_now_in_usec(0); + AddConnectionId(connection_id_); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); + size_t kConnectionCloseLength = 100; + AddConnectionId( + connection_id_, + QuicVersionMax(), + new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true)); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); + + EXPECT_CALL(writer_, WritePacket(_, + kConnectionCloseLength, + server_address_.address(), + client_address_)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + const int kRandomSequenceNumber = 1; - scoped_ptr<QuicEncryptedPacket> packet; + ProcessPacket(connection_id_, kRandomSequenceNumber); - AddGuid(guid_, net::test::QuicVersionMin(), NULL); - framer_.set_version(net::test::QuicVersionMin()); - packet.reset( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber)); + const QuicTime::Delta time_wait_period = + QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); - // Reset packet should be written, using the minimum quic version. - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(1) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); - ProcessPacket(guid_, *packet); - EXPECT_EQ(QuicTimeWaitListManagerPeer::version(&time_wait_list_manager_), - net::test::QuicVersionMin()); - - // New guid - ++guid_; - - AddGuid(guid_, net::test::QuicVersionMax(), NULL); - framer_.set_version(net::test::QuicVersionMax()); - packet.reset( - ConstructEncryptedPacket(ENCRYPTION_NONE, guid_, kRandomSequenceNumber)); - - // Reset packet should be written, using the maximum quic version. - EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(1) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); - ProcessPacket(guid_, *packet); - EXPECT_EQ(QuicTimeWaitListManagerPeer::version(&time_wait_list_manager_), - net::test::QuicVersionMax()); -} + QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); + // Now set the current time as time_wait_period + offset usecs. + epoll_server_.set_now_in_usec(time_wait_period.Add(offset).ToMicroseconds()); + // After the connection_ids are cleaned up, check the next alarm interval. + int64 next_alarm_time = epoll_server_.ApproximateNowInUsec() + + time_wait_period.ToMicroseconds(); -TEST_F(QuicTimeWaitListManagerTest, GetQuicVersionFromMap) { - const int kGuid1 = 123; - const int kGuid2 = 456; - const int kGuid3 = 789; - - AddGuid(kGuid1, net::test::QuicVersionMin(), NULL); - AddGuid(kGuid2, net::test::QuicVersionMax(), NULL); - AddGuid(kGuid3, net::test::QuicVersionMax(), NULL); - - EXPECT_EQ(net::test::QuicVersionMin(), - QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid( - &time_wait_list_manager_, kGuid1)); - EXPECT_EQ(net::test::QuicVersionMax(), - QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid( - &time_wait_list_manager_, kGuid2)); - EXPECT_EQ(net::test::QuicVersionMax(), - QuicTimeWaitListManagerPeer::GetQuicVersionFromGuid( - &time_wait_list_manager_, kGuid3)); + EXPECT_CALL(epoll_server_, RegisterAlarm(next_alarm_time, _)); + time_wait_list_manager_.CleanUpOldConnectionIds(); + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_)); } +TEST_F(QuicTimeWaitListManagerTest, ConnectionIdsOrderedByTime) { + // Simple randomization: the values of connection_ids are swapped based on the + // current seconds on the clock. If the container is broken, the test will be + // 50% flaky. + int odd_second = static_cast<int>(epoll_server_.ApproximateNowInUsec()) % 2; + EXPECT_TRUE(odd_second == 0 || odd_second == 1); + const QuicConnectionId kConnectionId1 = odd_second; + const QuicConnectionId kConnectionId2 = 1 - odd_second; + + // 1 will hash lower than 2, but we add it later. They should come out in the + // add order, not hash order. + epoll_server_.set_now_in_usec(0); + AddConnectionId(kConnectionId1); + epoll_server_.set_now_in_usec(10); + AddConnectionId(kConnectionId2); + + const QuicTime::Delta time_wait_period = + QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); + epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds() + 1); + + EXPECT_CALL(epoll_server_, RegisterAlarm(_, _)); + + time_wait_list_manager_.CleanUpOldConnectionIds(); + EXPECT_FALSE(IsConnectionIdInTimeWait(kConnectionId1)); + EXPECT_TRUE(IsConnectionIdInTimeWait(kConnectionId2)); +} } // namespace } // namespace test } // namespace tools diff --git a/chromium/net/tools/quic/spdy_utils.cc b/chromium/net/tools/quic/spdy_utils.cc index c350a96f9e2..7f146493671 100644 --- a/chromium/net/tools/quic/spdy_utils.cc +++ b/chromium/net/tools/quic/spdy_utils.cc @@ -38,8 +38,8 @@ void PopulateSpdyHeaderBlock(const BalsaHeaders& headers, hi != headers.header_lines_end(); ++hi) { if ((hi->second.length() == 0) && !allow_empty_values) { - DLOG(INFO) << "Dropping empty header " << hi->first.as_string() - << " from headers"; + DVLOG(1) << "Dropping empty header " << hi->first.as_string() + << " from headers"; continue; } @@ -157,8 +157,8 @@ string SpdyUtils::SerializeResponseHeaders( // static string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) { - int length = SpdyFramer::GetSerializedLength(SPDY3, &headers); - SpdyFrameBuilder builder(length); + size_t length = SpdyFramer::GetSerializedLength(SPDY3, &headers); + SpdyFrameBuilder builder(length, SPDY3); SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers); scoped_ptr<SpdyFrame> block(builder.take()); return string(block->data(), length); diff --git a/chromium/net/tools/quic/spdy_utils.h b/chromium/net/tools/quic/spdy_utils.h index cfad5f1918b..e0ae4edb905 100644 --- a/chromium/net/tools/quic/spdy_utils.h +++ b/chromium/net/tools/quic/spdy_utils.h @@ -37,6 +37,9 @@ class SpdyUtils { static std::string SerializeUncompressedHeaders( const SpdyHeaderBlock& headers); + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyUtils); }; } // namespace tools diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc b/chromium/net/tools/quic/test_tools/http_message.cc index 70eb59290fa..9bd3cffc312 100644 --- a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc +++ b/chromium/net/tools/quic/test_tools/http_message.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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/quic/test_tools/http_message_test_utils.h" +#include "net/tools/quic/test_tools/http_message.h" #include <vector> diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.h b/chromium/net/tools/quic/test_tools/http_message.h index fefdb4909cd..6b63dafd76c 100644 --- a/chromium/net/tools/quic/test_tools/http_message_test_utils.h +++ b/chromium/net/tools/quic/test_tools/http_message.h @@ -1,9 +1,9 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ -#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ +#ifndef NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_ #include <string> #include <vector> @@ -130,4 +130,4 @@ class HTTPMessage { } // namespace tools } // namespace net -#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ +#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_H_ diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.h b/chromium/net/tools/quic/test_tools/mock_epoll_server.h index fbc16956d72..cdb6a36a7ce 100644 --- a/chromium/net/tools/quic/test_tools/mock_epoll_server.h +++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.h @@ -43,6 +43,8 @@ class FakeTimeEpollServer : public EpollServer { private: int64 now_in_usec_; + + DISALLOW_COPY_AND_ASSIGN(FakeTimeEpollServer); }; class MockEpollServer : public FakeTimeEpollServer { @@ -83,20 +85,22 @@ class MockEpollServer : public FakeTimeEpollServer { protected: // functions // These functions do nothing here, as we're not actually // using the epoll_* syscalls. - virtual void DelFD(int fd) const OVERRIDE { } - virtual void AddFD(int fd, int event_mask) const OVERRIDE { } - virtual void ModFD(int fd, int event_mask) const OVERRIDE { } + virtual void DelFD(int fd) const OVERRIDE {} + virtual void AddFD(int fd, int event_mask) const OVERRIDE {} + virtual void ModFD(int fd, int event_mask) const OVERRIDE {} // Replaces the epoll_server's epoll_wait_impl. virtual int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events, int timeout_in_ms) OVERRIDE; - virtual void SetNonblocking (int fd) OVERRIDE { } + virtual void SetNonblocking (int fd) OVERRIDE {} private: // members EventQueue event_queue_; int64 until_in_usec_; + + DISALLOW_COPY_AND_ASSIGN(MockEpollServer); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc index 2d9c1ec184f..13271ca8afc 100644 --- a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc +++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc @@ -4,6 +4,8 @@ #include "net/tools/quic/test_tools/mock_quic_dispatcher.h" +#include "net/quic/test_tools/quic_test_utils.h" + namespace net { namespace tools { namespace test { @@ -11,11 +13,12 @@ namespace test { MockQuicDispatcher::MockQuicDispatcher( const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, - QuicGuid guid, EpollServer* eps) - : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), guid, - eps) { -} + : QuicDispatcher(config, + crypto_config, + QuicSupportedVersions(), + eps) {} + MockQuicDispatcher::~MockQuicDispatcher() {} } // namespace test diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h index 5f885e875b6..d1559115bf2 100644 --- a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h +++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h @@ -21,15 +21,16 @@ class MockQuicDispatcher : public QuicDispatcher { public: MockQuicDispatcher(const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, - QuicGuid guid, EpollServer* eps); + virtual ~MockQuicDispatcher(); - MOCK_METHOD5(ProcessPacket, void(const IPEndPoint& server_address, + MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address, const IPEndPoint& client_address, - QuicGuid guid, - bool has_version_flag, const QuicEncryptedPacket& packet)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc index 11aafb69495..05a7385181f 100644 --- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc +++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.cc @@ -10,8 +10,6 @@ #include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_socket_utils.h" -using net::test::QuicTestWriter; - namespace net { namespace tools { namespace test { @@ -21,11 +19,11 @@ namespace test { class WriteUnblockedAlarm : public QuicAlarm::Delegate { public: explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer) - : writer_(writer) { } + : writer_(writer) {} virtual QuicTime OnAlarm() OVERRIDE { - DCHECK(writer_->blocked_writer()); - writer_->blocked_writer()->OnCanWrite(); + DVLOG(1) << "Unblocking socket."; + writer_->OnCanWrite(); return QuicTime::Zero(); } @@ -37,8 +35,7 @@ class WriteUnblockedAlarm : public QuicAlarm::Delegate { // later point. class DelayAlarm : public QuicAlarm::Delegate { public: - explicit DelayAlarm(PacketDroppingTestWriter* writer) - : writer_(writer) { } + explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {} virtual QuicTime OnAlarm() OVERRIDE { return writer_->ReleaseOldPackets(); @@ -50,7 +47,6 @@ class DelayAlarm : public QuicAlarm::Delegate { PacketDroppingTestWriter::PacketDroppingTestWriter() : clock_(NULL), - blocked_writer_(NULL), cur_buffer_size_(0), config_mutex_(), fake_packet_loss_percentage_(0), @@ -60,45 +56,45 @@ PacketDroppingTestWriter::PacketDroppingTestWriter() fake_bandwidth_(QuicBandwidth::Zero()), buffer_size_(0) { uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max()); - LOG(INFO) << "Seeding packet loss with " << seed; + VLOG(1) << "Seeding packet loss with " << seed; simple_random_.set_seed(seed); } -PacketDroppingTestWriter::~PacketDroppingTestWriter() { } +PacketDroppingTestWriter::~PacketDroppingTestWriter() {} -void PacketDroppingTestWriter::SetConnectionHelper( - QuicEpollConnectionHelper* helper) { +void PacketDroppingTestWriter::Initialize( + QuicEpollConnectionHelper* helper, + Delegate* on_can_write) { clock_ = helper->GetClock(); write_unblocked_alarm_.reset( helper->CreateAlarm(new WriteUnblockedAlarm(this))); delay_alarm_.reset( helper->CreateAlarm(new DelayAlarm(this))); + on_can_write_.reset(on_can_write); } WriteResult PacketDroppingTestWriter::WritePacket( - const char* buffer, size_t buf_len, + const char* buffer, + size_t buf_len, const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) { + const net::IPEndPoint& peer_address) { ReleaseOldPackets(); base::AutoLock locked(config_mutex_); if (fake_packet_loss_percentage_ > 0 && simple_random_.RandUint64() % 100 < static_cast<uint64>(fake_packet_loss_percentage_)) { - DLOG(INFO) << "Dropping packet."; + DVLOG(1) << "Dropping packet."; return WriteResult(WRITE_STATUS_OK, buf_len); } if (fake_blocked_socket_percentage_ > 0 && simple_random_.RandUint64() % 100 < static_cast<uint64>(fake_blocked_socket_percentage_)) { - DLOG(INFO) << "Blocking socket."; + CHECK(on_can_write_.get() != NULL); + DVLOG(1) << "Blocking socket."; if (!write_unblocked_alarm_->IsSet()) { - blocked_writer_ = blocked_writer; - // Set the alarm for 1ms in the future. - write_unblocked_alarm_->Set( - clock_->ApproximateNow().Add( - QuicTime::Delta::FromMilliseconds(1))); + // Set the alarm to fire immediately. + write_unblocked_alarm_->Set(clock_->ApproximateNow()); } return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN); } @@ -106,7 +102,7 @@ WriteResult PacketDroppingTestWriter::WritePacket( if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) { if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) { // Drop packets which do not fit into the buffer. - DLOG(INFO) << "Dropping packet because the buffer is full."; + DVLOG(1) << "Dropping packet because the buffer is full."; return WriteResult(WRITE_STATUS_OK, buf_len); } @@ -133,12 +129,22 @@ WriteResult PacketDroppingTestWriter::WritePacket( return WriteResult(WRITE_STATUS_OK, buf_len); } - return writer()->WritePacket(buffer, buf_len, self_address, peer_address, - blocked_writer); + return QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_address, peer_address); } -bool PacketDroppingTestWriter::IsWriteBlockedDataBuffered() const { - return false; +bool PacketDroppingTestWriter::IsWriteBlocked() const { + if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) { + return true; + } + return QuicPacketWriterWrapper::IsWriteBlocked(); +} + +void PacketDroppingTestWriter::SetWritable() { + if (write_unblocked_alarm_.get() != NULL && write_unblocked_alarm_->IsSet()) { + write_unblocked_alarm_->Cancel(); + } + QuicPacketWriterWrapper::SetWritable(); } QuicTime PacketDroppingTestWriter::ReleaseNextPacket() { @@ -151,17 +157,18 @@ QuicTime PacketDroppingTestWriter::ReleaseNextPacket() { if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 && simple_random_.RandUint64() % 100 < static_cast<uint64>(fake_packet_reorder_percentage_)) { - DLOG(INFO) << "Reordering packets."; + DVLOG(1) << "Reordering packets."; ++iter; // Swap the send times when re-ordering packets. delayed_packets_.begin()->send_time = iter->send_time; } - DLOG(INFO) << "Releasing packet. " << (delayed_packets_.size() - 1) - << " remaining."; + DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1) + << " remaining."; // Grab the next one off the queue and send it. - writer()->WritePacket(iter->buffer.data(), iter->buffer.length(), - iter->self_address, iter->peer_address, NULL); + QuicPacketWriterWrapper::WritePacket( + iter->buffer.data(), iter->buffer.length(), + iter->self_address, iter->peer_address); DCHECK_GE(cur_buffer_size_, iter->buffer.length()); cur_buffer_size_ -= iter->buffer.length(); delayed_packets_.erase(iter); @@ -184,6 +191,10 @@ QuicTime PacketDroppingTestWriter::ReleaseOldPackets() { return QuicTime::Zero(); } +void PacketDroppingTestWriter::OnCanWrite() { + on_can_write_->OnCanWrite(); +} + PacketDroppingTestWriter::DelayedWrite::DelayedWrite( const char* buffer, size_t buf_len, @@ -193,8 +204,7 @@ PacketDroppingTestWriter::DelayedWrite::DelayedWrite( : buffer(buffer, buf_len), self_address(self_address), peer_address(peer_address), - send_time(send_time) { -} + send_time(send_time) {} PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() {} diff --git a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h index 2a736e0cfe2..35097229cf3 100644 --- a/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h +++ b/chromium/net/tools/quic/test_tools/packet_dropping_test_writer.h @@ -7,14 +7,14 @@ #include <list> +#include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "net/quic/quic_alarm.h" -#include "net/quic/quic_blocked_writer_interface.h" -#include "net/quic/quic_packet_writer.h" -#include "net/quic/test_tools/quic_test_writer.h" +#include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_epoll_clock.h" +#include "net/tools/quic/quic_packet_writer_wrapper.h" #include "net/tools/quic/test_tools/quic_test_client.h" #include "net/tools/quic/test_tools/quic_test_utils.h" @@ -25,29 +25,41 @@ namespace test { // Simulates a connection that drops packets a configured percentage of the time // and has a blocked socket a configured percentage of the time. Also provides // the options to delay packets and reorder packets if delay is enabled. -class PacketDroppingTestWriter : public net::test::QuicTestWriter { +class PacketDroppingTestWriter : public QuicPacketWriterWrapper { public: + class Delegate { + public: + virtual ~Delegate() {} + virtual void OnCanWrite() = 0; + }; + PacketDroppingTestWriter(); virtual ~PacketDroppingTestWriter(); - void SetConnectionHelper(QuicEpollConnectionHelper* helper); + // Must be called before blocking, reordering or delaying (loss is OK). May be + // called after connecting if the helper is not available before. + // |on_can_write| will be triggered when fake-unblocking; ownership will be + // assumed. + void Initialize(QuicEpollConnectionHelper* helper, Delegate* on_can_write); // QuicPacketWriter methods: virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, + const char* buffer, + size_t buf_len, const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE; + const IPEndPoint& peer_address) OVERRIDE; + + virtual bool IsWriteBlocked() const OVERRIDE; - virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual void SetWritable() OVERRIDE; // Writes out any packet which should have been sent by now // to the contained writer and returns the time // for the next delayed packet to be written. QuicTime ReleaseOldPackets(); - QuicBlockedWriterInterface* blocked_writer() { return blocked_writer_; } + void OnCanWrite(); // The percent of time a packet is simulated as being lost. void set_fake_packet_loss_percentage(int32 fake_packet_loss_percentage) { @@ -102,7 +114,7 @@ class PacketDroppingTestWriter : public net::test::QuicTestWriter { QuicTime ReleaseNextPacket(); // A single packet which will be sent at the supplied send_time. - class DelayedWrite { + struct DelayedWrite { public: DelayedWrite(const char* buffer, size_t buf_len, @@ -122,8 +134,8 @@ class PacketDroppingTestWriter : public net::test::QuicTestWriter { const QuicClock* clock_; scoped_ptr<QuicAlarm> write_unblocked_alarm_; scoped_ptr<QuicAlarm> delay_alarm_; - QuicBlockedWriterInterface* blocked_writer_; - SimpleRandom simple_random_; + scoped_ptr<Delegate> on_can_write_; + net::test::SimpleRandom simple_random_; // Stored packets delayed by fake packet delay or bandwidth restrictions. DelayedPacketList delayed_packets_; QuicByteCount cur_buffer_size_; diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.cc b/chromium/net/tools/quic/test_tools/quic_client_peer.cc index 25fdb7eedc5..89f1c82650c 100644 --- a/chromium/net/tools/quic/test_tools/quic_client_peer.cc +++ b/chromium/net/tools/quic/test_tools/quic_client_peer.cc @@ -11,8 +11,18 @@ namespace tools { namespace test { // static -int QuicClientPeer::GetFd(QuicClient* client) { - return client->fd_; +QuicCryptoClientConfig* QuicClientPeer::GetCryptoConfig(QuicClient* client) { + return &client->crypto_config_; +} + +// static +bool QuicClientPeer::CreateUDPSocket(QuicClient* client) { + return client->CreateUDPSocket(); +} + +// static +void QuicClientPeer::SetClientPort(QuicClient* client, int port) { + client->client_address_ = IPEndPoint(client->client_address_.address(), port); } } // namespace test diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.h b/chromium/net/tools/quic/test_tools/quic_client_peer.h index 016120aa8bf..b26fc6d829c 100644 --- a/chromium/net/tools/quic/test_tools/quic_client_peer.h +++ b/chromium/net/tools/quic/test_tools/quic_client_peer.h @@ -5,7 +5,12 @@ #ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ #define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ +#include "base/basictypes.h" + namespace net { + +class QuicCryptoClientConfig; + namespace tools { class QuicClient; @@ -14,7 +19,12 @@ namespace test { class QuicClientPeer { public: - static int GetFd(QuicClient* client); + static QuicCryptoClientConfig* GetCryptoConfig(QuicClient* client); + static bool CreateUDPSocket(QuicClient* client); + static void SetClientPort(QuicClient* client, int port); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicClientPeer); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc index c96eafd57e5..cd27802f512 100644 --- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc +++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.cc @@ -5,8 +5,7 @@ #include "net/tools/quic/test_tools/quic_dispatcher_peer.h" #include "net/tools/quic/quic_dispatcher.h" - -using net::test::QuicTestWriter; +#include "net/tools/quic/quic_packet_writer_wrapper.h" namespace net { namespace tools { @@ -20,13 +19,8 @@ void QuicDispatcherPeer::SetTimeWaitListManager( } // static -void QuicDispatcherPeer::SetWriteBlocked(QuicDispatcher* dispatcher) { - dispatcher->write_blocked_ = true; -} - -// static void QuicDispatcherPeer::UseWriter(QuicDispatcher* dispatcher, - QuicTestWriter* writer) { + QuicPacketWriterWrapper* writer) { writer->set_writer(dispatcher->writer_.release()); dispatcher->writer_.reset(writer); } @@ -42,6 +36,17 @@ QuicEpollConnectionHelper* QuicDispatcherPeer::GetHelper( return dispatcher->helper_.get(); } +// static +QuicConnection* QuicDispatcherPeer::CreateQuicConnection( + QuicDispatcher* dispatcher, + QuicConnectionId connection_id, + const IPEndPoint& server, + const IPEndPoint& client) { + return dispatcher->CreateQuicConnection(connection_id, + server, + client); +} + } // namespace test } // namespace tools } // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h index f463453f17a..45125c251d2 100644 --- a/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h +++ b/chromium/net/tools/quic/test_tools/quic_dispatcher_peer.h @@ -5,11 +5,15 @@ #ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_ #define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_ -#include "net/quic/test_tools/quic_test_writer.h" #include "net/tools/quic/quic_dispatcher.h" +#include "net/base/ip_endpoint.h" + namespace net { namespace tools { + +class QuicPacketWriterWrapper; + namespace test { class QuicDispatcherPeer { @@ -18,14 +22,22 @@ class QuicDispatcherPeer { QuicDispatcher* dispatcher, QuicTimeWaitListManager* time_wait_list_manager); - static void SetWriteBlocked(QuicDispatcher* dispatcher); - + // Injects |writer| into |dispatcher| as the top level writer. static void UseWriter(QuicDispatcher* dispatcher, - net::test::QuicTestWriter* writer); + QuicPacketWriterWrapper* writer); static QuicPacketWriter* GetWriter(QuicDispatcher* dispatcher); static QuicEpollConnectionHelper* GetHelper(QuicDispatcher* dispatcher); + + static QuicConnection* CreateQuicConnection( + QuicDispatcher* dispatcher, + QuicConnectionId connection_id, + const IPEndPoint& server, + const IPEndPoint& client); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicDispatcherPeer); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/quic_server_peer.cc b/chromium/net/tools/quic/test_tools/quic_server_peer.cc index 15f31297d17..33a086f2096 100644 --- a/chromium/net/tools/quic/test_tools/quic_server_peer.cc +++ b/chromium/net/tools/quic/test_tools/quic_server_peer.cc @@ -28,11 +28,6 @@ QuicDispatcher* QuicServerPeer::GetDispatcher(QuicServer* server) { return server->dispatcher_.get(); } -// static -int QuicServerPeer::GetFD(QuicServer* server) { - return server->fd_; -} - } // namespace test } // namespace tools } // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_server_peer.h b/chromium/net/tools/quic/test_tools/quic_server_peer.h index 65e2c5e96b2..f5f625db74f 100644 --- a/chromium/net/tools/quic/test_tools/quic_server_peer.h +++ b/chromium/net/tools/quic/test_tools/quic_server_peer.h @@ -5,6 +5,8 @@ #ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_ #define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_ +#include "base/basictypes.h" + namespace net { namespace tools { @@ -19,7 +21,9 @@ class QuicServerPeer { static bool SetSmallSocket(QuicServer* server); static void DisableRecvmmsg(QuicServer* server); static QuicDispatcher* GetDispatcher(QuicServer* server); - static int GetFD(QuicServer* server); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicServerPeer); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.cc b/chromium/net/tools/quic/test_tools/quic_test_client.cc index 57edadeec70..3e77bb51d6d 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_client.cc +++ b/chromium/net/tools/quic/test_tools/quic_test_client.cc @@ -10,39 +10,49 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" #include "net/quic/crypto/proof_verifier.h" +#include "net/quic/quic_server_id.h" #include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_session_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/reliable_quic_stream_peer.h" #include "net/tools/balsa/balsa_headers.h" #include "net/tools/quic/quic_epoll_connection_helper.h" +#include "net/tools/quic/quic_packet_writer_wrapper.h" #include "net/tools/quic/quic_spdy_client_stream.h" -#include "net/tools/quic/test_tools/http_message_test_utils.h" +#include "net/tools/quic/test_tools/http_message.h" +#include "net/tools/quic/test_tools/quic_client_peer.h" #include "url/gurl.h" using base::StringPiece; +using net::QuicServerId; using net::test::QuicConnectionPeer; -using net::test::QuicTestWriter; +using net::test::QuicSessionPeer; +using net::test::ReliableQuicStreamPeer; using std::string; using std::vector; +namespace net { +namespace tools { +namespace test { namespace { // RecordingProofVerifier accepts any certificate chain and records the common // name of the leaf. -class RecordingProofVerifier : public net::ProofVerifier { +class RecordingProofVerifier : public ProofVerifier { public: // ProofVerifier interface. - virtual net::ProofVerifier::Status VerifyProof( + virtual QuicAsyncStatus VerifyProof( const string& hostname, const string& server_config, const vector<string>& certs, const string& signature, + const ProofVerifyContext* context, string* error_details, - scoped_ptr<net::ProofVerifyDetails>* details, - net::ProofVerifierCallback* callback) OVERRIDE { - delete callback; - + scoped_ptr<ProofVerifyDetails>* details, + ProofVerifierCallback* callback) OVERRIDE { common_name_.clear(); if (certs.empty()) { - return FAILURE; + return QUIC_FAILURE; } // Convert certs to X509Certificate. @@ -53,11 +63,11 @@ class RecordingProofVerifier : public net::ProofVerifier { scoped_refptr<net::X509Certificate> cert = net::X509Certificate::CreateFromDERCertChain(cert_pieces); if (!cert.get()) { - return FAILURE; + return QUIC_FAILURE; } common_name_ = cert->subject().GetDisplayName(); - return SUCCESS; + return QUIC_SUCCESS; } const string& common_name() const { return common_name_; } @@ -68,10 +78,6 @@ class RecordingProofVerifier : public net::ProofVerifier { } // anonymous namespace -namespace net { -namespace tools { -namespace test { - BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers, bool secure) { StringPiece uri = const_headers->request_uri(); @@ -94,101 +100,130 @@ BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers, return headers; } -// A quic client which allows mocking out writes. -class QuicEpollClient : public QuicClient { - public: - typedef QuicClient Super; - - QuicEpollClient(IPEndPoint server_address, - const string& server_hostname, - const QuicVersionVector& supported_versions) - : Super(server_address, server_hostname, supported_versions, false), - override_guid_(0), test_writer_(NULL) { - } - - QuicEpollClient(IPEndPoint server_address, - const string& server_hostname, - const QuicConfig& config, - const QuicVersionVector& supported_versions) - : Super(server_address, server_hostname, config, supported_versions), - override_guid_(0), test_writer_(NULL) { - } - - virtual ~QuicEpollClient() { - if (connected()) { - Disconnect(); - } - } - - virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE { - QuicPacketWriter* writer = Super::CreateQuicPacketWriter(); - if (!test_writer_) { - return writer; - } - test_writer_->set_writer(writer); - return test_writer_; +MockableQuicClient::MockableQuicClient( + IPEndPoint server_address, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server) + : QuicClient(server_address, + server_id, + supported_versions, + false, + epoll_server), + override_connection_id_(0), + test_writer_(NULL) {} + +MockableQuicClient::MockableQuicClient( + IPEndPoint server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server) + : QuicClient(server_address, + server_id, + supported_versions, + false, + config, + epoll_server), + override_connection_id_(0), + test_writer_(NULL) {} + +MockableQuicClient::~MockableQuicClient() { + if (connected()) { + Disconnect(); } +} - virtual QuicGuid GenerateGuid() OVERRIDE { - return override_guid_ ? override_guid_ : Super::GenerateGuid(); +QuicPacketWriter* MockableQuicClient::CreateQuicPacketWriter() { + QuicPacketWriter* writer = QuicClient::CreateQuicPacketWriter(); + if (!test_writer_) { + return writer; } + test_writer_->set_writer(writer); + return test_writer_; +} - // Takes ownership of writer. - void UseWriter(QuicTestWriter* writer) { test_writer_ = writer; } - - void UseGuid(QuicGuid guid) { - override_guid_ = guid; - } +QuicConnectionId MockableQuicClient::GenerateConnectionId() { + return override_connection_id_ ? override_connection_id_ + : QuicClient::GenerateConnectionId(); +} - private: - QuicGuid override_guid_; // GUID to use, if nonzero - QuicTestWriter* test_writer_; -}; +// Takes ownership of writer. +void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) { + CHECK(test_writer_ == NULL); + test_writer_ = writer; +} -QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname, - const QuicVersionVector& supported_versions) - : client_(new QuicEpollClient(address, hostname, supported_versions)) { - Initialize(address, hostname, true); +void MockableQuicClient::UseConnectionId(QuicConnectionId connection_id) { + override_connection_id_ = connection_id; } -QuicTestClient::QuicTestClient(IPEndPoint address, - const string& hostname, +QuicTestClient::QuicTestClient(IPEndPoint server_address, + const string& server_hostname, + const QuicVersionVector& supported_versions) + : client_(new MockableQuicClient(server_address, + QuicServerId(server_hostname, + server_address.port(), + false, + PRIVACY_MODE_DISABLED), + supported_versions, + &epoll_server_)) { + Initialize(true); +} + +QuicTestClient::QuicTestClient(IPEndPoint server_address, + const string& server_hostname, bool secure, const QuicVersionVector& supported_versions) - : client_(new QuicEpollClient(address, hostname, supported_versions)) { - Initialize(address, hostname, secure); + : client_(new MockableQuicClient(server_address, + QuicServerId(server_hostname, + server_address.port(), + secure, + PRIVACY_MODE_DISABLED), + supported_versions, + &epoll_server_)) { + Initialize(secure); +} + +QuicTestClient::QuicTestClient( + IPEndPoint server_address, + const string& server_hostname, + bool secure, + const QuicConfig& config, + const QuicVersionVector& supported_versions) + : client_( + new MockableQuicClient(server_address, + QuicServerId(server_hostname, + server_address.port(), + secure, + PRIVACY_MODE_DISABLED), + config, + supported_versions, + &epoll_server_)) { + Initialize(secure); +} + +QuicTestClient::QuicTestClient() { } -QuicTestClient::QuicTestClient(IPEndPoint address, - const string& hostname, - bool secure, - const QuicConfig& config, - const QuicVersionVector& supported_versions) - : client_(new QuicEpollClient(address, hostname, config, - supported_versions)) { - Initialize(address, hostname, secure); +QuicTestClient::~QuicTestClient() { + if (stream_) { + stream_->set_visitor(NULL); + } } -void QuicTestClient::Initialize(IPEndPoint address, - const string& hostname, - bool secure) { - server_address_ = address; +void QuicTestClient::Initialize(bool secure) { priority_ = 3; connect_attempted_ = false; secure_ = secure; auto_reconnect_ = false; buffer_body_ = true; + fec_policy_ = FEC_PROTECT_OPTIONAL; proof_verifier_ = NULL; ClearPerRequestState(); ExpectCertificates(secure_); } -QuicTestClient::~QuicTestClient() { - if (stream_) { - stream_->set_visitor(NULL); - } -} - void QuicTestClient::ExpectCertificates(bool on) { if (on) { proof_verifier_ = new RecordingProofVerifier; @@ -199,8 +234,14 @@ void QuicTestClient::ExpectCertificates(bool on) { } } +void QuicTestClient::SetUserAgentID(const string& user_agent_id) { + client_->SetUserAgentID(user_agent_id); +} + ssize_t QuicTestClient::SendRequest(const string& uri) { - HTTPMessage message(HttpConstants::HTTP_1_1, HttpConstants::GET, uri); + HTTPMessage message(HttpConstants::HTTP_1_1, + HttpConstants::GET, + uri); return SendMessage(message); } @@ -211,7 +252,11 @@ ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) { if (!connected()) { GURL url(message.headers()->request_uri().as_string()); if (!url.host().empty()) { - client_->set_server_hostname(url.host()); + client_->set_server_id( + QuicServerId(url.host(), + url.EffectiveIntPort(), + url.SchemeIs("https"), + PRIVACY_MODE_DISABLED)); } } @@ -236,6 +281,34 @@ ssize_t QuicTestClient::SendData(string data, bool last_data) { return data.length(); } +bool QuicTestClient::response_complete() const { + return response_complete_; +} + +int QuicTestClient::response_header_size() const { + return response_header_size_; +} + +int64 QuicTestClient::response_body_size() const { + return response_body_size_; +} + +bool QuicTestClient::buffer_body() const { + return buffer_body_; +} + +void QuicTestClient::set_buffer_body(bool buffer_body) { + buffer_body_ = buffer_body; +} + +bool QuicTestClient::ServerInLameDuckMode() const { + return false; +} + +const string& QuicTestClient::response_body() { + return response_; +} + string QuicTestClient::SendCustomSynchronousRequest( const HTTPMessage& message) { SendMessage(message); @@ -268,26 +341,39 @@ QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() { } stream_->set_visitor(this); reinterpret_cast<QuicSpdyClientStream*>(stream_)->set_priority(priority_); + // Set FEC policy on stream. + ReliableQuicStreamPeer::SetFecPolicy(stream_, fec_policy_); } return stream_; } +QuicErrorCode QuicTestClient::connection_error() { + return client()->session()->error(); +} + +MockableQuicClient* QuicTestClient::client() { return client_.get(); } + const string& QuicTestClient::cert_common_name() const { return reinterpret_cast<RecordingProofVerifier*>(proof_verifier_) ->common_name(); } -bool QuicTestClient::connected() const { - return client_->connected(); +QuicTagValueMap QuicTestClient::GetServerConfig() const { + QuicCryptoClientConfig* config = + QuicClientPeer::GetCryptoConfig(client_.get()); + QuicCryptoClientConfig::CachedState* state = + config->LookupOrCreate(client_->server_id()); + const CryptoHandshakeMessage* handshake_msg = state->GetServerConfig(); + if (handshake_msg != NULL) { + return handshake_msg->tag_value_map(); + } else { + return QuicTagValueMap(); + } } -void QuicTestClient::WaitForResponse() { - if (stream_ == NULL) { - // The client has likely disconnected. - return; - } - client_->WaitForStreamToClose(stream_->id()); +bool QuicTestClient::connected() const { + return client_->connected(); } void QuicTestClient::Connect() { @@ -328,9 +414,9 @@ void QuicTestClient::ClearPerRequestState() { void QuicTestClient::WaitForResponseForMs(int timeout_ms) { int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond; - int64 old_timeout_us = client()->epoll_server()->timeout_in_us(); + int64 old_timeout_us = epoll_server()->timeout_in_us(); if (timeout_us > 0) { - client()->epoll_server()->set_timeout_in_us(timeout_us); + epoll_server()->set_timeout_in_us(timeout_us); } const QuicClock* clock = QuicConnectionPeer::GetHelper(client()->session()->connection())-> @@ -343,15 +429,15 @@ void QuicTestClient::WaitForResponseForMs(int timeout_ms) { client_->WaitForEvents(); } if (timeout_us > 0) { - client()->epoll_server()->set_timeout_in_us(old_timeout_us); + epoll_server()->set_timeout_in_us(old_timeout_us); } } void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) { int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond; - int64 old_timeout_us = client()->epoll_server()->timeout_in_us(); + int64 old_timeout_us = epoll_server()->timeout_in_us(); if (timeout_us > 0) { - client()->epoll_server()->set_timeout_in_us(timeout_us); + epoll_server()->set_timeout_in_us(timeout_us); } const QuicClock* clock = QuicConnectionPeer::GetHelper(client()->session()->connection())-> @@ -365,7 +451,7 @@ void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) { client_->WaitForEvents(); } if (timeout_us > 0) { - client()->epoll_server()->set_timeout_in_us(old_timeout_us); + epoll_server()->set_timeout_in_us(old_timeout_us); } } @@ -389,7 +475,7 @@ const BalsaHeaders* QuicTestClient::response_headers() const { } } -int QuicTestClient::response_size() const { +int64 QuicTestClient::response_size() const { return bytes_read_; } @@ -413,28 +499,71 @@ void QuicTestClient::OnClose(QuicDataStream* stream) { response_headers_complete_ = stream_->headers_decompressed(); headers_.CopyFrom(stream_->headers()); stream_error_ = stream_->stream_error(); - bytes_read_ = stream_->stream_bytes_read(); - bytes_written_ = stream_->stream_bytes_written(); + bytes_read_ = stream_->stream_bytes_read() + stream_->header_bytes_read(); + bytes_written_ = + stream_->stream_bytes_written() + stream_->header_bytes_written(); response_header_size_ = headers_.GetSizeForWriteBuffer(); response_body_size_ = stream_->data().size(); stream_ = NULL; } -void QuicTestClient::UseWriter(QuicTestWriter* writer) { - reinterpret_cast<QuicEpollClient*>(client_.get())->UseWriter(writer); +void QuicTestClient::UseWriter(QuicPacketWriterWrapper* writer) { + client_->UseWriter(writer); } -void QuicTestClient::UseGuid(QuicGuid guid) { +void QuicTestClient::UseConnectionId(QuicConnectionId connection_id) { DCHECK(!connected()); - reinterpret_cast<QuicEpollClient*>(client_.get())->UseGuid(guid); + client_->UseConnectionId(connection_id); +} + +ssize_t QuicTestClient::SendAndWaitForResponse(const void *buffer, + size_t size) { + LOG(DFATAL) << "Not implemented"; + return 0; +} + +void QuicTestClient::Bind(IPEndPoint* local_address) { + DLOG(WARNING) << "Bind will be done during connect"; +} + +string QuicTestClient::SerializeMessage(const HTTPMessage& message) { + LOG(DFATAL) << "Not implemented"; + return ""; +} + +IPAddressNumber QuicTestClient::bind_to_address() const { + return client_->bind_to_address(); +} + +void QuicTestClient::set_bind_to_address(IPAddressNumber address) { + client_->set_bind_to_address(address); +} + +const IPEndPoint& QuicTestClient::address() const { + LOG(DFATAL) << "Not implemented"; + return client_->server_address(); +} + +size_t QuicTestClient::requests_sent() const { + LOG(DFATAL) << "Not implemented"; + return 0; } void QuicTestClient::WaitForWriteToFlush() { - while (connected() && client()->session()->HasQueuedData()) { + while (connected() && client()->session()->HasDataToWrite()) { client_->WaitForEvents(); } } +void QuicTestClient::SetFecPolicy(FecPolicy fec_policy) { + fec_policy_ = fec_policy; + // Set policy for headers and crypto streams. + ReliableQuicStreamPeer::SetFecPolicy( + QuicSessionPeer::GetHeadersStream(client()->session()), fec_policy); + ReliableQuicStreamPeer::SetFecPolicy(client()->session()->GetCryptoStream(), + fec_policy); +} + } // namespace test } // namespace tools } // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.h b/chromium/net/tools/quic/test_tools/quic_test_client.h index e67a2a159bb..93c0352bca6 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_client.h +++ b/chromium/net/tools/quic/test_tools/quic_test_client.h @@ -2,18 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ -#define NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ +#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ #include <string> #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "net/base/ip_endpoint.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_protocol.h" -#include "net/quic/test_tools/quic_test_writer.h" +#include "net/tools/balsa/balsa_frame.h" +#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client.h" +#include "net/tools/quic/test_tools/simple_client.h" namespace net { @@ -21,14 +24,46 @@ class ProofVerifier; namespace tools { +class QuicPacketWriterWrapper; + namespace test { class HTTPMessage; +class MockableQuicClient; -// A toy QUIC client used for testing. -class QuicTestClient : public QuicDataStream::Visitor { +// A quic client which allows mocking out writes. +class MockableQuicClient : public QuicClient { public: - QuicTestClient(IPEndPoint server_address, const string& server_hostname, + MockableQuicClient(IPEndPoint server_address, + const QuicServerId& server_id, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server); + + MockableQuicClient(IPEndPoint server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const QuicVersionVector& supported_versions, + EpollServer* epoll_server); + + virtual ~MockableQuicClient() OVERRIDE; + virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE; + virtual QuicConnectionId GenerateConnectionId() OVERRIDE; + void UseWriter(QuicPacketWriterWrapper* writer); + void UseConnectionId(QuicConnectionId connection_id); + + private: + QuicConnectionId override_connection_id_; // ConnectionId to use, if nonzero + QuicPacketWriterWrapper* test_writer_; + + DISALLOW_COPY_AND_ASSIGN(MockableQuicClient); +}; + +// A toy QUIC client used for testing, mostly following the SimpleClient APIs. +class QuicTestClient : public SimpleClient, + public QuicDataStream::Visitor { + public: + QuicTestClient(IPEndPoint server_address, + const string& server_hostname, const QuicVersionVector& supported_versions); QuicTestClient(IPEndPoint server_address, const string& server_hostname, @@ -47,77 +82,99 @@ class QuicTestClient : public QuicDataStream::Visitor { // name is recorded and available with |cert_common_name()|. void ExpectCertificates(bool on); - // Clears any outstanding state and sends a simple GET of 'uri' to the - // server. Returns 0 if the request failed and no bytes were written. - ssize_t SendRequest(const string& uri); - ssize_t SendMessage(const HTTPMessage& message); - - string SendCustomSynchronousRequest(const HTTPMessage& message); - string SendSynchronousRequest(const string& uri); + // Sets the |user_agent_id| of the |client_|. + void SetUserAgentID(const string& user_agent_id); // Wraps data in a quic packet and sends it. ssize_t SendData(string data, bool last_data); - QuicPacketCreator::Options* options() { return client_->options(); } - - void WaitForResponse(); - - void Connect(); - void ResetConnection(); - void Disconnect(); - IPEndPoint LocalSocketAddress() const; - void ClearPerRequestState(); - void WaitForResponseForMs(int timeout_ms); - void WaitForInitialResponseForMs(int timeout_ms); - ssize_t Send(const void *buffer, size_t size); - bool response_complete() const { return response_complete_; } - bool response_headers_complete() const; - const BalsaHeaders* response_headers() const; - int response_size() const; - int response_header_size() const { return response_header_size_; } - int response_body_size() const { return response_body_size_; } - size_t bytes_read() const; - size_t bytes_written() const; - bool buffer_body() const { return buffer_body_; } - void set_buffer_body(bool buffer_body) { buffer_body_ = buffer_body; } + // From SimpleClient + // Clears any outstanding state and sends a simple GET of 'uri' to the + // server. Returns 0 if the request failed and no bytes were written. + virtual ssize_t SendRequest(const string& uri) OVERRIDE; + virtual ssize_t SendMessage(const HTTPMessage& message) OVERRIDE; + virtual string SendCustomSynchronousRequest( + const HTTPMessage& message) OVERRIDE; + virtual string SendSynchronousRequest(const string& uri) OVERRIDE; + virtual void Connect() OVERRIDE; + virtual void ResetConnection() OVERRIDE; + virtual void Disconnect() OVERRIDE; + virtual IPEndPoint LocalSocketAddress() const OVERRIDE; + virtual void ClearPerRequestState() OVERRIDE; + virtual void WaitForResponseForMs(int timeout_ms) OVERRIDE; + virtual void WaitForInitialResponseForMs(int timeout_ms) OVERRIDE; + virtual ssize_t Send(const void *buffer, size_t size) OVERRIDE; + virtual bool response_complete() const OVERRIDE; + virtual bool response_headers_complete() const OVERRIDE; + virtual const BalsaHeaders* response_headers() const OVERRIDE; + virtual int64 response_size() const OVERRIDE; + virtual int response_header_size() const OVERRIDE; + virtual int64 response_body_size() const OVERRIDE; + virtual size_t bytes_read() const OVERRIDE; + virtual size_t bytes_written() const OVERRIDE; + virtual bool buffer_body() const OVERRIDE; + virtual void set_buffer_body(bool buffer_body) OVERRIDE; + virtual bool ServerInLameDuckMode() const OVERRIDE; + virtual const string& response_body() OVERRIDE; + virtual bool connected() const OVERRIDE; + // These functions are all unimplemented functions from SimpleClient, and log + // DFATAL if called by users of SimpleClient. + virtual ssize_t SendAndWaitForResponse(const void *buffer, + size_t size) OVERRIDE; + virtual void Bind(IPEndPoint* local_address) OVERRIDE; + virtual string SerializeMessage(const HTTPMessage& message) OVERRIDE; + virtual IPAddressNumber bind_to_address() const OVERRIDE; + virtual void set_bind_to_address(IPAddressNumber address) OVERRIDE; + virtual const IPEndPoint& address() const OVERRIDE; + virtual size_t requests_sent() const OVERRIDE; // From QuicDataStream::Visitor virtual void OnClose(QuicDataStream* stream) OVERRIDE; // Configures client_ to take ownership of and use the writer. // Must be called before initial connect. - void UseWriter(net::test::QuicTestWriter* writer); - // If the given GUID is nonzero, configures client_ to use a specific GUID - // instead of a random one. - void UseGuid(QuicGuid guid); + void UseWriter(QuicPacketWriterWrapper* writer); + // If the given ConnectionId is nonzero, configures client_ to use a specific + // ConnectionId instead of a random one. + void UseConnectionId(QuicConnectionId connection_id); // Returns NULL if the maximum number of streams have already been created. QuicSpdyClientStream* GetOrCreateStream(); QuicRstStreamErrorCode stream_error() { return stream_error_; } - QuicErrorCode connection_error() { return client()->session()->error(); } + QuicErrorCode connection_error(); - QuicClient* client() { return client_.get(); } + MockableQuicClient* client(); // cert_common_name returns the common name value of the server's certificate, // or the empty string if no certificate was presented. const string& cert_common_name() const; - const string& response_body() {return response_;} - bool connected() const; + // Get the server config map. + QuicTagValueMap GetServerConfig() const; void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; } void set_priority(QuicPriority priority) { priority_ = priority; } + // Sets client's FEC policy. This policy applies to the data stream(s), and + // also to the headers and crypto streams. + void SetFecPolicy(FecPolicy fec_policy); + void WaitForWriteToFlush(); - private: - void Initialize(IPEndPoint address, const string& hostname, bool secure); + EpollServer* epoll_server() { return &epoll_server_; } + + protected: + QuicTestClient(); - IPEndPoint server_address_; - IPEndPoint client_address_; - scoped_ptr<QuicClient> client_; // The actual client + void Initialize(bool secure); + + void set_client(MockableQuicClient* client) { client_.reset(client); } + + private: + EpollServer epoll_server_; + scoped_ptr<MockableQuicClient> client_; // The actual client QuicSpdyClientStream* stream_; QuicRstStreamErrorCode stream_error_; @@ -132,7 +189,7 @@ class QuicTestClient : public QuicDataStream::Visitor { // The number of uncompressed HTTP header bytes received. int response_header_size_; // The number of HTTP body bytes received. - int response_body_size_; + int64 response_body_size_; // True if we tried to connect already since the last call to Disconnect(). bool connect_attempted_; bool secure_; @@ -142,10 +199,13 @@ class QuicTestClient : public QuicDataStream::Visitor { bool auto_reconnect_; // Should we buffer the response body? Defaults to true. bool buffer_body_; - + // FEC policy for data sent by this client. + FecPolicy fec_policy_; // proof_verifier_ points to a RecordingProofVerifier that is owned by // client_. ProofVerifier* proof_verifier_; + + DISALLOW_COPY_AND_ASSIGN(QuicTestClient); }; } // namespace test @@ -153,4 +213,4 @@ class QuicTestClient : public QuicDataStream::Visitor { } // namespace tools } // namespace net -#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_ diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.cc b/chromium/net/tools/quic/test_tools/quic_test_utils.cc index 1aad3273832..d46eae5b734 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_utils.cc +++ b/chromium/net/tools/quic/test_tools/quic_test_utils.cc @@ -4,47 +4,59 @@ #include "net/tools/quic/test_tools/quic_test_utils.h" -#include "base/sha1.h" #include "net/quic/quic_connection.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/quic/quic_epoll_connection_helper.h" using base::StringPiece; +using net::test::MakeAckFrame; using net::test::MockHelper; +using net::test::QuicConnectionPeer; namespace net { namespace tools { namespace test { MockConnection::MockConnection(bool is_server) - : QuicConnection(kTestGuid, + : QuicConnection(kTestConnectionId, IPEndPoint(net::test::Loopback4(), kTestPort), new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), - writer_(net::test::QuicConnectionPeer::GetWriter(this)), + writer_(QuicConnectionPeer::GetWriter(this)), helper_(helper()) { } MockConnection::MockConnection(IPEndPoint address, bool is_server) - : QuicConnection(kTestGuid, address, + : QuicConnection(kTestConnectionId, address, new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), - writer_(net::test::QuicConnectionPeer::GetWriter(this)), + writer_(QuicConnectionPeer::GetWriter(this)), helper_(helper()) { } -MockConnection::MockConnection(QuicGuid guid, +MockConnection::MockConnection(QuicConnectionId connection_id, bool is_server) - : QuicConnection(guid, + : QuicConnection(connection_id, IPEndPoint(net::test::Loopback4(), kTestPort), new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), - writer_(net::test::QuicConnectionPeer::GetWriter(this)), + writer_(QuicConnectionPeer::GetWriter(this)), + helper_(helper()) { +} + +MockConnection::MockConnection(bool is_server, + const QuicVersionVector& supported_versions) + : QuicConnection(kTestConnectionId, + IPEndPoint(net::test::Loopback4(), kTestPort), + new testing::NiceMock<MockHelper>(), + new testing::NiceMock<MockPacketWriter>(), + is_server, QuicSupportedVersions()), + writer_(QuicConnectionPeer::GetWriter(this)), helper_(helper()) { } @@ -55,18 +67,21 @@ void MockConnection::AdvanceTime(QuicTime::Delta delta) { static_cast<MockHelper*>(helper())->AdvanceTime(delta); } -uint64 SimpleRandom::RandUint64() { - unsigned char hash[base::kSHA1Length]; - base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_), - hash); - memcpy(&seed_, hash, sizeof(seed_)); - return seed_; +QuicAckFrame MakeAckFrameWithNackRanges( + size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) { + QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked, + least_unacked); + // Add enough missing packets to get num_nack_ranges nack ranges. + for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) { + ack.received_info.missing_packets.insert(least_unacked + i); + } + return ack; } TestSession::TestSession(QuicConnection* connection, const QuicConfig& config) - : QuicSession(connection, config), - crypto_stream_(NULL) { + : QuicSession(connection, config), + crypto_stream_(NULL) { } TestSession::~TestSession() {} @@ -85,19 +100,10 @@ MockPacketWriter::MockPacketWriter() { MockPacketWriter::~MockPacketWriter() { } -MockQuicSessionOwner::MockQuicSessionOwner() { -} - -MockQuicSessionOwner::~MockQuicSessionOwner() { -} - -bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { - data.AppendToString(&data_); - return true; +MockQuicServerSessionVisitor::MockQuicServerSessionVisitor() { } -void TestDecompressorVisitor::OnDecompressionError() { - error_ = true; +MockQuicServerSessionVisitor::~MockQuicServerSessionVisitor() { } MockAckNotifierDelegate::MockAckNotifierDelegate() { diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.h b/chromium/net/tools/quic/test_tools/quic_test_utils.h index 986665bd1ed..a889a42ee0d 100644 --- a/chromium/net/tools/quic/test_tools/quic_test_utils.h +++ b/chromium/net/tools/quic/test_tools/quic_test_utils.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Common utilities for Quic tests + #ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ #define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ @@ -11,7 +13,6 @@ #include "net/quic/quic_connection.h" #include "net/quic/quic_packet_writer.h" #include "net/quic/quic_session.h" -#include "net/quic/quic_spdy_decompressor.h" #include "net/spdy/spdy_framer.h" #include "net/tools/quic/quic_server_session.h" #include "testing/gmock/include/gmock/gmock.h" @@ -24,38 +25,31 @@ class IPEndPoint; namespace tools { namespace test { -static const QuicGuid kTestGuid = 42; +static const QuicConnectionId kTestConnectionId = 42; static const int kTestPort = 123; +static const uint32 kInitialStreamFlowControlWindowForTest = + 32 * 1024; // 32 KB +static const uint32 kInitialSessionFlowControlWindowForTest = + 64 * 1024; // 64 KB -// Simple random number generator used to compute random numbers suitable -// for pseudo-randomly dropping packets in tests. It works by computing -// the sha1 hash of the current seed, and using the first 64 bits as -// the next random number, and the next seed. -class SimpleRandom { - public: - SimpleRandom() : seed_(0) {} - - // Returns a random number in the range [0, kuint64max]. - uint64 RandUint64(); - - void set_seed(uint64 seed) { seed_ = seed; } - - private: - uint64 seed_; -}; +// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges| +// nack ranges of width 1 packet, starting from |least_unacked|. +QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges, + QuicPacketSequenceNumber least_unacked); class MockConnection : public QuicConnection { public: - // Uses a MockHelper, GUID of 42, and 127.0.0.1:123. + // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123. explicit MockConnection(bool is_server); - // Uses a MockHelper, GUID of 42. - MockConnection(IPEndPoint address, - bool is_server); + // Uses a MockHelper, ConnectionId of 42. + MockConnection(IPEndPoint address, bool is_server); // Uses a MockHelper, and 127.0.0.1:123 - MockConnection(QuicGuid guid, - bool is_server); + MockConnection(QuicConnectionId connection_id, bool is_server); + + // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123. + MockConnection(bool is_server, const QuicVersionVector& supported_versions); virtual ~MockConnection(); @@ -70,12 +64,19 @@ class MockConnection : public QuicConnection { MOCK_METHOD2(SendConnectionCloseWithDetails, void( QuicErrorCode error, const std::string& details)); - MOCK_METHOD2(SendRstStream, void(QuicStreamId id, - QuicRstStreamErrorCode error)); + MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error, + const std::string& details)); + MOCK_METHOD3(SendRstStream, void(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written)); MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, QuicStreamId last_good_stream_id, const std::string& reason)); - MOCK_METHOD0(OnCanWrite, bool()); + MOCK_METHOD1(SendBlocked, void(QuicStreamId id)); + MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id, + QuicStreamOffset byte_offset)); + MOCK_METHOD0(OnCanWrite, void()); + MOCK_CONST_METHOD0(HasPendingWrites, bool()); void ReallyProcessUdpPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, @@ -102,10 +103,11 @@ class TestSession : public QuicSession { void SetCryptoStream(QuicCryptoStream* stream); - virtual QuicCryptoStream* GetCryptoStream(); + virtual QuicCryptoStream* GetCryptoStream() OVERRIDE; private: QuicCryptoStream* crypto_stream_; + DISALLOW_COPY_AND_ASSIGN(TestSession); }; @@ -114,42 +116,46 @@ class MockPacketWriter : public QuicPacketWriter { MockPacketWriter(); virtual ~MockPacketWriter(); - MOCK_METHOD5(WritePacket, + MOCK_METHOD4(WritePacket, WriteResult(const char* buffer, size_t buf_len, const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer)); + const IPEndPoint& peer_address)); MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool()); -}; + MOCK_CONST_METHOD0(IsWriteBlocked, bool()); + MOCK_METHOD0(SetWritable, void()); -class MockQuicSessionOwner : public QuicSessionOwner { - public: - MockQuicSessionOwner(); - ~MockQuicSessionOwner(); - MOCK_METHOD2(OnConnectionClosed, void(QuicGuid guid, QuicErrorCode error)); + private: + DISALLOW_COPY_AND_ASSIGN(MockPacketWriter); }; -class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { +class MockQuicServerSessionVisitor : public QuicServerSessionVisitor { public: - virtual ~TestDecompressorVisitor() {} - virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; - virtual void OnDecompressionError() OVERRIDE; - - std::string data() { return data_; } - bool error() { return error_; } + MockQuicServerSessionVisitor(); + virtual ~MockQuicServerSessionVisitor(); + MOCK_METHOD2(OnConnectionClosed, void(QuicConnectionId connection_id, + QuicErrorCode error)); + MOCK_METHOD1(OnWriteBlocked, void(QuicBlockedWriterInterface* writer)); private: - std::string data_; - bool error_; + DISALLOW_COPY_AND_ASSIGN(MockQuicServerSessionVisitor); }; class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface { public: MockAckNotifierDelegate(); + + MOCK_METHOD5(OnAckNotification, void(int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed)); + + protected: + // Object is ref counted. virtual ~MockAckNotifierDelegate(); - MOCK_METHOD0(OnAckNotification, void()); + DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate); }; } // namespace test diff --git a/chromium/net/tools/quic/test_tools/server_thread.cc b/chromium/net/tools/quic/test_tools/server_thread.cc index a1bebc760d5..337cc64cd08 100644 --- a/chromium/net/tools/quic/test_tools/server_thread.cc +++ b/chromium/net/tools/quic/test_tools/server_thread.cc @@ -10,57 +10,62 @@ namespace net { namespace tools { namespace test { -ServerThread::ServerThread(IPEndPoint address, - const QuicConfig& config, - const QuicVersionVector& supported_versions, +ServerThread::ServerThread(QuicServer* server, + IPEndPoint address, bool strike_register_no_startup_period) : SimpleThread("server_thread"), - listening_(true, false), confirmed_(true, false), pause_(true, false), paused_(true, false), resume_(true, false), quit_(true, false), - server_(config, supported_versions), + server_(server), address_(address), - port_(0) { + port_(0), + initialized_(false) { if (strike_register_no_startup_period) { - server_.SetStrikeRegisterNoStartupPeriod(); + server_->SetStrikeRegisterNoStartupPeriod(); } } -ServerThread::~ServerThread() { -} +ServerThread::~ServerThread() {} -void ServerThread::Run() { - server_.Listen(address_); +void ServerThread::Initialize() { + if (initialized_) { + return; + } + + server_->Listen(address_); port_lock_.Acquire(); - port_ = server_.port(); + port_ = server_->port(); port_lock_.Release(); - listening_.Signal(); + initialized_ = true; +} + +void ServerThread::Run() { + if (!initialized_) { + Initialize(); + } + while (!quit_.IsSignaled()) { if (pause_.IsSignaled() && !resume_.IsSignaled()) { paused_.Signal(); resume_.Wait(); } - server_.WaitForEvents(); + server_->WaitForEvents(); MaybeNotifyOfHandshakeConfirmation(); } - server_.Shutdown(); + server_->Shutdown(); } int ServerThread::GetPort() { port_lock_.Acquire(); int rc = port_; port_lock_.Release(); - return rc; -} - -void ServerThread::WaitForServerStartup() { - listening_.Wait(); + return rc; } void ServerThread::WaitForCryptoHandshakeConfirmed() { diff --git a/chromium/net/tools/quic/test_tools/server_thread.h b/chromium/net/tools/quic/test_tools/server_thread.h index ed36c37b349..6066d974d18 100644 --- a/chromium/net/tools/quic/test_tools/server_thread.h +++ b/chromium/net/tools/quic/test_tools/server_thread.h @@ -17,18 +17,18 @@ namespace test { // Simple wrapper class to run server in a thread. class ServerThread : public base::SimpleThread { public: - ServerThread(IPEndPoint address, - const QuicConfig& config, - const QuicVersionVector& supported_versions, + ServerThread(QuicServer* server, + IPEndPoint address, bool strike_register_no_startup_period); virtual ~ServerThread(); - // SimpleThread implementation. - virtual void Run() OVERRIDE; + // Prepares the server, but does not start accepting connections. Useful for + // injecting mocks. + void Initialize(); - // Waits until the server has started and is listening for requests. - void WaitForServerStartup(); + // Runs the event loop. Will initialize if necessary. + virtual void Run() OVERRIDE; // Waits for the handshake to be confirmed for the first session created. void WaitForCryptoHandshakeConfirmed(); @@ -48,7 +48,7 @@ class ServerThread : public base::SimpleThread { // Returns the underlying server. Care must be taken to avoid data races // when accessing the server. It is always safe to access the server // after calling Pause() and before calling Resume(). - QuicServer* server() { return &server_; } + QuicServer* server() { return server_.get(); } // Returns the port that the server is listening on. int GetPort(); @@ -56,7 +56,6 @@ class ServerThread : public base::SimpleThread { private: void MaybeNotifyOfHandshakeConfirmation(); - base::WaitableEvent listening_; // Notified when the server is listening. base::WaitableEvent confirmed_; // Notified when the first handshake is // confirmed. base::WaitableEvent pause_; // Notified when the server should pause. @@ -64,11 +63,13 @@ class ServerThread : public base::SimpleThread { base::WaitableEvent resume_; // Notified when the server should resume. base::WaitableEvent quit_; // Notified when the server should quit. - tools::QuicServer server_; + scoped_ptr<QuicServer> server_; IPEndPoint address_; base::Lock port_lock_; int port_; + bool initialized_; + DISALLOW_COPY_AND_ASSIGN(ServerThread); }; diff --git a/chromium/net/tools/quic/test_tools/simple_client.cc b/chromium/net/tools/quic/test_tools/simple_client.cc new file mode 100644 index 00000000000..46f5b9d6877 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/simple_client.cc @@ -0,0 +1,36 @@ +// Copyright 2014 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/quic/test_tools/simple_client.h" + +namespace net { +namespace tools { +namespace test { + +void SimpleClient::WaitForResponse() { + WaitForResponseForMs(-1); +} + +// Waits for some data or response from the server. +void SimpleClient::WaitForInitialResponse() { + WaitForInitialResponseForMs(-1); +} + +int SimpleClient::ResetSocket() { + LOG(FATAL) << "SimpleClient::ResetSocket is not implemented"; + return 0; +} + +int SimpleClient::HalfClose() { + LOG(FATAL) << "SimpleClient::HalfClose is not implemented"; + return 0; +} + +int SimpleClient::response_header_size() const { return 0; } + +int64 SimpleClient::response_body_size() const { return 0; } + +} // namespace net +} // namespace tools +} // namespace test diff --git a/chromium/net/tools/quic/test_tools/simple_client.h b/chromium/net/tools/quic/test_tools/simple_client.h new file mode 100644 index 00000000000..9277fcbf18d --- /dev/null +++ b/chromium/net/tools/quic/test_tools/simple_client.h @@ -0,0 +1,159 @@ +// Copyright 2014 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_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_ + +#include <string> +#include <vector> + +#include "net/base/ip_endpoint.h" +#include "net/tools/balsa/balsa_frame.h" + +namespace net { +namespace tools { +namespace test { + +class HTTPMessage; + +class SimpleClient { + public: + virtual ~SimpleClient() {} + + // Clears any outstanding state and sends 'size' bytes from 'buffer' to the + // server, possibly with multiple send operations. Returns 'size' on success + // and -1 on error. Callers should assume that any return value other than + // 'size' indicates failure. + virtual ssize_t Send(const void *buffer, size_t size) = 0; + + // Serialize and send an HTTP request. + virtual ssize_t SendMessage(const HTTPMessage& message) = 0; + + // Clears any outstanding state, sends 'size' bytes from 'buffer' and waits + // for a response or an error. + virtual ssize_t SendAndWaitForResponse(const void *buffer, size_t size) = 0; + + // Clears any outstanding state and sends a simple GET of 'uri' to the + // server. + virtual ssize_t SendRequest(const std::string& uri) = 0; + + // The response body is returned as a string. + virtual std::string SendCustomSynchronousRequest( + const HTTPMessage& message) = 0; + virtual std::string SendSynchronousRequest(const std::string& url) = 0; + + // Returns once a complete response or a connection close has been received + // from the server. + virtual void WaitForResponse(); + + // Waits for some data or response from the server. + virtual void WaitForInitialResponse(); + + // Returns once a complete response or a connection close has been received + // from the server, or once the timeout expires. -1 for no timeout. + virtual void WaitForResponseForMs(int timeout_ms) = 0; + + // Waits for some data or response from the server, or once the timeout + // expires. -1 for no timeout. + virtual void WaitForInitialResponseForMs(int timeout_ms) = 0; + + // Clears any outstanding state from the last request. + virtual void ClearPerRequestState() = 0; + + // Closes and reopens the connection to the server. + virtual void ResetConnection() = 0; + + // Closes the connection to the server. + virtual void Disconnect() = 0; + + // Both will return 0 on success, -1 otherwise. + // Sends out RST packet to peer. + // TODO(yongfa): Probably should be an interface too. LOG(FATAL) here + // to prevent accidental invocation. + virtual int ResetSocket(); + + virtual int HalfClose(); + + // Connects to the server. This should be done implicitly by Send* + // functions, but can be done explicitly as well. + virtual void Connect() = 0; + + // Bind to the specified address. If set_bind_to_address() is called, this + // is called automatically on connect, but can be done explicitly to make + // LocalIPEndPoint() meaningful before actually connecting. + // Sets *local_address to the actual address bound to, which can be different + // if the given address has port 0. + virtual void Bind(IPEndPoint* local_address) = 0; + + // Returns the local socket address of the client fd. Call only when + // connected. + // To get the local IPAdress, use LocalSocketAddress().host(). + // To get the local port, use LocalSocketAddress.port(). + virtual IPEndPoint LocalSocketAddress() const = 0; + + // Returns the serialized message that would be sent by any of the HTTPMessage + // functions above. + virtual std::string SerializeMessage(const HTTPMessage& message) = 0; + + // Sets the IP address to bind to on future Connect()s in case Bind() is not + // called in advance. If it's set to uninitialized IPAddress, default loopback + // address will be used. + virtual IPAddressNumber bind_to_address() const = 0; + virtual void set_bind_to_address(IPAddressNumber address) = 0; + + // Returns true if the headers have been processed and are available. + virtual bool response_headers_complete() const = 0; + + // Returns the response headers, if a response was completely framed. + // Undefined behavior otherwise. + virtual const BalsaHeaders* response_headers() const = 0; + + // Returns true iff response has been fully received. + virtual bool response_complete() const = 0; + + // Returns the number of bytes read from the server during this request. + virtual int64 response_size() const = 0; + + // Returns the number of header bytes received during this request, if + // meaningful for the protocol. + virtual int response_header_size() const; + + // Returns the number of body bytes received during this request, if + // meaningful for the protocol. + virtual int64 response_body_size() const; + + // Returns the response body, if there was one. If there was no response, or + // if buffer_body() is false, returns an empty string. + virtual const std::string& response_body() = 0; + + // The address the client is connected to. + virtual const IPEndPoint& address() const = 0; + + // Returns true if the client is connected, false otherwise. + virtual bool connected() const = 0; + + // Returns true if the server has informed the client that it is + // in "lame duck" mode, indicating intent to shut down and + // requesting that no further connections be established. + virtual bool ServerInLameDuckMode() const = 0; + + // Return the number of bytes read off the wire by this client. + virtual size_t bytes_read() const = 0; + + // Returns the number of bytes written to the wire by this client. + virtual size_t bytes_written() const = 0; + + // Return the number of requests sent. + virtual size_t requests_sent() const = 0; + + // Instructs the client to populate response_body(). + virtual bool buffer_body() const = 0; + virtual void set_buffer_body(bool buffer_body) = 0; +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_SIMPLE_CLIENT_H_ diff --git a/chromium/net/tools/testserver/run_testserver.cc b/chromium/net/tools/testserver/run_testserver.cc index acf4177a8dd..717fee17a19 100644 --- a/chromium/net/tools/testserver/run_testserver.cc +++ b/chromium/net/tools/testserver/run_testserver.cc @@ -25,8 +25,8 @@ int main(int argc, const char* argv[]) { base::MessageLoopForIO message_loop; // Process command line - CommandLine::Init(argc, argv); - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_ALL; @@ -113,7 +113,8 @@ int main(int argc, const char* argv[]) { if (!base::DirectoryExists(test_server->document_root())) { printf("Error: invalid doc root: \"%s\" does not exist!\n", - UTF16ToUTF8(test_server->document_root().LossyDisplayName()).c_str()); + base::UTF16ToUTF8( + test_server->document_root().LossyDisplayName()).c_str()); return -1; } diff --git a/chromium/net/tools/testserver/testserver.py b/chromium/net/tools/testserver/testserver.py index 0a1f59b0c59..33faf463d3a 100755 --- a/chromium/net/tools/testserver/testserver.py +++ b/chromium/net/tools/testserver/testserver.py @@ -27,6 +27,7 @@ import re import select import socket import SocketServer +import ssl import struct import sys import threading @@ -38,20 +39,36 @@ import zlib BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(BASE_DIR))) -import echo_message -import testserver_base +# Temporary hack to deal with tlslite 0.3.8 -> 0.4.6 upgrade. +# +# TODO(davidben): Remove this when it has cycled through all the bots and +# developer checkouts or when http://crbug.com/356276 is resolved. +try: + os.remove(os.path.join(ROOT_DIR, 'third_party', 'tlslite', + 'tlslite', 'utils', 'hmac.pyc')) +except Exception: + pass # Append at the end of sys.path, it's fine to use the system library. sys.path.append(os.path.join(ROOT_DIR, 'third_party', 'pyftpdlib', 'src')) -sys.path.append(os.path.join(ROOT_DIR, 'third_party', 'tlslite')) -import pyftpdlib.ftpserver -import tlslite -import tlslite.api -# Insert at the beginning of the path, we want this to be used +# Insert at the beginning of the path, we want to use our copies of the library # unconditionally. sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'pywebsocket', 'src')) +sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'tlslite')) + +import mod_pywebsocket.standalone from mod_pywebsocket.standalone import WebSocketServer +# import manually +mod_pywebsocket.standalone.ssl = ssl + +import pyftpdlib.ftpserver + +import tlslite +import tlslite.api + +import echo_message +import testserver_base SERVER_HTTP = 0 SERVER_FTP = 1 @@ -84,6 +101,7 @@ class WebSocketOptions: self.certificate = None self.tls_client_auth = False self.tls_client_ca = None + self.tls_module = 'ssl' self.use_basic_auth = False @@ -134,10 +152,12 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, client verification.""" def __init__(self, server_address, request_hander_class, pem_cert_and_key, - ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers, + ssl_client_auth, ssl_client_cas, ssl_client_cert_types, + ssl_bulk_ciphers, ssl_key_exchanges, enable_npn, record_resume_info, tls_intolerant, signed_cert_timestamps, fallback_scsv_enabled, ocsp_response): - self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key) + self.cert_chain = tlslite.api.X509CertChain() + self.cert_chain.parsePemList(pem_cert_and_key) # Force using only python implementation - otherwise behavior is different # depending on whether m2crypto Python module is present (error is thrown # when it is). m2crypto uses a C (based on OpenSSL) implementation under @@ -147,19 +167,38 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, implementations=['python']) self.ssl_client_auth = ssl_client_auth self.ssl_client_cas = [] - self.tls_intolerant = tls_intolerant + self.ssl_client_cert_types = [] + if enable_npn: + self.next_protos = ['http/1.1'] + else: + self.next_protos = None + if tls_intolerant == 0: + self.tls_intolerant = None + else: + self.tls_intolerant = (3, tls_intolerant) self.signed_cert_timestamps = signed_cert_timestamps self.fallback_scsv_enabled = fallback_scsv_enabled self.ocsp_response = ocsp_response - for ca_file in ssl_client_cas: - s = open(ca_file).read() - x509 = tlslite.api.X509() - x509.parse(s) - self.ssl_client_cas.append(x509.subject) + if ssl_client_auth: + for ca_file in ssl_client_cas: + s = open(ca_file).read() + x509 = tlslite.api.X509() + x509.parse(s) + self.ssl_client_cas.append(x509.subject) + + for cert_type in ssl_client_cert_types: + self.ssl_client_cert_types.append({ + "rsa_sign": tlslite.api.ClientCertificateType.rsa_sign, + "dss_sign": tlslite.api.ClientCertificateType.dss_sign, + "ecdsa_sign": tlslite.api.ClientCertificateType.ecdsa_sign, + }[cert_type]) + self.ssl_handshake_settings = tlslite.api.HandshakeSettings() if ssl_bulk_ciphers is not None: self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers + if ssl_key_exchanges is not None: + self.ssl_handshake_settings.keyExchangeNames = ssl_key_exchanges if record_resume_info: # If record_resume_info is true then we'll replace the session cache with @@ -182,6 +221,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, reqCert=self.ssl_client_auth, settings=self.ssl_handshake_settings, reqCAs=self.ssl_client_cas, + reqCertTypes=self.ssl_client_cert_types, + nextProtos=self.next_protos, tlsIntolerant=self.tls_intolerant, signedCertTimestamps= self.signed_cert_timestamps, @@ -288,7 +329,6 @@ class TestPageHandler(testserver_base.BasePageHandler): self.NoContentHandler, self.ServerRedirectHandler, self.ClientRedirectHandler, - self.MultipartHandler, self.GetSSLSessionCacheHandler, self.SSLManySmallRecords, self.GetChannelID, @@ -1398,29 +1438,6 @@ class TestPageHandler(testserver_base.BasePageHandler): return True - def MultipartHandler(self): - """Send a multipart response (10 text/html pages).""" - - test_name = '/multipart' - if not self._ShouldHandleRequest(test_name): - return False - - num_frames = 10 - bound = '12345' - self.send_response(200) - self.send_header('Content-Type', - 'multipart/x-mixed-replace;boundary=' + bound) - self.end_headers() - - for i in xrange(num_frames): - self.wfile.write('--' + bound + '\r\n') - self.wfile.write('Content-Type: text/html\r\n\r\n') - self.wfile.write('<title>page ' + str(i) + '</title>') - self.wfile.write('page ' + str(i)) - - self.wfile.write('--' + bound + '--') - return True - def GetSSLSessionCacheHandler(self): """Send a reply containing a log of the session cache operations.""" @@ -1431,11 +1448,14 @@ class TestPageHandler(testserver_base.BasePageHandler): self.send_header('Content-Type', 'text/plain') self.end_headers() try: - for (action, sessionID) in self.server.session_cache.log: - self.wfile.write('%s\t%s\n' % (action, sessionID.encode('hex'))) + log = self.server.session_cache.log except AttributeError: self.wfile.write('Pass --https-record-resume in order to use' + ' this request') + return True + + for (action, sessionID) in log: + self.wfile.write('%s\t%s\n' % (action, bytes(sessionID).encode('hex'))) return True def SSLManySmallRecords(self): @@ -1465,7 +1485,7 @@ class TestPageHandler(testserver_base.BasePageHandler): self.send_response(200) self.send_header('Content-Type', 'text/plain') self.end_headers() - channel_id = self.server.tlsConnection.channel_id.tostring() + channel_id = bytes(self.server.tlsConnection.channel_id) self.wfile.write(hashlib.sha256(channel_id).digest().encode('base64')) return True @@ -1956,17 +1976,22 @@ class ServerRunner(testserver_base.TestServerRunner): server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, self.options.ssl_client_auth, self.options.ssl_client_ca, + self.options.ssl_client_cert_type, self.options.ssl_bulk_cipher, + self.options.ssl_key_exchange, + self.options.enable_npn, self.options.record_resume, self.options.tls_intolerant, self.options.signed_cert_timestamps_tls_ext.decode( "base64"), self.options.fallback_scsv, stapled_ocsp_response) - print 'HTTPS server started on %s:%d...' % (host, server.server_port) + print 'HTTPS server started on https://%s:%d...' % \ + (host, server.server_port) else: server = HTTPServer((host, port), TestPageHandler) - print 'HTTP server started on %s:%d...' % (host, server.server_port) + print 'HTTP server started on http://%s:%d...' % \ + (host, server.server_port) server.data_dir = self.__make_data_dir() server.file_root_url = self.options.file_root_url @@ -1979,7 +2004,9 @@ class ServerRunner(testserver_base.TestServerRunner): # is required to work correctly. It should be fixed from pywebsocket side. os.chdir(self.__make_data_dir()) websocket_options = WebSocketOptions(host, port, '.') + scheme = "ws" if self.options.cert_and_key_file: + scheme = "wss" websocket_options.use_tls = True websocket_options.private_key = self.options.cert_and_key_file websocket_options.certificate = self.options.cert_and_key_file @@ -1994,7 +2021,8 @@ class ServerRunner(testserver_base.TestServerRunner): self.options.ssl_client_ca[0] + ' exiting...') websocket_options.tls_client_ca = self.options.ssl_client_ca[0] server = WebSocketServer(websocket_options) - print 'WebSocket server started on %s:%d...' % (host, server.server_port) + print 'WebSocket server started on %s://%s:%d...' % \ + (scheme, host, server.server_port) server_data['port'] = server.server_port elif self.options.server_type == SERVER_TCP_ECHO: # Used for generating the key (randomly) that encodes the "echo request" @@ -2137,6 +2165,15 @@ class ServerRunner(testserver_base.TestServerRunner): 'file. This option may appear multiple ' 'times, indicating multiple CA names should ' 'be sent in the request.') + self.option_parser.add_option('--ssl-client-cert-type', action='append', + default=[], help='Specify that the client ' + 'certificate request should include the ' + 'specified certificate_type value. This ' + 'option may appear multiple times, ' + 'indicating multiple values should be send ' + 'in the request. Valid values are ' + '"rsa_sign", "dss_sign", and "ecdsa_sign". ' + 'If omitted, "rsa_sign" will be used.') self.option_parser.add_option('--ssl-bulk-cipher', action='append', help='Specify the bulk encryption ' 'algorithm(s) that will be accepted by the ' @@ -2145,6 +2182,21 @@ class ServerRunner(testserver_base.TestServerRunner): 'algorithms will be used. This option may ' 'appear multiple times, indicating ' 'multiple algorithms should be enabled.'); + self.option_parser.add_option('--ssl-key-exchange', action='append', + help='Specify the key exchange algorithm(s)' + 'that will be accepted by the SSL server. ' + 'Valid values are "rsa", "dhe_rsa". If ' + 'omitted, all algorithms will be used. This ' + 'option may appear multiple times, ' + 'indicating multiple algorithms should be ' + 'enabled.'); + # TODO(davidben): Add ALPN support to tlslite. + self.option_parser.add_option('--enable-npn', dest='enable_npn', + default=False, const=True, + action='store_const', + help='Enable server support for the NPN ' + 'extension. The server will advertise ' + 'support for exactly one protocol, http/1.1') self.option_parser.add_option('--file-root-url', default='/files/', help='Specify a root URL for files served.') diff --git a/chromium/net/tools/testserver/testserver_base.py b/chromium/net/tools/testserver/testserver_base.py index 455ca5c3202..0d3f65fdca9 100644 --- a/chromium/net/tools/testserver/testserver_base.py +++ b/chromium/net/tools/testserver/testserver_base.py @@ -14,6 +14,8 @@ import struct import sys import warnings +import tlslite.errors + # Ignore deprecation warnings, they make our output more cluttered. warnings.filterwarnings("ignore", category=DeprecationWarning) @@ -71,6 +73,9 @@ class BrokenPipeHandlerMixIn: def handle_error(self, request, client_address): value = sys.exc_info()[1] + if isinstance(value, tlslite.errors.TLSClosedConnectionError): + print "testserver.py: Closed connection" + return if isinstance(value, socket.error): err = value.args[0] if sys.platform in ('win32', 'cygwin'): @@ -81,6 +86,9 @@ class BrokenPipeHandlerMixIn: if err == pipe_err: print "testserver.py: Broken pipe" return + if err == errno.ECONNRESET: + print "testserver.py: Connection reset by peer" + return SocketServer.BaseServer.handle_error(self, request, client_address) diff --git a/chromium/net/tools/tld_cleanup/BUILD.gn b/chromium/net/tools/tld_cleanup/BUILD.gn new file mode 100644 index 00000000000..bf89c008bb0 --- /dev/null +++ b/chromium/net/tools/tld_cleanup/BUILD.gn @@ -0,0 +1,14 @@ +# Copyright 2014 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. + +source_set("tld_cleanup") { + sources = [ + "tld_cleanup_util.cc", + "tld_cleanup_util.h", + ] + deps = [ + "//base", + "//url", + ] +} diff --git a/chromium/net/tools/tld_cleanup/PRESUBMIT.py b/chromium/net/tools/tld_cleanup/PRESUBMIT.py new file mode 100644 index 00000000000..8391e0e61a1 --- /dev/null +++ b/chromium/net/tools/tld_cleanup/PRESUBMIT.py @@ -0,0 +1,32 @@ +# Copyright 2014 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. + + +"""Chromium presubmit script for src/net/tools/tld_cleanup.""" + + +def _RunMakeDafsaTests(input_api, output_api): + """Runs unittest for make_dafsa if any related file has been modified.""" + files = ('net/tools/tld_cleanup/make_dafsa.py', + 'net/tools/tld_cleanup/make_dafsa_unittest.py') + if not any(f in input_api.LocalPaths() for f in files): + return [] + test_path = input_api.os_path.join(input_api.PresubmitLocalPath(), + 'make_dafsa_unittest.py') + cmd_name = 'make_dafsa_unittest' + cmd = [input_api.python_executable, test_path] + test_cmd = input_api.Command( + name=cmd_name, + cmd=cmd, + kwargs={}, + message=output_api.PresubmitPromptWarning) + return input_api.RunTests([test_cmd]) + + +def CheckChangeOnUpload(input_api, output_api): + return _RunMakeDafsaTests(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return _RunMakeDafsaTests(input_api, output_api) diff --git a/chromium/net/tools/tld_cleanup/README b/chromium/net/tools/tld_cleanup/README index a7f137d7660..13614700e42 100644 --- a/chromium/net/tools/tld_cleanup/README +++ b/chromium/net/tools/tld_cleanup/README @@ -20,12 +20,9 @@ When updating src/net/base/registry_controlled_domains/effective_tld_names.dat: src/build/Debug. It will re-generate src/net/base/registry_controlled_domains/effective_tld_names.gperf. -6. Run gperf on the new effective_tld_names.gperf: - pushd src/net/base/registry_controlled_domains; - gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -P -K name_offset -D -m 10 \ - effective_tld_names.gperf > effective_tld_names.cc; - popd; - It will produce a new effective_tld_names.cc. +6. Check in the updated effective_tld_names.dat, effective_tld_names.gperf -7. Check in the updated effective_tld_names.dat, effective_tld_names.gperf, - and effective_tld_names.cc together. +Note that gperf is no longer used for effective_tld_names, but when building +chromium the file effective_tld_names.gperf will be parsed by make_dafsa.py +to generate the file effective_tld_names-inc.cc, which is included in +registry_controlled_domain.cc diff --git a/chromium/net/tools/tld_cleanup/make_dafsa.py b/chromium/net/tools/tld_cleanup/make_dafsa.py new file mode 100755 index 00000000000..78358effa84 --- /dev/null +++ b/chromium/net/tools/tld_cleanup/make_dafsa.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +""" +A Deterministic acyclic finite state automaton (DAFSA) is a compact +representation of an unordered word list (dictionary). + +http://en.wikipedia.org/wiki/Deterministic_acyclic_finite_state_automaton + +This python program converts a list of strings to a byte array in C++. +This python program fetches strings and return values from a gperf file +and generates a C++ file with a byte array representing graph that can be +used as a memory efficient replacement for the perfect hash table. + +The input strings are assumed to consist of printable 7-bit ASCII characters +and the return values are assumed to be one digit integers. + +In this program a DAFSA is a diamond shaped graph starting at a common +source node and ending at a common sink node. All internal nodes contain +a label and each word is represented by the labels in one path from +the source node to the sink node. + +The following python represention is used for nodes: + + Source node: [ children ] + Internal node: (label, [ children ]) + Sink node: None + +The graph is first compressed by prefixes like a trie. In the next step +suffixes are compressed so that the graph gets diamond shaped. Finally +one to one linked nodes are replaced by nodes with the labels joined. + +The order of the operations is crucial since lookups will be performed +starting from the source with no backtracking. Thus a node must have at +most one child with a label starting by the same character. The output +is also arranged so that all jumps are to increasing addresses, thus forward +in memory. + +The generated output has suffix free decoding so that the sign of leading +bits in a link (a reference to a child node) indicate if it has a size of one, +two or three bytes and if it is the last outgoing link from the actual node. +A node label is terminated by a byte with the leading bit set. + +The generated byte array can described by the following BNF: + +<byte> ::= < 8-bit value in range [0x00-0xFF] > + +<char> ::= < printable 7-bit ASCII character, byte in range [0x20-0x7F] > +<end_char> ::= < char + 0x80, byte in range [0xA0-0xFF] > +<return value> ::= < value + 0x80, byte in range [0x80-0x8F] > + +<offset1> ::= < byte in range [0x00-0x3F] > +<offset2> ::= < byte in range [0x40-0x5F] > +<offset3> ::= < byte in range [0x60-0x7F] > + +<end_offset1> ::= < byte in range [0x80-0xBF] > +<end_offset2> ::= < byte in range [0xC0-0xDF] > +<end_offset3> ::= < byte in range [0xE0-0xFF] > + +<prefix> ::= <char> + +<label> ::= <end_char> + | <char> <label> + +<end_label> ::= <return_value> + | <char> <end_label> + +<offset> ::= <offset1> + | <offset2> <byte> + | <offset3> <byte> <byte> + +<end_offset> ::= <end_offset1> + | <end_offset2> <byte> + | <end_offset3> <byte> <byte> + +<offsets> ::= <end_offset> + | <offset> <offsets> + +<source> ::= <offsets> + +<node> ::= <label> <offsets> + | <prefix> <node> + | <end_label> + +<dafsa> ::= <source> + | <dafsa> <node> + +Decoding: + +<char> -> printable 7-bit ASCII character +<end_char> & 0x7F -> printable 7-bit ASCII character +<return value> & 0x0F -> integer +<offset1 & 0x3F> -> integer +((<offset2> & 0x1F>) << 8) + <byte> -> integer +((<offset3> & 0x1F>) << 16) + (<byte> << 8) + <byte> -> integer + +end_offset1, end_offset2 and and_offset3 are decoded same as offset1, +offset2 and offset3 respectively. + +The first offset in a list of offsets is the distance in bytes between the +offset itself and the first child node. Subsequent offsets are the distance +between previous child node and next child node. Thus each offset links a node +to a child node. The distance is always counted between start addresses, i.e. +first byte in decoded offset or first byte in child node. + +Example 1: + +%% +aa, 1 +a, 2 +%% + +The input is first parsed to a list of words: +["aa1", "a2"] + +A fully expanded graph is created from the words: +source = [node1, node4] +node1 = ("a", [node2]) +node2 = ("a", [node3]) +node3 = ("\x01", [sink]) +node4 = ("a", [node5]) +node5 = ("\x02", [sink]) +sink = None + +Compression results in the following graph: +source = [node1] +node1 = ("a", [node2, node3]) +node2 = ("\x02", [sink]) +node3 = ("a\x01", [sink]) +sink = None + +A C++ representation of the compressed graph is generated: + +const unsigned char dafsa[7] = { + 0x81, 0xE1, 0x02, 0x81, 0x82, 0x61, 0x81, +}; + +The bytes in the generated array has the following meaning: + + 0: 0x81 <end_offset1> child at position 0 + (0x81 & 0x3F) -> jump to 1 + + 1: 0xE1 <end_char> label character (0xE1 & 0x7F) -> match "a" + 2: 0x02 <offset1> child at position 2 + (0x02 & 0x3F) -> jump to 4 + + 3: 0x81 <end_offset1> child at position 4 + (0x81 & 0x3F) -> jump to 5 + 4: 0x82 <return_value> 0x82 & 0x0F -> return 2 + + 5: 0x61 <char> label character 0x61 -> match "a" + 6: 0x81 <return_value> 0x81 & 0x0F -> return 1 + +Example 2: + +%% +aa, 1 +bbb, 2 +baa, 1 +%% + +The input is first parsed to a list of words: +["aa1", "bbb2", "baa1"] + +Compression results in the following graph: +source = [node1, node2] +node1 = ("b", [node2, node3]) +node2 = ("aa\x01", [sink]) +node3 = ("bb\x02", [sink]) +sink = None + +A C++ representation of the compressed graph is generated: + +const unsigned char dafsa[11] = { + 0x02, 0x83, 0xE2, 0x02, 0x83, 0x61, 0x61, 0x81, 0x62, 0x62, 0x82, +}; + +The bytes in the generated array has the following meaning: + + 0: 0x02 <offset1> child at position 0 + (0x02 & 0x3F) -> jump to 2 + 1: 0x83 <end_offset1> child at position 2 + (0x83 & 0x3F) -> jump to 5 + + 2: 0xE2 <end_char> label character (0xE2 & 0x7F) -> match "b" + 3: 0x02 <offset1> child at position 3 + (0x02 & 0x3F) -> jump to 5 + 4: 0x83 <end_offset1> child at position 5 + (0x83 & 0x3F) -> jump to 8 + + 5: 0x61 <char> label character 0x61 -> match "a" + 6: 0x61 <char> label character 0x61 -> match "a" + 7: 0x81 <return_value> 0x81 & 0x0F -> return 1 + + 8: 0x62 <char> label character 0x62 -> match "b" + 9: 0x62 <char> label character 0x62 -> match "b" +10: 0x82 <return_value> 0x82 & 0x0F -> return 2 +""" + +import sys + +class InputError(Exception): + """Exception raised for errors in the input file.""" + + +def to_dafsa(words): + """Generates a DAFSA from a word list and returns the source node. + + Each word is split into characters so that each character is represented by + a unique node. It is assumed the word list is not empty. + """ + if not words: + raise InputError('The domain list must not be empty') + def ToNodes(word): + """Split words into characters""" + if not 0x1F < ord(word[0]) < 0x80: + raise InputError('Domain names must be printable 7-bit ASCII') + if len(word) == 1: + return chr(ord(word[0]) & 0x0F), [None] + return word[0], [ToNodes(word[1:])] + return [ToNodes(word) for word in words] + + +def to_words(node): + """Generates a word list from all paths starting from an internal node.""" + if not node: + return [''] + return [(node[0] + word) for child in node[1] for word in to_words(child)] + + +def reverse(dafsa): + """Generates a new DAFSA that is reversed, so that the old sink node becomes + the new source node. + """ + sink = [] + nodemap = {} + + def dfs(node, parent): + """Creates reverse nodes. + + A new reverse node will be created for each old node. The new node will + get a reversed label and the parents of the old node as children. + """ + if not node: + sink.append(parent) + elif id(node) not in nodemap: + nodemap[id(node)] = (node[0][::-1], [parent]) + for child in node[1]: + dfs(child, nodemap[id(node)]) + else: + nodemap[id(node)][1].append(parent) + + for node in dafsa: + dfs(node, None) + return sink + + +def join_labels(dafsa): + """Generates a new DAFSA where internal nodes are merged if there is a one to + one connection. + """ + parentcount = { id(None): 2 } + nodemap = { id(None): None } + + def count_parents(node): + """Count incoming references""" + if id(node) in parentcount: + parentcount[id(node)] += 1 + else: + parentcount[id(node)] = 1 + for child in node[1]: + count_parents(child) + + def join(node): + """Create new nodes""" + if id(node) not in nodemap: + children = [join(child) for child in node[1]] + if len(children) == 1 and parentcount[id(node[1][0])] == 1: + child = children[0] + nodemap[id(node)] = (node[0] + child[0], child[1]) + else: + nodemap[id(node)] = (node[0], children) + return nodemap[id(node)] + + for node in dafsa: + count_parents(node) + return [join(node) for node in dafsa] + + +def join_suffixes(dafsa): + """Generates a new DAFSA where nodes that represent the same word lists + towards the sink are merged. + """ + nodemap = { frozenset(('',)): None } + + def join(node): + """Returns a macthing node. A new node is created if no matching node + exists. The graph is accessed in dfs order. + """ + suffixes = frozenset(to_words(node)) + if suffixes not in nodemap: + nodemap[suffixes] = (node[0], [join(child) for child in node[1]]) + return nodemap[suffixes] + + return [join(node) for node in dafsa] + + +def top_sort(dafsa): + """Generates list of nodes in topological sort order.""" + incoming = {} + + def count_incoming(node): + """Counts incoming references.""" + if node: + if id(node) not in incoming: + incoming[id(node)] = 1 + for child in node[1]: + count_incoming(child) + else: + incoming[id(node)] += 1 + + for node in dafsa: + count_incoming(node) + + for node in dafsa: + incoming[id(node)] -= 1 + + waiting = [node for node in dafsa if incoming[id(node)] == 0] + nodes = [] + + while waiting: + node = waiting.pop() + assert incoming[id(node)] == 0 + nodes.append(node) + for child in node[1]: + if child: + incoming[id(child)] -= 1 + if incoming[id(child)] == 0: + waiting.append(child) + return nodes + + +def encode_links(children, offsets, current): + """Encodes a list of children as one, two or three byte offsets.""" + if not children[0]: + # This is an <end_label> node and no links follow such nodes + assert len(children) == 1 + return [] + guess = 3 * len(children) + assert children + children = sorted(children, key = lambda x: -offsets[id(x)]) + while True: + offset = current + guess + buf = [] + for child in children: + last = len(buf) + distance = offset - offsets[id(child)] + assert distance > 0 and distance < (1 << 21) + + if distance < (1 << 6): + # A 6-bit offset: "s0xxxxxx" + buf.append(distance) + elif distance < (1 << 13): + # A 13-bit offset: "s10xxxxxxxxxxxxx" + buf.append(0x40 | (distance >> 8)) + buf.append(distance & 0xFF) + else: + # A 21-bit offset: "s11xxxxxxxxxxxxxxxxxxxxx" + buf.append(0x60 | (distance >> 16)) + buf.append((distance >> 8) & 0xFF) + buf.append(distance & 0xFF) + # Distance in first link is relative to following record. + # Distance in other links are relative to previous link. + offset -= distance + if len(buf) == guess: + break + guess = len(buf) + # Set most significant bit to mark end of links in this node. + buf[last] |= (1 << 7) + buf.reverse() + return buf + + +def encode_prefix(label): + """Encodes a node label as a list of bytes without a trailing high byte. + + This method encodes a node if there is exactly one child and the + child follows immidiately after so that no jump is needed. This label + will then be a prefix to the label in the child node. + """ + assert label + return [ord(c) for c in reversed(label)] + + +def encode_label(label): + """Encodes a node label as a list of bytes with a trailing high byte >0x80. + """ + buf = encode_prefix(label) + # Set most significant bit to mark end of label in this node. + buf[0] |= (1 << 7) + return buf + + +def encode(dafsa): + """Encodes a DAFSA to a list of bytes""" + output = [] + offsets = {} + + for node in reversed(top_sort(dafsa)): + if (len(node[1]) == 1 and node[1][0] and + (offsets[id(node[1][0])] == len(output))): + output.extend(encode_prefix(node[0])) + else: + output.extend(encode_links(node[1], offsets, len(output))) + output.extend(encode_label(node[0])) + offsets[id(node)] = len(output) + + output.extend(encode_links(dafsa, offsets, len(output))) + output.reverse() + return output + + +def to_cxx(data): + """Generates C++ code from a list of encoded bytes.""" + text = '/* This file is generated. DO NOT EDIT!\n\n' + text += 'The byte array encodes effective tld names. See make_dafsa.py for' + text += ' documentation.' + text += '*/\n\n' + text += 'const unsigned char kDafsa[%s] = {\n' % len(data) + for i in range(0, len(data), 12): + text += ' ' + text += ', '.join('0x%02x' % byte for byte in data[i:i + 12]) + text += ',\n' + text += '};\n' + return text + + +def words_to_cxx(words): + """Generates C++ code from a word list""" + dafsa = to_dafsa(words) + for fun in (reverse, join_suffixes, reverse, join_suffixes, join_labels): + dafsa = fun(dafsa) + return to_cxx(encode(dafsa)) + + +def parse_gperf(infile): + """Parses gperf file and extract strings and return code""" + lines = [line.strip() for line in infile] + # Extract strings after the first '%%' and before the second '%%'. + begin = lines.index('%%') + 1 + end = lines.index('%%', begin) + lines = lines[begin:end] + for line in lines: + if line[-3:-1] != ', ': + raise InputError('Expected "domainname, <digit>", found "%s"' % line) + # Technically the DAFSA format could support return values in range [0-31], + # but the values below are the only with a defined meaning. + if line[-1] not in '0124': + raise InputError('Expected value to be one of {0,1,2,4}, found "%s"' % + line[-1]) + return [line[:-3] + line[-1] for line in lines] + + +def main(): + if len(sys.argv) != 3: + print('usage: %s infile outfile' % sys.argv[0]) + return 1 + with open(sys.argv[1], 'r') as infile, open(sys.argv[2], 'w') as outfile: + outfile.write(words_to_cxx(parse_gperf(infile))) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py b/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py new file mode 100755 index 00000000000..5ff92e62292 --- /dev/null +++ b/chromium/net/tools/tld_cleanup/make_dafsa_unittest.py @@ -0,0 +1,757 @@ +#!/usr/bin/env python +# Copyright 2014 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. + + +import sys +import unittest +import make_dafsa + + +class ParseGperfTest(unittest.TestCase): + def testMalformedKey(self): + """Tests exception is thrown at bad format.""" + infile1 = [ '%%', '', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile1) + + infile2 = [ '%%', 'apa,1', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile2) + + infile3 = [ '%%', 'apa, 1', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile3) + + def testBadValues(self): + """Tests exception is thrown when value is out of range.""" + infile1 = [ '%%', 'a, -1', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile1) + + infile2 = [ '%%', 'a, x', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile2) + + infile3 = [ '%%', 'a, 3', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile3) + + infile4 = [ '%%', 'a, 6', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile4) + + infile5 = [ '%%', 'a, 12', '%%' ] + self.assertRaises(make_dafsa.InputError, make_dafsa.parse_gperf, infile5) + + def testValues(self): + """Tests legal values are accepted.""" + infile1 = [ '%%', 'a, 0', '%%' ] + words1 = [ 'a0' ] + self.assertEqual(make_dafsa.parse_gperf(infile1), words1) + + infile2 = [ '%%', 'a, 1', '%%' ] + words2 = [ 'a1' ] + self.assertEqual(make_dafsa.parse_gperf(infile2), words2) + + infile3 = [ '%%', 'a, 2', '%%' ] + words3 = [ 'a2' ] + self.assertEqual(make_dafsa.parse_gperf(infile3), words3) + + infile4 = [ '%%', 'a, 4', '%%' ] + words4 = [ 'a4' ] + self.assertEqual(make_dafsa.parse_gperf(infile4), words4) + + def testOneWord(self): + """Tests a single key can be parsed.""" + infile = [ '%%', 'apa, 1', '%%' ] + words = [ 'apa1' ] + self.assertEqual(make_dafsa.parse_gperf(infile), words) + + def testTwoWords(self): + """Tests a sequence of keys can be parsed.""" + infile = [ '%%', 'apa, 1', 'bepa.com, 2', '%%' ] + words = [ 'apa1', 'bepa.com2' ] + self.assertEqual(make_dafsa.parse_gperf(infile), words) + + +class ToDafsaTest(unittest.TestCase): + def testEmptyInput(self): + """Tests exception is thrown at empty input.""" + words = () + self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words) + + def testNonASCII(self): + """Tests exception is thrown if illegal characters are used.""" + words1 = ( chr(0x1F) + 'a1', ) + self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words1) + + words2 = ( 'a' + chr(0x1F) + '1', ) + self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words2) + + words3 = ( chr(0x80) + 'a1', ) + self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words3) + + words4 = ( 'a' + chr(0x80) + '1', ) + self.assertRaises(make_dafsa.InputError, make_dafsa.to_dafsa, words4) + + def testChar(self): + """Tests a DAFSA can be created from a single character domain name.""" + words = [ 'a0' ] + node2 = ( chr(0), [ None ] ) + node1 = ( 'a', [ node2 ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.to_dafsa(words), source) + + def testChars(self): + """Tests a DAFSA can be created from a multi character domain name.""" + words = [ 'ab0' ] + node3 = ( chr(0), [ None ] ) + node2 = ( 'b', [ node3 ] ) + node1 = ( 'a', [ node2 ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.to_dafsa(words), source) + + def testWords(self): + """Tests a DAFSA can be created from a sequence of domain names.""" + words = [ 'a0', 'b1' ] + node4 = ( chr(1), [ None ] ) + node3 = ( 'b', [ node4 ] ) + node2 = ( chr(0), [ None ] ) + node1 = ( 'a', [ node2 ] ) + source = [ node1, node3 ] + self.assertEqual(make_dafsa.to_dafsa(words), source) + + +class ToWordsTest(unittest.TestCase): + def testSink(self): + """Tests the sink is exapnded to a list with an empty string.""" + node1 = None + words = [ '' ] + self.assertEqual(make_dafsa.to_words(node1), words) + + def testSingleNode(self): + """Tests a single node is expanded to a list with the label string.""" + + # 'ab' -> [ 'ab' ] + + node1 = ( 'ab', [ None ] ) + words = [ 'ab' ] + self.assertEqual(make_dafsa.to_words(node1), words) + + def testChain(self): + """Tests a sequence of nodes are preoperly expanded.""" + + # 'ab' -> 'cd' => [ 'abcd' ] + + node2 = ( 'cd', [ None ] ) + node1 = ( 'ab', [ node2 ] ) + words = [ 'abcd' ] + self.assertEqual(make_dafsa.to_words(node1), words) + + def testInnerTerminator(self): + """Tests a sequence with an inner terminator is expanded to two strings.""" + + # 'a' -> 'b' + # \ => [ 'ab', 'a' ] + # {sink} + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, None ] ) + words = [ 'ab', 'a' ] + self.assertEqual(make_dafsa.to_words(node1), words) + + def testDiamond(self): + """Tests a diamond can be expanded to a word list.""" + + # 'cd' + # / \ + # 'ab' 'gh' + # \ / + # 'ef' + + node4 = ( 'gh', [ None ] ) + node3 = ( 'ef', [ node4 ] ) + node2 = ( 'cd', [ node4 ] ) + node1 = ( 'ab', [ node2, node3 ] ) + words = [ 'abcdgh', 'abefgh' ] + self.assertEqual(make_dafsa.to_words(node1), words) + + +class JoinLabelsTest(unittest.TestCase): + def testLabel(self): + """Tests a single label passes unchanged.""" + + # 'a' => 'a' + + node1 = ( 'a', [ None ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_labels(source), source) + + def testInnerTerminator(self): + """Tests a sequence with an inner terminator passes unchanged.""" + + # 'a' -> 'b' 'a' -> 'b' + # \ => \ + # {sink} {sink} + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, None ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_labels(source), source) + + def testLabels(self): + """Tests a sequence of labels can be joined.""" + + # 'a' -> 'b' => 'ab' + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2 ] ) + source1 = [ node1 ] + node3 = ( 'ab', [ None ] ) + source2 = [ node3 ] + self.assertEqual(make_dafsa.join_labels(source1), source2) + + def testCompositeLabels(self): + """Tests a sequence of multi character labels can be joined.""" + + # 'ab' -> 'cd' => 'abcd' + + node2 = ( 'cd', [ None ] ) + node1 = ( 'ab', [ node2 ] ) + source1 = [ node1 ] + node3 = ( 'abcd', [ None ] ) + source2 = [ node3 ] + self.assertEqual(make_dafsa.join_labels(source1), source2) + + def testAtomicTrie(self): + """Tests a trie formed DAFSA with atomic labels passes unchanged.""" + + # 'b' 'b' + # / / + # 'a' => 'a' + # \ \ + # 'c' 'c' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, node3 ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_labels(source), source) + + def testReverseAtomicTrie(self): + """Tests a reverse trie formed DAFSA with atomic labels passes unchanged.""" + + # 'a' 'a' + # \ \ + # 'c' => 'c' + # / / + # 'b' 'b' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ node3 ] ) + node1 = ( 'a', [ node3 ] ) + source = [ node1, node2 ] + self.assertEqual(make_dafsa.join_labels(source), source) + + def testChainedTrie(self): + """Tests a trie formed DAFSA with chained labels can be joined.""" + + # 'c' -> 'd' 'cd' + # / / + # 'a' -> 'b' => 'ab' + # \ \ + # 'e' -> 'f' 'ef' + + node6 = ( 'f', [ None ] ) + node5 = ( 'e', [ node6 ] ) + node4 = ( 'd', [ None ] ) + node3 = ( 'c', [ node4 ] ) + node2 = ( 'b', [ node3, node5 ] ) + node1 = ( 'a', [ node2 ] ) + source1 = [ node1 ] + node9 = ( 'ef', [ None ] ) + node8 = ( 'cd', [ None ] ) + node7 = ( 'ab', [ node8, node9 ] ) + source2 = [ node7 ] + self.assertEqual(make_dafsa.join_labels(source1), source2) + + def testReverseChainedTrie(self): + """Tests a reverse trie formed DAFSA with chained labels can be joined.""" + + # 'a' -> 'b' 'ab' + # \ \ + # 'e' -> 'f' => 'ef' + # / / + # 'c' -> 'd' 'cd' + + node6 = ( 'f', [ None ] ) + node5 = ( 'e', [ node6 ] ) + node4 = ( 'd', [ node5 ] ) + node3 = ( 'c', [ node4 ] ) + node2 = ( 'b', [ node5 ] ) + node1 = ( 'a', [ node2 ] ) + source1 = [ node1, node3 ] + node9 = ( 'ef', [ None ] ) + node8 = ( 'cd', [ node9 ] ) + node7 = ( 'ab', [ node9 ] ) + source2 = [ node7, node8 ] + self.assertEqual(make_dafsa.join_labels(source1), source2) + + +class JoinSuffixesTest(unittest.TestCase): + def testSingleLabel(self): + """Tests a single label passes unchanged.""" + + # 'a' => 'a' + + node1 = ( 'a', [ None ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_suffixes(source), source) + + def testInnerTerminator(self): + """Tests a sequence with an inner terminator passes unchanged.""" + + # 'a' -> 'b' 'a' -> 'b' + # \ => \ + # {sink} {sink} + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, None ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_suffixes(source), source) + + def testDistinctTrie(self): + """Tests a trie formed DAFSA with distinct labels passes unchanged.""" + + # 'b' 'b' + # / / + # 'a' => 'a' + # \ \ + # 'c' 'c' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, node3 ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.join_suffixes(source), source) + + def testReverseDistinctTrie(self): + """Tests a reverse trie formed DAFSA with distinct labels passes unchanged. + """ + + # 'a' 'a' + # \ \ + # 'c' => 'c' + # / / + # 'b' 'b' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ node3 ] ) + node1 = ( 'a', [ node3 ] ) + source = [ node1, node2 ] + self.assertEqual(make_dafsa.join_suffixes(source), source) + + def testJoinTwoHeads(self): + """Tests two heads can be joined even if there is something else between.""" + + # 'a' ------'a' + # / + # 'b' => 'b' / + # / + # 'a' --- + # + # The picture above should shows that the new version should have just one + # instance of the node with label 'a'. + + node3 = ( 'a', [ None ] ) + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ None ] ) + source1 = [ node1, node2, node3 ] + source2 = make_dafsa.join_suffixes(source1) + + # Both versions should expand to the same content. + self.assertEqual(source1, source2) + # But the new version should have just one instance of 'a'. + self.assertIs(source2[0], source2[2]) + + def testJoinTails(self): + """Tests tails can be joined.""" + + # 'a' -> 'c' 'a' + # \ + # => 'c' + # / + # 'b' -> 'c' 'b' + + node4 = ( 'c', [ None ] ) + node3 = ( 'b', [ node4 ] ) + node2 = ( 'c', [ None ] ) + node1 = ( 'a', [ node2 ] ) + source1 = [ node1, node3 ] + source2 = make_dafsa.join_suffixes(source1) + + # Both versions should expand to the same content. + self.assertEqual(source1, source2) + # But the new version should have just one tail. + self.assertIs(source2[0][1][0], source2[1][1][0]) + + def testMakeRecursiveTrie(self): + """Tests recursive suffix join.""" + + # 'a' -> 'e' -> 'g' 'a' + # \ + # 'e' + # / \ + # 'b' -> 'e' -> 'g' 'b' \ + # \ + # => 'g' + # / + # 'c' -> 'f' -> 'g' 'c' / + # \ / + # 'f' + # / + # 'd' -> 'f' -> 'g' 'd' + + node7 = ( 'g', [ None ] ) + node6 = ( 'f', [ node7 ] ) + node5 = ( 'e', [ node7 ] ) + node4 = ( 'd', [ node6 ] ) + node3 = ( 'c', [ node6 ] ) + node2 = ( 'b', [ node5 ] ) + node1 = ( 'a', [ node5 ] ) + source1 = [ node1, node2, node3, node4 ] + source2 = make_dafsa.join_suffixes(source1) + + # Both versions should expand to the same content. + self.assertEqual(source1, source2) + # But the new version should have just one 'e'. + self.assertIs(source2[0][1][0], source2[1][1][0]) + # And one 'f'. + self.assertIs(source2[2][1][0], source2[3][1][0]) + # And one 'g'. + self.assertIs(source2[0][1][0][1][0], source2[2][1][0][1][0]) + + def testMakeDiamond(self): + """Test we can join suffixes of a trie.""" + + # 'b' -> 'd' 'b' + # / / \ + # 'a' => 'a' 'd' + # \ \ / + # 'c' -> 'd' 'c' + + node5 = ( 'd', [ None ] ) + node4 = ( 'c', [ node5 ] ) + node3 = ( 'd', [ None ] ) + node2 = ( 'b', [ node3 ] ) + node1 = ( 'a', [ node2, node4 ] ) + source1 = [ node1 ] + source2 = make_dafsa.join_suffixes(source1) + + # Both versions should expand to the same content. + self.assertEqual(source1, source2) + # But the new version should have just one 'd'. + self.assertIs(source2[0][1][0][1][0], source2[0][1][1][1][0]) + + def testJoinOneChild(self): + """Tests that we can join some children but not all.""" + + # 'c' ----'c' + # / / / + # 'a' 'a' / + # \ \ / + # 'd' 'd'/ + # => / + # 'c' / + # / / + # 'b' 'b' + # \ \ + # 'e' 'e' + + node6 = ( 'e', [ None ] ) + node5 = ( 'c', [ None ] ) + node4 = ( 'b', [ node5, node6 ] ) + node3 = ( 'd', [ None ] ) + node2 = ( 'c', [ None ] ) + node1 = ( 'a', [ node2, node3 ] ) + source1 = [ node1, node4 ] + source2 = make_dafsa.join_suffixes(source1) + + # Both versions should expand to the same content. + self.assertEqual(source1, source2) + # But the new version should have just one 'c'. + self.assertIs(source2[0][1][0], source2[1][1][0]) + + +class ReverseTest(unittest.TestCase): + def testAtomicLabel(self): + """Tests an atomic label passes unchanged.""" + + # 'a' => 'a' + + node1 = ( 'a', [ None ] ) + source = [ node1 ] + self.assertEqual(make_dafsa.reverse(source), source) + + def testLabel(self): + """Tests that labels are reversed.""" + + # 'ab' => 'ba' + + node1 = ( 'ab', [ None ] ) + source1 = [ node1 ] + node2 = ( 'ba', [ None ] ) + source2 = [ node2 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + def testChain(self): + """Tests that edges are reversed.""" + + # 'a' -> 'b' => 'b' -> 'a' + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2 ] ) + source1 = [ node1 ] + node4 = ( 'a', [ None ] ) + node3 = ( 'b', [ node4 ] ) + source2 = [ node3 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + def testInnerTerminator(self): + """Tests a sequence with an inner terminator can be reversed.""" + + # 'a' -> 'b' 'b' -> 'a' + # \ => / + # {sink} ------ + + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, None ] ) + source1 = [ node1 ] + node4 = ( 'a', [ None ] ) + node3 = ( 'b', [ node4 ] ) + source2 = [ node3, node4 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + def testAtomicTrie(self): + """Tests a trie formed DAFSA can be reversed.""" + + # 'b' 'b' + # / \ + # 'a' => 'a' + # \ / + # 'c' 'c' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ None ] ) + node1 = ( 'a', [ node2, node3 ] ) + source1 = [ node1 ] + node6 = ( 'a', [ None ] ) + node5 = ( 'c', [ node6 ] ) + node4 = ( 'b', [ node6 ] ) + source2 = [ node4, node5 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + def testReverseAtomicTrie(self): + """Tests a reverse trie formed DAFSA can be reversed.""" + + # 'a' 'a' + # \ / + # 'c' => 'c' + # / \ + # 'b' 'b' + + node3 = ( 'c', [ None ] ) + node2 = ( 'b', [ node3 ] ) + node1 = ( 'a', [ node3 ] ) + source1 = [ node1, node2 ] + node6 = ( 'b', [ None ] ) + node5 = ( 'a', [ None ] ) + node4 = ( 'c', [ node5, node6 ] ) + source2 = [ node4 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + def testDiamond(self): + """Tests we can reverse both edges and nodes in a diamond.""" + + # 'cd' 'dc' + # / \ / \ + # 'ab' 'gh' => 'hg' 'ba' + # \ / \ / + # 'ef' 'fe' + + node4 = ( 'gh', [ None ] ) + node3 = ( 'ef', [ node4 ] ) + node2 = ( 'cd', [ node4 ] ) + node1 = ( 'ab', [ node2, node3 ] ) + source1 = [ node1 ] + node8 = ( 'ba', [ None ] ) + node7 = ( 'fe', [ node8 ] ) + node6 = ( 'dc', [ node8 ] ) + node5 = ( 'hg', [ node6, node7 ] ) + source2 = [ node5 ] + self.assertEqual(make_dafsa.reverse(source1), source2) + + +class TopSortTest(unittest.TestCase): + def testNode(self): + """Tests a DAFSA with one node can be sorted.""" + + # 'a' => [ 'a' ] + + node1 = ( 'a', [ None ] ) + source = [ node1 ] + nodes = [ node1 ] + self.assertEqual(make_dafsa.top_sort(source), nodes) + + def testDiamond(self): + """Tests nodes in a diamond can be sorted.""" + + # 'b' + # / \ + # 'a' 'd' + # \ / + # 'c' + + node4 = ( 'd', [ None ] ) + node3 = ( 'c', [ node4 ] ) + node2 = ( 'b', [ node4 ] ) + node1 = ( 'a', [ node2, node3 ] ) + source = [ node1 ] + nodes = make_dafsa.top_sort(source) + self.assertLess(nodes.index(node1), nodes.index(node2)) + self.assertLess(nodes.index(node2), nodes.index(node4)) + self.assertLess(nodes.index(node3), nodes.index(node4)) + + +class EncodePrefixTest(unittest.TestCase): + def testChar(self): + """Tests to encode a single character prefix.""" + label = 'a' + bytes = [ ord('a') ] + self.assertEqual(make_dafsa.encode_prefix(label), bytes) + + def testChars(self): + """Tests to encode a multi character prefix.""" + label = 'ab' + bytes = [ ord('b'), ord('a') ] + self.assertEqual(make_dafsa.encode_prefix(label), bytes) + + +class EncodeLabelTest(unittest.TestCase): + def testChar(self): + """Tests to encode a single character label.""" + label = 'a' + bytes = [ ord('a') + 0x80 ] + self.assertEqual(make_dafsa.encode_label(label), bytes) + + def testChars(self): + """Tests to encode a multi character label.""" + label = 'ab' + bytes = [ ord('b') + 0x80, ord('a') ] + self.assertEqual(make_dafsa.encode_label(label), bytes) + + +class EncodeLinksTest(unittest.TestCase): + def testEndLabel(self): + """Tests to encode link to the sink.""" + children = [ None ] + offsets = {} + bytes = 0 + output = [] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testOneByteOffset(self): + """Tests to encode a single one byte offset.""" + node = ( '', [ None ] ) + children = [ node ] + offsets = { id(node) : 2 } + bytes = 5 + output = [ 132 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testOneByteOffsets(self): + """Tests to encode a sequence of one byte offsets.""" + node1 = ( '', [ None ] ) + node2 = ( '', [ None ] ) + children = [ node1, node2 ] + offsets = { id(node1) : 2, id(node2) : 1 } + bytes = 5 + output = [ 129, 5 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testTwoBytesOffset(self): + """Tests to encode a single two byte offset.""" + node = ( '', [ None ] ) + children = [ node ] + offsets = { id(node) : 2 } + bytes = 1005 + output = [ 237, 195] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testTwoBytesOffsets(self): + """Tests to encode a sequence of two byte offsets.""" + node1 = ( '', [ None ] ) + node2 = ( '', [ None ] ) + node3 = ( '', [ None ] ) + children = [ node1, node2, node3 ] + offsets = { id(node1) : 1002, id(node2) : 2, id(node3) : 2002 } + bytes = 3005 + output = [ 232, 195, 232, 67, 241, 67 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testThreeBytesOffset(self): + """Tests to encode a single three byte offset.""" + node = ( '', [ None ] ) + children = [ node ] + offsets = { id(node) : 2 } + bytes = 100005 + output = [ 166, 134, 225 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testThreeBytesOffsets(self): + """Tests to encode a sequence of three byte offsets.""" + node1 = ( '', [ None ] ) + node2 = ( '', [ None ] ) + node3 = ( '', [ None ] ) + children = [ node1, node2, node3 ] + offsets = { id(node1) : 100002, id(node2) : 2, id(node3) : 200002 } + bytes = 300005 + output = [ 160, 134, 225, 160, 134, 97, 172, 134, 97 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + def testOneTwoThreeBytesOffsets(self): + """Tests to encode offsets of different sizes.""" + node1 = ( '', [ None ] ) + node2 = ( '', [ None ] ) + node3 = ( '', [ None ] ) + children = [ node1, node2, node3 ] + offsets = { id(node1) : 10003, id(node2) : 10002, id(node3) : 100002 } + bytes = 300005 + output = [ 129, 143, 95, 97, 74, 13, 99 ] + self.assertEqual(make_dafsa.encode_links(children, offsets, bytes), + output) + + +class ExamplesTest(unittest.TestCase): + def testExample1(self): + """Tests Example 1 from make_dafsa.py.""" + infile = [ '%%', 'aa, 1', 'a, 2', '%%' ] + bytes = [ 0x81, 0xE1, 0x02, 0x81, 0x82, 0x61, 0x81 ] + outfile = make_dafsa.to_cxx(bytes) + self.assertEqual(make_dafsa.words_to_cxx(make_dafsa.parse_gperf(infile)), + outfile) + + def testExample2(self): + """Tests Example 2 from make_dafsa.py.""" + infile = [ '%%', 'aa, 1', 'bbb, 2', 'baa, 1', '%%' ] + bytes = [ 0x02, 0x83, 0xE2, 0x02, 0x83, 0x61, 0x61, 0x81, 0x62, 0x62, + 0x82 ] + outfile = make_dafsa.to_cxx(bytes) + self.assertEqual(make_dafsa.words_to_cxx(make_dafsa.parse_gperf(infile)), + outfile) + + +if __name__ == '__main__': + unittest.main() diff --git a/chromium/net/tools/tld_cleanup/tld_cleanup.cc b/chromium/net/tools/tld_cleanup/tld_cleanup.cc index a4b127bdf2c..5594145cd39 100644 --- a/chromium/net/tools/tld_cleanup/tld_cleanup.cc +++ b/chromium/net/tools/tld_cleanup/tld_cleanup.cc @@ -53,7 +53,7 @@ int main(int argc, const char* argv[]) { logging::LOG_TO_ALL; #endif - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); base::FilePath log_filename; PathService::Get(base::DIR_EXE, &log_filename); diff --git a/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc b/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc index 623403fed93..5a206e4c61a 100644 --- a/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc +++ b/chromium/net/tools/tld_cleanup/tld_cleanup_util.cc @@ -61,7 +61,7 @@ bool WriteRules(const RuleMap& rules, const base::FilePath& outfile) { data.append("%%\n"); - int written = file_util::WriteFile(outfile, + int written = base::WriteFile(outfile, data.data(), static_cast<int>(data.size())); @@ -115,7 +115,7 @@ NormalizeResult NormalizeRule(std::string* domain, Rule* rule) { url.append(*domain); GURL gurl(url); const std::string& spec = gurl.possibly_invalid_spec(); - url_parse::Component host = gurl.parsed_for_possibly_invalid_spec().host; + url::Component host = gurl.parsed_for_possibly_invalid_spec().host; if (host.len < 0) { LOG(ERROR) << "Ignoring rule that couldn't be normalized: " << *domain; return kError; |