Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / net / socket / ssl_client_socket_unittest.cc
index 0c6b335..05844cd 100644 (file)
@@ -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"
@@ -101,10 +102,10 @@ class WrappedStreamSocket : public StreamSocket {
                     const CompletionCallback& callback) OVERRIDE {
     return transport_->Write(buf, buf_len, callback);
   }
-  virtual bool SetReceiveBufferSize(int32 size) OVERRIDE {
+  virtual int SetReceiveBufferSize(int32 size) OVERRIDE {
     return transport_->SetReceiveBufferSize(size);
   }
-  virtual bool SetSendBufferSize(int32 size) OVERRIDE {
+  virtual int SetSendBufferSize(int32 size) OVERRIDE {
     return transport_->SetSendBufferSize(size);
   }
 
@@ -327,144 +328,204 @@ 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;
+
+  // Blocks read results on the socket. Reads will not complete until
+  // UnblockReadResult() has been called and a result is ready from the
+  // underlying transport. Note: if BlockReadResult() is called while there is a
+  // hanging asynchronous Read(), that Read is blocked.
+  void BlockReadResult();
+  void UnblockReadResult();
+
+  // Waits for the blocked Read() call to be complete at the underlying
+  // transport.
+  void WaitForReadResult();
 
-  // 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(); }
+  // Causes the next call to Write() to return ERR_IO_PENDING, not beginning the
+  // underlying transport until UnblockWrite() has been called. Note: if there
+  // is a pending asynchronous write, it is NOT blocked. For purposes of
+  // blocking writes, data is considered to have reached the underlying
+  // transport as soon as Write() is called.
+  void BlockWrite();
+  void UnblockWrite();
 
-  // 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 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_;
+
+  // WaitForReadResult() wait loop.
+  scoped_ptr<base::RunLoop> read_loop_;
+
+  // True if write calls are blocked.
+  bool should_block_write_;
 
-  BlockingState read_state_;
-  BlockingState write_state_;
+  // The buffer for the pending write, or NULL if not scheduled.
+  scoped_refptr<IOBuffer> pending_write_buf_;
 
-  DISALLOW_COPY_AND_ASSIGN(FakeBlockingStreamSocket);
+  // 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) {
+  DCHECK(pending_read_callback_.is_null());
+  DCHECK_EQ(ERR_IO_PENDING, pending_read_result_);
+  DCHECK(!callback.is_null());
+
+  int rv = transport_->Read(buf, len, base::Bind(
+      &FakeBlockingStreamSocket::OnReadCompleted, base::Unretained(this)));
+  if (rv == ERR_IO_PENDING) {
+    // Save the callback to be called later.
+    pending_read_callback_ = callback;
+  } else if (should_block_read_) {
+    // Save the callback and read result to be called later.
+    pending_read_callback_ = callback;
+    OnReadCompleted(rv);
+    rv = ERR_IO_PENDING;
+  }
+  return rv;
 }
 
-void FakeBlockingStreamSocket::BlockingState::Unblock() {
-  DCHECK(should_block_);
-  should_block_ = false;
+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());
+  DCHECK(!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::BlockReadResult() {
+  DCHECK(!should_block_read_);
+  should_block_read_ = true;
+}
+
+void FakeBlockingStreamSocket::UnblockReadResult() {
+  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::WaitForReadResult() {
+  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::BlockWrite() {
+  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 BlockWrite(),
+  // 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) {
+  DCHECK_EQ(ERR_IO_PENDING, pending_read_result_);
+  DCHECK(!pending_read_callback_.is_null());
+
+  if (should_block_read_) {
     // Store the result so that the callback can be invoked once Unblock() is
     // called.
-    have_result_ = true;
-    pending_result_ = result;
-    return;
+    pending_read_result_ = result;
+
+    // Stop the WaitForReadResult() call if any.
+    if (read_loop_)
+      read_loop_->Quit();
+  } else {
+    // Either the Read() was never blocked or UnblockReadResult() was called
+    // before the Read() completed. Either way, run the callback.
+    base::ResetAndReturn(&pending_read_callback_).Run(result);
   }
-
-  // Otherwise, the Unblock() function was called before the underlying
-  // transport completed, so run the user's callback immediately.
-  base::ResetAndReturn(&user_callback_).Run(result);
 }
 
 // CompletionCallback that will delete the associated StreamSocket when
@@ -565,6 +626,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->BlockReadResult();
+    rv = sock->Connect(callback.callback());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+    raw_transport->WaitForReadResult();
+
+    // 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->BlockWrite();
+    raw_transport->UnblockReadResult();
+    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->BlockReadResult();
+    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->UnblockReadResult();
+      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
@@ -997,7 +1145,7 @@ TEST_F(SSLClientSocketTest, Write_WithSynchronousError) {
   // Simulate an unclean/forcible shutdown on the underlying socket.
   // However, simulate this error asynchronously.
   raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
-  raw_transport->SetNextWriteShouldBlock();
+  raw_transport->BlockWrite();
 
   // This write should complete synchronously, because the TLS ciphertext
   // can be created and placed into the outgoing buffers independent of the
@@ -1142,8 +1290,8 @@ TEST_F(SSLClientSocketTest, Read_DeleteWhilePendingFullDuplex) {
   raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
   // ... but have those errors returned asynchronously. Because the Write() will
   // return first, this will trigger the error.
-  raw_transport->SetNextReadShouldBlock();
-  raw_transport->SetNextWriteShouldBlock();
+  raw_transport->BlockReadResult();
+  raw_transport->BlockWrite();
 
   // Enqueue a Read() before calling Write(), which should "hang" due to
   // the ERR_IO_PENDING caused by SetReadShouldBlock() and thus return.
@@ -1267,7 +1415,7 @@ TEST_F(SSLClientSocketTest, Read_WithWriteError) {
 
   // Start a hanging read.
   TestCompletionCallback read_callback;
-  raw_transport->SetNextReadShouldBlock();
+  raw_transport->BlockReadResult();
   scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
   rv = sock->Read(buf.get(), 4096, read_callback.callback());
   EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1311,7 +1459,7 @@ TEST_F(SSLClientSocketTest, Read_WithWriteError) {
 #endif
 
   // Release the read. Some bytes should go through.
-  raw_transport->UnblockRead();
+  raw_transport->UnblockReadResult();
   rv = read_callback.WaitForResult();
 
   // Per the fix for http://crbug.com/249848, write failures currently break
@@ -2183,4 +2331,36 @@ TEST_F(SSLClientSocketTest, ReuseStates) {
   // attempt to read one byte extra.
 }
 
+TEST_F(SSLClientSocketFalseStartTest, 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