diff options
| author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
|---|---|---|
| committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
| commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
| tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/tools/quic/test_tools | |
| download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz | |
Initial import.
Diffstat (limited to 'chromium/net/tools/quic/test_tools')
15 files changed, 1334 insertions, 0 deletions
diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc b/chromium/net/tools/quic/test_tools/http_message_test_utils.cc new file mode 100644 index 00000000000..7d6df7a7649 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/http_message_test_utils.cc @@ -0,0 +1,175 @@ +// 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/quic/test_tools/http_message_test_utils.h" + +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" + +using base::StringPiece; +using std::string; +using std::vector; + +namespace net { +namespace tools { +namespace test { + +namespace { + +//const char* kContentEncoding = "content-encoding"; +const char* kContentLength = "content-length"; +const char* kTransferCoding = "transfer-encoding"; + +// Both kHTTPVersionString and kMethodString arrays are constructed to match +// the enum values defined in Version and Method of HTTPMessage. +const char* kHTTPVersionString[] = { + "", + "HTTP/0.9", + "HTTP/1.0", + "HTTP/1.1" +}; + +const char* kMethodString[] = { + "", + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "CONNECT", + "MKCOL", + "UNLOCK", +}; + +// Returns true if the message represents a complete request or response. +// Messages are considered complete if: +// - Transfer-Encoding: chunked is present and message has a final chunk. +// - Content-Length header is present and matches the message body length. +// - Neither Transfer-Encoding nor Content-Length is present and message +// is tagged as complete. +bool IsCompleteMessage(const HTTPMessage& message) { + return true; + const BalsaHeaders* headers = message.headers(); + StringPiece content_length = headers->GetHeader(kContentLength); + if (!content_length.empty()) { + int parsed_content_length; + if (!base::StringToInt(content_length, &parsed_content_length)) { + return false; + } + return (message.body().size() == (uint)parsed_content_length); + } else { + // Assume messages without transfer coding or content-length are + // tagged correctly. + return message.has_complete_message(); + } +} + +} // namespace + +HTTPMessage::Method HTTPMessage::StringToMethod(StringPiece str) { + // Skip the first element of the array since it is empty string. + for (unsigned long i = 1; i < arraysize(kMethodString); ++i) { + if (strncmp(str.data(), kMethodString[i], str.length()) == 0) { + return static_cast<HTTPMessage::Method>(i); + } + } + return HttpConstants::UNKNOWN_METHOD; +} + +HTTPMessage::Version HTTPMessage::StringToVersion(StringPiece str) { + // Skip the first element of the array since it is empty string. + for (unsigned long i = 1; i < arraysize(kHTTPVersionString); ++i) { + if (strncmp(str.data(), kHTTPVersionString[i], str.length()) == 0) { + return static_cast<HTTPMessage::Version>(i); + } + } + return HttpConstants::HTTP_UNKNOWN; +} + +const char* HTTPMessage::MethodToString(Method method) { + CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString)); + return kMethodString[method]; +} + +const char* HTTPMessage::VersionToString(Version version) { + CHECK_LT(static_cast<size_t>(version), arraysize(kHTTPVersionString)); + return kHTTPVersionString[version]; +} + +HTTPMessage::HTTPMessage() + : is_request_(true) { + InitializeFields(); +} + +HTTPMessage::HTTPMessage(Version ver, Method request, const string& path) + : is_request_(true) { + InitializeFields(); + if (ver != HttpConstants::HTTP_0_9) { + headers()->SetRequestVersion(VersionToString(ver)); + } + headers()->SetRequestMethod(MethodToString(request)); + headers()->SetRequestUri(path); +} + +HTTPMessage::~HTTPMessage() { +} + +void HTTPMessage::InitializeFields() { + has_complete_message_ = true; + skip_message_validation_ = false; +} + +void HTTPMessage::AddHeader(const string& header, const string& value) { + headers()->AppendHeader(header, value); +} + +void HTTPMessage::RemoveHeader(const string& header) { + headers()->RemoveAllOfHeader(header); +} + +void HTTPMessage::ReplaceHeader(const string& header, const string& value) { + headers()->ReplaceOrAppendHeader(header, value); +} + +void HTTPMessage::AddBody(const string& body, bool add_content_length) { + body_ = body; + // Remove any transfer-encoding that was left by a previous body. + RemoveHeader(kTransferCoding); + if (add_content_length) { + ReplaceHeader(kContentLength, base::IntToString(body.size())); + } else { + RemoveHeader(kContentLength); + } +} + +void HTTPMessage::ValidateMessage() const { + if (skip_message_validation_) { + return; + } + + vector<StringPiece> transfer_encodings; + headers()->GetAllOfHeader(kTransferCoding, &transfer_encodings); + CHECK_GE(1ul, transfer_encodings.size()); + for (vector<StringPiece>::iterator it = transfer_encodings.begin(); + it != transfer_encodings.end(); + ++it) { + CHECK(StringPieceUtils::EqualIgnoreCase("identity", *it) || + StringPieceUtils::EqualIgnoreCase("chunked", *it)) << *it; + } + + vector<StringPiece> content_lengths; + headers()->GetAllOfHeader(kContentLength, &content_lengths); + CHECK_GE(1ul, content_lengths.size()); + + CHECK_EQ(has_complete_message_, IsCompleteMessage(*this)); +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.h b/chromium/net/tools/quic/test_tools/http_message_test_utils.h new file mode 100644 index 00000000000..d389e21c8f2 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/http_message_test_utils.h @@ -0,0 +1,133 @@ +// 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_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ + +#include <string> +#include <vector> + +#include "base/strings/string_piece.h" +#include "net/tools/flip_server/balsa_enums.h" +#include "net/tools/flip_server/balsa_headers.h" + +namespace net { +namespace tools { +namespace test { + +class HttpConstants { + public: + enum Version { + HTTP_UNKNOWN = 0, + HTTP_0_9, + HTTP_1_0, + HTTP_1_1 + }; + + enum Method { + UNKNOWN_METHOD = 0, + OPTIONS, + GET, + HEAD, + POST, + PUT, + DELETE, + TRACE, + CONNECT, + + MKCOL, + UNLOCK, + }; +}; + +// Stripped down wrapper class which basically contains headers and a body. +class HTTPMessage { + public: + typedef HttpConstants::Version Version; + typedef HttpConstants::Method Method; + + // Convenient functions to map strings into enums. The string passed in is + // not assumed to be NULL-terminated. + static Version StringToVersion(base::StringPiece str); + static Method StringToMethod(base::StringPiece str); + + static const char* MethodToString(Method method); + static const char* VersionToString(Version version); + + // Default constructor makes an empty HTTP/1.1 GET request. This is typically + // used to construct a message that will be Initialize()-ed. + HTTPMessage(); + + // Build a request message + HTTPMessage(Version version, Method request, const std::string& path); + + virtual ~HTTPMessage(); + + const std::string& body() const { return body_; } + + // Adds a header line to the message. + void AddHeader(const std::string& header, const std::string& value); + + // Removes a header line from the message. + void RemoveHeader(const std::string& header); + + // A utility function which calls RemoveHeader followed by AddHeader. + void ReplaceHeader(const std::string& header, const std::string& value); + + // Adds a body and the optional content-length header field (omitted to test + // read until close test case). To generate a message that has a header field + // of 0 content-length, call AddBody("", true). + // Multiple calls to AddBody()/AddChunkedBody() has the effect of overwriting + // the previous entry without warning. + void AddBody(const std::string& body, bool add_content_length); + + bool has_complete_message() const { return has_complete_message_; } + void set_has_complete_message(bool value) { has_complete_message_ = value; } + + // Do some basic http message consistency checks like: + // - Valid transfer-encoding header + // - Valid content-length header + // - Messages we expect to be complete are complete. + // This check can be disabled by setting skip_message_validation. + void ValidateMessage() const; + + bool skip_message_validation() const { return skip_message_validation_; } + void set_skip_message_validation(bool value) { + skip_message_validation_ = value; + } + + // Allow direct access to the body string. This should be used with caution: + // it will not update the request headers like AddBody and AddChunkedBody do. + void set_body(const std::string& body) { body_ = body; } + + const BalsaHeaders* headers() const { return &headers_; } + BalsaHeaders* headers() { return &headers_; } + + protected: + BalsaHeaders headers_; + + std::string body_; // the body with chunked framing/gzip compression + + bool is_request_; + + // True if the message should be considered complete during serialization. + // Used by SPDY and Streamed RPC clients to decide wherever or not + // to include fin flags and during message validation (if enabled). + bool has_complete_message_; + + // Allows disabling message validation when creating test messages + // that are intentionally invalid. + bool skip_message_validation_; + + private: + void InitializeFields(); + + DISALLOW_COPY_AND_ASSIGN(HTTPMessage); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_ diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.cc b/chromium/net/tools/quic/test_tools/mock_epoll_server.cc new file mode 100644 index 00000000000..4101c5e9ab0 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.cc @@ -0,0 +1,68 @@ +// 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/quic/test_tools/mock_epoll_server.h" + +namespace net { +namespace tools { +namespace test { + +FakeTimeEpollServer::FakeTimeEpollServer(): now_in_usec_(0) { +} + +FakeTimeEpollServer::~FakeTimeEpollServer() { +} + +int64 FakeTimeEpollServer::NowInUsec() const { + return now_in_usec_; +} + +MockEpollServer::MockEpollServer() : until_in_usec_(-1) { +} + +MockEpollServer::~MockEpollServer() { +} + +int MockEpollServer::epoll_wait_impl(int epfd, + struct epoll_event* events, + int max_events, + int timeout_in_ms) { + int num_events = 0; + while (!event_queue_.empty() && + num_events < max_events && + event_queue_.begin()->first <= NowInUsec() && + ((until_in_usec_ == -1) || + (event_queue_.begin()->first < until_in_usec_)) + ) { + int64 event_time_in_usec = event_queue_.begin()->first; + events[num_events] = event_queue_.begin()->second; + if (event_time_in_usec > NowInUsec()) { + set_now_in_usec(event_time_in_usec); + } + event_queue_.erase(event_queue_.begin()); + ++num_events; + } + if (num_events == 0) { // then we'd have waited 'till the timeout. + if (until_in_usec_ < 0) { // then we don't care what the final time is. + if (timeout_in_ms > 0) { + AdvanceBy(timeout_in_ms * 1000); + } + } else { // except we assume that we don't wait for the timeout + // period if until_in_usec_ is a positive number. + set_now_in_usec(until_in_usec_); + // And reset until_in_usec_ to signal no waiting (as + // the AdvanceByExactly* stuff is meant to be one-shot, + // as are all similar EpollServer functions) + until_in_usec_ = -1; + } + } + if (until_in_usec_ >= 0) { + CHECK(until_in_usec_ >= NowInUsec()); + } + return num_events; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.h b/chromium/net/tools/quic/test_tools/mock_epoll_server.h new file mode 100644 index 00000000000..710d5fdf772 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.h @@ -0,0 +1,106 @@ +// 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_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_ + +#include "base/logging.h" +#include "net/tools/flip_server/epoll_server.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace net { +namespace tools { +namespace test { + +// Unlike the full MockEpollServer, this only lies about the time but lets +// fd events operate normally. Usefully when interacting with real backends +// but wanting to skip forward in time to trigger timeouts. +class FakeTimeEpollServer : public EpollServer { + public: + FakeTimeEpollServer(); + virtual ~FakeTimeEpollServer(); + + // Replaces the EpollServer NowInUsec. + virtual int64 NowInUsec() const OVERRIDE; + + void set_now_in_usec(int64 nius) { now_in_usec_ = nius; } + + // Advances the virtual 'now' by advancement_usec. + void AdvanceBy(int64 advancement_usec) { + set_now_in_usec(NowInUsec() + advancement_usec); + } + + // Advances the virtual 'now' by advancement_usec, and + // calls WaitForEventAndExecteCallbacks. + // Note that the WaitForEventsAndExecuteCallbacks invocation + // may cause NowInUs to advance beyond what was specified here. + // If that is not desired, use the AdvanceByExactly calls. + void AdvanceByAndCallCallbacks(int64 advancement_usec) { + AdvanceBy(advancement_usec); + WaitForEventsAndExecuteCallbacks(); + } + + private: + int64 now_in_usec_; +}; + +class MockEpollServer : public FakeTimeEpollServer { + public: // type definitions + typedef base::hash_multimap<int64, struct epoll_event> EventQueue; + + MockEpollServer(); + virtual ~MockEpollServer(); + + // time_in_usec is the time at which the event specified + // by 'ee' will be delivered. Note that it -is- possible + // to add an event for a time which has already been passed.. + // .. upon the next time that the callbacks are invoked, + // all events which are in the 'past' will be delivered. + void AddEvent(int64 time_in_usec, const struct epoll_event& ee) { + event_queue_.insert(std::make_pair(time_in_usec, ee)); + } + + // Advances the virtual 'now' by advancement_usec, + // and ensure that the next invocation of + // WaitForEventsAndExecuteCallbacks goes no farther than + // advancement_usec from the current time. + void AdvanceByExactly(int64 advancement_usec) { + until_in_usec_ = NowInUsec() + advancement_usec; + set_now_in_usec(NowInUsec() + advancement_usec); + } + + // As above, except calls WaitForEventsAndExecuteCallbacks. + void AdvanceByExactlyAndCallCallbacks(int64 advancement_usec) { + AdvanceByExactly(advancement_usec); + WaitForEventsAndExecuteCallbacks(); + } + + base::hash_set<AlarmCB*>::size_type NumberOfAlarms() const { + return all_alarms_.size(); + } + + 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 { } + + // 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 { } + + private: // members + EventQueue event_queue_; + int64 until_in_usec_; +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_ diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc new file mode 100644 index 00000000000..3a2b1d98252 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc @@ -0,0 +1,21 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/quic/test_tools/mock_quic_dispatcher.h" + +namespace net { +namespace tools { +namespace test { + +MockQuicDispatcher::MockQuicDispatcher( + const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicGuid guid, + EpollServer* eps) + : QuicDispatcher(config, crypto_config, guid, eps) { } +MockQuicDispatcher::~MockQuicDispatcher() {} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h new file mode 100644 index 00000000000..563ab0de5fd --- /dev/null +++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ + +#include "net/base/ip_endpoint.h" +#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/quic_config.h" +#include "net/quic/quic_protocol.h" +#include "net/tools/flip_server/epoll_server.h" +#include "net/tools/quic/quic_dispatcher.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace net { +namespace tools { +namespace test { + +class MockQuicDispatcher : public QuicDispatcher { + public: + MockQuicDispatcher(const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicGuid guid, + EpollServer* eps); + virtual ~MockQuicDispatcher(); + + MOCK_METHOD4(ProcessPacket, void(const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicGuid guid, + const QuicEncryptedPacket& packet)); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.cc b/chromium/net/tools/quic/test_tools/quic_client_peer.cc new file mode 100644 index 00000000000..858359474c8 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_client_peer.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/quic/test_tools/quic_client_peer.h" + +#include "net/tools/quic/quic_client.h" + +namespace net { +namespace tools { +namespace test { + +// static +void QuicClientPeer::Reinitialize(QuicClient* client) { + client->initialized_ = false; + client->epoll_server_.UnregisterFD(client->fd_); + client->Initialize(); +} + +// static +int QuicClientPeer::GetFd(QuicClient* client) { + return client->fd_; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.h b/chromium/net/tools/quic/test_tools/quic_client_peer.h new file mode 100644 index 00000000000..8eaa17e675d --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_client_peer.h @@ -0,0 +1,25 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ + +namespace net { +namespace tools { + +class QuicClient; + +namespace test { + +class QuicClientPeer { + public: + static void Reinitialize(QuicClient* client); + static int GetFd(QuicClient* client); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ diff --git a/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc new file mode 100644 index 00000000000..e358273d0a2 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h" + +#include "net/tools/quic/quic_epoll_connection_helper.h" + +namespace net { +namespace tools { +namespace test { + +// static +void QuicEpollConnectionHelperPeer::SetWriter(QuicEpollConnectionHelper* helper, + QuicPacketWriter* writer) { + helper->writer_ = writer; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h new file mode 100644 index 00000000000..72085dfde1c --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h @@ -0,0 +1,31 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ + +#include "base/basictypes.h" + +namespace net { +namespace tools { + +class QuicPacketWriter; +class QuicEpollConnectionHelper; + +namespace test { + +class QuicEpollConnectionHelperPeer { + public: + static void SetWriter(QuicEpollConnectionHelper* helper, + QuicPacketWriter* writer); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicEpollConnectionHelperPeer); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.cc b/chromium/net/tools/quic/test_tools/quic_test_client.cc new file mode 100644 index 00000000000..859d7a56044 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_test_client.cc @@ -0,0 +1,342 @@ +// 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/quic/test_tools/quic_test_client.h" + +#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" +#include "net/cert/cert_verify_result.h" +#include "net/cert/x509_certificate.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/quic/quic_epoll_connection_helper.h" +#include "net/tools/quic/test_tools/http_message_test_utils.h" +#include "url/gurl.h" + +using std::string; +using std::vector; +using base::StringPiece; + +namespace { + +// RecordingProofVerifier accepts any certificate chain and records the common +// name of the leaf. +class RecordingProofVerifier : public net::ProofVerifier { + public: + // ProofVerifier interface. + virtual net::ProofVerifier::Status VerifyProof( + net::QuicVersion version, + const string& hostname, + const string& server_config, + const vector<string>& certs, + const string& signature, + string* error_details, + scoped_ptr<net::ProofVerifyDetails>* details, + net::ProofVerifierCallback* callback) OVERRIDE { + delete callback; + + common_name_.clear(); + if (certs.empty()) { + return FAILURE; + } + + // Convert certs to X509Certificate. + vector<StringPiece> cert_pieces(certs.size()); + for (unsigned i = 0; i < certs.size(); i++) { + cert_pieces[i] = StringPiece(certs[i]); + } + scoped_refptr<net::X509Certificate> cert = + net::X509Certificate::CreateFromDERCertChain(cert_pieces); + if (!cert.get()) { + return FAILURE; + } + + common_name_ = cert->subject().GetDisplayName(); + return SUCCESS; + } + + const string& common_name() const { return common_name_; } + + private: + string common_name_; +}; + +} // anonymous namespace + +namespace net { +namespace tools { +namespace test { + +BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers, + bool secure) { + StringPiece uri = const_headers->request_uri(); + if (uri.empty()) { + return NULL; + } + if (const_headers->request_method() == "CONNECT") { + return NULL; + } + BalsaHeaders* headers = new BalsaHeaders; + headers->CopyFrom(*const_headers); + if (!uri.starts_with("https://") && + !uri.starts_with("http://")) { + // If we have a relative URL, set some defaults. + string full_uri = secure ? "https://www.google.com" : + "http://www.google.com"; + full_uri.append(uri.as_string()); + headers->SetRequestUri(full_uri); + } + 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 QuicVersion version) + : Super(server_address, server_hostname, version) { + } + + QuicEpollClient(IPEndPoint server_address, + const string& server_hostname, + const QuicConfig& config, + const QuicVersion version) + : Super(server_address, server_hostname, config, version) { + } + + virtual ~QuicEpollClient() { + if (connected()) { + Disconnect(); + } + } + + virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper() OVERRIDE { + if (writer_.get() != NULL) { + writer_->set_fd(fd()); + return new QuicEpollConnectionHelper(writer_.get(), epoll_server()); + } else { + return Super::CreateQuicConnectionHelper(); + } + } + + void UseWriter(QuicTestWriter* writer) { writer_.reset(writer); } + + private: + scoped_ptr<QuicTestWriter> writer_; +}; + +QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname, + const QuicVersion version) + : client_(new QuicEpollClient(address, hostname, version)) { + Initialize(address, hostname, true); +} + +QuicTestClient::QuicTestClient(IPEndPoint address, + const string& hostname, + bool secure, + const QuicVersion version) + : client_(new QuicEpollClient(address, hostname, version)) { + Initialize(address, hostname, secure); +} + +QuicTestClient::QuicTestClient(IPEndPoint address, + const string& hostname, + bool secure, + const QuicConfig& config, + const QuicVersion version) + : client_(new QuicEpollClient(address, hostname, config, version)) { + Initialize(address, hostname, secure); +} + +void QuicTestClient::Initialize(IPEndPoint address, + const string& hostname, + bool secure) { + server_address_ = address; + stream_ = NULL; + stream_error_ = QUIC_STREAM_NO_ERROR; + bytes_read_ = 0; + bytes_written_= 0; + never_connected_ = true; + secure_ = secure; + auto_reconnect_ = false; + proof_verifier_ = NULL; + ExpectCertificates(secure_); +} + +QuicTestClient::~QuicTestClient() { + if (stream_) { + stream_->set_visitor(NULL); + } +} + +void QuicTestClient::ExpectCertificates(bool on) { + if (on) { + proof_verifier_ = new RecordingProofVerifier; + client_->SetProofVerifier(proof_verifier_); + } else { + proof_verifier_ = NULL; + client_->SetProofVerifier(NULL); + } +} + +ssize_t QuicTestClient::SendRequest(const string& uri) { + HTTPMessage message(HttpConstants::HTTP_1_1, HttpConstants::GET, uri); + return SendMessage(message); +} + +ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) { + stream_ = NULL; // Always force creation of a stream for SendMessage. + + // If we're not connected, try to find an sni hostname. + if (!connected()) { + GURL url(message.headers()->request_uri().as_string()); + if (!url.host().empty()) { + client_->set_server_hostname(url.host()); + } + } + + QuicReliableClientStream* stream = GetOrCreateStream(); + if (!stream) { return 0; } + + scoped_ptr<BalsaHeaders> munged_headers(MungeHeaders(message.headers(), + secure_)); + return GetOrCreateStream()->SendRequest( + munged_headers.get() ? *munged_headers.get() : *message.headers(), + message.body(), + message.has_complete_message()); +} + +ssize_t QuicTestClient::SendData(string data, bool last_data) { + QuicReliableClientStream* stream = GetOrCreateStream(); + if (!stream) { return 0; } + GetOrCreateStream()->SendBody(data, last_data); + return data.length(); +} + +string QuicTestClient::SendCustomSynchronousRequest( + const HTTPMessage& message) { + SendMessage(message); + WaitForResponse(); + return response_; +} + +string QuicTestClient::SendSynchronousRequest(const string& uri) { + if (SendRequest(uri) == 0) { + DLOG(ERROR) << "Failed the request for uri:" << uri; + return ""; + } + WaitForResponse(); + return response_; +} + +QuicReliableClientStream* QuicTestClient::GetOrCreateStream() { + if (never_connected_ == true || auto_reconnect_) { + if (!connected()) { + Connect(); + } + if (!connected()) { + return NULL; + } + } + if (!stream_) { + stream_ = client_->CreateReliableClientStream(); + if (stream_ != NULL) { + stream_->set_visitor(this); + } + } + return stream_; +} + +const string& QuicTestClient::cert_common_name() const { + return reinterpret_cast<RecordingProofVerifier*>(proof_verifier_) + ->common_name(); +} + +bool QuicTestClient::connected() const { + return client_->connected(); +} + +void QuicTestClient::WaitForResponse() { + if (stream_ == NULL) { + // The client has likely disconnected. + return; + } + client_->WaitForStreamToClose(stream_->id()); +} + +void QuicTestClient::Connect() { + DCHECK(!connected()); + client_->Initialize(); + client_->Connect(); + never_connected_ = false; +} + +void QuicTestClient::ResetConnection() { + Disconnect(); + Connect(); +} + +void QuicTestClient::Disconnect() { + client_->Disconnect(); +} + +IPEndPoint QuicTestClient::LocalSocketAddress() const { + return client_->client_address(); +} + +void QuicTestClient::ClearPerRequestState() { + stream_error_ = QUIC_STREAM_NO_ERROR; + stream_ = NULL; + response_ = ""; + headers_.Clear(); + bytes_read_ = 0; + bytes_written_ = 0; +} + +void QuicTestClient::WaitForInitialResponse() { + DCHECK(stream_ != NULL); + while (stream_ && stream_->stream_bytes_read() == 0) { + client_->WaitForEvents(); + } +} + +ssize_t QuicTestClient::Send(const void *buffer, size_t size) { + return SendData(string(static_cast<const char*>(buffer), size), false); +} + +int QuicTestClient::response_size() const { + return bytes_read_; +} + +size_t QuicTestClient::bytes_read() const { + return bytes_read_; +} + +size_t QuicTestClient::bytes_written() const { + return bytes_written_; +} + +void QuicTestClient::OnClose(ReliableQuicStream* stream) { + if (stream_ != stream) { + return; + } + response_ = stream_->data(); + headers_.CopyFrom(stream_->headers()); + stream_error_ = stream_->stream_error(); + bytes_read_ = stream_->stream_bytes_read(); + bytes_written_ = stream_->stream_bytes_written(); + stream_ = NULL; +} + +void QuicTestClient::UseWriter(QuicTestWriter* writer) { + DCHECK(!connected()); + reinterpret_cast<QuicEpollClient*>(client_.get())->UseWriter(writer); +} + +} // 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 new file mode 100644 index 00000000000..74bfc24646a --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_test_client.h @@ -0,0 +1,143 @@ +// 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_QUIC_TEST_TOOLS_QUIC_CLIENT_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_CLIENT_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_protocol.h" +#include "net/tools/quic/quic_client.h" +#include "net/tools/quic/quic_packet_writer.h" + +namespace net { + +class ProofVerifier; + +namespace tools { + +namespace test { + +// Allows setting a writer for the client's QuicConnectionHelper, to allow +// fine-grained control of writes. +class QuicTestWriter : public QuicPacketWriter { + public: + virtual ~QuicTestWriter() {} + virtual void set_fd(int fd) = 0; +}; + +class HTTPMessage; + +// A toy QUIC client used for testing. +class QuicTestClient : public ReliableQuicStream::Visitor { + public: + QuicTestClient(IPEndPoint server_address, const string& server_hostname, + const QuicVersion version); + QuicTestClient(IPEndPoint server_address, + const string& server_hostname, + bool secure, + const QuicVersion version); + QuicTestClient(IPEndPoint server_address, + const string& server_hostname, + bool secure, + const QuicConfig& config, + const QuicVersion version); + + virtual ~QuicTestClient(); + + // ExpectCertificates controls whether the server is expected to provide + // certificates. The certificates, if any, are not verified, but the common + // 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); + + // Wraps data in a quic packet and sends it. + ssize_t SendData(string data, bool last_data); + + QuicPacketCreator::Options* options() { return client_->options(); } + + const BalsaHeaders *response_headers() const {return &headers_;} + + void WaitForResponse(); + + void Connect(); + void ResetConnection(); + void Disconnect(); + IPEndPoint LocalSocketAddress() const; + void ClearPerRequestState(); + void WaitForInitialResponse(); + ssize_t Send(const void *buffer, size_t size); + int response_size() const; + size_t bytes_read() const; + size_t bytes_written() const; + + // From ReliableQuicStream::Visitor + virtual void OnClose(ReliableQuicStream* stream) OVERRIDE; + + // Configures client_ to take ownership of and use the writer. + // Must be called before initial connect. + void UseWriter(QuicTestWriter* writer); + + // Returns NULL if the maximum number of streams have already been created. + QuicReliableClientStream* GetOrCreateStream(); + + QuicRstStreamErrorCode stream_error() { return stream_error_; } + QuicErrorCode connection_error() { return client()->session()->error(); } + + QuicClient* client() { return client_.get(); } + + // 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; + + void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; } + + private: + void Initialize(IPEndPoint address, const string& hostname, bool secure); + + IPEndPoint server_address_; + IPEndPoint client_address_; + scoped_ptr<QuicClient> client_; // The actual client + QuicReliableClientStream* stream_; + + QuicRstStreamErrorCode stream_error_; + + BalsaHeaders headers_; + string response_; + uint64 bytes_read_; + uint64 bytes_written_; + // True if the client has never connected before. The client will + // auto-connect exactly once before sending data. If something causes a + // connection reset, it will not automatically reconnect. + bool never_connected_; + bool secure_; + // If true, the client will always reconnect if necessary before creating a + // stream. + bool auto_reconnect_; + + // proof_verifier_ points to a RecordingProofVerifier that is owned by + // client_. + ProofVerifier* proof_verifier_; +}; + +} // namespace test + +} // namespace tools +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_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 new file mode 100644 index 00000000000..95f1fb215ea --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_test_utils.cc @@ -0,0 +1,81 @@ +// 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/quic/test_tools/quic_test_utils.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::MockHelper; + +namespace net { +namespace tools { +namespace test { + +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + int fd, + EpollServer* eps, + bool is_server) + : QuicConnection(guid, address, + new QuicEpollConnectionHelper(fd, eps), is_server, + QuicVersionMax()), + has_mock_helper_(false) { +} + +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + bool is_server) + : QuicConnection(guid, address, new testing::NiceMock<MockHelper>(), + is_server, QuicVersionMax()), + has_mock_helper_(true) { +} + +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, + bool is_server) + : QuicConnection(guid, address, helper, is_server, QuicVersionMax()), + has_mock_helper_(false) { +} + +MockConnection::~MockConnection() { +} + +void MockConnection::AdvanceTime(QuicTime::Delta delta) { + CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being" + " used"; + static_cast<MockHelper*>(helper())->AdvanceTime(delta); +} + +bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { + data.AppendToString(&data_); + return true; +} + +void TestDecompressorVisitor::OnDecompressionError() { + error_ = true; +} + +TestSession::TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) + : QuicSession(connection, config, is_server), + crypto_stream_(NULL) { +} + +TestSession::~TestSession() {} + +void TestSession::SetCryptoStream(QuicCryptoStream* stream) { + crypto_stream_ = stream; +} + +QuicCryptoStream* TestSession::GetCryptoStream() { + return crypto_stream_; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.h b/chromium/net/tools/quic/test_tools/quic_test_utils.h new file mode 100644 index 00000000000..31ea1815e06 --- /dev/null +++ b/chromium/net/tools/quic/test_tools/quic_test_utils.h @@ -0,0 +1,112 @@ +// 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_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ + +#include <string> + +#include "base/strings/string_piece.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/spdy/spdy_framer.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace net { + +class EpollServer; +class IPEndPoint; + +namespace tools { +namespace test { + +std::string SerializeUncompressedHeaders(const SpdyHeaderBlock& headers); + +class MockConnection : public QuicConnection { + public: + // Uses a QuicConnectionHelper created with fd and eps. + MockConnection(QuicGuid guid, + IPEndPoint address, + int fd, + EpollServer* eps, + bool is_server); + // Uses a MockHelper. + MockConnection(QuicGuid guid, IPEndPoint address, bool is_server); + MockConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, bool is_server); + virtual ~MockConnection(); + + // If the constructor that uses a MockHelper has been used then this method + // will advance the time of the MockClock. + void AdvanceTime(QuicTime::Delta delta); + + MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet)); + MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error)); + MOCK_METHOD2(SendConnectionCloseWithDetails, void( + QuicErrorCode error, + const std::string& details)); + MOCK_METHOD2(SendRstStream, void(QuicStreamId id, + QuicRstStreamErrorCode error)); + MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, + QuicStreamId last_good_stream_id, + const std::string& reason)); + MOCK_METHOD0(OnCanWrite, bool()); + + void ReallyProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + return QuicConnection::ProcessUdpPacket(self_address, peer_address, packet); + } + + virtual bool OnProtocolVersionMismatch(QuicVersion version) { return false; } + + private: + const bool has_mock_helper_; + + DISALLOW_COPY_AND_ASSIGN(MockConnection); +}; + +class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { + public: + virtual ~TestDecompressorVisitor() {} + virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; + + std::string data() { return data_; } + bool error() { return error_; } + + private: + std::string data_; + bool error_; +}; + +class TestSession : public QuicSession { + public: + TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server); + virtual ~TestSession(); + + MOCK_METHOD1(CreateIncomingReliableStream, + ReliableQuicStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*()); + + void SetCryptoStream(QuicCryptoStream* stream); + + virtual QuicCryptoStream* GetCryptoStream(); + + private: + QuicCryptoStream* crypto_stream_; + DISALLOW_COPY_AND_ASSIGN(TestSession); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ diff --git a/chromium/net/tools/quic/test_tools/run_all_unittests.cc b/chromium/net/tools/quic/test_tools/run_all_unittests.cc new file mode 100644 index 00000000000..6d42d33eb8f --- /dev/null +++ b/chromium/net/tools/quic/test_tools/run_all_unittests.cc @@ -0,0 +1,11 @@ +// 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 "base/test/test_suite.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + + return test_suite.Run(); +} |
