mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
Bug 849364 - Provide per-websocket way to enable keepalive pings. r=mcmanus
This commit is contained in:
parent
99acd93e07
commit
122f1fecac
@ -20,7 +20,12 @@ namespace mozilla {
|
|||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
BaseWebSocketChannel::BaseWebSocketChannel()
|
BaseWebSocketChannel::BaseWebSocketChannel()
|
||||||
: mEncrypted(false)
|
: mEncrypted(0)
|
||||||
|
, mWasOpened(0)
|
||||||
|
, mClientSetPingInterval(0)
|
||||||
|
, mClientSetPingTimeout(0)
|
||||||
|
, mPingInterval(0)
|
||||||
|
, mPingResponseTimeout(10000)
|
||||||
{
|
{
|
||||||
#if defined(PR_LOGGING)
|
#if defined(PR_LOGGING)
|
||||||
if (!webSocketLog)
|
if (!webSocketLog)
|
||||||
@ -115,6 +120,46 @@ BaseWebSocketChannel::SetProtocol(const nsACString &aProtocol)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BaseWebSocketChannel::GetPingInterval(uint32_t *aMilliSeconds)
|
||||||
|
{
|
||||||
|
*aMilliSeconds = mPingInterval;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BaseWebSocketChannel::SetPingInterval(uint32_t aMilliSeconds)
|
||||||
|
{
|
||||||
|
if (mWasOpened) {
|
||||||
|
return NS_ERROR_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPingInterval = aMilliSeconds;
|
||||||
|
mClientSetPingInterval = 1;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BaseWebSocketChannel::GetPingTimeout(uint32_t *aMilliSeconds)
|
||||||
|
{
|
||||||
|
*aMilliSeconds = mPingResponseTimeout;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BaseWebSocketChannel::SetPingTimeout(uint32_t aMilliSeconds)
|
||||||
|
{
|
||||||
|
if (mWasOpened) {
|
||||||
|
return NS_ERROR_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPingResponseTimeout = aMilliSeconds;
|
||||||
|
mClientSetPingTimeout = 1;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// BaseWebSocketChannel::nsIProtocolHandler
|
// BaseWebSocketChannel::nsIProtocolHandler
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -42,6 +42,10 @@ class BaseWebSocketChannel : public nsIWebSocketChannel,
|
|||||||
NS_IMETHOD GetExtensions(nsACString &aExtensions);
|
NS_IMETHOD GetExtensions(nsACString &aExtensions);
|
||||||
NS_IMETHOD GetProtocol(nsACString &aProtocol);
|
NS_IMETHOD GetProtocol(nsACString &aProtocol);
|
||||||
NS_IMETHOD SetProtocol(const nsACString &aProtocol);
|
NS_IMETHOD SetProtocol(const nsACString &aProtocol);
|
||||||
|
NS_IMETHOD GetPingInterval(uint32_t *aMilliSeconds);
|
||||||
|
NS_IMETHOD SetPingInterval(uint32_t aMilliSeconds);
|
||||||
|
NS_IMETHOD GetPingTimeout(uint32_t *aMilliSeconds);
|
||||||
|
NS_IMETHOD SetPingTimeout(uint32_t aMilliSeconds);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsCOMPtr<nsIURI> mOriginalURI;
|
nsCOMPtr<nsIURI> mOriginalURI;
|
||||||
@ -54,8 +58,15 @@ class BaseWebSocketChannel : public nsIWebSocketChannel,
|
|||||||
nsCString mProtocol;
|
nsCString mProtocol;
|
||||||
nsCString mOrigin;
|
nsCString mOrigin;
|
||||||
|
|
||||||
bool mEncrypted;
|
|
||||||
nsCString mNegotiatedExtensions;
|
nsCString mNegotiatedExtensions;
|
||||||
|
|
||||||
|
uint32_t mEncrypted : 1;
|
||||||
|
uint32_t mWasOpened : 1;
|
||||||
|
uint32_t mClientSetPingInterval : 1;
|
||||||
|
uint32_t mClientSetPingTimeout : 1;
|
||||||
|
|
||||||
|
uint32_t mPingInterval; /* milliseconds */
|
||||||
|
uint32_t mPingResponseTimeout; /* milliseconds */
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace net
|
} // namespace net
|
||||||
|
@ -27,7 +27,12 @@ parent:
|
|||||||
AsyncOpen(URIParams aURI,
|
AsyncOpen(URIParams aURI,
|
||||||
nsCString aOrigin,
|
nsCString aOrigin,
|
||||||
nsCString aProtocol,
|
nsCString aProtocol,
|
||||||
bool aSecure);
|
bool aSecure,
|
||||||
|
// ping values only meaningful if client set them
|
||||||
|
uint32_t aPingInterval,
|
||||||
|
bool aClientSetPingInterval,
|
||||||
|
uint32_t aPingTimeout,
|
||||||
|
bool aClientSetPingTimeout);
|
||||||
Close(uint16_t code, nsCString reason);
|
Close(uint16_t code, nsCString reason);
|
||||||
SendMsg(nsCString aMsg);
|
SendMsg(nsCString aMsg);
|
||||||
SendBinaryMsg(nsCString aMsg);
|
SendBinaryMsg(nsCString aMsg);
|
||||||
|
@ -921,8 +921,6 @@ WebSocketChannel::WebSocketChannel() :
|
|||||||
mCloseTimeout(20000),
|
mCloseTimeout(20000),
|
||||||
mOpenTimeout(20000),
|
mOpenTimeout(20000),
|
||||||
mConnecting(NOT_CONNECTING),
|
mConnecting(NOT_CONNECTING),
|
||||||
mPingTimeout(0),
|
|
||||||
mPingResponseTimeout(10000),
|
|
||||||
mMaxConcurrentConnections(200),
|
mMaxConcurrentConnections(200),
|
||||||
mGotUpgradeOK(0),
|
mGotUpgradeOK(0),
|
||||||
mRecvdHttpUpgradeTransport(0),
|
mRecvdHttpUpgradeTransport(0),
|
||||||
@ -936,7 +934,6 @@ WebSocketChannel::WebSocketChannel() :
|
|||||||
mAutoFollowRedirects(0),
|
mAutoFollowRedirects(0),
|
||||||
mReleaseOnTransmit(0),
|
mReleaseOnTransmit(0),
|
||||||
mTCPClosed(0),
|
mTCPClosed(0),
|
||||||
mWasOpened(0),
|
|
||||||
mOpenedHttpChannel(0),
|
mOpenedHttpChannel(0),
|
||||||
mDataStarted(0),
|
mDataStarted(0),
|
||||||
mIncrementedSessionCount(0),
|
mIncrementedSessionCount(0),
|
||||||
@ -1153,15 +1150,11 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count)
|
|||||||
LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
|
LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
|
||||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
|
||||||
|
|
||||||
// reset the ping timer
|
// The purpose of ping/pong is to actively probe the peer so that an
|
||||||
if (mPingTimer) {
|
// unreachable peer is not mistaken for a period of idleness. This
|
||||||
// The purpose of ping/pong is to actively probe the peer so that an
|
// implementation accepts any application level read activity as a sign of
|
||||||
// unreachable peer is not mistaken for a period of idleness. This
|
// life, it does not necessarily have to be a pong.
|
||||||
// implementation accepts any application level read activity as a sign of
|
ResetPingTimer();
|
||||||
// life, it does not necessarily have to be a pong.
|
|
||||||
mPingOutstanding = 0;
|
|
||||||
mPingTimer->SetDelay(mPingTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t avail;
|
uint32_t avail;
|
||||||
|
|
||||||
@ -2233,6 +2226,20 @@ WebSocketChannel::StartWebsocketData()
|
|||||||
if (mListener)
|
if (mListener)
|
||||||
mListener->OnStart(mContext);
|
mListener->OnStart(mContext);
|
||||||
|
|
||||||
|
// Start keepalive ping timer, if we're using keepalive.
|
||||||
|
if (mPingInterval) {
|
||||||
|
nsresult rv;
|
||||||
|
mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("unable to create ping timer. Carrying on.");
|
||||||
|
} else {
|
||||||
|
LOG(("WebSocketChannel will generate ping after %d ms of receive silence\n",
|
||||||
|
mPingInterval));
|
||||||
|
mPingTimer->SetTarget(mSocketThread);
|
||||||
|
mPingTimer->InitWithCallback(this, mPingInterval, nsITimer::TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
|
return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2507,6 +2514,7 @@ WebSocketChannel::Notify(nsITimer *timer)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nsIWebSocketChannel
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
|
WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
|
||||||
@ -2576,12 +2584,12 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
|
|||||||
}
|
}
|
||||||
rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
|
rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
|
||||||
&intpref);
|
&intpref);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv) && !mClientSetPingInterval) {
|
||||||
mPingTimeout = clamped(intpref, 0, 86400) * 1000;
|
mPingInterval = clamped(intpref, 0, 86400) * 1000;
|
||||||
}
|
}
|
||||||
rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
|
rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
|
||||||
&intpref);
|
&intpref);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv) && !mClientSetPingTimeout) {
|
||||||
mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
|
mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
|
||||||
}
|
}
|
||||||
rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
|
rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
|
||||||
@ -2617,18 +2625,6 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
|
|||||||
return NS_ERROR_SOCKET_CREATE_FAILED;
|
return NS_ERROR_SOCKET_CREATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPingTimeout) {
|
|
||||||
mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("unable to create ping timer. Carrying on.");
|
|
||||||
} else {
|
|
||||||
LOG(("WebSocketChannel will generate ping after %d ms of receive silence\n",
|
|
||||||
mPingTimeout));
|
|
||||||
mPingTimer->SetTarget(mSocketThread);
|
|
||||||
mPingTimer->InitWithCallback(this, mPingTimeout, nsITimer::TYPE_ONE_SHOT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mOriginalURI = aURI;
|
mOriginalURI = aURI;
|
||||||
mURI = mOriginalURI;
|
mURI = mOriginalURI;
|
||||||
mOrigin = aOrigin;
|
mOrigin = aOrigin;
|
||||||
@ -2801,6 +2797,8 @@ WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
|
|||||||
nsIEventTarget::DISPATCH_NORMAL);
|
nsIEventTarget::DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nsIHttpUpgradeListener
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
|
WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
|
||||||
nsIAsyncInputStream *aSocketIn,
|
nsIAsyncInputStream *aSocketIn,
|
||||||
|
@ -151,6 +151,13 @@ private:
|
|||||||
uint32_t accumulatedFragments,
|
uint32_t accumulatedFragments,
|
||||||
uint32_t *available);
|
uint32_t *available);
|
||||||
|
|
||||||
|
inline void ResetPingTimer()
|
||||||
|
{
|
||||||
|
if (mPingTimer) {
|
||||||
|
mPingOutstanding = 0;
|
||||||
|
mPingTimer->SetDelay(mPingInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIEventTarget> mSocketThread;
|
nsCOMPtr<nsIEventTarget> mSocketThread;
|
||||||
nsCOMPtr<nsIHttpChannelInternal> mChannel;
|
nsCOMPtr<nsIHttpChannelInternal> mChannel;
|
||||||
@ -179,8 +186,6 @@ private:
|
|||||||
nsCOMPtr<nsITimer> mReconnectDelayTimer;
|
nsCOMPtr<nsITimer> mReconnectDelayTimer;
|
||||||
|
|
||||||
nsCOMPtr<nsITimer> mPingTimer;
|
nsCOMPtr<nsITimer> mPingTimer;
|
||||||
uint32_t mPingTimeout; /* milliseconds */
|
|
||||||
uint32_t mPingResponseTimeout; /* milliseconds */
|
|
||||||
|
|
||||||
nsCOMPtr<nsITimer> mLingeringCloseTimer;
|
nsCOMPtr<nsITimer> mLingeringCloseTimer;
|
||||||
const static int32_t kLingeringCloseTimeout = 1000;
|
const static int32_t kLingeringCloseTimeout = 1000;
|
||||||
@ -200,7 +205,6 @@ private:
|
|||||||
uint32_t mAutoFollowRedirects : 1;
|
uint32_t mAutoFollowRedirects : 1;
|
||||||
uint32_t mReleaseOnTransmit : 1;
|
uint32_t mReleaseOnTransmit : 1;
|
||||||
uint32_t mTCPClosed : 1;
|
uint32_t mTCPClosed : 1;
|
||||||
uint32_t mWasOpened : 1;
|
|
||||||
uint32_t mOpenedHttpChannel : 1;
|
uint32_t mOpenedHttpChannel : 1;
|
||||||
uint32_t mDataStarted : 1;
|
uint32_t mDataStarted : 1;
|
||||||
uint32_t mIncrementedSessionCount : 1;
|
uint32_t mIncrementedSessionCount : 1;
|
||||||
|
@ -344,7 +344,9 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
|
|||||||
|
|
||||||
gNeckoChild->SendPWebSocketConstructor(this, tabChild,
|
gNeckoChild->SendPWebSocketConstructor(this, tabChild,
|
||||||
IPC::SerializedLoadContext(this));
|
IPC::SerializedLoadContext(this));
|
||||||
if (!SendAsyncOpen(uri, nsCString(aOrigin), mProtocol, mEncrypted))
|
if (!SendAsyncOpen(uri, nsCString(aOrigin), mProtocol, mEncrypted,
|
||||||
|
mPingInterval, mClientSetPingInterval,
|
||||||
|
mPingResponseTimeout, mClientSetPingTimeout))
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
|
||||||
mOriginalURI = aURI;
|
mOriginalURI = aURI;
|
||||||
@ -352,6 +354,7 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
|
|||||||
mListener = aListener;
|
mListener = aListener;
|
||||||
mContext = aContext;
|
mContext = aContext;
|
||||||
mOrigin = aOrigin;
|
mOrigin = aOrigin;
|
||||||
|
mWasOpened = 1;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,11 @@ bool
|
|||||||
WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
|
WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsCString& aProtocol,
|
const nsCString& aProtocol,
|
||||||
const bool& aSecure)
|
const bool& aSecure,
|
||||||
|
const uint32_t& aPingInterval,
|
||||||
|
const bool& aClientSetPingInterval,
|
||||||
|
const uint32_t& aPingTimeout,
|
||||||
|
const bool& aClientSetPingTimeout)
|
||||||
{
|
{
|
||||||
LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this));
|
LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this));
|
||||||
|
|
||||||
@ -82,6 +86,14 @@ WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only use ping values from child if they were overridden by client code.
|
||||||
|
if (aClientSetPingInterval) {
|
||||||
|
mChannel->SetPingInterval(aPingInterval);
|
||||||
|
}
|
||||||
|
if (aClientSetPingTimeout) {
|
||||||
|
mChannel->SetPingTimeout(aPingTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
rv = mChannel->AsyncOpen(uri, aOrigin, this, nullptr);
|
rv = mChannel->AsyncOpen(uri, aOrigin, this, nullptr);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -38,7 +38,11 @@ class WebSocketChannelParent : public PWebSocketParent,
|
|||||||
bool RecvAsyncOpen(const URIParams& aURI,
|
bool RecvAsyncOpen(const URIParams& aURI,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsCString& aProtocol,
|
const nsCString& aProtocol,
|
||||||
const bool& aSecure);
|
const bool& aSecure,
|
||||||
|
const uint32_t& aPingInterval,
|
||||||
|
const bool& aClientSetPingInterval,
|
||||||
|
const uint32_t& aPingTimeout,
|
||||||
|
const bool& aClientSetPingTimeout);
|
||||||
bool RecvClose(const uint16_t & code, const nsCString & reason);
|
bool RecvClose(const uint16_t & code, const nsCString & reason);
|
||||||
bool RecvSendMsg(const nsCString& aMsg);
|
bool RecvSendMsg(const nsCString& aMsg);
|
||||||
bool RecvSendBinaryMsg(const nsCString& aMsg);
|
bool RecvSendBinaryMsg(const nsCString& aMsg);
|
||||||
|
@ -133,4 +133,26 @@ interface nsIWebSocketChannel : nsISupports
|
|||||||
*/
|
*/
|
||||||
void sendBinaryStream(in nsIInputStream aStream,
|
void sendBinaryStream(in nsIInputStream aStream,
|
||||||
in unsigned long length);
|
in unsigned long length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value determines how often (in milliseconds) websocket keepalive
|
||||||
|
* pings are sent. If set to 0 (the default), no pings are ever sent.
|
||||||
|
*
|
||||||
|
* This value can currently only be set before asyncOpen is called, else
|
||||||
|
* NS_ERROR_IN_PROGRESS is thrown.
|
||||||
|
*
|
||||||
|
* Be careful using this setting: ping traffic can consume lots of power and
|
||||||
|
* bandwidth over time.
|
||||||
|
*/
|
||||||
|
attribute unsigned long pingInterval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value determines how long (in milliseconds) the websocket waits for
|
||||||
|
* the server to reply to a ping that has been sent.
|
||||||
|
*
|
||||||
|
* This value can currently only be set before asyncOpen is called, else
|
||||||
|
* NS_ERROR_IN_PROGRESS is thrown.
|
||||||
|
*/
|
||||||
|
attribute unsigned long pingTimeout;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user