1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "WebSocketLog.h"
8 #include "WebSocketConnectionParent.h"
9 
10 #include "nsIHttpChannelInternal.h"
11 #include "nsSerializationHelper.h"
12 #include "nsThreadUtils.h"
13 #include "WebSocketConnectionListener.h"
14 
15 namespace mozilla {
16 namespace net {
17 
NS_IMPL_ISUPPORTS0(WebSocketConnectionParent)18 NS_IMPL_ISUPPORTS0(WebSocketConnectionParent)
19 
20 WebSocketConnectionParent::WebSocketConnectionParent(
21     nsIHttpUpgradeListener* aListener)
22     : mUpgradeListener(aListener), mBackgroundThread(GetCurrentEventTarget()) {
23   LOG(("WebSocketConnectionParent ctor %p\n", this));
24   MOZ_ASSERT(mUpgradeListener);
25 }
26 
~WebSocketConnectionParent()27 WebSocketConnectionParent::~WebSocketConnectionParent() {
28   LOG(("WebSocketConnectionParent dtor %p\n", this));
29 }
30 
RecvOnTransportAvailable(const nsCString & aSecurityInfoSerialization)31 mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnTransportAvailable(
32     const nsCString& aSecurityInfoSerialization) {
33   LOG(("WebSocketConnectionParent::RecvOnTransportAvailable %p\n", this));
34   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
35 
36   if (!aSecurityInfoSerialization.IsEmpty()) {
37     MutexAutoLock lock(mMutex);
38     nsresult rv = NS_DeserializeObject(aSecurityInfoSerialization,
39                                        getter_AddRefs(mSecurityInfo));
40     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
41                           "Deserializing security info should not fail");
42     Unused << rv;  // So we don't get an unused error in release builds.
43   }
44 
45   if (mUpgradeListener) {
46     Unused << mUpgradeListener->OnWebSocketConnectionAvailable(this);
47     mUpgradeListener = nullptr;
48   }
49   return IPC_OK();
50 }
51 
RecvOnError(const nsresult & aStatus)52 mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnError(
53     const nsresult& aStatus) {
54   LOG(("WebSocketConnectionParent::RecvOnError %p\n", this));
55   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
56 
57   if (mListener) {
58     mListener->OnError(aStatus);
59   }
60   return IPC_OK();
61 }
62 
RecvOnUpgradeFailed(const nsresult & aReason)63 mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnUpgradeFailed(
64     const nsresult& aReason) {
65   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
66 
67   if (mUpgradeListener) {
68     Unused << mUpgradeListener->OnUpgradeFailed(aReason);
69     mUpgradeListener = nullptr;
70   }
71   return IPC_OK();
72 }
73 
RecvOnTCPClosed()74 mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnTCPClosed() {
75   LOG(("WebSocketConnectionParent::RecvOnTCPClosed %p\n", this));
76   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
77 
78   if (mListener) {
79     mListener->OnTCPClosed();
80   }
81   return IPC_OK();
82 }
83 
RecvOnDataReceived(nsTArray<uint8_t> && aData)84 mozilla::ipc::IPCResult WebSocketConnectionParent::RecvOnDataReceived(
85     nsTArray<uint8_t>&& aData) {
86   LOG(("WebSocketConnectionParent::RecvOnDataReceived %p\n", this));
87   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
88 
89   if (mListener) {
90     uint8_t* buffer = const_cast<uint8_t*>(aData.Elements());
91     nsresult rv = mListener->OnDataReceived(buffer, aData.Length());
92     if (NS_FAILED(rv)) {
93       mListener->OnError(rv);
94     }
95   }
96   return IPC_OK();
97 }
98 
ActorDestroy(ActorDestroyReason aWhy)99 void WebSocketConnectionParent::ActorDestroy(ActorDestroyReason aWhy) {
100   LOG(("WebSocketConnectionParent::ActorDestroy %p aWhy=%d\n", this, aWhy));
101   if (!mClosed) {
102     // Treat this as an error when IPC is closed before
103     // WebSocketConnectionParent::Close() is called.
104     RefPtr<WebSocketConnectionListener> listener;
105     listener.swap(mListener);
106     if (listener) {
107       listener->OnError(NS_ERROR_FAILURE);
108     }
109   }
110 };
111 
Init(WebSocketConnectionListener * aListener)112 nsresult WebSocketConnectionParent::Init(
113     WebSocketConnectionListener* aListener) {
114   NS_ENSURE_ARG_POINTER(aListener);
115 
116   mListener = aListener;
117   return NS_OK;
118 }
119 
GetIoTarget(nsIEventTarget ** aTarget)120 void WebSocketConnectionParent::GetIoTarget(nsIEventTarget** aTarget) {
121   nsCOMPtr<nsIEventTarget> target = mBackgroundThread;
122   return target.forget(aTarget);
123 }
124 
Close()125 void WebSocketConnectionParent::Close() {
126   LOG(("WebSocketConnectionParent::Close %p\n", this));
127 
128   mClosed = true;
129 
130   RefPtr<WebSocketConnectionParent> self = this;
131   auto task = [self{std::move(self)}]() {
132     Unused << self->Send__delete__(self);
133     self->mListener = nullptr;
134   };
135 
136   if (mBackgroundThread->IsOnCurrentThread()) {
137     task();
138   } else {
139     mBackgroundThread->Dispatch(NS_NewRunnableFunction(
140         "WebSocketConnectionParent::Close", std::move(task)));
141   }
142 }
143 
WriteOutputData(const uint8_t * aHdrBuf,uint32_t aHdrBufLength,const uint8_t * aPayloadBuf,uint32_t aPayloadBufLength)144 nsresult WebSocketConnectionParent::WriteOutputData(
145     const uint8_t* aHdrBuf, uint32_t aHdrBufLength, const uint8_t* aPayloadBuf,
146     uint32_t aPayloadBufLength) {
147   LOG(("WebSocketConnectionParent::WriteOutputData %p", this));
148   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
149 
150   if (!CanSend()) {
151     return NS_ERROR_NOT_AVAILABLE;
152   }
153 
154   nsTArray<uint8_t> data;
155   data.AppendElements(aHdrBuf, aHdrBufLength);
156   data.AppendElements(aPayloadBuf, aPayloadBufLength);
157   return SendWriteOutputData(data) ? NS_OK : NS_ERROR_FAILURE;
158 }
159 
StartReading()160 nsresult WebSocketConnectionParent::StartReading() {
161   LOG(("WebSocketConnectionParent::StartReading %p\n", this));
162 
163   RefPtr<WebSocketConnectionParent> self = this;
164   auto task = [self{std::move(self)}]() {
165     if (!self->CanSend()) {
166       if (self->mListener) {
167         self->mListener->OnError(NS_ERROR_NOT_AVAILABLE);
168       }
169       return;
170     }
171 
172     Unused << self->SendStartReading();
173   };
174 
175   if (mBackgroundThread->IsOnCurrentThread()) {
176     task();
177   } else {
178     mBackgroundThread->Dispatch(NS_NewRunnableFunction(
179         "WebSocketConnectionParent::SendStartReading", std::move(task)));
180   }
181 
182   return NS_OK;
183 }
184 
DrainSocketData()185 void WebSocketConnectionParent::DrainSocketData() {
186   LOG(("WebSocketConnectionParent::DrainSocketData %p\n", this));
187   MOZ_ASSERT(mBackgroundThread->IsOnCurrentThread());
188 
189   if (!CanSend()) {
190     if (mListener) {
191       mListener->OnError(NS_ERROR_NOT_AVAILABLE);
192     }
193     return;
194   }
195 
196   Unused << SendDrainSocketData();
197 }
198 
GetSecurityInfo(nsISupports ** aSecurityInfo)199 nsresult WebSocketConnectionParent::GetSecurityInfo(
200     nsISupports** aSecurityInfo) {
201   LOG(("WebSocketConnectionParent::GetSecurityInfo() %p\n", this));
202   MOZ_ASSERT(NS_IsMainThread());
203 
204   NS_ENSURE_ARG_POINTER(aSecurityInfo);
205 
206   MutexAutoLock lock(mMutex);
207   nsCOMPtr<nsISupports> info = mSecurityInfo;
208   info.forget(aSecurityInfo);
209   return NS_OK;
210 }
211 
212 }  // namespace net
213 }  // namespace mozilla
214