Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
#include "WebSocketLog.h"
#include "WebSocketConnectionParent.h"
#include "nsIHttpChannelInternal.h"
#include "nsITransportSecurityInfo.h"
#include "nsSerializationHelper.h"
#include "nsThreadUtils.h"
#include "WebSocketConnectionListener.h"
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS0(WebSocketConnectionParent)
WebSocketConnectionParent::WebSocketConnectionParent(
nsIHttpUpgradeListener* aListener)
: mUpgradeListener(aListener),
mBackgroundThread(GetCurrentSerialEventTarget()) {
LOG(("WebSocketConnectionParent ctor %p\n", this));
MOZ_ASSERT(mUpgradeListener);
}
WebSocketConnectionParent::~WebSocketConnectionParent() {
LOG(("WebSocketConnectionParent dtor %p\n", this));
}
mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnTransportAvailable(
nsITransportSecurityInfo* aSecurityInfo) {
LOG(("WebSocketConnectionParent::RecvOnTransportAvailable %p\n", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
if (aSecurityInfo) {
MutexAutoLock lock(mMutex);
mSecurityInfo = aSecurityInfo;
}
if (mUpgradeListener) {
Unused << mUpgradeListener->OnWebSocketConnectionAvailable(this);
mUpgradeListener = nullptr;
}
return IPC_OK();
}
mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnError(
const nsresult& aStatus) {
LOG(("WebSocketConnectionParent::RecvOnError %p\n", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
MOZ_ASSERT(mListener);
mListener->OnError(aStatus);
return IPC_OK();
}
mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnUpgradeFailed(
const nsresult& aReason) {
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
if (mUpgradeListener) {
Unused << mUpgradeListener->OnUpgradeFailed(aReason);
mUpgradeListener = nullptr;
}
return IPC_OK();
}
mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnTCPClosed() {
LOG(("WebSocketConnectionParent::RecvOnTCPClosed %p\n", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
MOZ_ASSERT(mListener);
mListener->OnTCPClosed();
return IPC_OK();
}
mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnDataReceived(
nsTArray<uint8_t>&& aData) {
LOG(("WebSocketConnectionParent::RecvOnDataReceived %p\n", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
MOZ_ASSERT(mListener);
uint8_t* buffer = const_cast<uint8_t*>(aData.Elements());
nsresult rv = mListener->OnDataReceived(buffer, aData.Length());
if (NS_FAILED(rv)) {
mListener->OnError(rv);
}
return IPC_OK();
}
void WebSocketConnectionParent::ActorDestroy(ActorDestroyReason aWhy) {
LOG(("WebSocketConnectionParent::ActorDestroy %p aWhy=%d\n", this, aWhy));
if (!mClosed) {
// Treat this as an error when IPC is closed before
// WebSocketConnectionParent::Close() is called.
RefPtr<WebSocketConnectionListener> listener;
listener.swap(mListener);
if (listener) {
listener->OnError(NS_ERROR_FAILURE);
}
}
mBackgroundThread->Dispatch(NS_NewRunnableFunction(
"WebSocketConnectionParent::DefereredDestroy", [self = RefPtr{this}]() {
LOG(("WebSocketConnectionParent::DefereredDestroy"));
}));
};
nsresult WebSocketConnectionParent::Init(
WebSocketConnectionListener* aListener) {
NS_ENSURE_ARG_POINTER(aListener);
mListener = aListener;
return NS_OK;
}
void WebSocketConnectionParent::GetIoTarget(nsIEventTarget** aTarget) {
nsCOMPtr<nsIEventTarget> target = mBackgroundThread;
return target.forget(aTarget);
}
void WebSocketConnectionParent::Close() {
LOG(("WebSocketConnectionParent::Close %p\n", this));
mClosed = true;
auto task = [self = RefPtr{this}]() {
self->PWebSocketConnectionParent::Close();
};
if (mBackgroundThread->IsOnCurrentThread()) {
task();
} else {
mBackgroundThread->Dispatch(NS_NewRunnableFunction(
"WebSocketConnectionParent::Close", std::move(task)));
}
}
nsresult WebSocketConnectionParent::WriteOutputData(
const uint8_t* aHdrBuf, uint32_t aHdrBufLength, const uint8_t* aPayloadBuf,
uint32_t aPayloadBufLength) {
LOG(("WebSocketConnectionParent::WriteOutputData %p", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
if (!CanSend()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsTArray<uint8_t> data;
data.AppendElements(aHdrBuf, aHdrBufLength);
data.AppendElements(aPayloadBuf, aPayloadBufLength);
return SendWriteOutputData(data) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult WebSocketConnectionParent::StartReading() {
LOG(("WebSocketConnectionParent::StartReading %p\n", this));
RefPtr<WebSocketConnectionParent> self = this;
auto task = [self{std::move(self)}]() {
if (!self->CanSend()) {
if (self->mListener) {
self->mListener->OnError(NS_ERROR_NOT_AVAILABLE);
}
return;
}
Unused << self->SendStartReading();
};
if (mBackgroundThread->IsOnCurrentThread()) {
task();
} else {
mBackgroundThread->Dispatch(NS_NewRunnableFunction(
"WebSocketConnectionParent::SendStartReading", std::move(task)));
}
return NS_OK;
}
void WebSocketConnectionParent::DrainSocketData() {
LOG(("WebSocketConnectionParent::DrainSocketData %p\n", this));
MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
if (!CanSend()) {
MOZ_ASSERT(mListener);
mListener->OnError(NS_ERROR_NOT_AVAILABLE);
return;
}
Unused << SendDrainSocketData();
}
nsresult WebSocketConnectionParent::GetSecurityInfo(
nsITransportSecurityInfo** aSecurityInfo) {
LOG(("WebSocketConnectionParent::GetSecurityInfo() %p\n", this));
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG_POINTER(aSecurityInfo);
MutexAutoLock lock(mMutex);
NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
return NS_OK;
}
} // namespace net
} // namespace mozilla