// Copyright 2016 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/test/quic_simple_test_server.h" #include #include #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/message_loop/message_pump_type.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/quic/crypto/proof_source_chromium.h" #include "net/test/test_data_directory.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_dispatcher.h" #include "net/third_party/quiche/src/quiche/quic/tools/quic_memory_cache_backend.h" #include "net/tools/quic/quic_simple_server.h" namespace { const char kTestServerDomain[] = "example.com"; // This must match the certificate used (quic-chain.pem and quic-leaf-cert.key). const char kTestServerHost[] = "test.example.com"; const char kStatusHeader[] = ":status"; const char kHelloPath[] = "/hello.txt"; const char kHelloBodyValue[] = "Hello from QUIC Server"; const char kHelloStatus[] = "200"; const char kHelloHeaderName[] = "hello_header"; const char kHelloHeaderValue[] = "hello header value"; const char kHelloTrailerName[] = "hello_trailer"; const char kHelloTrailerValue[] = "hello trailer value"; const char kSimplePath[] = "/simple.txt"; const char kSimpleBodyValue[] = "Simple Hello from QUIC Server"; const char kSimpleStatus[] = "200"; const char kSimpleHeaderName[] = "hello_header"; const char kSimpleHeaderValue[] = "hello header value"; const std::string kCombinedHelloHeaderValue = std::string("foo\0bar", 7); const char kCombinedHeaderName[] = "combined"; base::Thread* g_quic_server_thread = nullptr; quic::QuicMemoryCacheBackend* g_quic_cache_backend = nullptr; net::QuicSimpleServer* g_quic_server = nullptr; int g_quic_server_port = 0; } // namespace namespace net { std::string const QuicSimpleTestServer::GetDomain() { return kTestServerDomain; } std::string const QuicSimpleTestServer::GetHost() { return kTestServerHost; } HostPortPair const QuicSimpleTestServer::GetHostPort() { return HostPortPair(kTestServerHost, GetPort()); } GURL QuicSimpleTestServer::GetFileURL(const std::string& file_path) { return GURL("https://test.example.com:" + base::NumberToString(GetPort())) .Resolve(file_path); } GURL QuicSimpleTestServer::GetHelloURL() { // Don't include |port| into Hello URL as it is mapped differently. return GURL("https://test.example.com").Resolve(kHelloPath); } std::string const QuicSimpleTestServer::GetStatusHeaderName() { return kStatusHeader; } // Hello Url returns response with HTTP/2 headers and trailers. std::string const QuicSimpleTestServer::GetHelloPath() { return kHelloPath; } std::string const QuicSimpleTestServer::GetHelloBodyValue() { return kHelloBodyValue; } std::string const QuicSimpleTestServer::GetHelloStatus() { return kHelloStatus; } std::string const QuicSimpleTestServer::GetHelloHeaderName() { return kHelloHeaderName; } std::string const QuicSimpleTestServer::GetHelloHeaderValue() { return kHelloHeaderValue; } std::string const QuicSimpleTestServer::GetCombinedHeaderName() { return kCombinedHeaderName; } std::string const QuicSimpleTestServer::GetHelloTrailerName() { return kHelloTrailerName; } std::string const QuicSimpleTestServer::GetHelloTrailerValue() { return kHelloTrailerValue; } // Simple Url returns response without HTTP/2 trailers. GURL QuicSimpleTestServer::GetSimpleURL() { // Don't include |port| into Simple URL as it is mapped differently. return GURL("https://test.example.com").Resolve(kSimplePath); } std::string const QuicSimpleTestServer::GetSimpleBodyValue() { return kSimpleBodyValue; } std::string const QuicSimpleTestServer::GetSimpleStatus() { return kSimpleStatus; } std::string const QuicSimpleTestServer::GetSimpleHeaderName() { return kSimpleHeaderName; } std::string const QuicSimpleTestServer::GetSimpleHeaderValue() { return kSimpleHeaderValue; } void SetupQuicMemoryCacheBackend() { spdy::Http2HeaderBlock headers; headers[kHelloHeaderName] = kHelloHeaderValue; headers[kStatusHeader] = kHelloStatus; headers[kCombinedHeaderName] = kCombinedHelloHeaderValue; spdy::Http2HeaderBlock trailers; trailers[kHelloTrailerName] = kHelloTrailerValue; g_quic_cache_backend = new quic::QuicMemoryCacheBackend(); g_quic_cache_backend->AddResponse(base::StringPrintf("%s", kTestServerHost), kHelloPath, std::move(headers), kHelloBodyValue, std::move(trailers)); headers[kSimpleHeaderName] = kSimpleHeaderValue; headers[kStatusHeader] = kSimpleStatus; g_quic_cache_backend->AddResponse(base::StringPrintf("%s", kTestServerHost), kSimplePath, std::move(headers), kSimpleBodyValue); } void StartQuicServerOnServerThread(const base::FilePath& test_files_root, base::WaitableEvent* server_started_event) { DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread()); DCHECK(!g_quic_server); quic::QuicConfig config; // Set up server certs. base::FilePath directory; directory = test_files_root; auto proof_source = std::make_unique(); CHECK(proof_source->Initialize(directory.AppendASCII("quic-chain.pem"), directory.AppendASCII("quic-leaf-cert.key"), base::FilePath())); SetupQuicMemoryCacheBackend(); g_quic_server = new QuicSimpleServer(std::move(proof_source), config, quic::QuicCryptoServerConfig::ConfigOptions(), quic::AllSupportedVersions(), g_quic_cache_backend); // Start listening on an unbound port. int rv = g_quic_server->Listen(IPEndPoint(IPAddress::IPv4AllZeros(), 0)); CHECK_GE(rv, 0) << "Quic server fails to start"; g_quic_server_port = g_quic_server->server_address().port(); server_started_event->Signal(); } void ShutdownOnServerThread(base::WaitableEvent* server_stopped_event) { DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread()); g_quic_server->Shutdown(); delete g_quic_server; g_quic_server = nullptr; delete g_quic_cache_backend; g_quic_cache_backend = nullptr; server_stopped_event->Signal(); } void ShutdownDispatcherOnServerThread( base::WaitableEvent* dispatcher_stopped_event) { DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread()); g_quic_server->dispatcher()->Shutdown(); dispatcher_stopped_event->Signal(); } bool QuicSimpleTestServer::Start() { DVLOG(3) << g_quic_server_thread; DCHECK(!g_quic_server_thread); g_quic_server_thread = new base::Thread("quic server thread"); base::Thread::Options thread_options; thread_options.message_pump_type = base::MessagePumpType::IO; bool started = g_quic_server_thread->StartWithOptions(std::move(thread_options)); DCHECK(started); base::FilePath test_files_root = GetTestCertsDirectory(); base::WaitableEvent server_started_event( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); g_quic_server_thread->task_runner()->PostTask( FROM_HERE, base::BindOnce(&StartQuicServerOnServerThread, test_files_root, &server_started_event)); server_started_event.Wait(); return true; } void QuicSimpleTestServer::AddResponseWithEarlyHints( const std::string& path, const spdy::Http2HeaderBlock& response_headers, const std::string& response_body, const std::vector& early_hints) { g_quic_cache_backend->AddResponseWithEarlyHints(kTestServerHost, path, response_headers.Clone(), response_body, early_hints); } // Shut down the server dispatcher, and the stream should error out. void QuicSimpleTestServer::ShutdownDispatcherForTesting() { if (!g_quic_server) return; DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread()); base::WaitableEvent dispatcher_stopped_event( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); g_quic_server_thread->task_runner()->PostTask( FROM_HERE, base::BindOnce(&ShutdownDispatcherOnServerThread, &dispatcher_stopped_event)); dispatcher_stopped_event.Wait(); } void QuicSimpleTestServer::Shutdown() { if (!g_quic_server) return; DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread()); base::WaitableEvent server_stopped_event( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); g_quic_server_thread->task_runner()->PostTask( FROM_HERE, base::BindOnce(&ShutdownOnServerThread, &server_stopped_event)); server_stopped_event.Wait(); delete g_quic_server_thread; g_quic_server_thread = nullptr; } int QuicSimpleTestServer::GetPort() { return g_quic_server_port; } } // namespace net