Bug 846553: Use non-blocking connect() for SCTP DataChannelConnections, notify disconnects r=mcmanus

This commit is contained in:
Randell Jesup 2013-03-05 11:17:07 -05:00
Родитель 8a25067bd3
Коммит b19879ed6d
2 изменённых файлов: 85 добавлений и 68 удалений

Просмотреть файл

@ -313,6 +313,15 @@ DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUs
return false;
}
// Make non-blocking for bind/connect. SCTP over UDP defaults to non-blocking
// in associations for normal IO
if (usrsctp_set_non_blocking(mMasterSocket, 1) < 0) {
LOG(("Couldn't set non_blocking on SCTP socket"));
// We can't handle connect() safely if it will block, not that this will
// even happen.
goto error_cleanup;
}
// Make sure when we close the socket, make sure it doesn't call us back again!
// This would cause it try to use an invalid DataChannelConnection pointer
struct linger l;
@ -321,18 +330,21 @@ DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUs
if (usrsctp_setsockopt(mMasterSocket, SOL_SOCKET, SO_LINGER,
(const void *)&l, (socklen_t)sizeof(struct linger)) < 0) {
LOG(("Couldn't set SO_LINGER on SCTP socket"));
// unsafe to allow it to continue if this fails
goto error_cleanup;
}
// XXX Consider disabling this when we add proper SDP negotiation.
// We may want to leave enabled for supporting 'cloning' of SDP offers, which
// implies re-use of the same pseudo-port number, or forcing a renegotiation.
uint32_t on = 1;
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_REUSE_PORT,
(const void *)&on, (socklen_t)sizeof(on)) < 0) {
LOG(("Couldn't set SCTP_REUSE_PORT on SCTP socket"));
{
uint32_t on = 1;
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_REUSE_PORT,
(const void *)&on, (socklen_t)sizeof(on)) < 0) {
LOG(("Couldn't set SCTP_REUSE_PORT on SCTP socket"));
}
}
if (!aUsingDtls) {
memset(&encaps, 0, sizeof(encaps));
encaps.sue_address.ss_family = AF_INET;
@ -453,60 +465,6 @@ DataChannelConnection::Notify(nsITimer *timer)
}
#ifdef MOZ_PEERCONNECTION
class DataChannelConnectRunnable : public nsRunnable
{
public:
DataChannelConnectRunnable(DataChannelConnection *aConnection)
: mConnection(aConnection) {}
NS_IMETHOD Run()
{
struct sockaddr_conn addr;
memset(&addr, 0, sizeof(addr));
addr.sconn_family = AF_CONN;
#if defined(__Userspace_os_Darwin)
addr.sconn_len = sizeof(addr);
#endif
addr.sconn_port = htons(mConnection->mLocalPort);
int r = usrsctp_bind(mConnection->mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
LOG(("usrsctp_bind failed: %d", r));
} else {
// This is the remote addr
addr.sconn_port = htons(mConnection->mRemotePort);
addr.sconn_addr = static_cast<void *>(mConnection.get());
r = usrsctp_connect(mConnection->mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
LOG(("usrsctp_connect failed: %d", r));
} else {
// Notify Connection open
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, mConnection.get()));
mConnection->mSocket = mConnection->mMasterSocket;
mConnection->mState = DataChannelConnection::OPEN;
LOG(("DTLS connect() succeeded! Entering connected mode"));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
mConnection, true));
return NS_OK;
}
}
// on errors, we simply don't notify there was a connection, but we
// want to kill the thread (can we kill ourselves here? That would be better)
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
mConnection, false));
return NS_OK;
}
private:
nsRefPtr<DataChannelConnection> mConnection;
};
bool
DataChannelConnection::ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
{
@ -519,11 +477,54 @@ DataChannelConnection::ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uin
mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
mLocalPort = localport;
mRemotePort = remoteport;
mState = CONNECTING;
nsCOMPtr<nsIRunnable> connect_event = new DataChannelConnectRunnable(this);
nsresult rv = NS_NewThread(getter_AddRefs(mConnectThread), connect_event);
struct sockaddr_conn addr;
memset(&addr, 0, sizeof(addr));
addr.sconn_family = AF_CONN;
#if defined(__Userspace_os_Darwin)
addr.sconn_len = sizeof(addr);
#endif
addr.sconn_port = htons(mLocalPort);
return NS_SUCCEEDED(rv);
LOG(("Calling usrsctp_bind"));
int r = usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
LOG(("usrsctp_bind failed: %d", r));
} else {
// This is the remote addr
addr.sconn_port = htons(mRemotePort);
addr.sconn_addr = static_cast<void *>(this);
LOG(("Calling usrsctp_connect"));
r = usrsctp_connect(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
if (errno == EINPROGRESS) {
// non-blocking
return true;
} else {
LOG(("usrsctp_connect failed: %d", errno));
mState = CLOSED;
}
} else {
// Notify Connection open
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
mSocket = mMasterSocket;
mState = OPEN;
LOG(("DTLS connect() succeeded! Entering connected mode"));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, true));
return true;
}
}
// Note: currently this doesn't actually notify the application
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, false));
return false;
}
void
@ -1340,15 +1341,34 @@ DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_chan
switch (sac->sac_state) {
case SCTP_COMM_UP:
LOG(("Association change: SCTP_COMM_UP"));
if (mState == CONNECTING) {
mSocket = mMasterSocket;
mState = OPEN;
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, true));
LOG(("DTLS connect() succeeded! Entering connected mode"));
} else if (mState == OPEN) {
LOG(("DataConnection Already OPEN"));
} else {
LOG(("Unexpected state: %d", mState));
}
break;
case SCTP_COMM_LOST:
LOG(("Association change: SCTP_COMM_LOST"));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_DISCONNECTED,
this));
break;
case SCTP_RESTART:
LOG(("Association change: SCTP_RESTART"));
break;
case SCTP_SHUTDOWN_COMP:
LOG(("Association change: SCTP_SHUTDOWN_COMP"));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_DISCONNECTED,
this));
break;
case SCTP_CANT_STR_ASSOC:
LOG(("Association change: SCTP_CANT_STR_ASSOC"));

Просмотреть файл

@ -265,9 +265,6 @@ private:
nsCOMPtr<nsITimer> mDeferredTimer;
uint32_t mDeferTimeout; // in ms
bool mTimerRunning;
// Thread used for connections
nsCOMPtr<nsIThread> mConnectThread;
};
#define ENSURE_DATACONNECTION \
@ -445,10 +442,10 @@ public:
mChannel(aChannel),
mConnection(aConnection) {}
// for ON_CONNECTION
// for ON_CONNECTION/ON_DISCONNECTED
DataChannelOnMessageAvailable(int32_t aType,
DataChannelConnection *aConnection,
bool aResult)
bool aResult = true)
: mType(aType),
mConnection(aConnection),
mResult(aResult) {}
@ -501,7 +498,7 @@ public:
if (mResult) {
mConnection->mListener->NotifyConnection();
}
mConnection->mConnectThread = nullptr; // kill the connection thread
// FIX - on mResult false (failure) we should do something. Needs spec work here
break;
case ON_DISCONNECTED:
mConnection->mListener->NotifyClosedConnection();