WebSocketChannelParent.cpp (11394B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WebSocketLog.h" 8 #include "WebSocketChannelParent.h" 9 #include "nsIAuthPromptProvider.h" 10 #include "mozilla/dom/ContentParent.h" 11 #include "mozilla/ipc/InputStreamUtils.h" 12 #include "mozilla/ipc/URIUtils.h" 13 #include "mozilla/ipc/BackgroundUtils.h" 14 #include "SerializedLoadContext.h" 15 #include "mozilla/net/NeckoCommon.h" 16 #include "mozilla/net/WebSocketChannel.h" 17 #include "nsComponentManagerUtils.h" 18 #include "IPCTransportProvider.h" 19 #include "mozilla/net/ChannelEventQueue.h" 20 21 using namespace mozilla::ipc; 22 23 namespace mozilla { 24 namespace net { 25 26 NS_IMPL_ISUPPORTS(WebSocketChannelParent, nsIWebSocketListener, 27 nsIInterfaceRequestor) 28 29 WebSocketChannelParent::WebSocketChannelParent( 30 nsIAuthPromptProvider* aAuthProvider, nsILoadContext* aLoadContext, 31 PBOverrideStatus aOverrideStatus, uint32_t aSerial) 32 : mAuthProvider(aAuthProvider), 33 mLoadContext(aLoadContext), 34 mSerial(aSerial) { 35 // Websocket channels can't have a private browsing override 36 MOZ_ASSERT_IF(!aLoadContext, aOverrideStatus == kPBOverride_Unset); 37 } 38 //----------------------------------------------------------------------------- 39 // WebSocketChannelParent::PWebSocketChannelParent 40 //----------------------------------------------------------------------------- 41 42 mozilla::ipc::IPCResult WebSocketChannelParent::RecvDeleteSelf() { 43 LOG(("WebSocketChannelParent::RecvDeleteSelf() %p\n", this)); 44 mChannel = nullptr; 45 mAuthProvider = nullptr; 46 IProtocol* mgr = Manager(); 47 if (CanRecv() && !Send__delete__(this)) { 48 return IPC_FAIL_NO_REASON(mgr); 49 } 50 return IPC_OK(); 51 } 52 53 mozilla::ipc::IPCResult WebSocketChannelParent::RecvAsyncOpen( 54 nsIURI* aURI, const nsCString& aOrigin, 55 const OriginAttributes& aOriginAttributes, const uint64_t& aInnerWindowID, 56 const nsCString& aProtocol, const bool& aSecure, 57 const uint32_t& aPingInterval, const bool& aClientSetPingInterval, 58 const uint32_t& aPingTimeout, const bool& aClientSetPingTimeout, 59 const LoadInfoArgs& aLoadInfoArgs, 60 const Maybe<PTransportProviderParent*>& aTransportProvider, 61 const nsCString& aNegotiatedExtensions) { 62 LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this)); 63 64 nsresult rv; 65 nsCOMPtr<nsILoadInfo> loadInfo; 66 nsCOMPtr<nsIURI> uri; 67 68 rv = LoadInfoArgsToLoadInfo( 69 aLoadInfoArgs, 70 mozilla::dom::ContentParent::Cast(Manager()->Manager())->GetRemoteType(), 71 getter_AddRefs(loadInfo)); 72 if (NS_FAILED(rv)) { 73 goto fail; 74 } 75 76 if (aSecure) { 77 mChannel = 78 do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv); 79 } else { 80 mChannel = 81 do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv); 82 } 83 if (NS_FAILED(rv)) goto fail; 84 85 rv = mChannel->SetSerial(mSerial); 86 if (NS_WARN_IF(NS_FAILED(rv))) { 87 goto fail; 88 } 89 90 rv = mChannel->SetLoadInfo(loadInfo); 91 if (NS_FAILED(rv)) { 92 goto fail; 93 } 94 95 rv = mChannel->SetNotificationCallbacks(this); 96 if (NS_FAILED(rv)) goto fail; 97 98 rv = mChannel->SetProtocol(aProtocol); 99 if (NS_FAILED(rv)) goto fail; 100 101 if (aTransportProvider.isSome()) { 102 RefPtr<TransportProviderParent> provider = 103 static_cast<TransportProviderParent*>(aTransportProvider.value()); 104 rv = mChannel->SetServerParameters(provider, aNegotiatedExtensions); 105 if (NS_FAILED(rv)) { 106 goto fail; 107 } 108 } else { 109 uri = aURI; 110 if (!uri) { 111 rv = NS_ERROR_FAILURE; 112 goto fail; 113 } 114 } 115 116 // only use ping values from child if they were overridden by client code. 117 if (aClientSetPingInterval) { 118 // IDL allows setting in seconds, so must be multiple of 1000 ms 119 MOZ_ASSERT(aPingInterval >= 1000 && !(aPingInterval % 1000)); 120 DebugOnly<nsresult> rv = mChannel->SetPingInterval(aPingInterval / 1000); 121 MOZ_ASSERT(NS_SUCCEEDED(rv)); 122 } 123 if (aClientSetPingTimeout) { 124 MOZ_ASSERT(aPingTimeout >= 1000 && !(aPingTimeout % 1000)); 125 DebugOnly<nsresult> rv = mChannel->SetPingTimeout(aPingTimeout / 1000); 126 MOZ_ASSERT(NS_SUCCEEDED(rv)); 127 } 128 129 rv = mChannel->AsyncOpenNative(uri, aOrigin, aOriginAttributes, 130 aInnerWindowID, this, nullptr); 131 if (NS_FAILED(rv)) goto fail; 132 133 return IPC_OK(); 134 135 fail: 136 mChannel = nullptr; 137 if (!SendOnStop(rv)) { 138 return IPC_FAIL_NO_REASON(this); 139 } 140 return IPC_OK(); 141 } 142 143 mozilla::ipc::IPCResult WebSocketChannelParent::RecvClose( 144 const uint16_t& code, const nsCString& reason) { 145 LOG(("WebSocketChannelParent::RecvClose() %p\n", this)); 146 if (mChannel) { 147 nsresult rv = mChannel->Close(code, reason); 148 NS_ENSURE_SUCCESS(rv, IPC_OK()); 149 } 150 151 return IPC_OK(); 152 } 153 154 mozilla::ipc::IPCResult WebSocketChannelParent::RecvSendMsg( 155 const nsCString& aMsg) { 156 LOG(("WebSocketChannelParent::RecvSendMsg() %p\n", this)); 157 if (mChannel) { 158 nsresult rv = mChannel->SendMsg(aMsg); 159 NS_ENSURE_SUCCESS(rv, IPC_OK()); 160 } 161 return IPC_OK(); 162 } 163 164 mozilla::ipc::IPCResult WebSocketChannelParent::RecvSendBinaryMsg( 165 const nsCString& aMsg) { 166 LOG(("WebSocketChannelParent::RecvSendBinaryMsg() %p\n", this)); 167 if (mChannel) { 168 nsresult rv = mChannel->SendBinaryMsg(aMsg); 169 NS_ENSURE_SUCCESS(rv, IPC_OK()); 170 } 171 return IPC_OK(); 172 } 173 174 mozilla::ipc::IPCResult WebSocketChannelParent::RecvSendBinaryStream( 175 const IPCStream& aStream, const uint32_t& aLength) { 176 LOG(("WebSocketChannelParent::RecvSendBinaryStream() %p\n", this)); 177 if (mChannel) { 178 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream); 179 if (!stream) { 180 return IPC_FAIL_NO_REASON(this); 181 } 182 nsresult rv = mChannel->SendBinaryStream(stream, aLength); 183 NS_ENSURE_SUCCESS(rv, IPC_OK()); 184 } 185 return IPC_OK(); 186 } 187 188 //----------------------------------------------------------------------------- 189 // WebSocketChannelParent::nsIRequestObserver 190 //----------------------------------------------------------------------------- 191 192 NS_IMETHODIMP 193 WebSocketChannelParent::OnStart(nsISupports* aContext) { 194 LOG(("WebSocketChannelParent::OnStart() %p\n", this)); 195 nsAutoCString protocol, extensions; 196 nsString effectiveURL; 197 bool encrypted = false; 198 uint64_t httpChannelId = 0; 199 if (mChannel) { 200 DebugOnly<nsresult> rv = mChannel->GetProtocol(protocol); 201 MOZ_ASSERT(NS_SUCCEEDED(rv)); 202 rv = mChannel->GetExtensions(extensions); 203 MOZ_ASSERT(NS_SUCCEEDED(rv)); 204 205 RefPtr<WebSocketChannel> channel; 206 channel = static_cast<WebSocketChannel*>(mChannel.get()); 207 MOZ_ASSERT(channel); 208 209 channel->GetEffectiveURL(effectiveURL); 210 encrypted = channel->IsEncrypted(); 211 httpChannelId = channel->HttpChannelId(); 212 } 213 if (!CanRecv() || !SendOnStart(protocol, extensions, effectiveURL, encrypted, 214 httpChannelId)) { 215 return NS_ERROR_FAILURE; 216 } 217 return NS_OK; 218 } 219 220 NS_IMETHODIMP 221 WebSocketChannelParent::OnStop(nsISupports* aContext, nsresult aStatusCode) { 222 LOG(("WebSocketChannelParent::OnStop() %p\n", this)); 223 if (!CanRecv() || !SendOnStop(aStatusCode)) { 224 return NS_ERROR_FAILURE; 225 } 226 return NS_OK; 227 } 228 229 static bool SendOnMessageAvailableHelper( 230 const nsACString& aMsg, 231 const std::function<bool(const nsDependentCSubstring&, bool)>& aSendFunc) { 232 // To avoid the crash caused by too large IPC message, we have to split the 233 // data in small chunks and send them to child process. Note that the chunk 234 // size used here is the same as what we used for PHttpChannel. 235 static uint32_t const kCopyChunkSize = 128 * 1024; 236 uint32_t count = aMsg.Length(); 237 if (count <= kCopyChunkSize) { 238 return aSendFunc(nsDependentCSubstring(aMsg), false); 239 } 240 241 uint32_t start = 0; 242 uint32_t toRead = std::min<uint32_t>(count, kCopyChunkSize); 243 while (count) { 244 nsDependentCSubstring data(Substring(aMsg, start, toRead)); 245 246 if (!aSendFunc(data, count > kCopyChunkSize)) { 247 return false; 248 } 249 250 start += toRead; 251 count -= toRead; 252 toRead = std::min<uint32_t>(count, kCopyChunkSize); 253 } 254 255 return true; 256 } 257 258 NS_IMETHODIMP 259 WebSocketChannelParent::OnMessageAvailable(nsISupports* aContext, 260 const nsACString& aMsg) { 261 LOG(("WebSocketChannelParent::OnMessageAvailable() %p\n", this)); 262 263 if (!CanRecv()) { 264 return NS_ERROR_FAILURE; 265 } 266 267 auto sendFunc = [self = UnsafePtr<WebSocketChannelParent>(this)]( 268 const nsDependentCSubstring& aMsg, bool aMoreData) { 269 return self->SendOnMessageAvailable(aMsg, aMoreData); 270 }; 271 272 if (!SendOnMessageAvailableHelper(aMsg, sendFunc)) { 273 return NS_ERROR_FAILURE; 274 } 275 276 return NS_OK; 277 } 278 279 NS_IMETHODIMP 280 WebSocketChannelParent::OnBinaryMessageAvailable(nsISupports* aContext, 281 const nsACString& aMsg) { 282 LOG(("WebSocketChannelParent::OnBinaryMessageAvailable() %p\n", this)); 283 284 if (!CanRecv()) { 285 return NS_ERROR_FAILURE; 286 } 287 288 auto sendFunc = [self = UnsafePtr<WebSocketChannelParent>(this)]( 289 const nsDependentCSubstring& aMsg, bool aMoreData) { 290 return self->SendOnBinaryMessageAvailable(aMsg, aMoreData); 291 }; 292 293 if (!SendOnMessageAvailableHelper(aMsg, sendFunc)) { 294 return NS_ERROR_FAILURE; 295 } 296 297 return NS_OK; 298 } 299 300 NS_IMETHODIMP 301 WebSocketChannelParent::OnAcknowledge(nsISupports* aContext, uint32_t aSize) { 302 LOG(("WebSocketChannelParent::OnAcknowledge() %p\n", this)); 303 if (!CanRecv() || !SendOnAcknowledge(aSize)) { 304 return NS_ERROR_FAILURE; 305 } 306 return NS_OK; 307 } 308 309 NS_IMETHODIMP 310 WebSocketChannelParent::OnServerClose(nsISupports* aContext, uint16_t code, 311 const nsACString& reason) { 312 LOG(("WebSocketChannelParent::OnServerClose() %p\n", this)); 313 if (!CanRecv() || !SendOnServerClose(code, reason)) { 314 return NS_ERROR_FAILURE; 315 } 316 return NS_OK; 317 } 318 319 NS_IMETHODIMP 320 WebSocketChannelParent::OnError() { return NS_OK; } 321 322 void WebSocketChannelParent::ActorDestroy(ActorDestroyReason why) { 323 LOG(("WebSocketChannelParent::ActorDestroy() %p\n", this)); 324 325 // Make sure we close the channel if the content process dies without going 326 // through a clean shutdown. 327 if (mChannel) { 328 (void)mChannel->Close(nsIWebSocketChannel::CLOSE_GOING_AWAY, 329 "Child was killed"_ns); 330 } 331 } 332 333 //----------------------------------------------------------------------------- 334 // WebSocketChannelParent::nsIInterfaceRequestor 335 //----------------------------------------------------------------------------- 336 337 NS_IMETHODIMP 338 WebSocketChannelParent::GetInterface(const nsIID& iid, void** result) { 339 LOG(("WebSocketChannelParent::GetInterface() %p\n", this)); 340 if (mAuthProvider && iid.Equals(NS_GET_IID(nsIAuthPromptProvider))) { 341 nsresult rv = mAuthProvider->GetAuthPrompt( 342 nsIAuthPromptProvider::PROMPT_NORMAL, iid, result); 343 if (NS_FAILED(rv)) { 344 return NS_ERROR_NO_INTERFACE; 345 } 346 return NS_OK; 347 } 348 349 // Only support nsILoadContext if child channel's callbacks did too 350 if (iid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { 351 nsCOMPtr<nsILoadContext> copy = mLoadContext; 352 copy.forget(result); 353 return NS_OK; 354 } 355 356 return QueryInterface(iid, result); 357 } 358 359 } // namespace net 360 } // namespace mozilla