summaryrefslogtreecommitdiff
path: root/chromium/net/tools/quic/test_tools/quic_test_client.cc
diff options
context:
space:
mode:
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.cc342
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