diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-25 04:23:29 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-25 04:23:29 +0000 |
commit | 6b302a8670e0562c7196c5438bacfc57b6937816 (patch) | |
tree | efba0c6e3488720cb1a3b9a1f2edd56eb056b9cc | |
parent | 4115f132109572ff1578f956d8a0a36e0c8f9af0 (diff) | |
download | chromium_src-6b302a8670e0562c7196c5438bacfc57b6937816.zip chromium_src-6b302a8670e0562c7196c5438bacfc57b6937816.tar.gz chromium_src-6b302a8670e0562c7196c5438bacfc57b6937816.tar.bz2 |
Fix crash condition if caller deletes PseudoTcpAdaptor from within a callback.
BUG=82171
TEST=
Review URL: http://codereview.chromium.org/7040021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86570 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | jingle/glue/pseudotcp_adapter.cc | 410 | ||||
-rw-r--r-- | jingle/glue/pseudotcp_adapter.h | 50 | ||||
-rw-r--r-- | jingle/glue/pseudotcp_adapter_unittest.cc | 29 | ||||
-rw-r--r-- | remoting/protocol/jingle_session.cc | 2 |
4 files changed, 314 insertions, 177 deletions
diff --git a/jingle/glue/pseudotcp_adapter.cc b/jingle/glue/pseudotcp_adapter.cc index 6ea9fbd..4590dff 100644 --- a/jingle/glue/pseudotcp_adapter.cc +++ b/jingle/glue/pseudotcp_adapter.cc @@ -21,39 +21,106 @@ const uint16 kDefaultMtu = 1280; namespace jingle_glue { -PseudoTcpAdapter::PseudoTcpAdapter(net::Socket* socket) - : socket_(socket), - ALLOW_THIS_IN_INITIALIZER_LIST(pseudotcp_(this, 0)), - connect_callback_(NULL), +class PseudoTcpAdapter::Core : public cricket::IPseudoTcpNotify, + public base::RefCounted<Core> { + public: + Core(net::Socket* socket); + virtual ~Core(); + + // Functions used to implement net::StreamSocket. + int Read(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback); + int Write(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback); + int Connect(net::CompletionCallback* callback); + void Disconnect(); + bool IsConnected() const; + + // cricket::IPseudoTcpNotify interface. + // These notifications are triggered from NotifyPacket. + virtual void OnTcpOpen(cricket::PseudoTcp* tcp) OVERRIDE; + virtual void OnTcpReadable(cricket::PseudoTcp* tcp) OVERRIDE; + virtual void OnTcpWriteable(cricket::PseudoTcp* tcp) OVERRIDE; + // This is triggered by NotifyClock or NotifyPacket. + virtual void OnTcpClosed(cricket::PseudoTcp* tcp, uint32 error) OVERRIDE; + // This is triggered by NotifyClock, NotifyPacket, Recv and Send. + virtual WriteResult TcpWritePacket(cricket::PseudoTcp* tcp, + const char* buffer, size_t len) OVERRIDE; + private: + // These are invoked by the underlying Socket, and may trigger callbacks. + // They hold a reference to |this| while running, to protect from deletion. + void OnRead(int result); + void OnWritten(int result); + + // These may trigger callbacks, so the holder must hold a reference on + // the stack while calling them. + void DoReadFromSocket(); + void HandleReadResults(int result); + void HandleTcpClock(); + + // This re-sets |timer| without triggering callbacks. + void AdjustClock(); + + net::CompletionCallback* connect_callback_; + net::CompletionCallback* read_callback_; + net::CompletionCallback* write_callback_; + + cricket::PseudoTcp pseudo_tcp_; + scoped_ptr<net::Socket> socket_; + + scoped_refptr<net::IOBuffer> read_buffer_; + int read_buffer_size_; + scoped_refptr<net::IOBuffer> write_buffer_; + int write_buffer_size_; + + bool socket_write_pending_; + scoped_refptr<net::IOBuffer> socket_read_buffer_; + + net::CompletionCallbackImpl<Core> socket_read_callback_; + net::CompletionCallbackImpl<Core> socket_write_callback_; + + base::OneShotTimer<Core> timer_; + + DISALLOW_COPY_AND_ASSIGN(Core); +}; + + +PseudoTcpAdapter::Core::Core(net::Socket* socket) + : connect_callback_(NULL), read_callback_(NULL), write_callback_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(pseudo_tcp_(this, 0)), + socket_(socket), socket_write_pending_(false), ALLOW_THIS_IN_INITIALIZER_LIST( - socket_read_callback_(this, &PseudoTcpAdapter::OnRead)), + socket_read_callback_(this, &PseudoTcpAdapter::Core::OnRead)), ALLOW_THIS_IN_INITIALIZER_LIST( - socket_write_callback_(this, &PseudoTcpAdapter::OnWritten)) { - pseudotcp_.NotifyMTU(kDefaultMtu); + socket_write_callback_(this, &PseudoTcpAdapter::Core::OnWritten)) { + // Doesn't trigger callbacks. + pseudo_tcp_.NotifyMTU(kDefaultMtu); } -PseudoTcpAdapter::~PseudoTcpAdapter() { +PseudoTcpAdapter::Core::~Core() { } -int PseudoTcpAdapter::Read(net::IOBuffer* buffer, int buffer_size, - net::CompletionCallback* callback) { - DCHECK(CalledOnValidThread()); +int PseudoTcpAdapter::Core::Read(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback) { + DCHECK(!read_callback_); - // Verify that there is no other pending read. - DCHECK(read_callback_ == NULL); + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); - PseudoTcp::TcpState state = pseudotcp_.State(); + // TODO(wez): This is a hack for remoting. See JingleSession. + PseudoTcp::TcpState state = pseudo_tcp_.State(); int result; if (state == PseudoTcp::TCP_SYN_SENT || state == PseudoTcp::TCP_SYN_RECEIVED) { result = net::ERR_IO_PENDING; + } else { - result = pseudotcp_.Recv(buffer->data(), buffer_size); + result = pseudo_tcp_.Recv(buffer->data(), buffer_size); if (result < 0) { - result = net::MapSystemError(pseudotcp_.GetError()); + result = net::MapSystemError(pseudo_tcp_.GetError()); DCHECK(result < 0); } } @@ -69,22 +136,24 @@ int PseudoTcpAdapter::Read(net::IOBuffer* buffer, int buffer_size, return result; } -int PseudoTcpAdapter::Write(net::IOBuffer* buffer, int buffer_size, - net::CompletionCallback* callback) { - DCHECK(CalledOnValidThread()); +int PseudoTcpAdapter::Core::Write(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback) { + DCHECK(!write_callback_); - // Verify that there is no other pending write. - DCHECK(write_callback_ == NULL); + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); - PseudoTcp::TcpState state = pseudotcp_.State(); + // TODO(wez): This is a hack for remoting. See JingleSession. + PseudoTcp::TcpState state = pseudo_tcp_.State(); int result; if (state == PseudoTcp::TCP_SYN_SENT || state == PseudoTcp::TCP_SYN_RECEIVED) { result = net::ERR_IO_PENDING; + } else { - result = pseudotcp_.Send(buffer->data(), buffer_size); + result = pseudo_tcp_.Send(buffer->data(), buffer_size); if (result < 0) { - result = net::MapSystemError(pseudotcp_.GetError()); + result = net::MapSystemError(pseudo_tcp_.GetError()); DCHECK(result < 0); } } @@ -100,114 +169,68 @@ int PseudoTcpAdapter::Write(net::IOBuffer* buffer, int buffer_size, return result; } -bool PseudoTcpAdapter::SetReceiveBufferSize(int32 size) { - DCHECK(CalledOnValidThread()); - // TODO(sergeyu): Implement support for adjustable buffer size and - // used it here. - return false; -} - -bool PseudoTcpAdapter::SetSendBufferSize(int32 size) { - DCHECK(CalledOnValidThread()); - // TODO(sergeyu): Implement support for adjustable buffer size and - // used it here. - return false; -} - -int PseudoTcpAdapter::Connect(net::CompletionCallback* callback) { - DCHECK(CalledOnValidThread()); +int PseudoTcpAdapter::Core::Connect(net::CompletionCallback* callback) { + DCHECK_EQ(pseudo_tcp_.State(), cricket::PseudoTcp::TCP_LISTEN); - // Start reading from the socket. - DoReadFromSocket(); + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); - int result = pseudotcp_.Connect(); + // Start the connection attempt. + int result = pseudo_tcp_.Connect(); if (result < 0) return net::ERR_FAILED; AdjustClock(); connect_callback_ = callback; - return net::ERR_IO_PENDING; -} - -void PseudoTcpAdapter::Disconnect() { - DCHECK(CalledOnValidThread()); - pseudotcp_.Close(false); -} - -bool PseudoTcpAdapter::IsConnected() const { - DCHECK(CalledOnValidThread()); - return pseudotcp_.State() == PseudoTcp::TCP_ESTABLISHED; -} - -bool PseudoTcpAdapter::IsConnectedAndIdle() const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - return false; -} - -int PseudoTcpAdapter::GetPeerAddress(net::AddressList* address) const { - DCHECK(CalledOnValidThread()); - - // We actually don't know the peer address. Returning so the upper layers - // won't complain. - net::IPAddressNumber ip_address(4); - *address = net::AddressList::CreateFromIPAddress(ip_address, 0); - return net::OK; -} - -int PseudoTcpAdapter::GetLocalAddress(net::IPEndPoint* address) const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - return net::ERR_FAILED; -} + DoReadFromSocket(); -const net::BoundNetLog& PseudoTcpAdapter::NetLog() const { - DCHECK(CalledOnValidThread()); - return net_log_; + return net::ERR_IO_PENDING; } -void PseudoTcpAdapter::SetSubresourceSpeculation() { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} +void PseudoTcpAdapter::Core::Disconnect() { + // Don't dispatch outstanding callbacks, as mandated by net::StreamSocket. + read_callback_ = NULL; + read_buffer_ = NULL; + write_callback_ = NULL; + write_buffer_ = NULL; + connect_callback_ = NULL; -void PseudoTcpAdapter::SetOmniboxSpeculation() { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); + // TODO(wez): Connect should succeed if called after Disconnect, which + // PseudoTcp doesn't support, so we need to teardown the internal PseudoTcp + // and create a new one in Connect. + // TODO(wez): Close sets a shutdown flag inside PseudoTcp but has no other + // effect. This should be addressed in PseudoTcp, really. + // In the meantime we can fake OnTcpClosed notification and tear down the + // PseudoTcp. + pseudo_tcp_.Close(true); } -bool PseudoTcpAdapter::WasEverUsed() const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - return true; +bool PseudoTcpAdapter::Core::IsConnected() const { + return pseudo_tcp_.State() == PseudoTcp::TCP_ESTABLISHED; } -bool PseudoTcpAdapter::UsingTCPFastOpen() const { - DCHECK(CalledOnValidThread()); - return false; -} +void PseudoTcpAdapter::Core::OnTcpOpen(PseudoTcp* tcp) { + DCHECK(tcp == &pseudo_tcp_); -void PseudoTcpAdapter::OnTcpOpen(PseudoTcp* tcp) { - DCHECK(CalledOnValidThread()); - DCHECK(connect_callback_); - connect_callback_->Run(net::OK); - connect_callback_ = NULL; + if (connect_callback_) { + net::CompletionCallback* callback = connect_callback_; + connect_callback_ = NULL; + callback->Run(net::OK); + } OnTcpReadable(tcp); OnTcpWriteable(tcp); } -void PseudoTcpAdapter::OnTcpReadable(PseudoTcp* tcp) { - DCHECK(CalledOnValidThread()); - - if (!read_buffer_) +void PseudoTcpAdapter::Core::OnTcpReadable(PseudoTcp* tcp) { + DCHECK_EQ(tcp, &pseudo_tcp_); + if (!read_callback_) return; - // Try to send the data we have pending. - int result = pseudotcp_.Recv(read_buffer_->data(), read_buffer_size_); + int result = pseudo_tcp_.Recv(read_buffer_->data(), read_buffer_size_); if (result < 0) { - result = net::MapSystemError(pseudotcp_.GetError()); + result = net::MapSystemError(pseudo_tcp_.GetError()); DCHECK(result < 0); if (result == net::ERR_IO_PENDING) return; @@ -215,22 +238,20 @@ void PseudoTcpAdapter::OnTcpReadable(PseudoTcp* tcp) { AdjustClock(); - net::CompletionCallback* cb = read_callback_; + net::CompletionCallback* callback = read_callback_; read_callback_ = NULL; read_buffer_ = NULL; - cb->Run(result); + callback->Run(result); } -void PseudoTcpAdapter::OnTcpWriteable(PseudoTcp* tcp) { - DCHECK(CalledOnValidThread()); - - if (!write_buffer_) +void PseudoTcpAdapter::Core::OnTcpWriteable(PseudoTcp* tcp) { + DCHECK_EQ(tcp, &pseudo_tcp_); + if (!write_callback_) return; - // Try to send the data we have pending. - int result = pseudotcp_.Send(write_buffer_->data(), write_buffer_size_); + int result = pseudo_tcp_.Send(write_buffer_->data(), write_buffer_size_); if (result < 0) { - result = net::MapSystemError(pseudotcp_.GetError()); + result = net::MapSystemError(pseudo_tcp_.GetError()); DCHECK(result < 0); if (result == net::ERR_IO_PENDING) return; @@ -238,36 +259,39 @@ void PseudoTcpAdapter::OnTcpWriteable(PseudoTcp* tcp) { AdjustClock(); - net::CompletionCallback* cb = write_callback_; + net::CompletionCallback* callback = write_callback_; write_callback_ = NULL; write_buffer_ = NULL; - cb->Run(result); + callback->Run(result); } -void PseudoTcpAdapter::OnTcpClosed(PseudoTcp* tcp, uint32 error) { - DCHECK(CalledOnValidThread()); +void PseudoTcpAdapter::Core::OnTcpClosed(PseudoTcp* tcp, uint32 error) { + DCHECK_EQ(tcp, &pseudo_tcp_); if (connect_callback_) { - connect_callback_->Run(net::MapSystemError(error)); + net::CompletionCallback* callback = connect_callback_; connect_callback_ = NULL; + callback->Run(net::MapSystemError(error)); } if (read_callback_) { - read_callback_->Run(net::MapSystemError(error)); + net::CompletionCallback* callback = read_callback_; read_callback_ = NULL; + callback->Run(net::MapSystemError(error)); } if (write_callback_) { - write_callback_->Run(net::MapSystemError(error)); + net::CompletionCallback* callback = write_callback_; write_callback_ = NULL; + callback->Run(net::MapSystemError(error)); } } -cricket::IPseudoTcpNotify::WriteResult PseudoTcpAdapter::TcpWritePacket( +cricket::IPseudoTcpNotify::WriteResult PseudoTcpAdapter::Core::TcpWritePacket( PseudoTcp* tcp, const char* buffer, size_t len) { - DCHECK(CalledOnValidThread()); + DCHECK_EQ(tcp, &pseudo_tcp_); if (socket_write_pending_) return IPseudoTcpNotify::WR_SUCCESS; @@ -288,10 +312,9 @@ cricket::IPseudoTcpNotify::WriteResult PseudoTcpAdapter::TcpWritePacket( } } -void PseudoTcpAdapter::DoReadFromSocket() { - if (!socket_read_buffer_) { +void PseudoTcpAdapter::Core::DoReadFromSocket() { + if (!socket_read_buffer_) socket_read_buffer_ = new net::IOBuffer(kReadBufferSize); - } while (true) { int result = socket_->Read(socket_read_buffer_, kReadBufferSize, @@ -303,41 +326,154 @@ void PseudoTcpAdapter::DoReadFromSocket() { } } -void PseudoTcpAdapter::HandleReadResults(int result) { +void PseudoTcpAdapter::Core::HandleReadResults(int result) { if (result <= 0) { LOG(ERROR) << "Read returned " << result; return; } - pseudotcp_.NotifyPacket(socket_read_buffer_->data(), result); + // TODO(wez): Disconnect on failure of NotifyPacket? + pseudo_tcp_.NotifyPacket(socket_read_buffer_->data(), result); AdjustClock(); } -void PseudoTcpAdapter::OnRead(int result) { +void PseudoTcpAdapter::Core::OnRead(int result) { + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); + HandleReadResults(result); if (result >= 0) DoReadFromSocket(); } -void PseudoTcpAdapter::OnWritten(int result) { +void PseudoTcpAdapter::Core::OnWritten(int result) { + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); + socket_write_pending_ = false; if (result < 0) { LOG(WARNING) << "Write failed. Error code: " << result; } } -void PseudoTcpAdapter::AdjustClock() { +void PseudoTcpAdapter::Core::AdjustClock() { long timeout = 0; - if (pseudotcp_.GetNextClock(PseudoTcp::Now(), timeout)) { + if (pseudo_tcp_.GetNextClock(PseudoTcp::Now(), timeout)) { timer_.Stop(); timer_.Start(base::TimeDelta::FromMilliseconds(std::max(timeout, 0L)), this, - &PseudoTcpAdapter::HandleTcpClock); + &PseudoTcpAdapter::Core::HandleTcpClock); } } -void PseudoTcpAdapter::HandleTcpClock() { - pseudotcp_.NotifyClock(PseudoTcp::Now()); +void PseudoTcpAdapter::Core::HandleTcpClock() { + // Reference the Core in case a callback deletes the adapter. + scoped_refptr<Core> core(this); + + pseudo_tcp_.NotifyClock(PseudoTcp::Now()); AdjustClock(); } +// Public interface implemention. + +PseudoTcpAdapter::PseudoTcpAdapter(net::Socket* socket) + : core_(new Core(socket)) { +} + +PseudoTcpAdapter::~PseudoTcpAdapter() { + Disconnect(); +} + +int PseudoTcpAdapter::Read(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + return core_->Read(buffer, buffer_size, callback); +} + +int PseudoTcpAdapter::Write(net::IOBuffer* buffer, int buffer_size, + net::CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + return core_->Write(buffer, buffer_size, callback); +} + +bool PseudoTcpAdapter::SetReceiveBufferSize(int32 size) { + DCHECK(CalledOnValidThread()); + // TODO(sergeyu): Implement support for adjustable buffer size and + // used it here. + return false; +} + +bool PseudoTcpAdapter::SetSendBufferSize(int32 size) { + DCHECK(CalledOnValidThread()); + // TODO(sergeyu): Implement support for adjustable buffer size and + // used it here. + return false; +} + +int PseudoTcpAdapter::Connect(net::CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + + // net::StreamSocket requires that Connect return OK if already connected. + if (IsConnected()) + return net::OK; + + return core_->Connect(callback); +} + +void PseudoTcpAdapter::Disconnect() { + DCHECK(CalledOnValidThread()); + core_->Disconnect(); +} + +bool PseudoTcpAdapter::IsConnected() const { + return core_->IsConnected(); +} + +bool PseudoTcpAdapter::IsConnectedAndIdle() const { + DCHECK(CalledOnValidThread()); + NOTIMPLEMENTED(); + return false; +} + +int PseudoTcpAdapter::GetPeerAddress(net::AddressList* address) const { + DCHECK(CalledOnValidThread()); + + // We actually don't know the peer address. Returning so the upper layers + // won't complain. + net::IPAddressNumber ip_address(4); + *address = net::AddressList::CreateFromIPAddress(ip_address, 0); + return net::OK; +} + +int PseudoTcpAdapter::GetLocalAddress(net::IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +const net::BoundNetLog& PseudoTcpAdapter::NetLog() const { + DCHECK(CalledOnValidThread()); + return net_log_; +} + +void PseudoTcpAdapter::SetSubresourceSpeculation() { + DCHECK(CalledOnValidThread()); + NOTIMPLEMENTED(); +} + +void PseudoTcpAdapter::SetOmniboxSpeculation() { + DCHECK(CalledOnValidThread()); + NOTIMPLEMENTED(); +} + +bool PseudoTcpAdapter::WasEverUsed() const { + DCHECK(CalledOnValidThread()); + NOTIMPLEMENTED(); + return true; +} + +bool PseudoTcpAdapter::UsingTCPFastOpen() const { + DCHECK(CalledOnValidThread()); + return false; +} + } // namespace jingle_glue diff --git a/jingle/glue/pseudotcp_adapter.h b/jingle/glue/pseudotcp_adapter.h index 35df8ce..977dcf1 100644 --- a/jingle/glue/pseudotcp_adapter.h +++ b/jingle/glue/pseudotcp_adapter.h @@ -17,12 +17,15 @@ namespace jingle_glue { -class PseudoTcpAdapter : public net::StreamSocket, - public cricket::IPseudoTcpNotify, - public base::NonThreadSafe { +// PseudoTcpAdapter adapts a connectionless net::Socket to a connection- +// oriented net::StreamSocket using PseudoTcp. Because net::StreamSockets +// can be deleted during callbacks, while PseudoTcp cannot, the core of the +// PseudoTcpAdapter is reference counted, with a reference held by the +// adapter, and an additional reference held on the stack during callbacks. +class PseudoTcpAdapter : public net::StreamSocket, base::NonThreadSafe { public: - // Creates adapter for the specified |socket|. |socket| is assumed - // to be already connected. Takes ownership of |socket|. + // Creates an adapter for the supplied Socket. |socket| should already + // be ready for use, and ownership of it will be assumed by the adapter. PseudoTcpAdapter(net::Socket* socket); virtual ~PseudoTcpAdapter(); @@ -47,43 +50,10 @@ class PseudoTcpAdapter : public net::StreamSocket, virtual bool WasEverUsed() const OVERRIDE; virtual bool UsingTCPFastOpen() const OVERRIDE; - // cricket::IPseudoTcpNotify implementation. - virtual void OnTcpOpen(cricket::PseudoTcp* tcp) OVERRIDE; - virtual void OnTcpReadable(cricket::PseudoTcp* tcp) OVERRIDE; - virtual void OnTcpWriteable(cricket::PseudoTcp* tcp) OVERRIDE; - virtual void OnTcpClosed(cricket::PseudoTcp* tcp, uint32 error) OVERRIDE; - virtual WriteResult TcpWritePacket(cricket::PseudoTcp* tcp, - const char* buffer, size_t len) OVERRIDE; - private: - void DoReadFromSocket(); - void HandleReadResults(int result); - - // Callback functions for Read() and Write() in |socket_| - void OnRead(int result); - void OnWritten(int result); - - void AdjustClock(); - void HandleTcpClock(); - - scoped_ptr<net::Socket> socket_; - cricket::PseudoTcp pseudotcp_; - - net::CompletionCallback* connect_callback_; - scoped_refptr<net::IOBuffer> read_buffer_; - int read_buffer_size_; - net::CompletionCallback* read_callback_; - scoped_refptr<net::IOBuffer> write_buffer_; - int write_buffer_size_; - net::CompletionCallback* write_callback_; - - bool socket_write_pending_; - scoped_refptr<net::IOBuffer> socket_read_buffer_; - - net::CompletionCallbackImpl<PseudoTcpAdapter> socket_read_callback_; - net::CompletionCallbackImpl<PseudoTcpAdapter> socket_write_callback_; + class Core; - base::OneShotTimer<PseudoTcpAdapter> timer_; + scoped_refptr<Core> core_; net::BoundNetLog net_log_; diff --git a/jingle/glue/pseudotcp_adapter_unittest.cc b/jingle/glue/pseudotcp_adapter_unittest.cc index 977c0ac..14bc0d2 100644 --- a/jingle/glue/pseudotcp_adapter_unittest.cc +++ b/jingle/glue/pseudotcp_adapter_unittest.cc @@ -307,6 +307,35 @@ TEST_F(P2PTransportImplTest, TestLossyChannel) { tester->CheckResults(); } +class DeleteOnConnected { + public: + DeleteOnConnected(MessageLoop* message_loop, + scoped_ptr<PseudoTcpAdapter>* adapter) + : message_loop_(message_loop), adapter_(adapter) {} + void OnConnected(int error) { + adapter_->reset(); + message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + MessageLoop* message_loop_; + scoped_ptr<PseudoTcpAdapter>* adapter_; +}; + +TEST_F(P2PTransportImplTest, TestDeleteOnConnected) { + // This test verifies that deleting the adapter mid-callback doesn't lead + // to deleted structures being touched as the stack unrolls, so the failure + // mode is a crash rather than a normal test failure. + TestCompletionCallback client_connect_cb; + DeleteOnConnected host_delete(&message_loop_, &host_pseudotcp_); + net::CompletionCallbackImpl<DeleteOnConnected> + host_connect_cb(&host_delete, &DeleteOnConnected::OnConnected); + + host_pseudotcp_->Connect(&host_connect_cb); + client_pseudotcp_->Connect(&client_connect_cb); + message_loop_.Run(); + + ASSERT_EQ(NULL, host_pseudotcp_.get()); +} + } // namespace } // namespace jingle_glue diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc index 60fab26..90aaf08 100644 --- a/remoting/protocol/jingle_session.cc +++ b/remoting/protocol/jingle_session.cc @@ -381,6 +381,8 @@ bool JingleSession::EstablishSSLConnection( new jingle_glue::PseudoTcpAdapter(channel); pseudotcp->Connect(&connect_callback_); + // TODO(wez): We shouldn't try to start SSL until the socket we're + // starting it on is connected. if (cricket_session_->initiator()) { // Create client SSL socket. net::SSLClientSocket* socket = CreateSSLClientSocket( |