Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(617)

Unified Diff: net/socket/ssl_client_socket_unittest.cc

Issue 208293002: Add False Start tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase to trunk. Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | net/test/spawned_test_server/base_test_server.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/socket/ssl_client_socket_unittest.cc
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 7dd9d640cab8f6fd6baf96b0b00fb36459bf97d1..9d7f2e9a16cd4f0e43809a4b3bc14c261b8c591c 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -6,6 +6,7 @@
#include "base/callback_helpers.h"
#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
#include "net/base/address_list.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -327,144 +328,195 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket {
// Socket implementation:
virtual int Read(IOBuffer* buf,
int buf_len,
- const CompletionCallback& callback) OVERRIDE {
- return read_state_.RunWrappedFunction(buf, buf_len, callback);
- }
+ const CompletionCallback& callback) OVERRIDE;
virtual int Write(IOBuffer* buf,
int buf_len,
- const CompletionCallback& callback) OVERRIDE {
- return write_state_.RunWrappedFunction(buf, buf_len, callback);
- }
+ const CompletionCallback& callback) OVERRIDE;
// Causes the next call to Read() to return ERR_IO_PENDING, not completing
// (invoking the callback) until UnblockRead() has been called and the
// underlying transport has completed.
- void SetNextReadShouldBlock() { read_state_.SetShouldBlock(); }
- void UnblockRead() { read_state_.Unblock(); }
+ void SetNextReadShouldBlock();
+ void UnblockRead();
- // Causes the next call to Write() to return ERR_IO_PENDING, not completing
- // (invoking the callback) until UnblockWrite() has been called and the
- // underlying transport has completed.
- void SetNextWriteShouldBlock() { write_state_.SetShouldBlock(); }
- void UnblockWrite() { write_state_.Unblock(); }
+ // Waits for the blocked Read() call to be complete at the underlying
+ // transport.
+ void WaitForRead();
+
+ // Causes the next call to Write() to return ERR_IO_PENDING, not beginning the
+ // underlying transport until UnblockWrite() has been called.
+ void SetNextWriteShouldBlock();
+ void UnblockWrite();
+
+ // Waits for the blocked Write() call to be scheduled.
+ void WaitForWrite();
private:
- // Tracks the state for simulating a blocking Read/Write operation.
- class BlockingState {
- public:
- // Wrapper for the underlying Socket function to call (ie: Read/Write).
- typedef base::Callback<int(IOBuffer*, int, const CompletionCallback&)>
- WrappedSocketFunction;
-
- explicit BlockingState(const WrappedSocketFunction& function);
- ~BlockingState() {}
-
- // Sets the next call to RunWrappedFunction() to block, returning
- // ERR_IO_PENDING and not invoking the user callback until Unblock() is
- // called.
- void SetShouldBlock();
-
- // Unblocks the currently blocked pending function, invoking the user
- // callback if the results are immediately available.
- // Note: It's not valid to call this unless SetShouldBlock() has been
- // called beforehand.
- void Unblock();
-
- // Performs the wrapped socket function on the underlying transport. If
- // configured to block via SetShouldBlock(), then |user_callback| will not
- // be invoked until Unblock() has been called.
- int RunWrappedFunction(IOBuffer* buf,
- int len,
- const CompletionCallback& user_callback);
-
- private:
- // Handles completion from the underlying wrapped socket function.
- void OnCompleted(int result);
-
- WrappedSocketFunction wrapped_function_;
- bool should_block_;
- bool have_result_;
- int pending_result_;
- CompletionCallback user_callback_;
- };
+ // Handles completion from the underlying transport read.
+ void OnReadCompleted(int result);
+
+ // True if read callbacks are blocked.
+ bool should_block_read_;
+
+ // The user callback for the pending read call.
+ CompletionCallback pending_read_callback_;
+
+ // The result for the blocked read callback, or ERR_IO_PENDING if not
+ // completed.
+ int pending_read_result_;
- BlockingState read_state_;
- BlockingState write_state_;
+ // WaitForRead() wait loop.
+ scoped_ptr<base::RunLoop> read_loop_;
- DISALLOW_COPY_AND_ASSIGN(FakeBlockingStreamSocket);
+ // True if write calls are blocked.
+ bool should_block_write_;
+
+ // The buffer for the pending write, or NULL if not scheduled.
+ scoped_refptr<IOBuffer> pending_write_buf_;
+
+ // The callback for the pending write call.
+ CompletionCallback pending_write_callback_;
+
+ // The length for the pending write, or -1 if not scheduled.
+ int pending_write_len_;
+
+ // WaitForWrite() wait loop.
+ scoped_ptr<base::RunLoop> write_loop_;
};
FakeBlockingStreamSocket::FakeBlockingStreamSocket(
scoped_ptr<StreamSocket> transport)
: WrappedStreamSocket(transport.Pass()),
- read_state_(base::Bind(&Socket::Read,
- base::Unretained(transport_.get()))),
- write_state_(base::Bind(&Socket::Write,
- base::Unretained(transport_.get()))) {}
-
-FakeBlockingStreamSocket::BlockingState::BlockingState(
- const WrappedSocketFunction& function)
- : wrapped_function_(function),
- should_block_(false),
- have_result_(false),
- pending_result_(OK) {}
-
-void FakeBlockingStreamSocket::BlockingState::SetShouldBlock() {
- DCHECK(!should_block_);
- should_block_ = true;
+ should_block_read_(false),
+ pending_read_result_(ERR_IO_PENDING),
+ should_block_write_(false),
+ pending_write_len_(-1) {}
+
+int FakeBlockingStreamSocket::Read(IOBuffer* buf,
+ int len,
+ const CompletionCallback& callback) {
+ if (!should_block_read_)
+ return transport_->Read(buf, len, callback);
+
+ DCHECK(pending_read_callback_.is_null());
+ DCHECK_EQ(ERR_IO_PENDING, pending_read_result_);
+
+ int rv = transport_->Read(buf, len, base::Bind(
+ &FakeBlockingStreamSocket::OnReadCompleted, base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ pending_read_callback_ = callback;
+ } else {
+ OnReadCompleted(rv);
+ }
+ return ERR_IO_PENDING;
+}
+
+int FakeBlockingStreamSocket::Write(IOBuffer* buf,
+ int len,
+ const CompletionCallback& callback) {
+ DCHECK(buf);
+ DCHECK_LE(0, len);
+
+ if (!should_block_write_)
+ return transport_->Write(buf, len, callback);
+
+ // Schedule the write, but do nothing.
+ DCHECK(!pending_write_buf_);
+ DCHECK_EQ(-1, pending_write_len_);
+ DCHECK(pending_write_callback_.is_null());
+ pending_write_buf_ = buf;
+ pending_write_len_ = len;
+ pending_write_callback_ = callback;
+
+ // Stop the write loop, if any.
+ if (write_loop_)
+ write_loop_->Quit();
+ return ERR_IO_PENDING;
}
-void FakeBlockingStreamSocket::BlockingState::Unblock() {
- DCHECK(should_block_);
- should_block_ = false;
+void FakeBlockingStreamSocket::SetNextReadShouldBlock() {
+ DCHECK(!should_block_read_);
+ should_block_read_ = true;
+}
+
+void FakeBlockingStreamSocket::UnblockRead() {
+ DCHECK(should_block_read_);
+ should_block_read_ = false;
// If the operation is still pending in the underlying transport, immediately
- // return - OnCompleted() will handle invoking the callback once the transport
- // has completed.
- if (!have_result_)
+ // return - OnReadCompleted() will handle invoking the callback once the
+ // transport has completed.
+ if (pending_read_result_ == ERR_IO_PENDING)
return;
+ int result = pending_read_result_;
+ pending_read_result_ = ERR_IO_PENDING;
+ base::ResetAndReturn(&pending_read_callback_).Run(result);
+}
- have_result_ = false;
+void FakeBlockingStreamSocket::WaitForRead() {
+ DCHECK(should_block_read_);
+ DCHECK(!read_loop_);
- base::ResetAndReturn(&user_callback_).Run(pending_result_);
+ if (pending_read_result_ != ERR_IO_PENDING)
+ return;
+ read_loop_.reset(new base::RunLoop);
+ read_loop_->Run();
+ read_loop_.reset();
+ DCHECK_NE(ERR_IO_PENDING, pending_read_result_);
}
-int FakeBlockingStreamSocket::BlockingState::RunWrappedFunction(
- IOBuffer* buf,
- int len,
- const CompletionCallback& callback) {
-
- // The callback to be called by the underlying transport. Either forward
- // directly to the user's callback if not set to block, or intercept it with
- // OnCompleted so that the user's callback is not invoked until Unblock() is
- // called.
- CompletionCallback transport_callback =
- !should_block_ ? callback : base::Bind(&BlockingState::OnCompleted,
- base::Unretained(this));
- int rv = wrapped_function_.Run(buf, len, transport_callback);
- if (should_block_) {
- user_callback_ = callback;
- // May have completed synchronously.
- have_result_ = (rv != ERR_IO_PENDING);
- pending_result_ = rv;
- return ERR_IO_PENDING;
+void FakeBlockingStreamSocket::SetNextWriteShouldBlock() {
+ DCHECK(!should_block_write_);
+ should_block_write_ = true;
+}
+
+void FakeBlockingStreamSocket::UnblockWrite() {
+ DCHECK(should_block_write_);
+ should_block_write_ = false;
+
+ // Do nothing if UnblockWrite() was called after SetNextWriteShouldBlock(),
+ // without a Write() in between.
+ if (!pending_write_buf_)
+ return;
+
+ int rv = transport_->Write(pending_write_buf_, pending_write_len_,
+ pending_write_callback_);
+ pending_write_buf_ = NULL;
+ pending_write_len_ = -1;
+ if (rv == ERR_IO_PENDING) {
+ pending_write_callback_.Reset();
+ } else {
+ base::ResetAndReturn(&pending_write_callback_).Run(rv);
}
+}
- return rv;
+void FakeBlockingStreamSocket::WaitForWrite() {
+ DCHECK(should_block_write_);
+ DCHECK(!write_loop_);
+
+ if (pending_write_buf_)
+ return;
+ write_loop_.reset(new base::RunLoop);
+ write_loop_->Run();
+ write_loop_.reset();
+ DCHECK(pending_write_buf_);
}
-void FakeBlockingStreamSocket::BlockingState::OnCompleted(int result) {
- if (should_block_) {
+void FakeBlockingStreamSocket::OnReadCompleted(int result) {
+ if (should_block_read_) {
// Store the result so that the callback can be invoked once Unblock() is
// called.
- have_result_ = true;
- pending_result_ = result;
+ pending_read_result_ = result;
+
+ // Stop the WaitForRead() call if any.
+ if (read_loop_)
+ read_loop_->Quit();
return;
}
// Otherwise, the Unblock() function was called before the underlying
// transport completed, so run the user's callback immediately.
- base::ResetAndReturn(&user_callback_).Run(result);
+ base::ResetAndReturn(&pending_read_callback_).Run(result);
}
// CompletionCallback that will delete the associated StreamSocket when
@@ -565,6 +617,93 @@ class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest {
}
};
+class SSLClientSocketFalseStartTest : public SSLClientSocketTest {
+ protected:
+ void TestFalseStart(const SpawnedTestServer::SSLOptions& server_options,
+ const SSLConfig& client_config,
+ bool expect_false_start) {
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ server_options,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> real_transport(
+ new TCPClientSocket(addr, NULL, NetLog::Source()));
+ scoped_ptr<FakeBlockingStreamSocket> transport(
+ new FakeBlockingStreamSocket(real_transport.Pass()));
+ int rv = callback.GetResult(transport->Connect(callback.callback()));
+ EXPECT_EQ(OK, rv);
+
+ FakeBlockingStreamSocket* raw_transport = transport.get();
+ scoped_ptr<SSLClientSocket> sock(
+ CreateSSLClientSocket(transport.PassAs<StreamSocket>(),
+ test_server.host_port_pair(),
+ client_config));
+
+ // Connect. Stop before the client processes the first server leg
+ // (ServerHello, etc.)
+ raw_transport->SetNextReadShouldBlock();
+ rv = sock->Connect(callback.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ raw_transport->WaitForRead();
+
+ // Release the ServerHello and wait for the client to write
+ // ClientKeyExchange, etc. (A proxy for waiting for the entirety of the
+ // server's leg to complete, since it may span multiple reads.)
+ EXPECT_FALSE(callback.have_result());
+ raw_transport->SetNextWriteShouldBlock();
+ raw_transport->UnblockRead();
+ raw_transport->WaitForWrite();
+
+ // And, finally, release that and block the next server leg
+ // (ChangeCipherSpec, Finished). Note: callback.have_result() may or may not
+ // be true at this point depending on whether the SSL implementation waits
+ // for the client second leg to clear the internal write buffer and hit the
+ // network.
+ raw_transport->SetNextReadShouldBlock();
+ raw_transport->UnblockWrite();
+
+ if (expect_false_start) {
+ // When False Starting, the handshake should complete before receiving the
+ // Change Cipher Spec and Finished messages.
+ rv = callback.GetResult(rv);
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock->IsConnected());
+
+ const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+ static const int kRequestTextSize =
+ static_cast<int>(arraysize(request_text) - 1);
+ scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kRequestTextSize));
+ memcpy(request_buffer->data(), request_text, kRequestTextSize);
+
+ // Write the request.
+ rv = callback.GetResult(sock->Write(request_buffer.get(),
+ kRequestTextSize,
+ callback.callback()));
+ EXPECT_EQ(kRequestTextSize, rv);
+
+ // The read will hang; it's waiting for the peer to complete the
+ // handshake, and the handshake is still blocked.
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
+ rv = sock->Read(buf.get(), 4096, callback.callback());
+
+ // After releasing reads, the connection proceeds.
+ raw_transport->UnblockRead();
+ rv = callback.GetResult(rv);
+ EXPECT_LT(0, rv);
+ } else {
+ // False Start is not enabled, so the handshake will not complete because
+ // the server second leg is blocked.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(callback.have_result());
+ }
+ }
+};
+
//-----------------------------------------------------------------------------
// LogContainsSSLConnectEndEvent returns true if the given index in the given
@@ -2183,4 +2322,43 @@ TEST_F(SSLClientSocketTest, ReuseStates) {
// attempt to read one byte extra.
}
+// This test is only enabled on NSS until False Start support is added for
+// OpenSSL. http://crbug.com/354132
+#if defined(USE_NSS)
+#define MAYBE_FalseStartEnabled FalseStartEnabled
+#else
+#define MAYBE_FalseStartEnabled DISABLED_FalseStartEnabled
+#endif // USE_NSS
+TEST_F(SSLClientSocketFalseStartTest, MAYBE_FalseStartEnabled) {
+ // False Start requires NPN and a forward-secret cipher suite.
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ server_options.enable_npn = true;
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+ TestFalseStart(server_options, client_config, true);
+}
+
+// Test that False Start is disabled without NPN.
+TEST_F(SSLClientSocketFalseStartTest, NoNPN) {
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+ SSLConfig client_config;
+ client_config.next_protos.clear();
+ TestFalseStart(server_options, client_config, false);
+}
+
+// Test that False Start is disabled without a forward-secret cipher suite.
+TEST_F(SSLClientSocketFalseStartTest, NoForwardSecrecy) {
+ SpawnedTestServer::SSLOptions server_options;
+ server_options.key_exchanges =
+ SpawnedTestServer::SSLOptions::KEY_EXCHANGE_RSA;
+ server_options.enable_npn = true;
+ SSLConfig client_config;
+ client_config.next_protos.push_back("http/1.1");
+ TestFalseStart(server_options, client_config, false);
+}
+
} // namespace net
« no previous file with comments | « no previous file | net/test/spawned_test_server/base_test_server.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698