text: // Copyright 2013 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 "google_apis/gcm/engine/connection_handler.h"
  
  #include "base/bind.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/run_loop.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/test/test_timeouts.h"
  #include "google/protobuf/io/coded_stream.h"
  #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
  #include "google_apis/gcm/base/mcs_util.h"
  #include "google_apis/gcm/base/socket_stream.h"
  #include "google_apis/gcm/protocol/mcs.pb.h"
  #include "net/socket/socket_test_util.h"
  #include "net/socket/stream_socket.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace gcm {
  namespace {
  
  typedef scoped_ptr<google::protobuf::MessageLite> ScopedMessage;
  typedef std::vector<net::MockRead> ReadList;
  typedef std::vector<net::MockWrite> WriteList;
  
  const uint64 kAuthId = 54321;
  const uint64 kAuthToken = 12345;
  const char kMCSVersion = 38;  // The protocol version.
  const int kMCSPort = 5228;    // The server port.
  const char kDataMsgFrom[] = "data_from";
  const char kDataMsgCategory[] = "data_category";
  const char kDataMsgFrom2[] = "data_from2";
  const char kDataMsgCategory2[] = "data_category2";
  const char kDataMsgFromLong[] =
      "this is a long from that will result in a message > 128 bytes";
  const char kDataMsgCategoryLong[] =
      "this is a long category that will result in a message > 128 bytes";
  const char kDataMsgFromLong2[] =
      "this is a second long from that will result in a message > 128 bytes";
  const char kDataMsgCategoryLong2[] =
      "this is a second long category that will result in a message > 128 bytes";
  
  // ---- Helpers for building messages. ----
  
  // Encode a protobuf packet with protobuf type |tag| and serialized protobuf
  // bytes |proto| into the MCS message form (tag + varint size + bytes).
  std::string EncodePacket(uint8 tag, const std::string& proto) {
    std::string result;
    google::protobuf::io::StringOutputStream string_output_stream(&result);
    google::protobuf::io::CodedOutputStream coded_output_stream(
        &string_output_stream);
    const unsigned char tag_byte[1] = {tag};
    coded_output_stream.WriteRaw(tag_byte, 1);
    coded_output_stream.WriteVarint32(proto.size());
    coded_output_stream.WriteRaw(proto.c_str(), proto.size());
    return result;
  }
  
  // Encode a handshake request into the MCS message form.
  std::string EncodeHandshakeRequest() {
    std::string result;
    const char version_byte[1] = {kMCSVersion};
    result.append(version_byte, 1);
    ScopedMessage login_request(BuildLoginRequest(kAuthId, kAuthToken));
    result.append(EncodePacket(kLoginRequestTag,
                               login_request->SerializeAsString()));
    return result;
  }
  
  // Build a serialized login response protobuf.
  std::string BuildLoginResponse() {
    std::string result;
    mcs_proto::LoginResponse login_response;
    login_response.set_id("id");
    result.append(login_response.SerializeAsString());
    return result;
  }
  
  // Encoode a handshake response into the MCS message form.
  std::string EncodeHandshakeResponse() {
    std::string result;
    const char version_byte[1] = {kMCSVersion};
    result.append(version_byte, 1);
    result.append(EncodePacket(kLoginResponseTag, BuildLoginResponse()));
    return result;
  }
  
  // Build a serialized data message stanza protobuf.
  std::string BuildDataMessage(const std::string& from,
                               const std::string& category) {
    std::string result;
    mcs_proto::DataMessageStanza data_message;
    data_message.set_from(from);
    data_message.set_category(category);
    return data_message.SerializeAsString();
  }
  
  class GCMConnectionHandlerTest : public testing::Test {
   public:
    GCMConnectionHandlerTest();
    virtual ~GCMConnectionHandlerTest();
  
    net::StreamSocket* BuildSocket(const ReadList& read_list,
                                   const WriteList& write_list);
  
    // Pump |message_loop_|, resetting |run_loop_| after completion.
    void PumpLoop();
  
    ConnectionHandler* connection_handler() { return &connection_handler_; }
    base::MessageLoop* message_loop() { return &message_loop_; };
    net::DelayedSocketData* data_provider() { return data_provider_.get(); }
    int last_error() const { return last_error_; }
  
    // Initialize the connection handler, setting |dst_proto| as the destination
    // for any received messages.
    void Connect(ScopedMessage* dst_proto);
  
    // Runs the message loop until a message is received.
    void WaitForMessage();
  
   private:
    void ReadContinuation(ScopedMessage* dst_proto, ScopedMessage new_proto);
    void WriteContinuation();
    void ConnectionContinuation(int error);
  
    // SocketStreams and their data provider.
    ReadList mock_reads_;
    WriteList mock_writes_;
    scoped_ptr<net::DelayedSocketData> data_provider_;
    scoped_ptr<SocketInputStream> socket_input_stream_;
    scoped_ptr<SocketOutputStream> socket_output_stream_;
  
    // The connection handler being tested.
    ConnectionHandler connection_handler_;
  
    // The last connection error received.
    int last_error_;
  
    // net:: components.
    scoped_ptr<net::StreamSocket> socket_;
    net::MockClientSocketFactory socket_factory_;
    net::AddressList address_list_;
  
    base::MessageLoopForIO message_loop_;
    scoped_ptr<base::RunLoop> run_loop_;
  };
  
  GCMConnectionHandlerTest::GCMConnectionHandlerTest()
      : connection_handler_(TestTimeouts::tiny_timeout()),
        last_error_(0) {
    net::IPAddressNumber ip_number;
    net::ParseIPLiteralToNumber("127.0.0.1", &ip_number);
    address_list_ = net::AddressList::CreateFromIPAddress(ip_number, kMCSPort);
  }
  
  GCMConnectionHandlerTest::~GCMConnectionHandlerTest() {
  }
  
  net::StreamSocket* GCMConnectionHandlerTest::BuildSocket(
      const ReadList& read_list,
      const WriteList& write_list) {
    mock_reads_ = read_list;
    mock_writes_ = write_list;
    data_provider_.reset(
        new net::DelayedSocketData(0,
                                   &(mock_reads_[0]), mock_reads_.size(),
                                   &(mock_writes_[0]), mock_writes_.size()));
    socket_factory_.AddSocketDataProvider(data_provider_.get());
  
    socket_ = socket_factory_.CreateTransportClientSocket(
        address_list_, NULL, net::NetLog::Source());
    socket_->Connect(net::CompletionCallback());
  
    run_loop_.reset(new base::RunLoop());
    PumpLoop();
  
    DCHECK(socket_->IsConnected());
    return socket_.get();
  }
  
  void GCMConnectionHandlerTest::PumpLoop() {
    run_loop_->RunUntilIdle();
    run_loop_.reset(new base::RunLoop());
  }
  
  void GCMConnectionHandlerTest::Connect(
      ScopedMessage* dst_proto) {
    connection_handler_.Init(
        socket_.Pass(),
        *BuildLoginRequest(kAuthId, kAuthToken),
        base::Bind(&GCMConnectionHandlerTest::ReadContinuation,
                   base::Unretained(this),
                   dst_proto),
        base::Bind(&GCMConnectionHandlerTest::WriteContinuation,
                   base::Unretained(this)),
        base::Bind(&GCMConnectionHandlerTest::ConnectionContinuation,
                   base::Unretained(this)));
  }
  
  void GCMConnectionHandlerTest::ReadContinuation(
      ScopedMessage* dst_proto,
      ScopedMessage new_proto) {
    *dst_proto = new_proto.Pass();
    run_loop_->Quit();
  }
  
  void GCMConnectionHandlerTest::WaitForMessage() {
    run_loop_->Run();
    run_loop_.reset(new base::RunLoop());
  }
  
  void GCMConnectionHandlerTest::WriteContinuation() {
    run_loop_->Quit();
  }
  
  void GCMConnectionHandlerTest::ConnectionContinuation(int error) {
    last_error_ = error;
    run_loop_->Quit();
  }
  
  // Initialize the connection handler and ensure the handshake completes
  // successfully.
  TEST_F(GCMConnectionHandlerTest, Init) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
    ReadList read_list(1, net::MockRead(net::ASYNC,
                                        handshake_response.c_str(),
                                        handshake_response.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    Connect(&received_message);
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(BuildLoginResponse(), received_message->SerializeAsString());
    EXPECT_TRUE(connection_handler()->CanSendMessage());
  }
  
  // Simulate the handshake response returning an older version. Initialization
  // should fail.
  TEST_F(GCMConnectionHandlerTest, InitFailedVersionCheck) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
    // Overwrite the version byte.
    handshake_response[0] = 37;
    ReadList read_list(1, net::MockRead(net::ASYNC,
                                        handshake_response.c_str(),
                                        handshake_response.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response. Should result in a connection error.
    EXPECT_FALSE(received_message.get());
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    EXPECT_EQ(net::ERR_FAILED, last_error());
  }
  
  // Attempt to initialize, but receive no server response, resulting in a time
  // out.
  TEST_F(GCMConnectionHandlerTest, InitTimeout) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    ReadList read_list(1, net::MockRead(net::SYNCHRONOUS,
                                        net::ERR_IO_PENDING));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response. Should result in a connection error.
    EXPECT_FALSE(received_message.get());
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    EXPECT_EQ(net::ERR_TIMED_OUT, last_error());
  }
  
  // Attempt to initialize, but receive an incomplete server response, resulting
  // in a time out.
  TEST_F(GCMConnectionHandlerTest, InitIncompleteTimeout) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size() / 2));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS,
                                      net::ERR_IO_PENDING));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response. Should result in a connection error.
    EXPECT_FALSE(received_message.get());
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    EXPECT_EQ(net::ERR_TIMED_OUT, last_error());
  }
  
  // Reinitialize the connection handler after failing to initialize.
  TEST_F(GCMConnectionHandlerTest, ReInit) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    ReadList read_list(1, net::MockRead(net::SYNCHRONOUS,
                                        net::ERR_IO_PENDING));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response. Should result in a connection error.
    EXPECT_FALSE(received_message.get());
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    EXPECT_EQ(net::ERR_TIMED_OUT, last_error());
  
    // Build a new socket and reconnect, successfully this time.
    std::string handshake_response = EncodeHandshakeResponse();
    read_list[0] = net::MockRead(net::ASYNC,
                                 handshake_response.c_str(),
                                 handshake_response.size());
    BuildSocket(read_list, write_list);
    Connect(&received_message);
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(BuildLoginResponse(), received_message->SerializeAsString());
    EXPECT_TRUE(connection_handler()->CanSendMessage());
  }
  
  // Verify that messages can be received after initialization.
  TEST_F(GCMConnectionHandlerTest, RecvMsg) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_proto = BuildDataMessage(kDataMsgFrom,
                                                      kDataMsgCategory);
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message_proto);
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::ASYNC,
                                      data_message_pkt.c_str(),
                                      data_message_pkt.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    WaitForMessage();  // The data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto, received_message->SerializeAsString());
  }
  
  // Verify that if two messages arrive at once, they're treated appropriately.
  TEST_F(GCMConnectionHandlerTest, Recv2Msgs) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_proto = BuildDataMessage(kDataMsgFrom,
                                                      kDataMsgCategory);
    std::string data_message_proto2 = BuildDataMessage(kDataMsgFrom2,
                                                       kDataMsgCategory2);
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message_proto);
    data_message_pkt += EncodePacket(kDataMessageStanzaTag, data_message_proto2);
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS,
                                      data_message_pkt.c_str(),
                                      data_message_pkt.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    WaitForMessage();  // The first data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto, received_message->SerializeAsString());
    received_message.reset();
    WaitForMessage();  // The second data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto2, received_message->SerializeAsString());
  }
  
  // Receive a long (>128 bytes) message.
  TEST_F(GCMConnectionHandlerTest, RecvLongMsg) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_proto =
        BuildDataMessage(kDataMsgFromLong, kDataMsgCategoryLong);
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message_proto);
    DCHECK_GT(data_message_pkt.size(), 128U);
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::ASYNC,
                                      data_message_pkt.c_str(),
                                      data_message_pkt.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    WaitForMessage();  // The data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto, received_message->SerializeAsString());
  }
  
  // Receive two long (>128 bytes) message.
  TEST_F(GCMConnectionHandlerTest, Recv2LongMsgs) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_proto =
        BuildDataMessage(kDataMsgFromLong, kDataMsgCategoryLong);
    std::string data_message_proto2 =
        BuildDataMessage(kDataMsgFromLong2, kDataMsgCategoryLong2);
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message_proto);
    data_message_pkt += EncodePacket(kDataMessageStanzaTag, data_message_proto2);
    DCHECK_GT(data_message_pkt.size(), 256U);
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS,
                                      data_message_pkt.c_str(),
                                      data_message_pkt.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    WaitForMessage();  // The first data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto, received_message->SerializeAsString());
    received_message.reset();
    WaitForMessage();  // The second data message.
    ASSERT_TRUE(received_message.get());
    EXPECT_EQ(data_message_proto2, received_message->SerializeAsString());
  }
  
  // Simulate a message where the end of the data does not arrive in time and the
  // read times out.
  TEST_F(GCMConnectionHandlerTest, ReadTimeout) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_proto = BuildDataMessage(kDataMsgFrom,
                                                      kDataMsgCategory);
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message_proto);
    int bytes_in_first_message = data_message_pkt.size() / 2;
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::ASYNC,
                                      data_message_pkt.c_str(),
                                      bytes_in_first_message));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS,
                                      net::ERR_IO_PENDING));
    read_list.push_back(net::MockRead(net::ASYNC,
                                      data_message_pkt.c_str() +
                                          bytes_in_first_message,
                                      data_message_pkt.size() -
                                          bytes_in_first_message));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    received_message.reset();
    WaitForMessage();  // Should time out.
    EXPECT_FALSE(received_message.get());
    EXPECT_EQ(net::ERR_TIMED_OUT, last_error());
    EXPECT_FALSE(connection_handler()->CanSendMessage());
  
    // Finish the socket read. Should have no effect.
    data_provider()->ForceNextRead();
  }
  
  // Receive a message with zero data bytes.
  TEST_F(GCMConnectionHandlerTest, RecvMsgNoData) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list(1, net::MockWrite(net::ASYNC,
                                           handshake_request.c_str(),
                                           handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
  
    std::string data_message_pkt = EncodePacket(kHeartbeatPingTag, "");
    ASSERT_EQ(data_message_pkt.size(), 2U);
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::ASYNC,
                                      data_message_pkt.c_str(),
                                      data_message_pkt.size()));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    received_message.reset();
    WaitForMessage();  // The heartbeat ping.
    EXPECT_TRUE(received_message.get());
    EXPECT_EQ(GetMCSProtoTag(*received_message), kHeartbeatPingTag);
    EXPECT_EQ(net::OK, last_error());
    EXPECT_TRUE(connection_handler()->CanSendMessage());
  }
  
  // Send a message after performing the handshake.
  TEST_F(GCMConnectionHandlerTest, SendMsg) {
    mcs_proto::DataMessageStanza data_message;
    data_message.set_from(kDataMsgFrom);
    data_message.set_category(kDataMsgCategory);
    std::string handshake_request = EncodeHandshakeRequest();
    std::string data_message_pkt =
        EncodePacket(kDataMessageStanzaTag, data_message.SerializeAsString());
    WriteList write_list;
    write_list.push_back(net::MockWrite(net::ASYNC,
                                        handshake_request.c_str(),
                                        handshake_request.size()));
    write_list.push_back(net::MockWrite(net::ASYNC,
                                        data_message_pkt.c_str(),
                                        data_message_pkt.size()));
    std::string handshake_response = EncodeHandshakeResponse();
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING));
    BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    EXPECT_TRUE(connection_handler()->CanSendMessage());
    connection_handler()->SendMessage(data_message);
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    WaitForMessage();  // The message send.
    EXPECT_TRUE(connection_handler()->CanSendMessage());
  }
  
  // Attempt to send a message after the socket is disconnected due to a timeout.
  TEST_F(GCMConnectionHandlerTest, SendMsgSocketDisconnected) {
    std::string handshake_request = EncodeHandshakeRequest();
    WriteList write_list;
    write_list.push_back(net::MockWrite(net::ASYNC,
                                        handshake_request.c_str(),
                                        handshake_request.size()));
    std::string handshake_response = EncodeHandshakeResponse();
    ReadList read_list;
    read_list.push_back(net::MockRead(net::ASYNC,
                                      handshake_response.c_str(),
                                      handshake_response.size()));
    read_list.push_back(net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING));
    net::StreamSocket* socket = BuildSocket(read_list, write_list);
  
    ScopedMessage received_message;
    Connect(&received_message);
    WaitForMessage();  // The login send.
    WaitForMessage();  // The login response.
    EXPECT_TRUE(connection_handler()->CanSendMessage());
    socket->Disconnect();
    mcs_proto::DataMessageStanza data_message;
    data_message.set_from(kDataMsgFrom);
    data_message.set_category(kDataMsgCategory);
    connection_handler()->SendMessage(data_message);
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    WaitForMessage();  // The message send. Should result in an error
    EXPECT_FALSE(connection_handler()->CanSendMessage());
    EXPECT_EQ(net::ERR_CONNECTION_CLOSED, last_error());
  }
  
  }  // namespace
  }  // namespace gcm
  