WebrtcTCPSocket.cpp (23945B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 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 "WebrtcTCPSocket.h" 8 9 #include "WebrtcTCPSocketCallback.h" 10 #include "WebrtcTCPSocketLog.h" 11 #include "mozilla/StaticPrefs_media.h" 12 #include "mozilla/dom/BrowserParent.h" 13 #include "mozilla/dom/ContentProcessManager.h" 14 #include "nsHttpChannel.h" 15 #include "nsICancelable.h" 16 #include "nsIChannel.h" 17 #include "nsIClassOfService.h" 18 #include "nsIContentPolicy.h" 19 #include "nsICookieJarSettings.h" 20 #include "nsIIOService.h" 21 #include "nsILoadInfo.h" 22 #include "nsIProtocolProxyService.h" 23 #include "nsISocketTransportService.h" 24 #include "nsIURIMutator.h" 25 #include "nsProxyRelease.h" 26 #include "nsSocketTransportService2.h" 27 #include "nsString.h" 28 29 namespace mozilla::net { 30 31 class WebrtcTCPData { 32 public: 33 explicit WebrtcTCPData(nsTArray<uint8_t>&& aData) : mData(std::move(aData)) { 34 MOZ_COUNT_CTOR(WebrtcTCPData); 35 } 36 37 MOZ_COUNTED_DTOR(WebrtcTCPData) 38 39 const nsTArray<uint8_t>& GetData() const { return mData; } 40 41 private: 42 nsTArray<uint8_t> mData; 43 }; 44 45 NS_IMPL_ISUPPORTS(WebrtcTCPSocket, nsIAuthPromptProvider, 46 nsIHttpUpgradeListener, nsIInputStreamCallback, 47 nsIInterfaceRequestor, nsIOutputStreamCallback, 48 nsIRequestObserver, nsIStreamListener, 49 nsIProtocolProxyCallback) 50 51 WebrtcTCPSocket::WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks) 52 : mProxyCallbacks(aCallbacks), 53 mClosed(false), 54 mOpened(false), 55 mWriteOffset(0), 56 mAuthProvider(nullptr), 57 mTransport(nullptr), 58 mSocketIn(nullptr), 59 mSocketOut(nullptr) { 60 LOG(("WebrtcTCPSocket::WebrtcTCPSocket %p\n", this)); 61 mMainThread = GetMainThreadSerialEventTarget(); 62 mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); 63 MOZ_RELEASE_ASSERT(mMainThread, "no main thread"); 64 MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread"); 65 } 66 67 WebrtcTCPSocket::~WebrtcTCPSocket() { 68 LOG(("WebrtcTCPSocket::~WebrtcTCPSocket %p\n", this)); 69 70 NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread, 71 mAuthProvider.forget()); 72 } 73 74 void WebrtcTCPSocket::SetTabId(dom::TabId aTabId) { 75 MOZ_ASSERT(NS_IsMainThread()); 76 dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton(); 77 if (cpm) { 78 dom::ContentParentId cpId = cpm->GetTabProcessId(aTabId); 79 mAuthProvider = cpm->GetBrowserParentByProcessAndTabId(cpId, aTabId); 80 } 81 } 82 83 nsresult WebrtcTCPSocket::Write(nsTArray<uint8_t>&& aWriteData) { 84 LOG(("WebrtcTCPSocket::Write %p\n", this)); 85 MOZ_ASSERT(NS_IsMainThread()); 86 nsresult rv = mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( 87 "WebrtcTCPSocket::Write", this, &WebrtcTCPSocket::EnqueueWrite_s, 88 std::move(aWriteData))); 89 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); 90 91 return rv; 92 } 93 94 nsresult WebrtcTCPSocket::Close() { 95 LOG(("WebrtcTCPSocket::Close %p\n", this)); 96 97 CloseWithReason(NS_OK); 98 99 return NS_OK; 100 } 101 102 void WebrtcTCPSocket::CloseWithReason(nsresult aReason) { 103 LOG(("WebrtcTCPSocket::CloseWithReason %p reason=%u\n", this, 104 static_cast<uint32_t>(aReason))); 105 106 if (!OnSocketThread()) { 107 MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); 108 109 // Let's pretend we got an open even if we didn't to prevent an Open later. 110 mOpened = true; 111 112 DebugOnly<nsresult> rv = 113 mSocketThread->Dispatch(NewRunnableMethod<nsresult>( 114 "WebrtcTCPSocket::CloseWithReason", this, 115 &WebrtcTCPSocket::CloseWithReason, aReason)); 116 117 // This was MOZ_ALWAYS_SUCCEEDS, but that now uses NS_WARNING_ASSERTION. 118 // In order to convert this back to MOZ_ALWAYS_SUCCEEDS we would need 119 // OnSocketThread to return true if we're shutting down and doing the 120 // "running all of STS's queued events on main" thing. 121 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); 122 123 return; 124 } 125 126 if (mClosed) { 127 return; 128 } 129 130 mClosed = true; 131 132 if (mSocketIn) { 133 mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); 134 mSocketIn = nullptr; 135 } 136 137 if (mSocketOut) { 138 mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); 139 mSocketOut = nullptr; 140 } 141 142 if (mTransport) { 143 mTransport->Close(NS_BASE_STREAM_CLOSED); 144 mTransport = nullptr; 145 } 146 147 NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread, 148 mAuthProvider.forget()); 149 InvokeOnClose(aReason); 150 } 151 152 nsresult WebrtcTCPSocket::Open( 153 const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress, 154 const int& aLocalPort, bool aUseTls, 155 const Maybe<net::WebrtcProxyConfig>& aProxyConfig) { 156 LOG(("WebrtcTCPSocket::Open %p remote-host=%s local-addr=%s local-port=%d", 157 this, PromiseFlatCString(aHost).get(), 158 PromiseFlatCString(aLocalAddress).get(), aLocalPort)); 159 MOZ_ASSERT(NS_IsMainThread()); 160 161 if (NS_WARN_IF(mOpened)) { 162 LOG(("WebrtcTCPSocket %p: TCP socket already open\n", this)); 163 CloseWithReason(NS_ERROR_FAILURE); 164 return NS_ERROR_FAILURE; 165 } 166 167 mOpened = true; 168 const nsLiteralCString schemePrefix = aUseTls ? "https://"_ns : "http://"_ns; 169 nsAutoCString spec(schemePrefix); 170 171 bool ipv6Literal = aHost.Find(":") != kNotFound; 172 if (ipv6Literal) { 173 spec += "["; 174 spec += aHost; 175 spec += "]"; 176 } else { 177 spec += aHost; 178 } 179 180 nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) 181 .SetSpec(spec) 182 .SetPort(aPort) 183 .Finalize(mURI); 184 185 if (NS_WARN_IF(NS_FAILED(rv))) { 186 CloseWithReason(NS_ERROR_FAILURE); 187 return NS_ERROR_FAILURE; 188 } 189 190 mTls = aUseTls; 191 mLocalAddress = aLocalAddress; 192 mLocalPort = aLocalPort; 193 mProxyConfig = aProxyConfig; 194 195 if (!mProxyConfig.isSome()) { 196 OpenWithoutHttpProxy(nullptr); 197 return NS_OK; 198 } 199 200 // We need to figure out whether a proxy needs to be used for mURI before 201 // we can start on establishing a connection. 202 rv = DoProxyConfigLookup(); 203 204 if (NS_WARN_IF(NS_FAILED(rv))) { 205 CloseWithReason(rv); 206 } 207 208 return rv; 209 } 210 211 nsresult WebrtcTCPSocket::DoProxyConfigLookup() { 212 MOZ_ASSERT(NS_IsMainThread()); 213 nsresult rv; 214 nsCOMPtr<nsIProtocolProxyService> pps = 215 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); 216 if (NS_WARN_IF(NS_FAILED(rv))) { 217 return rv; 218 } 219 220 nsCOMPtr<nsIChannel> channel; 221 rv = NS_NewChannel(getter_AddRefs(channel), mURI, 222 nsContentUtils::GetSystemPrincipal(), 223 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 224 nsIContentPolicy::TYPE_OTHER); 225 if (NS_WARN_IF(NS_FAILED(rv))) { 226 return rv; 227 } 228 229 rv = pps->AsyncResolve(channel, 230 nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | 231 nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, 232 this, nullptr, getter_AddRefs(mProxyRequest)); 233 if (NS_WARN_IF(NS_FAILED(rv))) { 234 return rv; 235 } 236 237 // We pick back up in OnProxyAvailable 238 239 return NS_OK; 240 } 241 242 NS_IMETHODIMP WebrtcTCPSocket::OnProxyAvailable(nsICancelable* aRequest, 243 nsIChannel* aChannel, 244 nsIProxyInfo* aProxyinfo, 245 nsresult aResult) { 246 MOZ_ASSERT(NS_IsMainThread()); 247 mProxyRequest = nullptr; 248 249 if (NS_SUCCEEDED(aResult) && aProxyinfo) { 250 nsresult rv = aProxyinfo->GetType(mProxyType); 251 if (NS_WARN_IF(NS_FAILED(rv))) { 252 CloseWithReason(rv); 253 return rv; 254 } 255 256 if (mProxyType == "http" || mProxyType == "https") { 257 rv = OpenWithHttpProxy(); 258 if (NS_WARN_IF(NS_FAILED(rv))) { 259 CloseWithReason(rv); 260 } 261 return rv; 262 } 263 264 if (mProxyType == "socks" || mProxyType == "socks4" || 265 mProxyType == "direct") { 266 OpenWithoutHttpProxy(aProxyinfo); 267 return NS_OK; 268 } 269 } 270 271 OpenWithoutHttpProxy(nullptr); 272 273 return NS_OK; 274 } 275 276 void WebrtcTCPSocket::OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo) { 277 if (!OnSocketThread()) { 278 DebugOnly<nsresult> rv = 279 mSocketThread->Dispatch(NewRunnableMethod<nsCOMPtr<nsIProxyInfo>>( 280 "WebrtcTCPSocket::OpenWithoutHttpProxy", this, 281 &WebrtcTCPSocket::OpenWithoutHttpProxy, aSocksProxyInfo)); 282 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); 283 return; 284 } 285 286 LOG(("WebrtcTCPSocket::OpenWithoutHttpProxy %p\n", this)); 287 288 if (mClosed) { 289 return; 290 } 291 292 if (NS_WARN_IF(mProxyConfig.isSome() && mProxyConfig->forceProxy() && 293 !aSocksProxyInfo)) { 294 CloseWithReason(NS_ERROR_FAILURE); 295 return; 296 } 297 298 nsCString host; 299 int32_t port; 300 301 nsresult rv = mURI->GetHost(host); 302 if (NS_WARN_IF(NS_FAILED(rv))) { 303 CloseWithReason(rv); 304 return; 305 } 306 307 rv = mURI->GetPort(&port); 308 if (NS_WARN_IF(NS_FAILED(rv))) { 309 CloseWithReason(rv); 310 return; 311 } 312 313 AutoTArray<nsCString, 1> socketTypes; 314 if (mTls) { 315 socketTypes.AppendElement("ssl"_ns); 316 } 317 318 nsCOMPtr<nsISocketTransportService> sts = 319 do_GetService("@mozilla.org/network/socket-transport-service;1"); 320 rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, nullptr, 321 getter_AddRefs(mTransport)); 322 if (NS_WARN_IF(NS_FAILED(rv))) { 323 CloseWithReason(rv); 324 return; 325 } 326 327 mTransport->SetReuseAddrPort(true); 328 329 PRNetAddr prAddr; 330 if (NS_WARN_IF(PR_SUCCESS != 331 PR_InitializeNetAddr(PR_IpAddrAny, mLocalPort, &prAddr))) { 332 CloseWithReason(NS_ERROR_FAILURE); 333 return; 334 } 335 336 if (NS_WARN_IF(PR_SUCCESS != 337 PR_StringToNetAddr(mLocalAddress.BeginReading(), &prAddr))) { 338 CloseWithReason(NS_ERROR_FAILURE); 339 return; 340 } 341 342 mozilla::net::NetAddr addr(&prAddr); 343 rv = mTransport->Bind(&addr); 344 if (NS_WARN_IF(NS_FAILED(rv))) { 345 CloseWithReason(rv); 346 return; 347 } 348 349 // Binding to a V4 address is not sufficient to cause this socket to use 350 // V4, and the same goes for V6. So, we disable as needed here. 351 uint32_t flags = 0; 352 if (addr.raw.family == AF_INET) { 353 flags |= nsISocketTransport::DISABLE_IPV6; 354 } else if (addr.raw.family == AF_INET6) { 355 flags |= nsISocketTransport::DISABLE_IPV4; 356 } else { 357 MOZ_CRASH(); 358 } 359 360 mTransport->SetConnectionFlags(flags); 361 362 nsCOMPtr<nsIInputStream> socketIn; 363 rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(socketIn)); 364 if (NS_WARN_IF(NS_FAILED(rv))) { 365 CloseWithReason(rv); 366 return; 367 } 368 mSocketIn = do_QueryInterface(socketIn); 369 if (NS_WARN_IF(!mSocketIn)) { 370 CloseWithReason(NS_ERROR_NULL_POINTER); 371 return; 372 } 373 374 nsCOMPtr<nsIOutputStream> socketOut; 375 rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, 376 getter_AddRefs(socketOut)); 377 if (NS_WARN_IF(NS_FAILED(rv))) { 378 CloseWithReason(rv); 379 return; 380 } 381 mSocketOut = do_QueryInterface(socketOut); 382 if (NS_WARN_IF(!mSocketOut)) { 383 CloseWithReason(NS_ERROR_NULL_POINTER); 384 return; 385 } 386 387 FinishOpen(); 388 } 389 390 nsresult WebrtcTCPSocket::OpenWithHttpProxy() { 391 MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); 392 LOG(("WebrtcTCPSocket::OpenWithHttpProxy %p\n", this)); 393 nsresult rv; 394 nsCOMPtr<nsIIOService> ioService; 395 ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); 396 if (NS_FAILED(rv)) { 397 LOG(("WebrtcTCPSocket %p: io service missing\n", this)); 398 return rv; 399 } 400 401 nsCOMPtr<nsILoadInfo> loadInfo; 402 403 // FIXME: We don't know the remote type of the process which provided these 404 // LoadInfoArgs. Pass in `NOT_REMOTE_TYPE` as the origin process to blindly 405 // accept whatever value was passed by the other side for now, as we aren't 406 // using it for security checks here. 407 // If this code ever starts checking the triggering remote type, this needs to 408 // be changed. 409 rv = ipc::LoadInfoArgsToLoadInfo(mProxyConfig->loadInfoArgs(), 410 NOT_REMOTE_TYPE, getter_AddRefs(loadInfo)); 411 if (NS_FAILED(rv)) { 412 LOG(("WebrtcTCPSocket %p: could not init load info\n", this)); 413 return rv; 414 } 415 416 // -need to always tunnel since we're using a proxy 417 // -there shouldn't be an opportunity to send cookies, but explicitly disallow 418 // them anyway. 419 // -the previous proxy tunnel didn't support redirects e.g. 307. don't need to 420 // introduce new behavior. can't follow redirects on connect anyway. 421 nsCOMPtr<nsIChannel> localChannel; 422 rv = ioService->NewChannelFromURIWithProxyFlags( 423 mURI, mURI, 424 nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | 425 nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, 426 loadInfo->LoadingNode(), loadInfo->GetLoadingPrincipal(), 427 loadInfo->TriggeringPrincipal(), 428 // We need this flag to allow loads from any origin since this channel 429 // is being used to CONNECT to an HTTP proxy. 430 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 431 nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA, 432 getter_AddRefs(localChannel)); 433 if (NS_FAILED(rv)) { 434 LOG(("WebrtcTCPSocket %p: bad open channel\n", this)); 435 return rv; 436 } 437 438 nsCOMPtr<nsILoadInfo> channelLoadInfo = localChannel->LoadInfo(); 439 nsCOMPtr<nsICookieJarSettings> cookieJarSettings; 440 loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); 441 channelLoadInfo->SetCookieJarSettings(cookieJarSettings); 442 443 RefPtr<nsHttpChannel> httpChannel; 444 CallQueryInterface(localChannel, httpChannel.StartAssignment()); 445 446 if (!httpChannel) { 447 LOG(("WebrtcTCPSocket %p: not an http channel\n", this)); 448 return NS_ERROR_FAILURE; 449 } 450 451 if (!mTls && 452 mozilla::StaticPrefs::media_webrtc_disallow_HTTPS_upgrade_for_TURN()) { 453 loadInfo->SetSkipHTTPSUpgrade(true); 454 } 455 456 rv = localChannel->SetLoadInfo(loadInfo); 457 NS_ENSURE_SUCCESS(rv, rv); 458 459 httpChannel->SetNotificationCallbacks(this); 460 461 // don't block webrtc proxy setup with other requests 462 // often more than one of these channels will be created all at once by ICE 463 nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(localChannel); 464 if (cos) { 465 cos->AddClassFlags(nsIClassOfService::Unblocked | 466 nsIClassOfService::DontThrottle); 467 } else { 468 LOG(("WebrtcTCPSocket %p: could not set class of service\n", this)); 469 return NS_ERROR_FAILURE; 470 } 471 472 rv = httpChannel->HTTPUpgrade(mProxyConfig->alpn(), this); 473 if (NS_WARN_IF(NS_FAILED(rv))) { 474 return rv; 475 } 476 rv = httpChannel->SetConnectOnly( 477 mozilla::StaticPrefs::media_webrtc_tls_tunnel_for_all_proxy()); 478 if (NS_WARN_IF(NS_FAILED(rv))) { 479 return rv; 480 } 481 482 rv = httpChannel->AsyncOpen(this); 483 484 if (NS_FAILED(rv)) { 485 LOG(("WebrtcTCPSocket %p: cannot async open\n", this)); 486 return rv; 487 } 488 489 // This picks back up in OnTransportAvailable once we have connected to the 490 // proxy, and performed the http upgrade to switch the proxy into passthrough 491 // mode. 492 493 return NS_OK; 494 } 495 496 void WebrtcTCPSocket::EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData) { 497 LOG(("WebrtcTCPSocket::EnqueueWrite %p\n", this)); 498 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 499 500 if (mClosed) { 501 return; 502 } 503 504 mWriteQueue.emplace_back(std::move(aWriteData)); 505 506 if (mSocketOut) { 507 mSocketOut->AsyncWait(this, 0, 0, nullptr); 508 } 509 } 510 511 void WebrtcTCPSocket::InvokeOnClose(nsresult aReason) { 512 LOG(("WebrtcTCPSocket::InvokeOnClose %p\n", this)); 513 514 if (!NS_IsMainThread()) { 515 MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( 516 NewRunnableMethod<nsresult>("WebrtcTCPSocket::InvokeOnClose", this, 517 &WebrtcTCPSocket::InvokeOnClose, aReason))); 518 return; 519 } 520 521 MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); 522 523 if (mProxyRequest) { 524 mProxyRequest->Cancel(aReason); 525 mProxyRequest = nullptr; 526 } 527 528 mProxyCallbacks->OnClose(aReason); 529 mProxyCallbacks = nullptr; 530 } 531 532 void WebrtcTCPSocket::InvokeOnConnected() { 533 LOG(("WebrtcTCPSocket::InvokeOnConnected %p\n", this)); 534 535 if (!NS_IsMainThread()) { 536 MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( 537 NewRunnableMethod("WebrtcTCPSocket::InvokeOnConnected", this, 538 &WebrtcTCPSocket::InvokeOnConnected))); 539 return; 540 } 541 542 MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); 543 544 mProxyCallbacks->OnConnected(mProxyType); 545 } 546 547 void WebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) { 548 LOG(("WebrtcTCPSocket::InvokeOnRead %p count=%zu\n", this, 549 aReadData.Length())); 550 551 if (!NS_IsMainThread()) { 552 MOZ_ALWAYS_SUCCEEDS( 553 mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( 554 "WebrtcTCPSocket::InvokeOnRead", this, 555 &WebrtcTCPSocket::InvokeOnRead, std::move(aReadData)))); 556 return; 557 } 558 559 MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); 560 561 mProxyCallbacks->OnRead(std::move(aReadData)); 562 } 563 564 // nsIHttpUpgradeListener 565 NS_IMETHODIMP 566 WebrtcTCPSocket::OnTransportAvailable(nsISocketTransport* aTransport, 567 nsIAsyncInputStream* aSocketIn, 568 nsIAsyncOutputStream* aSocketOut) { 569 // This is called only in the http proxy case, once we have connected to the 570 // http proxy and performed the http upgrade to switch it over to passthrough 571 // mode. That process is started async by OpenWithHttpProxy. 572 LOG(("WebrtcTCPSocket::OnTransportAvailable %p\n", this)); 573 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 574 MOZ_ASSERT(!mTransport, 575 "already called transport available on webrtc TCP socket"); 576 577 // Cancel any pending callbacks. The caller doesn't always cancel these 578 // awaits. We need to make sure they don't get them. 579 aSocketIn->AsyncWait(nullptr, 0, 0, nullptr); 580 aSocketOut->AsyncWait(nullptr, 0, 0, nullptr); 581 582 if (mClosed) { 583 LOG(("WebrtcTCPSocket::OnTransportAvailable %p closed\n", this)); 584 return NS_OK; 585 } 586 587 mTransport = aTransport; 588 mSocketIn = aSocketIn; 589 mSocketOut = aSocketOut; 590 591 // pulled from nr_socket_prsock.cpp 592 uint32_t minBufferSize = 256 * 1024; 593 nsresult rv = mTransport->SetSendBufferSize(minBufferSize); 594 if (NS_FAILED(rv)) { 595 LOG(("WebrtcProxyChannel::OnTransportAvailable %p send failed\n", this)); 596 CloseWithReason(rv); 597 return rv; 598 } 599 rv = mTransport->SetRecvBufferSize(minBufferSize); 600 if (NS_FAILED(rv)) { 601 LOG(("WebrtcProxyChannel::OnTransportAvailable %p recv failed\n", this)); 602 CloseWithReason(rv); 603 return rv; 604 } 605 606 FinishOpen(); 607 return NS_OK; 608 } 609 610 void WebrtcTCPSocket::FinishOpen() { 611 MOZ_ASSERT(OnSocketThread()); 612 // mTransport, mSocketIn, and mSocketOut are all set. We may have set them in 613 // OnTransportAvailable (in the http/https proxy case), or in 614 // OpenWithoutHttpProxy. From here on out, this class functions the same for 615 // these two cases. 616 617 mSocketIn->AsyncWait(this, 0, 0, nullptr); 618 619 InvokeOnConnected(); 620 } 621 622 NS_IMETHODIMP 623 WebrtcTCPSocket::OnUpgradeFailed(nsresult aErrorCode) { 624 LOG(("WebrtcTCPSocket::OnUpgradeFailed %p\n", this)); 625 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 626 MOZ_ASSERT(!mTransport, 627 "already called transport available on webrtc TCP socket"); 628 629 if (mClosed) { 630 LOG(("WebrtcTCPSocket::OnUpgradeFailed %p closed\n", this)); 631 return NS_OK; 632 } 633 634 CloseWithReason(aErrorCode); 635 636 return NS_OK; 637 } 638 639 NS_IMETHODIMP 640 WebrtcTCPSocket::OnWebSocketConnectionAvailable( 641 WebSocketConnectionBase* aConnection) { 642 return NS_ERROR_NOT_IMPLEMENTED; 643 } 644 645 // nsIRequestObserver (from nsIStreamListener) 646 NS_IMETHODIMP 647 WebrtcTCPSocket::OnStartRequest(nsIRequest* aRequest) { 648 LOG(("WebrtcTCPSocket::OnStartRequest %p\n", this)); 649 650 return NS_OK; 651 } 652 653 NS_IMETHODIMP 654 WebrtcTCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { 655 LOG(("WebrtcTCPSocket::OnStopRequest %p status=%u\n", this, 656 static_cast<uint32_t>(aStatusCode))); 657 658 // see nsHttpChannel::ProcessFailedProxyConnect for most error codes 659 if (NS_FAILED(aStatusCode)) { 660 CloseWithReason(aStatusCode); 661 return aStatusCode; 662 } 663 664 return NS_OK; 665 } 666 667 // nsIStreamListener 668 NS_IMETHODIMP 669 WebrtcTCPSocket::OnDataAvailable(nsIRequest* aRequest, 670 nsIInputStream* aInputStream, uint64_t aOffset, 671 uint32_t aCount) { 672 LOG(("WebrtcTCPSocket::OnDataAvailable %p count=%u\n", this, aCount)); 673 MOZ_ASSERT(0, "unreachable data available"); 674 return NS_OK; 675 } 676 677 // nsIInputStreamCallback 678 NS_IMETHODIMP 679 WebrtcTCPSocket::OnInputStreamReady(nsIAsyncInputStream* in) { 680 LOG(("WebrtcTCPSocket::OnInputStreamReady %p unwritten=%zu\n", this, 681 CountUnwrittenBytes())); 682 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 683 MOZ_ASSERT(!mClosed, "webrtc TCP socket closed"); 684 MOZ_ASSERT(mTransport, "webrtc TCP socket not connected"); 685 MOZ_ASSERT(mSocketIn == in, "wrong input stream"); 686 687 while (true) { 688 char buffer[9216]; 689 uint32_t remainingCapacity = sizeof(buffer); 690 uint32_t read = 0; 691 692 while (remainingCapacity > 0) { 693 uint32_t count = 0; 694 nsresult rv = mSocketIn->Read(buffer + read, remainingCapacity, &count); 695 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 696 break; 697 } 698 699 if (NS_FAILED(rv)) { 700 LOG(("WebrtcTCPSocket::OnInputStreamReady %p failed %u\n", this, 701 static_cast<uint32_t>(rv))); 702 CloseWithReason(rv); 703 return rv; 704 } 705 706 // base stream closed 707 if (count == 0) { 708 LOG(("WebrtcTCPSocket::OnInputStreamReady %p connection closed\n", 709 this)); 710 CloseWithReason(NS_ERROR_FAILURE); 711 return NS_OK; 712 } 713 714 remainingCapacity -= count; 715 read += count; 716 } 717 718 if (read > 0) { 719 nsTArray<uint8_t> array(read); 720 array.AppendElements(buffer, read); 721 722 InvokeOnRead(std::move(array)); 723 } 724 725 if (remainingCapacity != 0) { 726 // Loop exited above, but not because we ran out of space. We're actually 727 // done, break out of the while(true) loop. 728 break; 729 } 730 } 731 732 mSocketIn->AsyncWait(this, 0, 0, nullptr); 733 return NS_OK; 734 } 735 736 // nsIOutputStreamCallback 737 NS_IMETHODIMP 738 WebrtcTCPSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) { 739 LOG(("WebrtcTCPSocket::OnOutputStreamReady %p unwritten=%zu\n", this, 740 CountUnwrittenBytes())); 741 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 742 MOZ_ASSERT(!mClosed, "webrtc TCP socket closed"); 743 MOZ_ASSERT(mTransport, "webrtc TCP socket not connected"); 744 MOZ_ASSERT(mSocketOut == out, "wrong output stream"); 745 746 while (!mWriteQueue.empty()) { 747 const WebrtcTCPData& data = mWriteQueue.front(); 748 749 char* buffer = reinterpret_cast<char*>( 750 const_cast<uint8_t*>(data.GetData().Elements())) + 751 mWriteOffset; 752 uint32_t toWrite = data.GetData().Length() - mWriteOffset; 753 754 uint32_t wrote = 0; 755 nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote); 756 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 757 mSocketOut->AsyncWait(this, 0, 0, nullptr); 758 return NS_OK; 759 } 760 761 if (NS_FAILED(rv)) { 762 LOG(("WebrtcTCPSocket::OnOutputStreamReady %p failed %u\n", this, 763 static_cast<uint32_t>(rv))); 764 CloseWithReason(rv); 765 return NS_OK; 766 } 767 768 mWriteOffset += wrote; 769 770 if (toWrite == wrote) { 771 mWriteOffset = 0; 772 mWriteQueue.pop_front(); 773 } 774 } 775 776 return NS_OK; 777 } 778 779 // nsIInterfaceRequestor 780 NS_IMETHODIMP 781 WebrtcTCPSocket::GetInterface(const nsIID& iid, void** result) { 782 LOG(("WebrtcTCPSocket::GetInterface %p\n", this)); 783 784 return QueryInterface(iid, result); 785 } 786 787 size_t WebrtcTCPSocket::CountUnwrittenBytes() const { 788 size_t count = 0; 789 790 for (const WebrtcTCPData& data : mWriteQueue) { 791 count += data.GetData().Length(); 792 } 793 794 MOZ_ASSERT(count >= mWriteOffset, "offset exceeds write buffer length"); 795 796 count -= mWriteOffset; 797 798 return count; 799 } 800 801 } // namespace mozilla::net