summaryrefslogtreecommitdiffstats
path: root/jingle
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-01 02:12:45 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-01 02:12:45 +0000
commit8a91a0a06c35b1487949c4037954e08172827f90 (patch)
tree6583e95c3a82749a73a0dbac57f354839ab4446d /jingle
parent29c6bb325b65ce953ed11cc344455e67be697b84 (diff)
downloadchromium_src-8a91a0a06c35b1487949c4037954e08172827f90.zip
chromium_src-8a91a0a06c35b1487949c4037954e08172827f90.tar.gz
chromium_src-8a91a0a06c35b1487949c4037954e08172827f90.tar.bz2
Added FakeSSLClentSocket for connecting to Google Talk on the SSL port
FakeSSLClientSocket is a wrapper around a ClientSocket that simulates an SSL handshake, which is needed to connect to Google Talk servers on the SSL port. This was adapted from the libjingle socket implementation. BUG=45612 TEST=new unit tests Review URL: http://codereview.chromium.org/3231006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58110 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'jingle')
-rw-r--r--jingle/jingle.gyp3
-rw-r--r--jingle/notifier/base/fake_ssl_client_socket.cc327
-rw-r--r--jingle/notifier/base/fake_ssl_client_socket.h124
-rw-r--r--jingle/notifier/base/fake_ssl_client_socket_unittest.cc339
4 files changed, 793 insertions, 0 deletions
diff --git a/jingle/jingle.gyp b/jingle/jingle.gyp
index eafe96b..0b093fa 100644
--- a/jingle/jingle.gyp
+++ b/jingle/jingle.gyp
@@ -20,6 +20,8 @@
'notifier/base/signal_thread_task.h',
'notifier/base/ssl_adapter.h',
'notifier/base/ssl_adapter.cc',
+ 'notifier/base/fake_ssl_client_socket.cc',
+ 'notifier/base/fake_ssl_client_socket.h',
'notifier/base/static_assert.h',
'notifier/base/task_pump.cc',
'notifier/base/task_pump.h',
@@ -96,6 +98,7 @@
# TODO(akalin): Write our own test suite and runner.
'../base/test/run_all_unittests.cc',
'notifier/base/chrome_async_socket_unittest.cc',
+ 'notifier/base/fake_ssl_client_socket_unittest.cc',
'notifier/listener/talk_mediator_unittest.cc',
'notifier/listener/send_update_task_unittest.cc',
'notifier/listener/subscribe_task_unittest.cc',
diff --git a/jingle/notifier/base/fake_ssl_client_socket.cc b/jingle/notifier/base/fake_ssl_client_socket.cc
new file mode 100644
index 0000000..a89a594
--- /dev/null
+++ b/jingle/notifier/base/fake_ssl_client_socket.cc
@@ -0,0 +1,327 @@
+// Copyright (c) 2010 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 "jingle/notifier/base/fake_ssl_client_socket.h"
+
+#include <cstdlib>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace notifier {
+
+namespace {
+
+// The constants below were taken from libjingle's socketadapters.cc.
+// Basically, we do a "fake" SSL handshake to fool proxies into
+// thinking this is a real SSL connection.
+
+// This is a SSL v2 CLIENT_HELLO message.
+// TODO(juberti): Should this have a session id? The response doesn't have a
+// certificate, so the hello should have a session id.
+static const uint8 kSslClientHello[] = {
+ 0x80, 0x46, // msg len
+ 0x01, // CLIENT_HELLO
+ 0x03, 0x01, // SSL 3.1
+ 0x00, 0x2d, // ciphersuite len
+ 0x00, 0x00, // session id len
+ 0x00, 0x10, // challenge len
+ 0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0, // ciphersuites
+ 0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, //
+ 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, //
+ 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, //
+ 0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, //
+ 0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, // challenge
+ 0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea //
+};
+
+// This is a TLSv1 SERVER_HELLO message.
+static const uint8 kSslServerHello[] = {
+ 0x16, // handshake message
+ 0x03, 0x01, // SSL 3.1
+ 0x00, 0x4a, // message len
+ 0x02, // SERVER_HELLO
+ 0x00, 0x00, 0x46, // handshake len
+ 0x03, 0x01, // SSL 3.1
+ 0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0, // server random
+ 0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, //
+ 0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1, //
+ 0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f, //
+ 0x20, // session id len
+ 0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f, // session id
+ 0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b, //
+ 0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38, //
+ 0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c, //
+ 0x00, 0x04, // RSA/RC4-128/MD5
+ 0x00 // null compression
+};
+
+net::DrainableIOBuffer* NewDrainableIOBufferWithSize(int size) {
+ return new net::DrainableIOBuffer(new net::IOBuffer(size), size);
+}
+
+} // namespace
+
+base::StringPiece FakeSSLClientSocket::GetSslClientHello() {
+ return base::StringPiece(reinterpret_cast<const char*>(kSslClientHello),
+ arraysize(kSslClientHello));
+}
+
+base::StringPiece FakeSSLClientSocket::GetSslServerHello() {
+ return base::StringPiece(reinterpret_cast<const char*>(kSslServerHello),
+ arraysize(kSslServerHello));
+}
+
+FakeSSLClientSocket::FakeSSLClientSocket(
+ net::ClientSocket* transport_socket)
+ : connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ &FakeSSLClientSocket::OnConnectDone),
+ send_client_hello_callback_(
+ ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ &FakeSSLClientSocket::OnSendClientHelloDone),
+ verify_server_hello_callback_(
+ ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ &FakeSSLClientSocket::OnVerifyServerHelloDone),
+ transport_socket_(transport_socket),
+ next_handshake_state_(STATE_NONE),
+ handshake_completed_(false),
+ user_connect_callback_(NULL),
+ write_buf_(NewDrainableIOBufferWithSize(arraysize(kSslClientHello))),
+ read_buf_(NewDrainableIOBufferWithSize(arraysize(kSslServerHello))) {
+ CHECK(transport_socket_.get());
+ std::memcpy(write_buf_->data(), kSslClientHello, arraysize(kSslClientHello));
+}
+
+FakeSSLClientSocket::~FakeSSLClientSocket() {}
+
+int FakeSSLClientSocket::Read(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK_EQ(next_handshake_state_, STATE_NONE);
+ DCHECK(handshake_completed_);
+ return transport_socket_->Read(buf, buf_len, callback);
+}
+
+int FakeSSLClientSocket::Write(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback) {
+ DCHECK_EQ(next_handshake_state_, STATE_NONE);
+ DCHECK(handshake_completed_);
+ return transport_socket_->Write(buf, buf_len, callback);
+}
+
+bool FakeSSLClientSocket::SetReceiveBufferSize(int32 size) {
+ return transport_socket_->SetReceiveBufferSize(size);
+}
+
+bool FakeSSLClientSocket::SetSendBufferSize(int32 size) {
+ return transport_socket_->SetSendBufferSize(size);
+}
+
+int FakeSSLClientSocket::Connect(net::CompletionCallback* callback) {
+ // We don't support synchronous operation, even if
+ // |transport_socket_| does.
+ DCHECK(callback);
+ DCHECK_EQ(next_handshake_state_, STATE_NONE);
+ DCHECK(!handshake_completed_);
+ DCHECK(!user_connect_callback_);
+ DCHECK_EQ(write_buf_->BytesConsumed(), 0);
+ DCHECK_EQ(read_buf_->BytesConsumed(), 0);
+
+ next_handshake_state_ = STATE_CONNECT;
+ int status = DoHandshakeLoop();
+ if (status == net::ERR_IO_PENDING) {
+ user_connect_callback_ = callback;
+ }
+ return status;
+}
+
+int FakeSSLClientSocket::DoHandshakeLoop() {
+ DCHECK_NE(next_handshake_state_, STATE_NONE);
+ int status = net::OK;
+ do {
+ HandshakeState state = next_handshake_state_;
+ next_handshake_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_CONNECT:
+ status = DoConnect();
+ break;
+ case STATE_SEND_CLIENT_HELLO:
+ status = DoSendClientHello();
+ break;
+ case STATE_VERIFY_SERVER_HELLO:
+ status = DoVerifyServerHello();
+ break;
+ default:
+ status = net::ERR_UNEXPECTED;
+ LOG(DFATAL) << "unexpected state: " << state;
+ break;
+ }
+ } while ((status != net::ERR_IO_PENDING) &&
+ (next_handshake_state_ != STATE_NONE));
+ return status;
+}
+
+void FakeSSLClientSocket::RunUserConnectCallback(int status) {
+ DCHECK_LE(status, net::OK);
+ next_handshake_state_ = STATE_NONE;
+ net::CompletionCallback* user_connect_callback = user_connect_callback_;
+ user_connect_callback_ = NULL;
+ user_connect_callback->Run(status);
+}
+
+void FakeSSLClientSocket::DoHandshakeLoopWithUserConnectCallback() {
+ int status = DoHandshakeLoop();
+ if (status != net::ERR_IO_PENDING) {
+ RunUserConnectCallback(status);
+ }
+}
+
+int FakeSSLClientSocket::DoConnect() {
+ int status = transport_socket_->Connect(&connect_callback_);
+ if (status != net::OK) {
+ return status;
+ }
+ ProcessConnectDone();
+ return net::OK;
+}
+
+void FakeSSLClientSocket::OnConnectDone(int status) {
+ DCHECK_NE(status, net::ERR_IO_PENDING);
+ DCHECK_LE(status, net::OK);
+ DCHECK(user_connect_callback_);
+ if (status != net::OK) {
+ RunUserConnectCallback(status);
+ return;
+ }
+ ProcessConnectDone();
+ DoHandshakeLoopWithUserConnectCallback();
+}
+
+void FakeSSLClientSocket::ProcessConnectDone() {
+ DCHECK_EQ(write_buf_->BytesConsumed(), 0);
+ DCHECK_EQ(read_buf_->BytesConsumed(), 0);
+ next_handshake_state_ = STATE_SEND_CLIENT_HELLO;
+}
+
+int FakeSSLClientSocket::DoSendClientHello() {
+ int status = transport_socket_->Write(
+ write_buf_, write_buf_->BytesRemaining(),
+ &send_client_hello_callback_);
+ if (status < net::OK) {
+ return status;
+ }
+ ProcessSendClientHelloDone(static_cast<size_t>(status));
+ return net::OK;
+}
+
+void FakeSSLClientSocket::OnSendClientHelloDone(int status) {
+ DCHECK_NE(status, net::ERR_IO_PENDING);
+ DCHECK(user_connect_callback_);
+ if (status < net::OK) {
+ RunUserConnectCallback(status);
+ return;
+ }
+ ProcessSendClientHelloDone(static_cast<size_t>(status));
+ DoHandshakeLoopWithUserConnectCallback();
+}
+
+void FakeSSLClientSocket::ProcessSendClientHelloDone(size_t written) {
+ DCHECK_LE(written, static_cast<size_t>(write_buf_->BytesRemaining()));
+ DCHECK_EQ(read_buf_->BytesConsumed(), 0);
+ if (written < static_cast<size_t>(write_buf_->BytesRemaining())) {
+ next_handshake_state_ = STATE_SEND_CLIENT_HELLO;
+ write_buf_->DidConsume(written);
+ } else {
+ next_handshake_state_ = STATE_VERIFY_SERVER_HELLO;
+ }
+}
+
+int FakeSSLClientSocket::DoVerifyServerHello() {
+ int status = transport_socket_->Read(
+ read_buf_, read_buf_->BytesRemaining(),
+ &verify_server_hello_callback_);
+ if (status < net::OK) {
+ return status;
+ }
+ size_t read = static_cast<size_t>(status);
+ return ProcessVerifyServerHelloDone(read);
+}
+
+void FakeSSLClientSocket::OnVerifyServerHelloDone(int status) {
+ DCHECK_NE(status, net::ERR_IO_PENDING);
+ DCHECK(user_connect_callback_);
+ if (status < net::OK) {
+ RunUserConnectCallback(status);
+ return;
+ }
+ size_t read = static_cast<size_t>(status);
+ status = ProcessVerifyServerHelloDone(read);
+ if (status < net::OK) {
+ RunUserConnectCallback(status);
+ return;
+ }
+ if (handshake_completed_) {
+ RunUserConnectCallback(net::OK);
+ } else {
+ DoHandshakeLoopWithUserConnectCallback();
+ }
+}
+
+net::Error FakeSSLClientSocket::ProcessVerifyServerHelloDone(size_t read) {
+ DCHECK_LE(read, static_cast<size_t>(read_buf_->BytesRemaining()));
+ if (read == 0U) {
+ return net::ERR_UNEXPECTED;
+ }
+ const uint8* expected_data_start =
+ &kSslServerHello[arraysize(kSslServerHello) -
+ read_buf_->BytesRemaining()];
+ if (std::memcmp(expected_data_start, read_buf_->data(), read) != 0) {
+ return net::ERR_UNEXPECTED;
+ }
+ if (read < static_cast<size_t>(read_buf_->BytesRemaining())) {
+ next_handshake_state_ = STATE_VERIFY_SERVER_HELLO;
+ read_buf_->DidConsume(read);
+ } else {
+ next_handshake_state_ = STATE_NONE;
+ handshake_completed_ = true;
+ }
+ return net::OK;
+}
+
+void FakeSSLClientSocket::Disconnect() {
+ transport_socket_->Disconnect();
+ next_handshake_state_ = STATE_NONE;
+ handshake_completed_ = false;
+ user_connect_callback_ = NULL;
+ write_buf_->SetOffset(0);
+ read_buf_->SetOffset(0);
+}
+
+bool FakeSSLClientSocket::IsConnected() const {
+ return handshake_completed_ && transport_socket_->IsConnected();
+}
+
+bool FakeSSLClientSocket::IsConnectedAndIdle() const {
+ return handshake_completed_ && transport_socket_->IsConnectedAndIdle();
+}
+
+int FakeSSLClientSocket::GetPeerAddress(net::AddressList* address) const {
+ return transport_socket_->GetPeerAddress(address);
+}
+
+const net::BoundNetLog& FakeSSLClientSocket::NetLog() const {
+ return transport_socket_->NetLog();
+}
+
+void FakeSSLClientSocket::SetSubresourceSpeculation() {
+ transport_socket_->SetSubresourceSpeculation();
+}
+
+void FakeSSLClientSocket::SetOmniboxSpeculation() {
+ transport_socket_->SetOmniboxSpeculation();
+}
+
+} // namespace notifier
diff --git a/jingle/notifier/base/fake_ssl_client_socket.h b/jingle/notifier/base/fake_ssl_client_socket.h
new file mode 100644
index 0000000..e4765c5
--- /dev/null
+++ b/jingle/notifier/base/fake_ssl_client_socket.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2010 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.
+//
+// This ClientSocket implementation is to be used with servers that
+// accept connections on port 443 but don't really use SSL. For
+// example, the Google Talk servers do this to bypass proxies. (The
+// connection is upgraded to TLS as part of the XMPP negotiation, so
+// security is preserved.) A "fake" SSL handshake is done immediately
+// after connection to fool proxies into thinking that this is a real
+// SSL connection.
+//
+// NOTE: This ClientSocket implementation does *not* do a real SSL
+// handshake nor does it do any encryption!
+
+#ifndef JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_
+#define JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_
+#pragma once
+
+#include <cstddef>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "net/socket/client_socket.h"
+
+namespace net {
+class DrainableIOBuffer;
+} // namespace net
+
+namespace notifier {
+
+class FakeSSLClientSocket : public net::ClientSocket {
+ public:
+ // Takes ownership of |transport_socket|.
+ explicit FakeSSLClientSocket(net::ClientSocket* transport_socket);
+
+ virtual ~FakeSSLClientSocket();
+
+ // Exposed for testing.
+ static base::StringPiece GetSslClientHello();
+ static base::StringPiece GetSslServerHello();
+
+ // net::ClientSocket implementation.
+
+ virtual int Read(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback);
+
+ virtual int Write(net::IOBuffer* buf, int buf_len,
+ net::CompletionCallback* callback);
+
+ virtual bool SetReceiveBufferSize(int32 size);
+
+ virtual bool SetSendBufferSize(int32 size);
+
+ virtual int Connect(net::CompletionCallback* callback);
+
+ virtual void Disconnect();
+
+ virtual bool IsConnected() const;
+
+ virtual bool IsConnectedAndIdle() const;
+
+ virtual int GetPeerAddress(net::AddressList* address) const;
+
+ virtual const net::BoundNetLog& NetLog() const;
+
+ virtual void SetSubresourceSpeculation();
+
+ virtual void SetOmniboxSpeculation();
+
+ private:
+ enum HandshakeState {
+ STATE_NONE,
+ STATE_CONNECT,
+ STATE_SEND_CLIENT_HELLO,
+ STATE_VERIFY_SERVER_HELLO,
+ };
+
+ int DoHandshakeLoop();
+ void RunUserConnectCallback(int status);
+ void DoHandshakeLoopWithUserConnectCallback();
+
+ int DoConnect();
+ void OnConnectDone(int status);
+ void ProcessConnectDone();
+
+ int DoSendClientHello();
+ void OnSendClientHelloDone(int status);
+ void ProcessSendClientHelloDone(size_t written);
+
+ int DoVerifyServerHello();
+ void OnVerifyServerHelloDone(int status);
+ net::Error ProcessVerifyServerHelloDone(size_t read);
+
+ // Callbacks passed to |transport_socket_|.
+ net::CompletionCallbackImpl<FakeSSLClientSocket> connect_callback_;
+ net::CompletionCallbackImpl<FakeSSLClientSocket>
+ send_client_hello_callback_;
+ net::CompletionCallbackImpl<FakeSSLClientSocket>
+ verify_server_hello_callback_;
+
+ scoped_ptr<net::ClientSocket> transport_socket_;
+
+ // During the handshake process, holds a value from HandshakeState.
+ // STATE_NONE otherwise.
+ HandshakeState next_handshake_state_;
+
+ // True iff we're connected and we've finished the handshake.
+ bool handshake_completed_;
+
+ // The callback passed to Connect().
+ net::CompletionCallback* user_connect_callback_;
+
+ scoped_refptr<net::DrainableIOBuffer> write_buf_;
+ scoped_refptr<net::DrainableIOBuffer> read_buf_;
+};
+
+} // namespace notifier
+
+#endif // JINGLE_NOTIFIER_BASE_FAKE_SSL_CLIENT_SOCKET_H_
diff --git a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc
new file mode 100644
index 0000000..2ca3adf
--- /dev/null
+++ b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc
@@ -0,0 +1,339 @@
+// Copyright (c) 2010 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 "jingle/notifier/base/fake_ssl_client_socket.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/client_socket.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifier {
+
+namespace {
+
+using ::testing::Return;
+using ::testing::ReturnRef;
+
+// Used by RunUnsuccessfulHandshakeTestHelper. Represents where in
+// the handshake step an error should be inserted.
+enum HandshakeErrorLocation {
+ CONNECT_ERROR,
+ SEND_CLIENT_HELLO_ERROR,
+ VERIFY_SERVER_HELLO_ERROR,
+};
+
+// Private error codes appended to the net::Error set.
+enum {
+ // An error representing a server hello that has been corrupted in
+ // transit.
+ ERR_MALFORMED_SERVER_HELLO = -15000,
+};
+
+// Used by PassThroughMethods test.
+class MockClientSocket : public net::ClientSocket {
+ public:
+ virtual ~MockClientSocket() {}
+
+ MOCK_METHOD3(Read, int(net::IOBuffer*, int, net::CompletionCallback*));
+ MOCK_METHOD3(Write, int(net::IOBuffer*, int, net::CompletionCallback*));
+ MOCK_METHOD1(SetReceiveBufferSize, bool(int32));
+ MOCK_METHOD1(SetSendBufferSize, bool(int32));
+ MOCK_METHOD1(Connect, int(net::CompletionCallback*));
+ MOCK_METHOD0(Disconnect, void());
+ MOCK_CONST_METHOD0(IsConnected, bool());
+ MOCK_CONST_METHOD0(IsConnectedAndIdle, bool());
+ MOCK_CONST_METHOD1(GetPeerAddress, int(net::AddressList*));
+ MOCK_CONST_METHOD0(NetLog, const net::BoundNetLog&());
+ MOCK_METHOD0(SetSubresourceSpeculation, void());
+ MOCK_METHOD0(SetOmniboxSpeculation, void());
+};
+
+// Break up |data| into a bunch of chunked MockReads/Writes and push
+// them onto |ops|.
+void AddChunkedOps(base::StringPiece data, size_t chunk_size, bool async,
+ std::vector<net::MockRead>* ops) {
+ DCHECK_GT(chunk_size, 0U);
+ size_t offset = 0;
+ while (offset < data.size()) {
+ size_t bounded_chunk_size = std::min(data.size() - offset, chunk_size);
+ // We take advantage of the fact that MockWrite is typedefed to
+ // MockRead.
+ ops->push_back(net::MockRead(async, data.data() + offset,
+ bounded_chunk_size));
+ offset += bounded_chunk_size;
+ }
+}
+
+class FakeSSLClientSocketTest : public testing::Test {
+ protected:
+ FakeSSLClientSocketTest()
+ : capturing_net_log_(net::CapturingNetLog::kUnbounded) {}
+
+ virtual ~FakeSSLClientSocketTest() {}
+
+ net::ClientSocket* MakeClientSocket() {
+ return mock_client_socket_factory_.CreateTCPClientSocket(
+ net::AddressList(), &capturing_net_log_, net::NetLog::Source());
+ }
+
+ void SetData(const net::MockConnect& mock_connect,
+ std::vector<net::MockRead>* reads,
+ std::vector<net::MockWrite>* writes) {
+ static_socket_data_provider_.reset(
+ new net::StaticSocketDataProvider(
+ &*reads->begin(), reads->size(),
+ &*writes->begin(), writes->size()));
+ static_socket_data_provider_->set_connect_data(mock_connect);
+ mock_client_socket_factory_.AddSocketDataProvider(
+ static_socket_data_provider_.get());
+ }
+
+ void ExpectStatus(
+ bool async, int expected_status, int immediate_status,
+ TestCompletionCallback* test_completion_callback) {
+ if (async) {
+ EXPECT_EQ(net::ERR_IO_PENDING, immediate_status);
+ int status = test_completion_callback->WaitForResult();
+ EXPECT_EQ(expected_status, status);
+ } else {
+ EXPECT_EQ(expected_status, immediate_status);
+ }
+ }
+
+ // Sets up the mock socket to generate a successful handshake
+ // (sliced up according to the parameters) and makes sure the
+ // FakeSSLClientSocket behaves as expected.
+ void RunSuccessfulHandshakeTest(
+ bool async, size_t read_chunk_size, size_t write_chunk_size,
+ int num_resets) {
+ base::StringPiece ssl_client_hello =
+ FakeSSLClientSocket::GetSslClientHello();
+ base::StringPiece ssl_server_hello =
+ FakeSSLClientSocket::GetSslServerHello();
+
+ net::MockConnect mock_connect(async, net::OK);
+ std::vector<net::MockRead> reads;
+ std::vector<net::MockWrite> writes;
+ static const char kReadTestData[] = "read test data";
+ static const char kWriteTestData[] = "write test data";
+ for (int i = 0; i < num_resets + 1; ++i) {
+ SCOPED_TRACE(i);
+ AddChunkedOps(ssl_server_hello, read_chunk_size, async, &reads);
+ AddChunkedOps(ssl_client_hello, write_chunk_size, async, &writes);
+ reads.push_back(
+ net::MockRead(async, kReadTestData, arraysize(kReadTestData)));
+ writes.push_back(
+ net::MockWrite(async, kWriteTestData, arraysize(kWriteTestData)));
+ }
+ SetData(mock_connect, &reads, &writes);
+
+ FakeSSLClientSocket fake_ssl_client_socket(MakeClientSocket());
+
+ for (int i = 0; i < num_resets + 1; ++i) {
+ SCOPED_TRACE(i);
+ TestCompletionCallback test_completion_callback;
+ int status = fake_ssl_client_socket.Connect(&test_completion_callback);
+ if (async) {
+ EXPECT_FALSE(fake_ssl_client_socket.IsConnected());
+ }
+ ExpectStatus(async, net::OK, status, &test_completion_callback);
+ if (fake_ssl_client_socket.IsConnected()) {
+ int read_len = arraysize(kReadTestData);
+ int read_buf_len = 2 * read_len;
+ scoped_refptr<net::IOBuffer> read_buf(
+ new net::IOBuffer(read_buf_len));
+ int read_status = fake_ssl_client_socket.Read(
+ read_buf, read_buf_len, &test_completion_callback);
+ ExpectStatus(async, read_len, read_status, &test_completion_callback);
+
+ scoped_refptr<net::IOBuffer> write_buf(
+ new net::StringIOBuffer(kWriteTestData));
+ int write_status = fake_ssl_client_socket.Write(
+ write_buf, arraysize(kWriteTestData), &test_completion_callback);
+ ExpectStatus(async, arraysize(kWriteTestData), write_status,
+ &test_completion_callback);
+ } else {
+ ADD_FAILURE();
+ }
+ fake_ssl_client_socket.Disconnect();
+ EXPECT_FALSE(fake_ssl_client_socket.IsConnected());
+ }
+ }
+
+ // Sets up the mock socket to generate an unsuccessful handshake
+ // FakeSSLClientSocket fails as expected.
+ void RunUnsuccessfulHandshakeTestHelper(
+ bool async, int error, HandshakeErrorLocation location) {
+ DCHECK_NE(error, net::OK);
+ base::StringPiece ssl_client_hello =
+ FakeSSLClientSocket::GetSslClientHello();
+ base::StringPiece ssl_server_hello =
+ FakeSSLClientSocket::GetSslServerHello();
+
+ net::MockConnect mock_connect(async, net::OK);
+ std::vector<net::MockRead> reads;
+ std::vector<net::MockWrite> writes;
+ const size_t kChunkSize = 1;
+ AddChunkedOps(ssl_server_hello, kChunkSize, async, &reads);
+ AddChunkedOps(ssl_client_hello, kChunkSize, async, &writes);
+ switch (location) {
+ case CONNECT_ERROR:
+ mock_connect.result = error;
+ writes.clear();
+ reads.clear();
+ break;
+ case SEND_CLIENT_HELLO_ERROR: {
+ // Use a fixed index for repeatability.
+ size_t index = 100 % writes.size();
+ writes[index].result = error;
+ writes[index].data = NULL;
+ writes[index].data_len = 0;
+ writes.resize(index + 1);
+ reads.clear();
+ break;
+ }
+ case VERIFY_SERVER_HELLO_ERROR: {
+ // Use a fixed index for repeatability.
+ size_t index = 50 % reads.size();
+ if (error == ERR_MALFORMED_SERVER_HELLO) {
+ static const char kBadData[] = "BAD_DATA";
+ reads[index].data = kBadData;
+ reads[index].data_len = arraysize(kBadData);
+ } else {
+ reads[index].result = error;
+ reads[index].data = NULL;
+ reads[index].data_len = 0;
+ }
+ reads.resize(index + 1);
+ if (error ==
+ net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ) {
+ static const char kDummyData[] = "DUMMY";
+ reads.push_back(net::MockRead(async, kDummyData));
+ }
+ break;
+ }
+ }
+ SetData(mock_connect, &reads, &writes);
+
+ FakeSSLClientSocket fake_ssl_client_socket(MakeClientSocket());
+
+ // The two errors below are interpreted by FakeSSLClientSocket as
+ // an unexpected event.
+ int expected_status =
+ ((error == net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ) ||
+ (error == ERR_MALFORMED_SERVER_HELLO)) ?
+ net::ERR_UNEXPECTED : error;
+
+ TestCompletionCallback test_completion_callback;
+ int status = fake_ssl_client_socket.Connect(&test_completion_callback);
+ EXPECT_FALSE(fake_ssl_client_socket.IsConnected());
+ ExpectStatus(async, expected_status, status, &test_completion_callback);
+ EXPECT_FALSE(fake_ssl_client_socket.IsConnected());
+ }
+
+ void RunUnsuccessfulHandshakeTest(
+ int error, HandshakeErrorLocation location) {
+ RunUnsuccessfulHandshakeTestHelper(false, error, location);
+ RunUnsuccessfulHandshakeTestHelper(true, error, location);
+ }
+
+ // MockTCPClientSocket needs a message loop.
+ MessageLoop message_loop_;
+
+ net::CapturingNetLog capturing_net_log_;
+ net::MockClientSocketFactory mock_client_socket_factory_;
+ scoped_ptr<net::StaticSocketDataProvider> static_socket_data_provider_;
+};
+
+TEST_F(FakeSSLClientSocketTest, PassThroughMethods) {
+ MockClientSocket* mock_client_socket = new MockClientSocket();
+ const int kReceiveBufferSize = 10;
+ const int kSendBufferSize = 20;
+ net::AddressList address_list;
+ const int kPeerAddress = 30;
+ net::BoundNetLog net_log;
+ EXPECT_CALL(*mock_client_socket, SetReceiveBufferSize(kReceiveBufferSize));
+ EXPECT_CALL(*mock_client_socket, SetSendBufferSize(kSendBufferSize));
+ EXPECT_CALL(*mock_client_socket, GetPeerAddress(&address_list)).
+ WillOnce(Return(kPeerAddress));
+ EXPECT_CALL(*mock_client_socket, NetLog()).WillOnce(ReturnRef(net_log));
+ EXPECT_CALL(*mock_client_socket, SetSubresourceSpeculation());
+ EXPECT_CALL(*mock_client_socket, SetOmniboxSpeculation());
+
+ // Takes ownership of |mock_client_socket|.
+ FakeSSLClientSocket fake_ssl_client_socket(mock_client_socket);
+ fake_ssl_client_socket.SetReceiveBufferSize(kReceiveBufferSize);
+ fake_ssl_client_socket.SetSendBufferSize(kSendBufferSize);
+ EXPECT_EQ(kPeerAddress,
+ fake_ssl_client_socket.GetPeerAddress(&address_list));
+ EXPECT_EQ(&net_log, &fake_ssl_client_socket.NetLog());
+ fake_ssl_client_socket.SetSubresourceSpeculation();
+ fake_ssl_client_socket.SetOmniboxSpeculation();
+}
+
+TEST_F(FakeSSLClientSocketTest, SuccessfulHandshakeSync) {
+ for (size_t i = 1; i < 100; i += 3) {
+ SCOPED_TRACE(i);
+ for (size_t j = 1; j < 100; j += 5) {
+ SCOPED_TRACE(j);
+ RunSuccessfulHandshakeTest(false, i, j, 0);
+ }
+ }
+}
+
+TEST_F(FakeSSLClientSocketTest, SuccessfulHandshakeAsync) {
+ for (size_t i = 1; i < 100; i += 7) {
+ SCOPED_TRACE(i);
+ for (size_t j = 1; j < 100; j += 9) {
+ SCOPED_TRACE(j);
+ RunSuccessfulHandshakeTest(true, i, j, 0);
+ }
+ }
+}
+
+TEST_F(FakeSSLClientSocketTest, ResetSocket) {
+ RunSuccessfulHandshakeTest(true, 1, 2, 3);
+}
+
+TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeConnectError) {
+ RunUnsuccessfulHandshakeTest(net::ERR_ACCESS_DENIED, CONNECT_ERROR);
+}
+
+TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeWriteError) {
+ RunUnsuccessfulHandshakeTest(net::ERR_OUT_OF_MEMORY,
+ SEND_CLIENT_HELLO_ERROR);
+}
+
+TEST_F(FakeSSLClientSocketTest, UnsuccessfulHandshakeReadError) {
+ RunUnsuccessfulHandshakeTest(net::ERR_CONNECTION_CLOSED,
+ VERIFY_SERVER_HELLO_ERROR);
+}
+
+TEST_F(FakeSSLClientSocketTest, PeerClosedDuringHandshake) {
+ RunUnsuccessfulHandshakeTest(
+ net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ,
+ VERIFY_SERVER_HELLO_ERROR);
+}
+
+TEST_F(FakeSSLClientSocketTest, MalformedServerHello) {
+ RunUnsuccessfulHandshakeTest(ERR_MALFORMED_SERVER_HELLO,
+ VERIFY_SERVER_HELLO_ERROR);
+}
+
+} // namespace
+
+} // namespace notifier
+