mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 10:33:33 +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 {
|
||||
|
||||
BaseWebSocketChannel::BaseWebSocketChannel()
|
||||
: mEncrypted(false)
|
||||
: mEncrypted(0)
|
||||
, mWasOpened(0)
|
||||
, mClientSetPingInterval(0)
|
||||
, mClientSetPingTimeout(0)
|
||||
, mPingInterval(0)
|
||||
, mPingResponseTimeout(10000)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!webSocketLog)
|
||||
@ -115,6 +120,46 @@ BaseWebSocketChannel::SetProtocol(const nsACString &aProtocol)
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -42,6 +42,10 @@ class BaseWebSocketChannel : public nsIWebSocketChannel,
|
||||
NS_IMETHOD GetExtensions(nsACString &aExtensions);
|
||||
NS_IMETHOD GetProtocol(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:
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
@ -54,8 +58,15 @@ class BaseWebSocketChannel : public nsIWebSocketChannel,
|
||||
nsCString mProtocol;
|
||||
nsCString mOrigin;
|
||||
|
||||
bool mEncrypted;
|
||||
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
|
||||
|
@ -27,7 +27,12 @@ parent:
|
||||
AsyncOpen(URIParams aURI,
|
||||
nsCString aOrigin,
|
||||
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);
|
||||
SendMsg(nsCString aMsg);
|
||||
SendBinaryMsg(nsCString aMsg);
|
||||
|
@ -921,8 +921,6 @@ WebSocketChannel::WebSocketChannel() :
|
||||
mCloseTimeout(20000),
|
||||
mOpenTimeout(20000),
|
||||
mConnecting(NOT_CONNECTING),
|
||||
mPingTimeout(0),
|
||||
mPingResponseTimeout(10000),
|
||||
mMaxConcurrentConnections(200),
|
||||
mGotUpgradeOK(0),
|
||||
mRecvdHttpUpgradeTransport(0),
|
||||
@ -936,7 +934,6 @@ WebSocketChannel::WebSocketChannel() :
|
||||
mAutoFollowRedirects(0),
|
||||
mReleaseOnTransmit(0),
|
||||
mTCPClosed(0),
|
||||
mWasOpened(0),
|
||||
mOpenedHttpChannel(0),
|
||||
mDataStarted(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));
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
|
||||
|
||||
// reset the ping timer
|
||||
if (mPingTimer) {
|
||||
// The purpose of ping/pong is to actively probe the peer so that an
|
||||
// unreachable peer is not mistaken for a period of idleness. This
|
||||
// implementation accepts any application level read activity as a sign of
|
||||
// life, it does not necessarily have to be a pong.
|
||||
mPingOutstanding = 0;
|
||||
mPingTimer->SetDelay(mPingTimeout);
|
||||
}
|
||||
// The purpose of ping/pong is to actively probe the peer so that an
|
||||
// unreachable peer is not mistaken for a period of idleness. This
|
||||
// implementation accepts any application level read activity as a sign of
|
||||
// life, it does not necessarily have to be a pong.
|
||||
ResetPingTimer();
|
||||
|
||||
uint32_t avail;
|
||||
|
||||
@ -2233,6 +2226,20 @@ WebSocketChannel::StartWebsocketData()
|
||||
if (mListener)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2507,6 +2514,7 @@ WebSocketChannel::Notify(nsITimer *timer)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIWebSocketChannel
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
|
||||
@ -2576,12 +2584,12 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
|
||||
}
|
||||
rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
|
||||
&intpref);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mPingTimeout = clamped(intpref, 0, 86400) * 1000;
|
||||
if (NS_SUCCEEDED(rv) && !mClientSetPingInterval) {
|
||||
mPingInterval = clamped(intpref, 0, 86400) * 1000;
|
||||
}
|
||||
rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
|
||||
&intpref);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (NS_SUCCEEDED(rv) && !mClientSetPingTimeout) {
|
||||
mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
|
||||
}
|
||||
rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
|
||||
@ -2617,18 +2625,6 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
|
||||
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;
|
||||
mURI = mOriginalURI;
|
||||
mOrigin = aOrigin;
|
||||
@ -2801,6 +2797,8 @@ WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
|
||||
nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
// nsIHttpUpgradeListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
|
||||
nsIAsyncInputStream *aSocketIn,
|
||||
|
@ -151,6 +151,13 @@ private:
|
||||
uint32_t accumulatedFragments,
|
||||
uint32_t *available);
|
||||
|
||||
inline void ResetPingTimer()
|
||||
{
|
||||
if (mPingTimer) {
|
||||
mPingOutstanding = 0;
|
||||
mPingTimer->SetDelay(mPingInterval);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mSocketThread;
|
||||
nsCOMPtr<nsIHttpChannelInternal> mChannel;
|
||||
@ -179,8 +186,6 @@ private:
|
||||
nsCOMPtr<nsITimer> mReconnectDelayTimer;
|
||||
|
||||
nsCOMPtr<nsITimer> mPingTimer;
|
||||
uint32_t mPingTimeout; /* milliseconds */
|
||||
uint32_t mPingResponseTimeout; /* milliseconds */
|
||||
|
||||
nsCOMPtr<nsITimer> mLingeringCloseTimer;
|
||||
const static int32_t kLingeringCloseTimeout = 1000;
|
||||
@ -200,7 +205,6 @@ private:
|
||||
uint32_t mAutoFollowRedirects : 1;
|
||||
uint32_t mReleaseOnTransmit : 1;
|
||||
uint32_t mTCPClosed : 1;
|
||||
uint32_t mWasOpened : 1;
|
||||
uint32_t mOpenedHttpChannel : 1;
|
||||
uint32_t mDataStarted : 1;
|
||||
uint32_t mIncrementedSessionCount : 1;
|
||||
|
@ -344,7 +344,9 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
|
||||
|
||||
gNeckoChild->SendPWebSocketConstructor(this, tabChild,
|
||||
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;
|
||||
|
||||
mOriginalURI = aURI;
|
||||
@ -352,6 +354,7 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
|
||||
mListener = aListener;
|
||||
mContext = aContext;
|
||||
mOrigin = aOrigin;
|
||||
mWasOpened = 1;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -51,7 +51,11 @@ bool
|
||||
WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
|
||||
const nsCString& aOrigin,
|
||||
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));
|
||||
|
||||
@ -82,6 +86,14 @@ WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
|
||||
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);
|
||||
if (NS_FAILED(rv))
|
||||
goto fail;
|
||||
|
@ -38,7 +38,11 @@ class WebSocketChannelParent : public PWebSocketParent,
|
||||
bool RecvAsyncOpen(const URIParams& aURI,
|
||||
const nsCString& aOrigin,
|
||||
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 RecvSendMsg(const nsCString& aMsg);
|
||||
bool RecvSendBinaryMsg(const nsCString& aMsg);
|
||||
|
@ -133,4 +133,26 @@ interface nsIWebSocketChannel : nsISupports
|
||||
*/
|
||||
void sendBinaryStream(in nsIInputStream aStream,
|
||||
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