1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TCPServerSocket.h"
9 #include "TCPSocketChild.h"
10 #include "TCPSocketParent.h"
11 #include "mozilla/BasePrincipal.h"
12 #include "mozilla/ErrorResult.h"
13 #include "mozilla/SyncRunnable.h"
14 #include "mozilla/dom/RootedDictionary.h"
15 #include "mozilla/dom/ScriptSettings.h"
16 #include "mozilla/dom/TCPSocketBinding.h"
17 #include "mozilla/dom/TCPSocketErrorEvent.h"
18 #include "mozilla/dom/TCPSocketErrorEventBinding.h"
19 #include "mozilla/dom/TCPSocketEvent.h"
20 #include "mozilla/dom/TCPSocketEventBinding.h"
21 #include "mozilla/dom/ToJSValue.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsContentUtils.h"
24 #include "nsIArrayBufferInputStream.h"
25 #include "nsIAsyncInputStream.h"
26 #include "nsIAsyncStreamCopier.h"
27 #include "nsIBinaryInputStream.h"
28 #include "nsICancelable.h"
29 #include "nsIChannel.h"
30 #include "nsIInputStream.h"
31 #include "nsIInputStreamPump.h"
32 #include "nsIMultiplexInputStream.h"
33 #include "nsINSSErrorsService.h"
34 #include "nsIObserverService.h"
35 #include "nsIOutputStream.h"
36 #include "nsIProtocolProxyService.h"
37 #include "nsIScriptableInputStream.h"
38 #include "nsISocketTransport.h"
39 #include "nsISocketTransportService.h"
40 #include "nsISupportsPrimitives.h"
41 #include "nsITLSSocketControl.h"
42 #include "nsITransport.h"
43 #include "nsIURIMutator.h"
45 #include "nsNetUtil.h"
46 #include "nsServiceManagerUtils.h"
48 #include "nsStringStream.h"
52 using namespace mozilla::dom
;
54 NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket
, mGlobal
)
56 NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket
)
57 NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket
)
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket
)
59 NS_INTERFACE_MAP_ENTRY(nsISupports
)
62 LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner
* aWindow
)
63 : mGlobal(do_QueryInterface(aWindow
)) {}
65 LegacyMozTCPSocket::~LegacyMozTCPSocket() = default;
67 already_AddRefed
<TCPSocket
> LegacyMozTCPSocket::Open(
68 const nsAString
& aHost
, uint16_t aPort
, const SocketOptions
& aOptions
,
69 mozilla::ErrorResult
& aRv
) {
71 if (NS_WARN_IF(!api
.Init(mGlobal
))) {
72 aRv
.Throw(NS_ERROR_FAILURE
);
75 GlobalObject
globalObj(api
.cx(), mGlobal
->GetGlobalJSObject());
76 return TCPSocket::Constructor(globalObj
, aHost
, aPort
, aOptions
, aRv
);
79 already_AddRefed
<TCPServerSocket
> LegacyMozTCPSocket::Listen(
80 uint16_t aPort
, const ServerSocketOptions
& aOptions
, uint16_t aBacklog
,
81 mozilla::ErrorResult
& aRv
) {
83 if (NS_WARN_IF(!api
.Init(mGlobal
))) {
86 GlobalObject
globalObj(api
.cx(), mGlobal
->GetGlobalJSObject());
87 return TCPServerSocket::Constructor(globalObj
, aPort
, aOptions
, aBacklog
,
91 bool LegacyMozTCPSocket::WrapObject(JSContext
* aCx
,
92 JS::Handle
<JSObject
*> aGivenProto
,
93 JS::MutableHandle
<JSObject
*> aReflector
) {
94 return LegacyMozTCPSocket_Binding::Wrap(aCx
, this, aGivenProto
, aReflector
);
97 NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket
)
99 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket
, DOMEventTargetHelper
)
100 NS_IMPL_CYCLE_COLLECTION_TRACE_END
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket
,
103 DOMEventTargetHelper
)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport
)
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream
)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream
)
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump
)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable
)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary
)
110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS
)
111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild
)
112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent
)
113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
115 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket
, DOMEventTargetHelper
)
116 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport
)
117 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream
)
118 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream
)
119 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump
)
120 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable
)
121 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary
)
122 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS
)
123 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild
)
124 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent
)
125 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
126 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
128 NS_IMPL_ADDREF_INHERITED(TCPSocket
, DOMEventTargetHelper
)
129 NS_IMPL_RELEASE_INHERITED(TCPSocket
, DOMEventTargetHelper
)
131 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocket
)
132 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink
)
133 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
134 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
135 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback
)
136 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
137 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
138 NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback
)
139 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback
)
140 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
142 TCPSocket::TCPSocket(nsIGlobalObject
* aGlobal
, const nsAString
& aHost
,
143 uint16_t aPort
, bool aSsl
, bool aUseArrayBuffers
)
144 : DOMEventTargetHelper(aGlobal
),
145 mReadyState(TCPReadyState::Closed
),
146 mUseArrayBuffers(aUseArrayBuffers
),
150 mAsyncCopierActive(false),
151 mWaitingForDrain(false),
156 mWaitingForStartTLS(false),
157 mObserversActive(false) {
159 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aGlobal
);
161 mInnerWindowID
= window
->WindowID();
166 TCPSocket::~TCPSocket() {
167 if (mObserversActive
) {
168 nsCOMPtr
<nsIObserverService
> obs
=
169 do_GetService("@mozilla.org/observer-service;1");
171 obs
->RemoveObserver(this, "inner-window-destroyed");
172 obs
->RemoveObserver(this, "profile-change-net-teardown");
177 nsresult
TCPSocket::CreateStream() {
179 mTransport
->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream
));
180 NS_ENSURE_SUCCESS(rv
, rv
);
181 rv
= mTransport
->OpenOutputStream(nsITransport::OPEN_UNBUFFERED
, 0, 0,
182 getter_AddRefs(mSocketOutputStream
));
183 NS_ENSURE_SUCCESS(rv
, rv
);
185 // If the other side is not listening, we will
186 // get an onInputStreamReady callback where available
187 // raises to indicate the connection was refused.
188 nsCOMPtr
<nsIAsyncInputStream
> asyncStream
=
189 do_QueryInterface(mSocketInputStream
);
190 NS_ENSURE_TRUE(asyncStream
, NS_ERROR_NOT_AVAILABLE
);
192 nsCOMPtr
<nsISerialEventTarget
> mainTarget
= GetMainThreadSerialEventTarget();
193 rv
= asyncStream
->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY
, 0,
195 NS_ENSURE_SUCCESS(rv
, rv
);
197 if (mUseArrayBuffers
) {
199 do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv
);
200 NS_ENSURE_SUCCESS(rv
, rv
);
201 rv
= mInputStreamBinary
->SetInputStream(mSocketInputStream
);
202 NS_ENSURE_SUCCESS(rv
, rv
);
204 mInputStreamScriptable
=
205 do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv
);
206 NS_ENSURE_SUCCESS(rv
, rv
);
207 rv
= mInputStreamScriptable
->Init(mSocketInputStream
);
208 NS_ENSURE_SUCCESS(rv
, rv
);
214 nsresult
TCPSocket::InitWithUnconnectedTransport(
215 nsISocketTransport
* aTransport
) {
216 mReadyState
= TCPReadyState::Connecting
;
217 mTransport
= aTransport
;
219 MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content
);
221 nsCOMPtr
<nsISerialEventTarget
> mainTarget
= GetMainThreadSerialEventTarget();
222 mTransport
->SetEventSink(this, mainTarget
);
224 nsresult rv
= CreateStream();
225 NS_ENSURE_SUCCESS(rv
, rv
);
230 nsresult
TCPSocket::Init(nsIProxyInfo
* aProxyInfo
) {
231 nsCOMPtr
<nsIObserverService
> obs
=
232 do_GetService("@mozilla.org/observer-service;1");
234 mObserversActive
= true;
235 obs
->AddObserver(this, "inner-window-destroyed", true); // weak reference
236 obs
->AddObserver(this, "profile-change-net-teardown", true); // weak ref
239 if (XRE_IsContentProcess()) {
240 mReadyState
= TCPReadyState::Connecting
;
242 nsCOMPtr
<nsISerialEventTarget
> target
;
243 if (nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal()) {
244 target
= global
->SerialEventTarget();
246 mSocketBridgeChild
= new TCPSocketChild(mHost
, mPort
, target
);
247 mSocketBridgeChild
->SendOpen(this, mSsl
, mUseArrayBuffers
);
251 nsCOMPtr
<nsISocketTransportService
> sts
=
252 do_GetService("@mozilla.org/network/socket-transport-service;1");
254 AutoTArray
<nsCString
, 1> socketTypes
;
256 socketTypes
.AppendElement("ssl"_ns
);
258 socketTypes
.AppendElement("starttls"_ns
);
260 nsCOMPtr
<nsISocketTransport
> transport
;
262 sts
->CreateTransport(socketTypes
, NS_ConvertUTF16toUTF8(mHost
), mPort
,
263 aProxyInfo
, nullptr, getter_AddRefs(transport
));
264 NS_ENSURE_SUCCESS(rv
, rv
);
266 return InitWithUnconnectedTransport(transport
);
269 void TCPSocket::InitWithSocketChild(TCPSocketChild
* aSocketBridge
) {
270 mSocketBridgeChild
= aSocketBridge
;
271 mReadyState
= TCPReadyState::Open
;
272 mSocketBridgeChild
->SetSocket(this);
273 mSocketBridgeChild
->GetHost(mHost
);
274 mSocketBridgeChild
->GetPort(&mPort
);
277 nsresult
TCPSocket::InitWithTransport(nsISocketTransport
* aTransport
) {
278 mTransport
= aTransport
;
279 nsresult rv
= CreateStream();
280 NS_ENSURE_SUCCESS(rv
, rv
);
282 mReadyState
= TCPReadyState::Open
;
283 rv
= CreateInputStreamPump();
284 NS_ENSURE_SUCCESS(rv
, rv
);
287 mTransport
->GetHost(host
);
288 CopyUTF8toUTF16(host
, mHost
);
290 mTransport
->GetPort(&port
);
296 void TCPSocket::UpgradeToSecure(mozilla::ErrorResult
& aRv
) {
297 if (mReadyState
!= TCPReadyState::Open
) {
298 aRv
.Throw(NS_ERROR_FAILURE
);
308 if (mSocketBridgeChild
) {
309 mSocketBridgeChild
->SendStartTLS();
313 if (!mAsyncCopierActive
) {
316 mWaitingForStartTLS
= true;
321 class CopierCallbacks final
: public nsIRequestObserver
{
322 RefPtr
<TCPSocket
> mOwner
;
325 explicit CopierCallbacks(TCPSocket
* aSocket
) : mOwner(aSocket
) {}
328 NS_DECL_NSIREQUESTOBSERVER
330 ~CopierCallbacks() = default;
333 NS_IMPL_ISUPPORTS(CopierCallbacks
, nsIRequestObserver
)
336 CopierCallbacks::OnStartRequest(nsIRequest
* aRequest
) { return NS_OK
; }
339 CopierCallbacks::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatus
) {
340 mOwner
->NotifyCopyComplete(aStatus
);
344 } // unnamed namespace
346 void TCPSocket::CalculateBufferedAmount() {
347 // Let's update the buffered amount of data.
348 uint64_t bufferedAmount
= 0;
349 for (uint32_t i
= 0, len
= mPendingData
.Length(); i
< len
; ++i
) {
350 nsCOMPtr
<nsIInputStream
> stream
= mPendingData
[i
];
351 uint64_t available
= 0;
352 if (NS_SUCCEEDED(stream
->Available(&available
))) {
353 bufferedAmount
+= available
;
356 mBufferedAmount
= bufferedAmount
;
359 nsresult
TCPSocket::EnsureCopying() {
360 if (mAsyncCopierActive
) {
364 mAsyncCopierActive
= true;
368 nsCOMPtr
<nsIMultiplexInputStream
> multiplexStream
=
369 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv
);
370 NS_ENSURE_SUCCESS(rv
, rv
);
372 nsCOMPtr
<nsIInputStream
> stream
= do_QueryInterface(multiplexStream
);
374 while (!mPendingData
.IsEmpty()) {
375 nsCOMPtr
<nsIInputStream
> stream
= mPendingData
[0];
376 multiplexStream
->AppendStream(stream
);
377 mPendingData
.RemoveElementAt(0);
380 nsCOMPtr
<nsIAsyncStreamCopier
> copier
=
381 do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv
);
382 NS_ENSURE_SUCCESS(rv
, rv
);
384 nsCOMPtr
<nsISocketTransportService
> sts
=
385 do_GetService("@mozilla.org/network/socket-transport-service;1");
387 nsCOMPtr
<nsISerialEventTarget
> target
= do_QueryInterface(sts
);
388 rv
= copier
->Init(stream
, mSocketOutputStream
, target
,
389 true, /* source buffered */
390 false, /* sink buffered */
391 BUFFER_SIZE
, false, /* close source */
392 false); /* close sink */
393 NS_ENSURE_SUCCESS(rv
, rv
);
395 RefPtr
<CopierCallbacks
> callbacks
= new CopierCallbacks(this);
396 rv
= copier
->AsyncCopy(callbacks
, nullptr);
397 NS_ENSURE_SUCCESS(rv
, rv
);
402 void TCPSocket::NotifyCopyComplete(nsresult aStatus
) {
403 mAsyncCopierActive
= false;
404 CalculateBufferedAmount();
406 if (mSocketBridgeParent
&& mSocketBridgeParent
->IPCOpen()) {
407 mozilla::Unused
<< mSocketBridgeParent
->SendUpdateBufferedAmount(
408 BufferedAmount(), mTrackingNumber
);
411 if (NS_FAILED(aStatus
)) {
412 MaybeReportErrorAndCloseIfOpen(aStatus
);
416 if (BufferedAmount() != 0) {
421 // Maybe we have some empty stream. We want to have an empty queue now.
422 mPendingData
.Clear();
424 // If we are waiting for initiating starttls, we can begin to
426 if (mWaitingForStartTLS
&& mReadyState
== TCPReadyState::Open
) {
428 mWaitingForStartTLS
= false;
429 // If we have pending data, we should send them, or fire
430 // a drain event if we are waiting for it.
431 if (!mPendingDataAfterStartTLS
.IsEmpty()) {
432 mPendingData
= std::move(mPendingDataAfterStartTLS
);
438 // If we have a connected child, we let the child decide whether
439 // ondrain should be dispatched.
440 if (mWaitingForDrain
&& !mSocketBridgeParent
) {
441 mWaitingForDrain
= false;
442 FireEvent(u
"drain"_ns
);
445 if (mReadyState
== TCPReadyState::Closing
) {
446 if (mSocketOutputStream
) {
447 mSocketOutputStream
->Close();
448 mSocketOutputStream
= nullptr;
450 mReadyState
= TCPReadyState::Closed
;
451 FireEvent(u
"close"_ns
);
455 void TCPSocket::ActivateTLS() {
457 nsCOMPtr
<nsIEventTarget
> socketThread
=
458 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
, &rv
);
463 bool alreadyOnSTST
= false;
464 if (NS_FAILED(socketThread
->IsOnCurrentThread(&alreadyOnSTST
))) {
473 auto CallActivateTLS
= [sock
= RefPtr
{this}]() mutable {
474 sock
->ActivateTLSHelper();
476 mozilla::SyncRunnable::DispatchToThread(
478 NS_NewRunnableFunction("TCPSocket::UpgradeToSecure->ActivateTLSHelper",
482 void TCPSocket::ActivateTLSHelper() {
483 nsCOMPtr
<nsITLSSocketControl
> tlsSocketControl
;
484 mTransport
->GetTlsSocketControl(getter_AddRefs(tlsSocketControl
));
485 if (tlsSocketControl
) {
486 tlsSocketControl
->StartTLS();
491 TCPSocket::FireErrorEvent(const nsAString
& aName
, const nsAString
& aType
,
492 nsresult aErrorCode
) {
493 if (mSocketBridgeParent
) {
494 mSocketBridgeParent
->FireErrorEvent(aName
, aType
, aErrorCode
, mReadyState
);
498 TCPSocketErrorEventInit init
;
499 init
.mBubbles
= false;
500 init
.mCancelable
= false;
502 init
.mMessage
= aType
;
503 static_assert(std::is_same_v
<std::underlying_type_t
<nsresult
>, uint32_t>);
504 init
.mErrorCode
= uint32_t(aErrorCode
);
506 RefPtr
<TCPSocketErrorEvent
> event
=
507 TCPSocketErrorEvent::Constructor(this, u
"error"_ns
, init
);
509 event
->SetTrusted(true);
510 DispatchEvent(*event
);
515 TCPSocket::FireEvent(const nsAString
& aType
) {
516 if (mSocketBridgeParent
) {
517 mSocketBridgeParent
->FireEvent(aType
, mReadyState
);
522 if (NS_WARN_IF(!api
.Init(GetOwnerGlobal()))) {
523 return NS_ERROR_FAILURE
;
525 JS::Rooted
<JS::Value
> val(api
.cx());
526 return FireDataEvent(api
.cx(), aType
, val
);
530 TCPSocket::FireDataArrayEvent(const nsAString
& aType
,
531 const nsTArray
<uint8_t>& buffer
) {
533 if (NS_WARN_IF(!api
.Init(GetOwnerGlobal()))) {
534 return NS_ERROR_FAILURE
;
536 JSContext
* cx
= api
.cx();
537 JS::Rooted
<JS::Value
> val(cx
);
539 bool ok
= IPC::DeserializeArrayBuffer(cx
, buffer
, &val
);
541 return FireDataEvent(cx
, aType
, val
);
543 return NS_ERROR_FAILURE
;
547 TCPSocket::FireDataStringEvent(const nsAString
& aType
,
548 const nsACString
& aString
) {
550 if (NS_WARN_IF(!api
.Init(GetOwnerGlobal()))) {
551 return NS_ERROR_FAILURE
;
553 JSContext
* cx
= api
.cx();
554 JS::Rooted
<JS::Value
> val(cx
);
556 bool ok
= ToJSValue(cx
, NS_ConvertASCIItoUTF16(aString
), &val
);
558 return FireDataEvent(cx
, aType
, val
);
560 return NS_ERROR_FAILURE
;
563 nsresult
TCPSocket::FireDataEvent(JSContext
* aCx
, const nsAString
& aType
,
564 JS::Handle
<JS::Value
> aData
) {
565 MOZ_ASSERT(!mSocketBridgeParent
);
567 RootedDictionary
<TCPSocketEventInit
> init(aCx
);
568 init
.mBubbles
= false;
569 init
.mCancelable
= false;
572 RefPtr
<TCPSocketEvent
> event
= TCPSocketEvent::Constructor(this, aType
, init
);
573 event
->SetTrusted(true);
574 DispatchEvent(*event
);
578 JSObject
* TCPSocket::WrapObject(JSContext
* aCx
,
579 JS::Handle
<JSObject
*> aGivenProto
) {
580 return TCPSocket_Binding::Wrap(aCx
, this, aGivenProto
);
583 void TCPSocket::GetHost(nsAString
& aHost
) { aHost
.Assign(mHost
); }
585 uint32_t TCPSocket::Port() const { return mPort
; }
587 bool TCPSocket::Ssl() const { return mSsl
; }
589 void TCPSocket::Suspend() {
590 if (mSocketBridgeChild
) {
591 mSocketBridgeChild
->SendSuspend();
594 if (mInputStreamPump
) {
595 mInputStreamPump
->Suspend();
600 void TCPSocket::Resume(mozilla::ErrorResult
& aRv
) {
601 if (mSocketBridgeChild
) {
602 mSocketBridgeChild
->SendResume();
605 if (!mSuspendCount
) {
606 aRv
.Throw(NS_ERROR_FAILURE
);
610 if (mInputStreamPump
) {
611 mInputStreamPump
->Resume();
616 nsresult
TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status
) {
617 // If we're closed, we've already reported the error or just don't need to
619 if (mReadyState
== TCPReadyState::Closed
) {
623 // go through ::Closing state and then mark ::Closed
625 mReadyState
= TCPReadyState::Closed
;
627 if (NS_FAILED(status
)) {
628 // Convert the status code to an appropriate error message.
630 nsString errorType
, errName
;
632 // security module? (and this is an error)
633 if ((static_cast<uint32_t>(status
) & 0xFF0000) == 0x5a0000) {
634 nsCOMPtr
<nsINSSErrorsService
> errSvc
=
635 do_GetService("@mozilla.org/nss_errors_service;1");
636 // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code
637 // is somehow not in the set of covered errors.
639 nsresult rv
= errSvc
->GetErrorClass(status
, &errorClass
);
641 errorType
.AssignLiteral("SecurityProtocol");
643 switch (errorClass
) {
644 case nsINSSErrorsService::ERROR_CLASS_BAD_CERT
:
645 errorType
.AssignLiteral("SecurityCertificate");
648 errorType
.AssignLiteral("SecurityProtocol");
653 // NSS_SEC errors (happen below the base value because of negative vals)
654 if ((static_cast<int32_t>(status
) & 0xFFFF) <
655 abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE
)) {
656 switch (static_cast<SECErrorCodes
>(status
)) {
657 case SEC_ERROR_EXPIRED_CERTIFICATE
:
658 errName
.AssignLiteral("SecurityExpiredCertificateError");
660 case SEC_ERROR_REVOKED_CERTIFICATE
:
661 errName
.AssignLiteral("SecurityRevokedCertificateError");
663 // per bsmith, we will be unable to tell these errors apart very
664 // soon, so it makes sense to just folder them all together already.
665 case SEC_ERROR_UNKNOWN_ISSUER
:
666 case SEC_ERROR_UNTRUSTED_ISSUER
:
667 case SEC_ERROR_UNTRUSTED_CERT
:
668 case SEC_ERROR_CA_CERT_INVALID
:
669 errName
.AssignLiteral("SecurityUntrustedCertificateIssuerError");
671 case SEC_ERROR_INADEQUATE_KEY_USAGE
:
672 errName
.AssignLiteral("SecurityInadequateKeyUsageError");
674 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
:
675 errName
.AssignLiteral(
676 "SecurityCertificateSignatureAlgorithmDisabledError");
679 errName
.AssignLiteral("SecurityError");
684 switch (static_cast<SSLErrorCodes
>(status
)) {
685 case SSL_ERROR_NO_CERTIFICATE
:
686 errName
.AssignLiteral("SecurityNoCertificateError");
688 case SSL_ERROR_BAD_CERTIFICATE
:
689 errName
.AssignLiteral("SecurityBadCertificateError");
691 case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE
:
692 errName
.AssignLiteral("SecurityUnsupportedCertificateTypeError");
694 case SSL_ERROR_UNSUPPORTED_VERSION
:
695 errName
.AssignLiteral("SecurityUnsupportedTLSVersionError");
697 case SSL_ERROR_BAD_CERT_DOMAIN
:
698 errName
.AssignLiteral("SecurityCertificateDomainMismatchError");
701 errName
.AssignLiteral("SecurityError");
707 errorType
.AssignLiteral("Network");
710 // connect to host:port failed
711 case NS_ERROR_CONNECTION_REFUSED
:
712 errName
.AssignLiteral("ConnectionRefusedError");
714 // network timeout error
715 case NS_ERROR_NET_TIMEOUT
:
716 errName
.AssignLiteral("NetworkTimeoutError");
718 // hostname lookup failed
719 case NS_ERROR_UNKNOWN_HOST
:
720 errName
.AssignLiteral("DomainNotFoundError");
722 case NS_ERROR_NET_INTERRUPT
:
723 errName
.AssignLiteral("NetworkInterruptError");
726 errName
.AssignLiteral("NetworkError");
731 Unused
<< NS_WARN_IF(NS_FAILED(FireErrorEvent(errName
, errorType
, status
)));
734 return FireEvent(u
"close"_ns
);
737 void TCPSocket::Close() { CloseHelper(true); }
739 void TCPSocket::CloseImmediately() { CloseHelper(false); }
741 void TCPSocket::CloseHelper(bool waitForUnsentData
) {
742 if (mReadyState
== TCPReadyState::Closed
||
743 mReadyState
== TCPReadyState::Closing
) {
747 mReadyState
= TCPReadyState::Closing
;
750 mProxyRequest
->Cancel(NS_BINDING_ABORTED
);
751 mProxyRequest
= nullptr;
754 if (mSocketBridgeChild
) {
755 mSocketBridgeChild
->SendClose();
759 if (!mAsyncCopierActive
|| !waitForUnsentData
) {
760 mPendingData
.Clear();
761 mPendingDataAfterStartTLS
.Clear();
763 if (mSocketOutputStream
) {
764 mSocketOutputStream
->Close();
765 mSocketOutputStream
= nullptr;
769 if (mSocketInputStream
) {
770 mSocketInputStream
->Close();
771 mSocketInputStream
= nullptr;
775 bool TCPSocket::Send(const nsACString
& aData
, mozilla::ErrorResult
& aRv
) {
776 if (mReadyState
!= TCPReadyState::Open
) {
777 aRv
.Throw(NS_ERROR_FAILURE
);
782 nsCOMPtr
<nsIInputStream
> stream
;
783 if (mSocketBridgeChild
) {
784 mSocketBridgeChild
->SendSend(aData
);
785 byteLength
= aData
.Length();
787 nsresult rv
= NS_NewCStringInputStream(getter_AddRefs(stream
), aData
);
792 rv
= stream
->Available(&byteLength
);
798 return Send(stream
, byteLength
);
801 bool TCPSocket::Send(const ArrayBuffer
& aData
, uint32_t aByteOffset
,
802 const Optional
<uint32_t>& aByteLength
,
803 mozilla::ErrorResult
& aRv
) {
804 if (mReadyState
!= TCPReadyState::Open
) {
805 aRv
.Throw(NS_ERROR_FAILURE
);
809 nsCOMPtr
<nsIArrayBufferInputStream
> stream
;
812 auto calculateOffsetAndCount
= [&](uint32_t aLength
) {
813 uint32_t offset
= std::min(aLength
, aByteOffset
);
814 nbytes
= std::min(aLength
- aByteOffset
,
815 aByteLength
.WasPassed() ? aByteLength
.Value() : aLength
);
816 return std::pair(offset
, nbytes
);
819 if (mSocketBridgeChild
) {
820 nsTArray
<uint8_t> arrayBuffer
;
821 if (!aData
.AppendDataTo(arrayBuffer
, calculateOffsetAndCount
)) {
822 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
826 mSocketBridgeChild
->SendSend(std::move(arrayBuffer
));
828 mozilla::Maybe
<mozilla::UniquePtr
<uint8_t[]>> arrayBuffer
=
829 aData
.CreateFromData
<mozilla::UniquePtr
<uint8_t[]>>(
830 calculateOffsetAndCount
);
831 if (arrayBuffer
.isNothing()) {
832 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
836 stream
= do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1");
837 nsresult rv
= stream
->SetData(arrayBuffer
.extract(), nbytes
);
838 if (NS_WARN_IF(NS_FAILED(rv
))) {
843 return Send(stream
, nbytes
);
846 bool TCPSocket::Send(nsIInputStream
* aStream
, uint32_t aByteLength
) {
847 uint64_t newBufferedAmount
= BufferedAmount() + aByteLength
;
848 bool bufferFull
= newBufferedAmount
> BUFFER_SIZE
;
850 // If we buffered more than some arbitrary amount of data,
851 // (65535 right now) we should tell the caller so they can
852 // wait until ondrain is called if they so desire. Once all the
853 // buffered data has been written to the socket, ondrain is
855 mWaitingForDrain
= true;
858 if (mSocketBridgeChild
) {
859 // In the child, we just add the buffer length to our bufferedAmount and let
860 // the parent update our bufferedAmount when the data have been sent.
861 mBufferedAmount
= newBufferedAmount
;
865 // This is used to track how many packets we have been told to send. Signaling
866 // this back to the user of this API allows the user to know how many packets
867 // are currently in flight over IPC.
869 if (mWaitingForStartTLS
) {
870 // When we are waiting for starttls, newStream is added to pendingData
871 // and will be appended to multiplexStream after tls had been set up.
872 mPendingDataAfterStartTLS
.AppendElement(aStream
);
874 mPendingData
.AppendElement(aStream
);
877 CalculateBufferedAmount();
883 TCPReadyState
TCPSocket::ReadyState() { return mReadyState
; }
885 TCPSocketBinaryType
TCPSocket::BinaryType() const {
886 if (mUseArrayBuffers
) {
887 return TCPSocketBinaryType::Arraybuffer
;
889 return TCPSocketBinaryType::String
;
892 already_AddRefed
<TCPSocket
> TCPSocket::CreateAcceptedSocket(
893 nsIGlobalObject
* aGlobal
, nsISocketTransport
* aTransport
,
894 bool aUseArrayBuffers
) {
895 RefPtr
<TCPSocket
> socket
=
896 new TCPSocket(aGlobal
, u
""_ns
, 0, false, aUseArrayBuffers
);
897 nsresult rv
= socket
->InitWithTransport(aTransport
);
898 NS_ENSURE_SUCCESS(rv
, nullptr);
899 return socket
.forget();
902 already_AddRefed
<TCPSocket
> TCPSocket::CreateAcceptedSocket(
903 nsIGlobalObject
* aGlobal
, TCPSocketChild
* aBridge
, bool aUseArrayBuffers
) {
904 RefPtr
<TCPSocket
> socket
=
905 new TCPSocket(aGlobal
, u
""_ns
, 0, false, aUseArrayBuffers
);
906 socket
->InitWithSocketChild(aBridge
);
907 return socket
.forget();
910 already_AddRefed
<TCPSocket
> TCPSocket::Constructor(
911 const GlobalObject
& aGlobal
, const nsAString
& aHost
, uint16_t aPort
,
912 const SocketOptions
& aOptions
, mozilla::ErrorResult
& aRv
) {
913 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
914 RefPtr
<TCPSocket
> socket
=
915 new TCPSocket(global
, aHost
, aPort
, aOptions
.mUseSecureTransport
,
916 aOptions
.mBinaryType
== TCPSocketBinaryType::Arraybuffer
);
917 socket
->ResolveProxy();
919 return socket
.forget();
922 nsresult
TCPSocket::ResolveProxy() {
924 nsCOMPtr
<nsIProtocolProxyService
> pps
=
925 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID
, &rv
);
926 if (NS_WARN_IF(NS_FAILED(rv
))) {
930 nsCOMPtr
<nsIURI
> uri
;
931 nsCString spec
= mSsl
? "https://"_ns
: "http://"_ns
;
932 bool maybeIPv6
= mHost
.FindChar(':') != -1;
933 if (maybeIPv6
) spec
.Append('[');
934 if (!AppendUTF16toUTF8(mHost
, spec
, fallible
)) {
935 return NS_ERROR_OUT_OF_MEMORY
;
937 if (maybeIPv6
) spec
.Append(']');
938 rv
= NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID
)
942 if (NS_WARN_IF(NS_FAILED(rv
))) {
946 nsCOMPtr
<nsIChannel
> channel
;
947 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
948 nsContentUtils::GetSystemPrincipal(),
949 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
950 nsIContentPolicy::TYPE_OTHER
);
951 if (NS_WARN_IF(NS_FAILED(rv
))) {
955 rv
= pps
->AsyncResolve(channel
,
956 nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY
,
957 this, nullptr, getter_AddRefs(mProxyRequest
));
958 if (NS_WARN_IF(NS_FAILED(rv
))) {
966 TCPSocket::OnProxyAvailable(nsICancelable
* aRequest
, nsIChannel
* aChannel
,
967 nsIProxyInfo
* aProxyInfo
, nsresult aResult
) {
968 mProxyRequest
= nullptr;
969 if (NS_SUCCEEDED(aResult
) && aProxyInfo
) {
971 nsresult rv
= aProxyInfo
->GetType(proxyType
);
972 if (NS_WARN_IF(NS_FAILED(rv
))) {
976 // Only supports SOCKS proxy for now.
977 if (proxyType
== "socks" || proxyType
== "socks4") {
978 return Init(aProxyInfo
);
981 return Init(nullptr);
984 nsresult
TCPSocket::CreateInputStreamPump() {
985 if (!mSocketInputStream
) {
986 return NS_ERROR_NOT_AVAILABLE
;
990 do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv
);
991 NS_ENSURE_SUCCESS(rv
, rv
);
993 rv
= mInputStreamPump
->Init(mSocketInputStream
, 0, 0, false, nullptr);
994 NS_ENSURE_SUCCESS(rv
, rv
);
996 uint64_t suspendCount
= mSuspendCount
;
997 while (suspendCount
--) {
998 mInputStreamPump
->Suspend();
1001 rv
= mInputStreamPump
->AsyncRead(this);
1002 NS_ENSURE_SUCCESS(rv
, rv
);
1007 TCPSocket::OnTransportStatus(nsITransport
* aTransport
, nsresult aStatus
,
1008 int64_t aProgress
, int64_t aProgressMax
) {
1009 if (static_cast<uint32_t>(aStatus
) !=
1010 static_cast<uint32_t>(nsISocketTransport::STATUS_CONNECTED_TO
)) {
1014 mReadyState
= TCPReadyState::Open
;
1015 nsresult rv
= CreateInputStreamPump();
1016 NS_ENSURE_SUCCESS(rv
, rv
);
1017 FireEvent(u
"open"_ns
);
1023 TCPSocket::OnInputStreamReady(nsIAsyncInputStream
* aStream
) {
1024 // Only used for detecting if the connection was refused.
1027 nsresult rv
= aStream
->Available(&dummy
);
1028 if (NS_FAILED(rv
)) {
1029 MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED
);
1035 TCPSocket::OnStartRequest(nsIRequest
* aRequest
) { return NS_OK
; }
1038 TCPSocket::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aStream
,
1039 uint64_t aOffset
, uint32_t aCount
) {
1040 if (mUseArrayBuffers
) {
1041 nsTArray
<uint8_t> buffer
;
1042 buffer
.SetCapacity(aCount
);
1044 nsresult rv
= aStream
->Read(reinterpret_cast<char*>(buffer
.Elements()),
1046 NS_ENSURE_SUCCESS(rv
, rv
);
1047 MOZ_ASSERT(actual
== aCount
);
1048 buffer
.SetLength(actual
);
1050 if (mSocketBridgeParent
) {
1051 mSocketBridgeParent
->FireArrayBufferDataEvent(buffer
, mReadyState
);
1056 if (!api
.Init(GetOwnerGlobal())) {
1057 return NS_ERROR_FAILURE
;
1059 JSContext
* cx
= api
.cx();
1061 JS::Rooted
<JS::Value
> value(cx
);
1062 if (!ToJSValue(cx
, TypedArrayCreator
<ArrayBuffer
>(buffer
), &value
)) {
1063 return NS_ERROR_FAILURE
;
1065 FireDataEvent(cx
, u
"data"_ns
, value
);
1070 nsresult rv
= mInputStreamScriptable
->ReadBytes(aCount
, data
);
1071 NS_ENSURE_SUCCESS(rv
, rv
);
1073 if (mSocketBridgeParent
) {
1074 mSocketBridgeParent
->FireStringDataEvent(data
, mReadyState
);
1079 if (!api
.Init(GetOwnerGlobal())) {
1080 return NS_ERROR_FAILURE
;
1082 JSContext
* cx
= api
.cx();
1084 JS::Rooted
<JS::Value
> value(cx
);
1085 if (!ToJSValue(cx
, NS_ConvertASCIItoUTF16(data
), &value
)) {
1086 return NS_ERROR_FAILURE
;
1088 FireDataEvent(cx
, u
"data"_ns
, value
);
1094 TCPSocket::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatus
) {
1095 mInputStreamPump
= nullptr;
1097 if (mAsyncCopierActive
&& NS_SUCCEEDED(aStatus
)) {
1098 // If we have some buffered output still, and status is not an
1099 // error, the other side has done a half-close, but we don't
1100 // want to be in the close state until we are done sending
1101 // everything that was buffered. We also don't want to call onclose
1106 // We call this even if there is no error.
1107 MaybeReportErrorAndCloseIfOpen(aStatus
);
1111 void TCPSocket::SetSocketBridgeParent(TCPSocketParent
* aBridgeParent
) {
1112 MOZ_ASSERT(NS_IsMainThread());
1114 mSocketBridgeParent
= aBridgeParent
;
1118 TCPSocket::UpdateReadyState(uint32_t aReadyState
) {
1119 MOZ_ASSERT(mSocketBridgeChild
);
1120 mReadyState
= static_cast<TCPReadyState
>(aReadyState
);
1125 TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount
,
1126 uint32_t aTrackingNumber
) {
1127 if (aTrackingNumber
!= mTrackingNumber
) {
1130 mBufferedAmount
= aBufferedAmount
;
1131 if (!mBufferedAmount
) {
1132 if (mWaitingForDrain
) {
1133 mWaitingForDrain
= false;
1134 return FireEvent(u
"drain"_ns
);
1141 TCPSocket::Observe(nsISupports
* aSubject
, const char* aTopic
,
1142 const char16_t
* aData
) {
1143 if (!strcmp(aTopic
, "inner-window-destroyed")) {
1144 nsCOMPtr
<nsISupportsPRUint64
> wrapper
= do_QueryInterface(aSubject
);
1145 NS_ENSURE_TRUE(wrapper
, NS_ERROR_FAILURE
);
1147 nsresult rv
= wrapper
->GetData(&innerID
);
1148 if (NS_WARN_IF(NS_FAILED(rv
))) {
1152 if (innerID
== mInnerWindowID
) {
1155 } else if (!strcmp(aTopic
, "profile-change-net-teardown")) {
1163 bool TCPSocket::ShouldTCPSocketExist(JSContext
* aCx
, JSObject
* aGlobal
) {
1164 JS::Rooted
<JSObject
*> global(aCx
, aGlobal
);
1165 return nsContentUtils::ObjectPrincipal(global
)->IsSystemPrincipal();