1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "jingle/glue/fake_ssl_client_socket.h"
10 #include "base/basictypes.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/log/net_log.h"
17 #include "net/socket/socket_test_util.h"
18 #include "net/socket/stream_socket.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace jingle_glue
{
26 using ::testing::Return
;
27 using ::testing::ReturnRef
;
29 // Used by RunUnsuccessfulHandshakeTestHelper. Represents where in
30 // the handshake step an error should be inserted.
31 enum HandshakeErrorLocation
{
33 SEND_CLIENT_HELLO_ERROR
,
34 VERIFY_SERVER_HELLO_ERROR
,
37 // Private error codes appended to the net::Error set.
39 // An error representing a server hello that has been corrupted in
41 ERR_MALFORMED_SERVER_HELLO
= -15000,
44 // Used by PassThroughMethods test.
45 class MockClientSocket
: public net::StreamSocket
{
47 virtual ~MockClientSocket() {}
49 MOCK_METHOD3(Read
, int(net::IOBuffer
*, int,
50 const net::CompletionCallback
&));
51 MOCK_METHOD3(Write
, int(net::IOBuffer
*, int,
52 const net::CompletionCallback
&));
53 MOCK_METHOD1(SetReceiveBufferSize
, int(int32
));
54 MOCK_METHOD1(SetSendBufferSize
, int(int32
));
55 MOCK_METHOD1(Connect
, int(const net::CompletionCallback
&));
56 MOCK_METHOD0(Disconnect
, void());
57 MOCK_CONST_METHOD0(IsConnected
, bool());
58 MOCK_CONST_METHOD0(IsConnectedAndIdle
, bool());
59 MOCK_CONST_METHOD1(GetPeerAddress
, int(net::IPEndPoint
*));
60 MOCK_CONST_METHOD1(GetLocalAddress
, int(net::IPEndPoint
*));
61 MOCK_CONST_METHOD0(NetLog
, const net::BoundNetLog
&());
62 MOCK_METHOD0(SetSubresourceSpeculation
, void());
63 MOCK_METHOD0(SetOmniboxSpeculation
, void());
64 MOCK_CONST_METHOD0(WasEverUsed
, bool());
65 MOCK_CONST_METHOD0(UsingTCPFastOpen
, bool());
66 MOCK_CONST_METHOD0(NumBytesRead
, int64());
67 MOCK_CONST_METHOD0(GetConnectTimeMicros
, base::TimeDelta());
68 MOCK_CONST_METHOD0(WasNpnNegotiated
, bool());
69 MOCK_CONST_METHOD0(GetNegotiatedProtocol
, net::NextProto());
70 MOCK_METHOD1(GetSSLInfo
, bool(net::SSLInfo
*));
71 MOCK_CONST_METHOD1(GetConnectionAttempts
, void(net::ConnectionAttempts
*));
72 MOCK_METHOD0(ClearConnectionAttempts
, void());
73 MOCK_METHOD1(AddConnectionAttempts
, void(const net::ConnectionAttempts
&));
76 // Break up |data| into a bunch of chunked MockReads/Writes and push
78 template <net::MockReadWriteType type
>
79 void AddChunkedOps(base::StringPiece data
, size_t chunk_size
, net::IoMode mode
,
80 std::vector
<net::MockReadWrite
<type
> >* ops
) {
81 DCHECK_GT(chunk_size
, 0U);
83 while (offset
< data
.size()) {
84 size_t bounded_chunk_size
= std::min(data
.size() - offset
, chunk_size
);
85 ops
->push_back(net::MockReadWrite
<type
>(mode
, data
.data() + offset
,
87 offset
+= bounded_chunk_size
;
91 class FakeSSLClientSocketTest
: public testing::Test
{
93 FakeSSLClientSocketTest() {}
95 ~FakeSSLClientSocketTest() override
{}
97 scoped_ptr
<net::StreamSocket
> MakeClientSocket() {
98 return mock_client_socket_factory_
.CreateTransportClientSocket(
99 net::AddressList(), NULL
, net::NetLog::Source());
102 void SetData(const net::MockConnect
& mock_connect
,
103 std::vector
<net::MockRead
>* reads
,
104 std::vector
<net::MockWrite
>* writes
) {
105 static_socket_data_provider_
.reset(
106 new net::StaticSocketDataProvider(
107 reads
->empty() ? NULL
: &*reads
->begin(), reads
->size(),
108 writes
->empty() ? NULL
: &*writes
->begin(), writes
->size()));
109 static_socket_data_provider_
->set_connect_data(mock_connect
);
110 mock_client_socket_factory_
.AddSocketDataProvider(
111 static_socket_data_provider_
.get());
115 net::IoMode mode
, int expected_status
, int immediate_status
,
116 net::TestCompletionCallback
* test_completion_callback
) {
117 if (mode
== net::ASYNC
) {
118 EXPECT_EQ(net::ERR_IO_PENDING
, immediate_status
);
119 int status
= test_completion_callback
->WaitForResult();
120 EXPECT_EQ(expected_status
, status
);
122 EXPECT_EQ(expected_status
, immediate_status
);
126 // Sets up the mock socket to generate a successful handshake
127 // (sliced up according to the parameters) and makes sure the
128 // FakeSSLClientSocket behaves as expected.
129 void RunSuccessfulHandshakeTest(
130 net::IoMode mode
, size_t read_chunk_size
, size_t write_chunk_size
,
132 base::StringPiece ssl_client_hello
=
133 FakeSSLClientSocket::GetSslClientHello();
134 base::StringPiece ssl_server_hello
=
135 FakeSSLClientSocket::GetSslServerHello();
137 net::MockConnect
mock_connect(mode
, net::OK
);
138 std::vector
<net::MockRead
> reads
;
139 std::vector
<net::MockWrite
> writes
;
140 static const char kReadTestData
[] = "read test data";
141 static const char kWriteTestData
[] = "write test data";
142 for (int i
= 0; i
< num_resets
+ 1; ++i
) {
144 AddChunkedOps(ssl_server_hello
, read_chunk_size
, mode
, &reads
);
145 AddChunkedOps(ssl_client_hello
, write_chunk_size
, mode
, &writes
);
147 net::MockRead(mode
, kReadTestData
, arraysize(kReadTestData
)));
149 net::MockWrite(mode
, kWriteTestData
, arraysize(kWriteTestData
)));
151 SetData(mock_connect
, &reads
, &writes
);
153 FakeSSLClientSocket
fake_ssl_client_socket(MakeClientSocket());
155 for (int i
= 0; i
< num_resets
+ 1; ++i
) {
157 net::TestCompletionCallback test_completion_callback
;
158 int status
= fake_ssl_client_socket
.Connect(
159 test_completion_callback
.callback());
160 if (mode
== net::ASYNC
) {
161 EXPECT_FALSE(fake_ssl_client_socket
.IsConnected());
163 ExpectStatus(mode
, net::OK
, status
, &test_completion_callback
);
164 if (fake_ssl_client_socket
.IsConnected()) {
165 int read_len
= arraysize(kReadTestData
);
166 int read_buf_len
= 2 * read_len
;
167 scoped_refptr
<net::IOBuffer
> read_buf(
168 new net::IOBuffer(read_buf_len
));
169 int read_status
= fake_ssl_client_socket
.Read(
170 read_buf
.get(), read_buf_len
, test_completion_callback
.callback());
171 ExpectStatus(mode
, read_len
, read_status
, &test_completion_callback
);
173 scoped_refptr
<net::IOBuffer
> write_buf(
174 new net::StringIOBuffer(kWriteTestData
));
176 fake_ssl_client_socket
.Write(write_buf
.get(),
177 arraysize(kWriteTestData
),
178 test_completion_callback
.callback());
179 ExpectStatus(mode
, arraysize(kWriteTestData
), write_status
,
180 &test_completion_callback
);
184 fake_ssl_client_socket
.Disconnect();
185 EXPECT_FALSE(fake_ssl_client_socket
.IsConnected());
189 // Sets up the mock socket to generate an unsuccessful handshake
190 // FakeSSLClientSocket fails as expected.
191 void RunUnsuccessfulHandshakeTestHelper(
192 net::IoMode mode
, int error
, HandshakeErrorLocation location
) {
193 DCHECK_NE(error
, net::OK
);
194 base::StringPiece ssl_client_hello
=
195 FakeSSLClientSocket::GetSslClientHello();
196 base::StringPiece ssl_server_hello
=
197 FakeSSLClientSocket::GetSslServerHello();
199 net::MockConnect
mock_connect(mode
, net::OK
);
200 std::vector
<net::MockRead
> reads
;
201 std::vector
<net::MockWrite
> writes
;
202 const size_t kChunkSize
= 1;
203 AddChunkedOps(ssl_server_hello
, kChunkSize
, mode
, &reads
);
204 AddChunkedOps(ssl_client_hello
, kChunkSize
, mode
, &writes
);
207 mock_connect
.result
= error
;
211 case SEND_CLIENT_HELLO_ERROR
: {
212 // Use a fixed index for repeatability.
213 size_t index
= 100 % writes
.size();
214 writes
[index
].result
= error
;
215 writes
[index
].data
= NULL
;
216 writes
[index
].data_len
= 0;
217 writes
.resize(index
+ 1);
221 case VERIFY_SERVER_HELLO_ERROR
: {
222 // Use a fixed index for repeatability.
223 size_t index
= 50 % reads
.size();
224 if (error
== ERR_MALFORMED_SERVER_HELLO
) {
225 static const char kBadData
[] = "BAD_DATA";
226 reads
[index
].data
= kBadData
;
227 reads
[index
].data_len
= arraysize(kBadData
);
229 reads
[index
].result
= error
;
230 reads
[index
].data
= NULL
;
231 reads
[index
].data_len
= 0;
233 reads
.resize(index
+ 1);
235 net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ
) {
236 static const char kDummyData
[] = "DUMMY";
237 reads
.push_back(net::MockRead(mode
, kDummyData
));
242 SetData(mock_connect
, &reads
, &writes
);
244 FakeSSLClientSocket
fake_ssl_client_socket(MakeClientSocket());
246 // The two errors below are interpreted by FakeSSLClientSocket as
247 // an unexpected event.
248 int expected_status
=
249 ((error
== net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ
) ||
250 (error
== ERR_MALFORMED_SERVER_HELLO
)) ?
251 net::ERR_UNEXPECTED
: error
;
253 net::TestCompletionCallback test_completion_callback
;
254 int status
= fake_ssl_client_socket
.Connect(
255 test_completion_callback
.callback());
256 EXPECT_FALSE(fake_ssl_client_socket
.IsConnected());
257 ExpectStatus(mode
, expected_status
, status
, &test_completion_callback
);
258 EXPECT_FALSE(fake_ssl_client_socket
.IsConnected());
261 void RunUnsuccessfulHandshakeTest(
262 int error
, HandshakeErrorLocation location
) {
263 RunUnsuccessfulHandshakeTestHelper(net::SYNCHRONOUS
, error
, location
);
264 RunUnsuccessfulHandshakeTestHelper(net::ASYNC
, error
, location
);
267 // MockTCPClientSocket needs a message loop.
268 base::MessageLoop message_loop_
;
270 net::MockClientSocketFactory mock_client_socket_factory_
;
271 scoped_ptr
<net::StaticSocketDataProvider
> static_socket_data_provider_
;
274 TEST_F(FakeSSLClientSocketTest
, PassThroughMethods
) {
275 scoped_ptr
<MockClientSocket
> mock_client_socket(new MockClientSocket());
276 const int kReceiveBufferSize
= 10;
277 const int kSendBufferSize
= 20;
278 net::IPEndPoint
ip_endpoint(net::IPAddressNumber(net::kIPv4AddressSize
), 80);
279 const int kPeerAddress
= 30;
280 net::BoundNetLog net_log
;
281 EXPECT_CALL(*mock_client_socket
, SetReceiveBufferSize(kReceiveBufferSize
));
282 EXPECT_CALL(*mock_client_socket
, SetSendBufferSize(kSendBufferSize
));
283 EXPECT_CALL(*mock_client_socket
, GetPeerAddress(&ip_endpoint
)).
284 WillOnce(Return(kPeerAddress
));
285 EXPECT_CALL(*mock_client_socket
, NetLog()).WillOnce(ReturnRef(net_log
));
286 EXPECT_CALL(*mock_client_socket
, SetSubresourceSpeculation());
287 EXPECT_CALL(*mock_client_socket
, SetOmniboxSpeculation());
289 // Takes ownership of |mock_client_socket|.
290 FakeSSLClientSocket
fake_ssl_client_socket(mock_client_socket
.Pass());
291 fake_ssl_client_socket
.SetReceiveBufferSize(kReceiveBufferSize
);
292 fake_ssl_client_socket
.SetSendBufferSize(kSendBufferSize
);
293 EXPECT_EQ(kPeerAddress
,
294 fake_ssl_client_socket
.GetPeerAddress(&ip_endpoint
));
295 EXPECT_EQ(&net_log
, &fake_ssl_client_socket
.NetLog());
296 fake_ssl_client_socket
.SetSubresourceSpeculation();
297 fake_ssl_client_socket
.SetOmniboxSpeculation();
300 TEST_F(FakeSSLClientSocketTest
, SuccessfulHandshakeSync
) {
301 for (size_t i
= 1; i
< 100; i
+= 3) {
303 for (size_t j
= 1; j
< 100; j
+= 5) {
305 RunSuccessfulHandshakeTest(net::SYNCHRONOUS
, i
, j
, 0);
310 TEST_F(FakeSSLClientSocketTest
, SuccessfulHandshakeAsync
) {
311 for (size_t i
= 1; i
< 100; i
+= 7) {
313 for (size_t j
= 1; j
< 100; j
+= 9) {
315 RunSuccessfulHandshakeTest(net::ASYNC
, i
, j
, 0);
320 TEST_F(FakeSSLClientSocketTest
, ResetSocket
) {
321 RunSuccessfulHandshakeTest(net::ASYNC
, 1, 2, 3);
324 TEST_F(FakeSSLClientSocketTest
, UnsuccessfulHandshakeConnectError
) {
325 RunUnsuccessfulHandshakeTest(net::ERR_ACCESS_DENIED
, CONNECT_ERROR
);
328 TEST_F(FakeSSLClientSocketTest
, UnsuccessfulHandshakeWriteError
) {
329 RunUnsuccessfulHandshakeTest(net::ERR_OUT_OF_MEMORY
,
330 SEND_CLIENT_HELLO_ERROR
);
333 TEST_F(FakeSSLClientSocketTest
, UnsuccessfulHandshakeReadError
) {
334 RunUnsuccessfulHandshakeTest(net::ERR_CONNECTION_CLOSED
,
335 VERIFY_SERVER_HELLO_ERROR
);
338 TEST_F(FakeSSLClientSocketTest
, PeerClosedDuringHandshake
) {
339 RunUnsuccessfulHandshakeTest(
340 net::ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ
,
341 VERIFY_SERVER_HELLO_ERROR
);
344 TEST_F(FakeSSLClientSocketTest
, MalformedServerHello
) {
345 RunUnsuccessfulHandshakeTest(ERR_MALFORMED_SERVER_HELLO
,
346 VERIFY_SERVER_HELLO_ERROR
);
351 } // namespace jingle_glue