diff options
Diffstat (limited to 'chromium/net/tools/quic/test_tools/quic_test_client.cc')
-rw-r--r-- | chromium/net/tools/quic/test_tools/quic_test_client.cc | 342 |
1 files changed, 342 insertions, 0 deletions
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 |