Bug 711205: Increase inbound WebSocket message size from 16MB to whatever malloc can handle. r=mcmanus

This commit is contained in:
Jason Duell 2011-12-20 00:20:12 -08:00
parent 6fc87006e3
commit a1f113cf0d
3 changed files with 47 additions and 18 deletions

View File

@ -816,8 +816,8 @@ pref("network.ftp.control.qos", 0);
// <ws>: WebSocket
pref("network.websocket.enabled", true);
// mobile might want to set this much smaller
pref("network.websocket.max-message-size", 16000000);
// 2147483647 == PR_INT32_MAX == ~2 GB
pref("network.websocket.max-message-size", 2147483647);
// Should we automatically follow http 3xx redirects during handshake
pref("network.websocket.auto-follow-http-redirects", false);

View File

@ -692,14 +692,14 @@ WebSocketChannel::WebSocketChannel() :
mOpenBlocked(0),
mOpenRunning(0),
mChannelWasOpened(0),
mMaxMessageSize(16000000),
mMaxMessageSize(PR_INT32_MAX),
mStopOnClose(NS_OK),
mServerCloseCode(CLOSE_ABNORMAL),
mScriptCloseCode(0),
mFragmentOpcode(kContinuation),
mFragmentAccumulator(0),
mBuffered(0),
mBufferSize(16384),
mBufferSize(kIncomingBufferInitialSize),
mCurrentOut(nsnull),
mCurrentOutSent(0),
mCompressor(nsnull),
@ -827,9 +827,10 @@ WebSocketChannel::IsPersistentFramePtr()
// variable beacuse when transitioning from the stack to the persistent
// read buffer we want to explicitly include them in the buffer instead
// of as already existing data.
PRUint32
bool
WebSocketChannel::UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
PRUint32 accumulatedFragments)
PRUint32 accumulatedFragments,
PRUint32 *available)
{
LOG(("WebSocketChannel::UpdateReadBuffer() %p [%p %u]\n",
this, buffer, count));
@ -853,17 +854,24 @@ WebSocketChannel::UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
mFramePtr = mBuffer + accumulatedFragments;
} else {
// existing buffer is not sufficient, extend it
mBufferSize += count + 8192;
mBufferSize += count + 8192 + mBufferSize/3;
LOG(("WebSocketChannel: update read buffer extended to %u\n", mBufferSize));
PRUint8 *old = mBuffer;
mBuffer = (PRUint8 *)moz_xrealloc(mBuffer, mBufferSize);
mBuffer = (PRUint8 *)moz_realloc(mBuffer, mBufferSize);
if (!mBuffer) {
mBuffer = old;
return false;
}
mFramePtr = mBuffer + (mFramePtr - old);
}
::memcpy(mBuffer + mBuffered, buffer, count);
mBuffered += count;
return mBuffered - (mFramePtr - mBuffer);
if (available)
*available = mBuffered - (mFramePtr - mBuffer);
return true;
}
nsresult
@ -890,7 +898,10 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
mFramePtr = buffer;
avail = count;
} else {
avail = UpdateReadBuffer(buffer, count, mFragmentAccumulator);
if (!UpdateReadBuffer(buffer, count, mFragmentAccumulator, &avail)) {
AbortSession(NS_ERROR_FILE_TOO_BIG);
return NS_ERROR_FILE_TOO_BIG;
}
}
PRUint8 *payload;
@ -943,8 +954,6 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
LOG(("WebSocketChannel::ProcessInput: payload %lld avail %lu\n",
payloadLength, avail));
// we don't deal in > 31 bit websocket lengths.. and probably
// something considerably shorter (16MB by default)
if (payloadLength + mFragmentAccumulator > mMaxMessageSize) {
AbortSession(NS_ERROR_FILE_TOO_BIG);
return NS_ERROR_FILE_TOO_BIG;
@ -1163,21 +1172,34 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
if (mFragmentAccumulator) {
LOG(("WebSocketChannel:: Setup Buffer due to fragment"));
UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
totalAvail + mFragmentAccumulator, 0);
if (!UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
totalAvail + mFragmentAccumulator, 0, nsnull)) {
AbortSession(NS_ERROR_ILLEGAL_VALUE);
return NS_ERROR_ILLEGAL_VALUE;
}
// UpdateReadBuffer will reset the frameptr to the beginning
// of new saved state, so we need to skip past processed framgents
mFramePtr += mFragmentAccumulator;
} else if (totalAvail) {
LOG(("WebSocketChannel:: Setup Buffer due to partial frame"));
UpdateReadBuffer(mFramePtr, totalAvail, 0);
if (!UpdateReadBuffer(mFramePtr, totalAvail, 0, nsnull)) {
AbortSession(NS_ERROR_ILLEGAL_VALUE);
return NS_ERROR_ILLEGAL_VALUE;
}
}
} else if (!mFragmentAccumulator && !totalAvail) {
// If we were working off a saved buffer state and there is no partial
// frame or fragment in process, then revert to stack behavior
LOG(("WebSocketChannel:: Internal buffering not needed anymore"));
mBuffered = 0;
// release memory if we've been processing a large message
if (mBufferSize > kIncomingBufferStableSize) {
mBufferSize = kIncomingBufferStableSize;
moz_free(mBuffer);
mBuffer = (PRUint8 *)moz_xmalloc(mBufferSize);
}
}
return NS_OK;
}
@ -2163,7 +2185,7 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
rv = prefService->GetIntPref("network.websocket.max-message-size",
&intpref);
if (NS_SUCCEEDED(rv)) {
mMaxMessageSize = clamped(intpref, 1024, 1 << 30);
mMaxMessageSize = clamped(intpref, 1024, PR_INT32_MAX);
}
rv = prefService->GetIntPref("network.websocket.timeout.close", &intpref);
if (NS_SUCCEEDED(rv)) {

View File

@ -167,8 +167,9 @@ private:
bool IsPersistentFramePtr();
nsresult ProcessInput(PRUint8 *buffer, PRUint32 count);
PRUint32 UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
PRUint32 accumulatedFragments);
bool UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
PRUint32 accumulatedFragments,
PRUint32 *available);
nsCOMPtr<nsIEventTarget> mSocketThread;
@ -225,6 +226,12 @@ private:
nsCString mScriptCloseReason;
// These are for the read buffers
const static PRUint32 kIncomingBufferInitialSize = 16 * 1024;
// We're ok with keeping a buffer this size or smaller around for the life of
// the websocket. If a particular message needs bigger than this we'll
// increase the buffer temporarily, then drop back down to this size.
const static PRUint32 kIncomingBufferStableSize = 128 * 1024;
PRUint8 *mFramePtr;
PRUint8 *mBuffer;
PRUint8 mFragmentOpcode;