bug 674716 - websockets API close reason codes and messages r=sicking r=biesi sr=bz

This commit is contained in:
Patrick McManus
2011-08-03 15:15:25 -04:00
parent 65797c5394
commit 8a11c14206
17 changed files with 427 additions and 93 deletions

View File

@@ -179,13 +179,17 @@ public:
NS_DECL_ISUPPORTS
CallOnServerClose(nsIWebSocketListener *aListener,
nsISupports *aContext)
nsISupports *aContext,
PRUint16 aCode,
nsCString &aReason)
: mListener(aListener),
mContext(aContext) {}
mContext(aContext),
mCode(aCode),
mReason(aReason) {}
NS_SCRIPTABLE NS_IMETHOD Run()
{
mListener->OnServerClose(mContext);
mListener->OnServerClose(mContext, mCode, mReason);
return NS_OK;
}
@@ -194,6 +198,8 @@ private:
nsCOMPtr<nsIWebSocketListener> mListener;
nsCOMPtr<nsISupports> mContext;
PRUint16 mCode;
nsCString mReason;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnServerClose, nsIRunnable)
@@ -502,7 +508,8 @@ WebSocketChannel::WebSocketChannel() :
mTCPClosed(0),
mMaxMessageSize(16000000),
mStopOnClose(NS_OK),
mCloseCode(kCloseAbnormal),
mServerCloseCode(CLOSE_ABNORMAL),
mScriptCloseCode(0),
mFragmentOpcode(0),
mFragmentAccumulator(0),
mBuffered(0),
@@ -869,26 +876,29 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
LOG(("WebSocketChannel:: close received\n"));
mServerClosed = 1;
mCloseCode = kCloseNoStatus;
mServerCloseCode = CLOSE_NO_STATUS;
if (payloadLength >= 2) {
memcpy(&mCloseCode, payload, 2);
mCloseCode = PR_ntohs(mCloseCode);
LOG(("WebSocketChannel:: close recvd code %u\n", mCloseCode));
memcpy(&mServerCloseCode, payload, 2);
mServerCloseCode = PR_ntohs(mServerCloseCode);
LOG(("WebSocketChannel:: close recvd code %u\n", mServerCloseCode));
PRUint16 msglen = payloadLength - 2;
if (msglen > 0) {
nsCString utf8Data((const char *)payload + 2, msglen);
mServerCloseReason.SetLength(msglen);
memcpy(mServerCloseReason.BeginWriting(),
(const char *)payload + 2, msglen);
// section 8.1 says to replace received non utf-8 sequences
// (which are non-conformant to send) with u+fffd,
// but secteam feels that silently rewriting messages is
// inappropriate - so we will fail the connection instead.
if (!IsUTF8(utf8Data)) {
if (!IsUTF8(mServerCloseReason)) {
LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
AbortSession(NS_ERROR_ILLEGAL_VALUE);
return NS_ERROR_ILLEGAL_VALUE;
}
LOG(("WebSocketChannel:: close msg %s\n", utf8Data.get()));
LOG(("WebSocketChannel:: close msg %s\n",
mServerCloseReason.get()));
}
}
@@ -897,7 +907,9 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
mCloseTimer = nsnull;
}
if (mListener)
NS_DispatchToMainThread(new CallOnServerClose(mListener, mContext));
NS_DispatchToMainThread(
new CallOnServerClose(mListener, mContext,
mServerCloseCode, mServerCloseReason));
if (mClientClosed)
ReleaseSession();
@@ -1058,16 +1070,16 @@ PRUint16
WebSocketChannel::ResultToCloseCode(nsresult resultCode)
{
if (NS_SUCCEEDED(resultCode))
return kCloseNormal;
return CLOSE_NORMAL;
if (resultCode == NS_ERROR_FILE_TOO_BIG)
return kCloseTooLarge;
return CLOSE_TOO_LARGE;
if (resultCode == NS_BASE_STREAM_CLOSED ||
resultCode == NS_ERROR_NET_TIMEOUT ||
resultCode == NS_ERROR_CONNECTION_REFUSED) {
return kCloseAbnormal;
return CLOSE_ABNORMAL;
}
return kCloseProtocolError;
return CLOSE_PROTOCOL_ERROR;
}
void
@@ -1107,16 +1119,34 @@ WebSocketChannel::PrimeNewOutgoingMessage()
LOG(("WebSocketChannel:: PrimeNewOutgoingMessage() found close request\n"));
mClientClosed = 1;
mOutHeader[0] = kFinalFragBit | kClose;
mOutHeader[1] = 0x02; // payload len = 2
mOutHeader[1] = 0x02; // payload len = 2, maybe more for reason
mOutHeader[1] |= kMaskBit;
// payload is offset 6 including 4 for the mask
payload = mOutHeader + 6;
// The close reason code sits in the first 2 bytes of payload
*((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
// length is 8 plus any reason information
mHdrOutToSend = 8;
// The close reason code sits in the first 2 bytes of payload
// If the channel user provided a code and reason during Close()
// and there isn't an internal error, use that.
if (NS_SUCCEEDED(mStopOnClose) && mScriptCloseCode) {
*((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
if (!mScriptCloseReason.IsEmpty()) {
NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
"Close Reason Too Long");
mOutHeader[1] += mScriptCloseReason.Length();
mHdrOutToSend += mScriptCloseReason.Length();
memcpy (payload + 2,
mScriptCloseReason.BeginReading(),
mScriptCloseReason.Length());
}
}
else {
*((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
}
if (mServerClosed) {
/* bidi close complete */
mReleaseOnTransmit = 1;
@@ -1984,7 +2014,7 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
}
NS_IMETHODIMP
WebSocketChannel::Close()
WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
{
LOG(("WebSocketChannel::Close() %p\n", this));
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
@@ -2000,8 +2030,14 @@ WebSocketChannel::Close()
return NS_ERROR_UNEXPECTED;
}
mRequestedClose = 1;
// The API requires the UTF-8 string to be 123 or less bytes
if (reason.Length() > 123)
return NS_ERROR_ILLEGAL_VALUE;
mRequestedClose = 1;
mScriptCloseReason = reason;
mScriptCloseCode = code;
return mSocketThread->Dispatch(new nsPostMessage(this, kFinMessage, -1),
nsIEventTarget::DISPATCH_NORMAL);
}