123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "mozilla/ErrorResult.h"
- #include "TCPSocket.h"
- #include "TCPServerSocket.h"
- #include "TCPSocketParent.h"
- #include "TCPSocketChild.h"
- #include "mozilla/Unused.h"
- #include "mozilla/dom/DOMError.h"
- #include "mozilla/dom/TCPSocketBinding.h"
- #include "mozilla/dom/TCPSocketErrorEvent.h"
- #include "mozilla/dom/TCPSocketErrorEventBinding.h"
- #include "mozilla/dom/TCPSocketEvent.h"
- #include "mozilla/dom/TCPSocketEventBinding.h"
- #include "mozilla/dom/ToJSValue.h"
- #include "nsContentUtils.h"
- #include "nsIArrayBufferInputStream.h"
- #include "nsISocketTransportService.h"
- #include "nsISocketTransport.h"
- #include "nsIMultiplexInputStream.h"
- #include "nsIAsyncStreamCopier.h"
- #include "nsIInputStream.h"
- #include "nsIBinaryInputStream.h"
- #include "nsIScriptableInputStream.h"
- #include "nsIInputStreamPump.h"
- #include "nsIAsyncInputStream.h"
- #include "nsISupportsPrimitives.h"
- #include "nsITransport.h"
- #include "nsIOutputStream.h"
- #include "nsINSSErrorsService.h"
- #include "nsISSLSocketControl.h"
- #include "nsStringStream.h"
- #include "secerr.h"
- #include "sslerr.h"
- #define BUFFER_SIZE 65536
- #define NETWORK_STATS_THRESHOLD 65536
- using namespace mozilla::dom;
- NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket, mGlobal)
- NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_END
- LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow)
- : mGlobal(do_QueryInterface(aWindow))
- {
- }
- LegacyMozTCPSocket::~LegacyMozTCPSocket()
- {
- }
- already_AddRefed<TCPSocket>
- LegacyMozTCPSocket::Open(const nsAString& aHost,
- uint16_t aPort,
- const SocketOptions& aOptions,
- mozilla::ErrorResult& aRv)
- {
- AutoJSAPI api;
- if (NS_WARN_IF(!api.Init(mGlobal))) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
- GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
- return TCPSocket::Constructor(globalObj, aHost, aPort, aOptions, aRv);
- }
- already_AddRefed<TCPServerSocket>
- LegacyMozTCPSocket::Listen(uint16_t aPort,
- const ServerSocketOptions& aOptions,
- uint16_t aBacklog,
- mozilla::ErrorResult& aRv)
- {
- AutoJSAPI api;
- if (NS_WARN_IF(!api.Init(mGlobal))) {
- return nullptr;
- }
- GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
- return TCPServerSocket::Constructor(globalObj, aPort, aOptions, aBacklog, aRv);
- }
- bool
- LegacyMozTCPSocket::WrapObject(JSContext* aCx,
- JS::Handle<JSObject*> aGivenProto,
- JS::MutableHandle<JSObject*> aReflector)
- {
- return LegacyMozTCPSocketBinding::Wrap(aCx, this, aGivenProto, aReflector);
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket)
- NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket,
- DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_TRACE_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket,
- DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMultiplexStream)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMultiplexStreamCopier)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket,
- DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mMultiplexStream)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mMultiplexStreamCopier)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_ADDREF_INHERITED(TCPSocket, DOMEventTargetHelper)
- NS_IMPL_RELEASE_INHERITED(TCPSocket, DOMEventTargetHelper)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPSocket)
- NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
- NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
- NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
- NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
- NS_INTERFACE_MAP_ENTRY(nsIObserver)
- NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
- NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
- NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
- TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
- bool aSsl, bool aUseArrayBuffers)
- : DOMEventTargetHelper(aGlobal)
- , mReadyState(TCPReadyState::Closed)
- , mUseArrayBuffers(aUseArrayBuffers)
- , mHost(aHost)
- , mPort(aPort)
- , mSsl(aSsl)
- , mAsyncCopierActive(false)
- , mWaitingForDrain(false)
- , mInnerWindowID(0)
- , mBufferedAmount(0)
- , mSuspendCount(0)
- , mTrackingNumber(0)
- , mWaitingForStartTLS(false)
- , mObserversActive(false)
- {
- if (aGlobal) {
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
- if (window) {
- mInnerWindowID = window->WindowID();
- }
- }
- }
- TCPSocket::~TCPSocket()
- {
- if (mObserversActive) {
- nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
- if (obs) {
- obs->RemoveObserver(this, "inner-window-destroyed");
- obs->RemoveObserver(this, "profile-change-net-teardown");
- }
- }
- }
- nsresult
- TCPSocket::CreateStream()
- {
- nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
- NS_ENSURE_SUCCESS(rv, rv);
- // If the other side is not listening, we will
- // get an onInputStreamReady callback where available
- // raises to indicate the connection was refused.
- nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
- NS_ENSURE_TRUE(asyncStream, NS_ERROR_NOT_AVAILABLE);
- nsCOMPtr<nsIThread> mainThread;
- NS_GetMainThread(getter_AddRefs(mainThread));
- rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainThread);
- NS_ENSURE_SUCCESS(rv, rv);
- if (mUseArrayBuffers) {
- mInputStreamBinary = do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mInputStreamBinary->SetInputStream(mSocketInputStream);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mInputStreamScriptable->Init(mSocketInputStream);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsISocketTransportService> sts =
- do_GetService("@mozilla.org/network/socket-transport-service;1");
- nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
- rv = mMultiplexStreamCopier->Init(mMultiplexStream,
- mSocketOutputStream,
- target,
- true, /* source buffered */
- false, /* sink buffered */
- BUFFER_SIZE,
- false, /* close source */
- false); /* close sink */
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- TCPSocket::InitWithUnconnectedTransport(nsISocketTransport* aTransport)
- {
- mReadyState = TCPReadyState::Connecting;
- mTransport = aTransport;
- MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
- nsCOMPtr<nsIThread> mainThread;
- NS_GetMainThread(getter_AddRefs(mainThread));
- mTransport->SetEventSink(this, mainThread);
- nsresult rv = CreateStream();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- TCPSocket::Init()
- {
- nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
- if (obs) {
- mObserversActive = true;
- obs->AddObserver(this, "inner-window-destroyed", true); // weak reference
- obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref
- }
- if (XRE_GetProcessType() == GeckoProcessType_Content) {
- mReadyState = TCPReadyState::Connecting;
- mSocketBridgeChild = new TCPSocketChild(mHost, mPort);
- mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
- return NS_OK;
- }
- nsCOMPtr<nsISocketTransportService> sts =
- do_GetService("@mozilla.org/network/socket-transport-service;1");
- const char* socketTypes[1];
- if (mSsl) {
- socketTypes[0] = "ssl";
- } else {
- socketTypes[0] = "starttls";
- }
- nsCOMPtr<nsISocketTransport> transport;
- nsresult rv = sts->CreateTransport(socketTypes, 1, NS_ConvertUTF16toUTF8(mHost), mPort,
- nullptr, getter_AddRefs(transport));
- NS_ENSURE_SUCCESS(rv, rv);
- return InitWithUnconnectedTransport(transport);
- }
- void
- TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge)
- {
- mSocketBridgeChild = aSocketBridge;
- mReadyState = TCPReadyState::Open;
- mSocketBridgeChild->SetSocket(this);
- mSocketBridgeChild->GetHost(mHost);
- mSocketBridgeChild->GetPort(&mPort);
- }
- nsresult
- TCPSocket::InitWithTransport(nsISocketTransport* aTransport)
- {
- mTransport = aTransport;
- nsresult rv = CreateStream();
- NS_ENSURE_SUCCESS(rv, rv);
- mReadyState = TCPReadyState::Open;
- rv = CreateInputStreamPump();
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString host;
- mTransport->GetHost(host);
- mHost = NS_ConvertUTF8toUTF16(host);
- int32_t port;
- mTransport->GetPort(&port);
- mPort = port;
- return NS_OK;
- }
- void
- TCPSocket::UpgradeToSecure(mozilla::ErrorResult& aRv)
- {
- if (mReadyState != TCPReadyState::Open) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- if (mSsl) {
- return;
- }
- mSsl = true;
- if (mSocketBridgeChild) {
- mSocketBridgeChild->SendStartTLS();
- return;
- }
- uint32_t count = 0;
- mMultiplexStream->GetCount(&count);
- if (!count) {
- ActivateTLS();
- } else {
- mWaitingForStartTLS = true;
- }
- }
- namespace {
- class CopierCallbacks final : public nsIRequestObserver
- {
- RefPtr<TCPSocket> mOwner;
- public:
- explicit CopierCallbacks(TCPSocket* aSocket) : mOwner(aSocket) {}
- NS_DECL_ISUPPORTS
- NS_DECL_NSIREQUESTOBSERVER
- private:
- ~CopierCallbacks() {}
- };
- NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
- NS_IMETHODIMP
- CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
- {
- mOwner->NotifyCopyComplete(aStatus);
- mOwner = nullptr;
- return NS_OK;
- }
- } // unnamed namespace
- nsresult
- TCPSocket::EnsureCopying()
- {
- if (mAsyncCopierActive) {
- return NS_OK;
- }
- mAsyncCopierActive = true;
- RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
- return mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr);
- }
- void
- TCPSocket::NotifyCopyComplete(nsresult aStatus)
- {
- mAsyncCopierActive = false;
-
- uint32_t countRemaining;
- nsresult rvRemaining = mMultiplexStream->GetCount(&countRemaining);
- NS_ENSURE_SUCCESS_VOID(rvRemaining);
- while (countRemaining--) {
- mMultiplexStream->RemoveStream(0);
- }
- while (!mPendingDataWhileCopierActive.IsEmpty()) {
- nsCOMPtr<nsIInputStream> stream = mPendingDataWhileCopierActive[0];
- mMultiplexStream->AppendStream(stream);
- mPendingDataWhileCopierActive.RemoveElementAt(0);
- }
-
- if (mSocketBridgeParent) {
- mozilla::Unused << mSocketBridgeParent->SendUpdateBufferedAmount(BufferedAmount(),
- mTrackingNumber);
- }
- if (NS_FAILED(aStatus)) {
- MaybeReportErrorAndCloseIfOpen(aStatus);
- return;
- }
- uint32_t count;
- nsresult rv = mMultiplexStream->GetCount(&count);
- NS_ENSURE_SUCCESS_VOID(rv);
- if (count) {
- EnsureCopying();
- return;
- }
- // If we are waiting for initiating starttls, we can begin to
- // activate tls now.
- if (mWaitingForStartTLS && mReadyState == TCPReadyState::Open) {
- ActivateTLS();
- mWaitingForStartTLS = false;
- // If we have pending data, we should send them, or fire
- // a drain event if we are waiting for it.
- if (!mPendingDataAfterStartTLS.IsEmpty()) {
- while (!mPendingDataAfterStartTLS.IsEmpty()) {
- nsCOMPtr<nsIInputStream> stream = mPendingDataAfterStartTLS[0];
- mMultiplexStream->AppendStream(stream);
- mPendingDataAfterStartTLS.RemoveElementAt(0);
- }
- EnsureCopying();
- return;
- }
- }
- // If we have a connected child, we let the child decide whether
- // ondrain should be dispatched.
- if (mWaitingForDrain && !mSocketBridgeParent) {
- mWaitingForDrain = false;
- FireEvent(NS_LITERAL_STRING("drain"));
- }
- if (mReadyState == TCPReadyState::Closing) {
- if (mSocketOutputStream) {
- mSocketOutputStream->Close();
- mSocketOutputStream = nullptr;
- }
- mReadyState = TCPReadyState::Closed;
- FireEvent(NS_LITERAL_STRING("close"));
- }
- }
- void
- TCPSocket::ActivateTLS()
- {
- nsCOMPtr<nsISupports> securityInfo;
- mTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
- nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(securityInfo);
- if (socketControl) {
- socketControl->StartTLS();
- }
- }
- NS_IMETHODIMP
- TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
- {
- if (mSocketBridgeParent) {
- mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
- return NS_OK;
- }
- TCPSocketErrorEventInit init;
- init.mBubbles = false;
- init.mCancelable = false;
- init.mName = aName;
- init.mMessage = aType;
- RefPtr<TCPSocketErrorEvent> event =
- TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
- MOZ_ASSERT(event);
- event->SetTrusted(true);
- bool dummy;
- DispatchEvent(event, &dummy);
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::FireEvent(const nsAString& aType)
- {
- if (mSocketBridgeParent) {
- mSocketBridgeParent->FireEvent(aType, mReadyState);
- return NS_OK;
- }
- AutoJSAPI api;
- if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
- return NS_ERROR_FAILURE;
- }
- JS::Rooted<JS::Value> val(api.cx());
- return FireDataEvent(api.cx(), aType, val);
- }
- NS_IMETHODIMP
- TCPSocket::FireDataArrayEvent(const nsAString& aType,
- const InfallibleTArray<uint8_t>& buffer)
- {
- AutoJSAPI api;
- if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = api.cx();
- JS::Rooted<JS::Value> val(cx);
- bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
- if (ok) {
- return FireDataEvent(cx, aType, val);
- }
- return NS_ERROR_FAILURE;
- }
- NS_IMETHODIMP
- TCPSocket::FireDataStringEvent(const nsAString& aType,
- const nsACString& aString)
- {
- AutoJSAPI api;
- if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = api.cx();
- JS::Rooted<JS::Value> val(cx);
- bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
- if (ok) {
- return FireDataEvent(cx, aType, val);
- }
- return NS_ERROR_FAILURE;
- }
- nsresult
- TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData)
- {
- MOZ_ASSERT(!mSocketBridgeParent);
- RootedDictionary<TCPSocketEventInit> init(aCx);
- init.mBubbles = false;
- init.mCancelable = false;
- init.mData = aData;
- RefPtr<TCPSocketEvent> event =
- TCPSocketEvent::Constructor(this, aType, init);
- event->SetTrusted(true);
- bool dummy;
- DispatchEvent(event, &dummy);
- return NS_OK;
- }
- JSObject*
- TCPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return TCPSocketBinding::Wrap(aCx, this, aGivenProto);
- }
- void
- TCPSocket::GetHost(nsAString& aHost)
- {
- aHost.Assign(mHost);
- }
- uint32_t
- TCPSocket::Port()
- {
- return mPort;
- }
- bool
- TCPSocket::Ssl()
- {
- return mSsl;
- }
- uint64_t
- TCPSocket::BufferedAmount()
- {
- if (mSocketBridgeChild) {
- return mBufferedAmount;
- }
- if (mMultiplexStream) {
- uint64_t available = 0;
- mMultiplexStream->Available(&available);
- return available;
- }
- return 0;
- }
- void
- TCPSocket::Suspend()
- {
- if (mSocketBridgeChild) {
- mSocketBridgeChild->SendSuspend();
- return;
- }
- if (mInputStreamPump) {
- mInputStreamPump->Suspend();
- }
- mSuspendCount++;
- }
- void
- TCPSocket::Resume(mozilla::ErrorResult& aRv)
- {
- if (mSocketBridgeChild) {
- mSocketBridgeChild->SendResume();
- return;
- }
- if (!mSuspendCount) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- if (mInputStreamPump) {
- mInputStreamPump->Resume();
- }
- mSuspendCount--;
- }
- nsresult
- TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
- // If we're closed, we've already reported the error or just don't need to
- // report the error.
- if (mReadyState == TCPReadyState::Closed) {
- return NS_OK;
- }
- // go through ::Closing state and then mark ::Closed
- Close();
- mReadyState = TCPReadyState::Closed;
- if (NS_FAILED(status)) {
- // Convert the status code to an appropriate error message.
- nsString errorType, errName;
- // security module? (and this is an error)
- if ((static_cast<uint32_t>(status) & 0xFF0000) == 0x5a0000) {
- nsCOMPtr<nsINSSErrorsService> errSvc = do_GetService("@mozilla.org/nss_errors_service;1");
- // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
- // somehow not in the set of covered errors.
- uint32_t errorClass;
- nsresult rv = errSvc->GetErrorClass(status, &errorClass);
- if (NS_FAILED(rv)) {
- errorType.AssignLiteral("SecurityProtocol");
- } else {
- switch (errorClass) {
- case nsINSSErrorsService::ERROR_CLASS_BAD_CERT:
- errorType.AssignLiteral("SecurityCertificate");
- break;
- default:
- errorType.AssignLiteral("SecurityProtocol");
- break;
- }
- }
- // NSS_SEC errors (happen below the base value because of negative vals)
- if ((static_cast<int32_t>(status) & 0xFFFF) < abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE)) {
- switch (static_cast<SECErrorCodes>(status)) {
- case SEC_ERROR_EXPIRED_CERTIFICATE:
- errName.AssignLiteral("SecurityExpiredCertificateError");
- break;
- case SEC_ERROR_REVOKED_CERTIFICATE:
- errName.AssignLiteral("SecurityRevokedCertificateError");
- break;
- // per bsmith, we will be unable to tell these errors apart very soon,
- // so it makes sense to just folder them all together already.
- case SEC_ERROR_UNKNOWN_ISSUER:
- case SEC_ERROR_UNTRUSTED_ISSUER:
- case SEC_ERROR_UNTRUSTED_CERT:
- case SEC_ERROR_CA_CERT_INVALID:
- errName.AssignLiteral("SecurityUntrustedCertificateIssuerError");
- break;
- case SEC_ERROR_INADEQUATE_KEY_USAGE:
- errName.AssignLiteral("SecurityInadequateKeyUsageError");
- break;
- case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
- errName.AssignLiteral("SecurityCertificateSignatureAlgorithmDisabledError");
- break;
- default:
- errName.AssignLiteral("SecurityError");
- break;
- }
- } else {
- // NSS_SSL errors
- switch (static_cast<SSLErrorCodes>(status)) {
- case SSL_ERROR_NO_CERTIFICATE:
- errName.AssignLiteral("SecurityNoCertificateError");
- break;
- case SSL_ERROR_BAD_CERTIFICATE:
- errName.AssignLiteral("SecurityBadCertificateError");
- break;
- case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:
- errName.AssignLiteral("SecurityUnsupportedCertificateTypeError");
- break;
- case SSL_ERROR_UNSUPPORTED_VERSION:
- errName.AssignLiteral("SecurityUnsupportedTLSVersionError");
- break;
- case SSL_ERROR_BAD_CERT_DOMAIN:
- errName.AssignLiteral("SecurityCertificateDomainMismatchError");
- break;
- default:
- errName.AssignLiteral("SecurityError");
- break;
- }
- }
- } else {
- // must be network
- errorType.AssignLiteral("Network");
- switch (status) {
- // connect to host:port failed
- case NS_ERROR_CONNECTION_REFUSED:
- errName.AssignLiteral("ConnectionRefusedError");
- break;
- // network timeout error
- case NS_ERROR_NET_TIMEOUT:
- errName.AssignLiteral("NetworkTimeoutError");
- break;
- // hostname lookup failed
- case NS_ERROR_UNKNOWN_HOST:
- errName.AssignLiteral("DomainNotFoundError");
- break;
- case NS_ERROR_NET_INTERRUPT:
- errName.AssignLiteral("NetworkInterruptError");
- break;
- default:
- errName.AssignLiteral("NetworkError");
- break;
- }
- }
- Unused << NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
- }
- return FireEvent(NS_LITERAL_STRING("close"));
- }
- void
- TCPSocket::Close()
- {
- CloseHelper(true);
- }
- void
- TCPSocket::CloseImmediately()
- {
- CloseHelper(false);
- }
- void
- TCPSocket::CloseHelper(bool waitForUnsentData)
- {
- if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
- return;
- }
- mReadyState = TCPReadyState::Closing;
- if (mSocketBridgeChild) {
- mSocketBridgeChild->SendClose();
- return;
- }
- uint32_t count = 0;
- if (mMultiplexStream) {
- mMultiplexStream->GetCount(&count);
- }
- if (!count || !waitForUnsentData) {
- if (mSocketOutputStream) {
- mSocketOutputStream->Close();
- mSocketOutputStream = nullptr;
- }
- }
- if (mSocketInputStream) {
- mSocketInputStream->Close();
- mSocketInputStream = nullptr;
- }
- }
- void
- TCPSocket::SendWithTrackingNumber(const nsACString& aData,
- const uint32_t& aTrackingNumber,
- mozilla::ErrorResult& aRv)
- {
- MOZ_ASSERT(mSocketBridgeParent);
- mTrackingNumber = aTrackingNumber;
- // The JSContext isn't necessary for string values; it's a codegen limitation.
- Send(nullptr, aData, aRv);
- }
- bool
- TCPSocket::Send(JSContext* aCx, const nsACString& aData, mozilla::ErrorResult& aRv)
- {
- if (mReadyState != TCPReadyState::Open) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
- uint64_t byteLength;
- nsCOMPtr<nsIInputStream> stream;
- if (mSocketBridgeChild) {
- mSocketBridgeChild->SendSend(aData, ++mTrackingNumber);
- byteLength = aData.Length();
- } else {
- nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), aData);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return false;
- }
- rv = stream->Available(&byteLength);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return false;
- }
- }
- return Send(stream, byteLength);
- }
- void
- TCPSocket::SendWithTrackingNumber(JSContext* aCx,
- const ArrayBuffer& aData,
- uint32_t aByteOffset,
- const Optional<uint32_t>& aByteLength,
- const uint32_t& aTrackingNumber,
- mozilla::ErrorResult& aRv)
- {
- MOZ_ASSERT(mSocketBridgeParent);
- mTrackingNumber = aTrackingNumber;
- Send(aCx, aData, aByteOffset, aByteLength, aRv);
- }
- bool
- TCPSocket::Send(JSContext* aCx,
- const ArrayBuffer& aData,
- uint32_t aByteOffset,
- const Optional<uint32_t>& aByteLength,
- mozilla::ErrorResult& aRv)
- {
- if (mReadyState != TCPReadyState::Open) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
- nsCOMPtr<nsIArrayBufferInputStream> stream;
- aData.ComputeLengthAndData();
- uint32_t byteLength = aByteLength.WasPassed() ? aByteLength.Value() : aData.Length();
- if (mSocketBridgeChild) {
- nsresult rv = mSocketBridgeChild->SendSend(aData, aByteOffset, byteLength, ++mTrackingNumber);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- aRv.Throw(rv);
- return false;
- }
- } else {
- JS::Rooted<JSObject*> obj(aCx, aData.Obj());
- JSAutoCompartment ac(aCx, obj);
- JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*obj));
- stream = do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1");
- nsresult rv = stream->SetData(value, aByteOffset, byteLength, aCx);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- aRv.Throw(rv);
- return false;
- }
- }
- return Send(stream, byteLength);
- }
- bool
- TCPSocket::Send(nsIInputStream* aStream, uint32_t aByteLength)
- {
- uint64_t newBufferedAmount = BufferedAmount() + aByteLength;
- bool bufferFull = newBufferedAmount > BUFFER_SIZE;
- if (bufferFull) {
- // If we buffered more than some arbitrary amount of data,
- // (65535 right now) we should tell the caller so they can
- // wait until ondrain is called if they so desire. Once all the
- // buffered data has been written to the socket, ondrain is
- // called.
- mWaitingForDrain = true;
- }
- if (mSocketBridgeChild) {
- // In the child, we just add the buffer length to our bufferedAmount and let
- // the parent update our bufferedAmount when the data have been sent.
- mBufferedAmount = newBufferedAmount;
- return !bufferFull;
- }
- if (mWaitingForStartTLS) {
- // When we are waiting for starttls, newStream is added to pendingData
- // and will be appended to multiplexStream after tls had been set up.
- mPendingDataAfterStartTLS.AppendElement(aStream);
- } else if (mAsyncCopierActive) {
- // While the AsyncCopier is still active..
- mPendingDataWhileCopierActive.AppendElement(aStream);
- } else {
- mMultiplexStream->AppendStream(aStream);
- }
- EnsureCopying();
- return !bufferFull;
- }
- TCPReadyState
- TCPSocket::ReadyState()
- {
- return mReadyState;
- }
- TCPSocketBinaryType
- TCPSocket::BinaryType()
- {
- if (mUseArrayBuffers) {
- return TCPSocketBinaryType::Arraybuffer;
- } else {
- return TCPSocketBinaryType::String;
- }
- }
- already_AddRefed<TCPSocket>
- TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
- nsISocketTransport* aTransport,
- bool aUseArrayBuffers)
- {
- RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
- nsresult rv = socket->InitWithTransport(aTransport);
- NS_ENSURE_SUCCESS(rv, nullptr);
- return socket.forget();
- }
- already_AddRefed<TCPSocket>
- TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
- TCPSocketChild* aBridge,
- bool aUseArrayBuffers)
- {
- RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
- socket->InitWithSocketChild(aBridge);
- return socket.forget();
- }
- already_AddRefed<TCPSocket>
- TCPSocket::Constructor(const GlobalObject& aGlobal,
- const nsAString& aHost,
- uint16_t aPort,
- const SocketOptions& aOptions,
- mozilla::ErrorResult& aRv)
- {
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
- RefPtr<TCPSocket> socket =
- new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport,
- aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer);
- nsresult rv = socket->Init();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- aRv.Throw(rv);
- return nullptr;
- }
- return socket.forget();
- }
- nsresult
- TCPSocket::CreateInputStreamPump()
- {
- if (!mSocketInputStream) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsresult rv;
- mInputStreamPump = do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false);
- NS_ENSURE_SUCCESS(rv, rv);
- uint64_t suspendCount = mSuspendCount;
- while (suspendCount--) {
- mInputStreamPump->Suspend();
- }
- rv = mInputStreamPump->AsyncRead(this, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
- int64_t aProgress, int64_t aProgressMax)
- {
- if (static_cast<uint32_t>(aStatus) != nsISocketTransport::STATUS_CONNECTED_TO) {
- return NS_OK;
- }
- mReadyState = TCPReadyState::Open;
- FireEvent(NS_LITERAL_STRING("open"));
- nsresult rv = CreateInputStreamPump();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream)
- {
- // Only used for detecting if the connection was refused.
- uint64_t dummy;
- nsresult rv = aStream->Available(&dummy);
- if (NS_FAILED(rv)) {
- MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
- uint64_t aOffset, uint32_t aCount)
- {
- if (mUseArrayBuffers) {
- nsTArray<uint8_t> buffer;
- buffer.SetCapacity(aCount);
- uint32_t actual;
- nsresult rv = aStream->Read(reinterpret_cast<char*>(buffer.Elements()), aCount, &actual);
- NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(actual == aCount);
- buffer.SetLength(actual);
- if (mSocketBridgeParent) {
- mSocketBridgeParent->FireArrayBufferDataEvent(buffer, mReadyState);
- return NS_OK;
- }
- AutoJSAPI api;
- if (!api.Init(GetOwnerGlobal())) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = api.cx();
- JS::Rooted<JS::Value> value(cx);
- if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) {
- return NS_ERROR_FAILURE;
- }
- FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
- return NS_OK;
- }
- nsCString data;
- nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
- NS_ENSURE_SUCCESS(rv, rv);
- if (mSocketBridgeParent) {
- mSocketBridgeParent->FireStringDataEvent(data, mReadyState);
- return NS_OK;
- }
- AutoJSAPI api;
- if (!api.Init(GetOwnerGlobal())) {
- return NS_ERROR_FAILURE;
- }
- JSContext* cx = api.cx();
- JS::Rooted<JS::Value> value(cx);
- if (!ToJSValue(cx, NS_ConvertASCIItoUTF16(data), &value)) {
- return NS_ERROR_FAILURE;
- }
- FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
- {
- uint32_t count;
- nsresult rv = mMultiplexStream->GetCount(&count);
- NS_ENSURE_SUCCESS(rv, rv);
- bool bufferedOutput = count != 0;
- mInputStreamPump = nullptr;
- if (bufferedOutput && NS_SUCCEEDED(aStatus)) {
- // If we have some buffered output still, and status is not an
- // error, the other side has done a half-close, but we don't
- // want to be in the close state until we are done sending
- // everything that was buffered. We also don't want to call onclose
- // yet.
- return NS_OK;
- }
- // We call this even if there is no error.
- MaybeReportErrorAndCloseIfOpen(aStatus);
- return NS_OK;
- }
- void
- TCPSocket::SetSocketBridgeParent(TCPSocketParent* aBridgeParent)
- {
- mSocketBridgeParent = aBridgeParent;
- }
- void
- TCPSocket::SetAppIdAndBrowser(uint32_t aAppId, bool aInIsolatedMozBrowser)
- {
- /*** STUB ***/
- }
- NS_IMETHODIMP
- TCPSocket::UpdateReadyState(uint32_t aReadyState)
- {
- MOZ_ASSERT(mSocketBridgeChild);
- mReadyState = static_cast<TCPReadyState>(aReadyState);
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
- {
- if (aTrackingNumber != mTrackingNumber) {
- return NS_OK;
- }
- mBufferedAmount = aBufferedAmount;
- if (!mBufferedAmount) {
- if (mWaitingForDrain) {
- mWaitingForDrain = false;
- return FireEvent(NS_LITERAL_STRING("drain"));
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
- {
- if (!strcmp(aTopic, "inner-window-destroyed")) {
- nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
- NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
- uint64_t innerID;
- nsresult rv = wrapper->GetData(&innerID);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (innerID == mInnerWindowID) {
- Close();
- }
- } else if (!strcmp(aTopic, "profile-change-net-teardown")) {
- Close();
- }
- return NS_OK;
- }
- /* static */
- bool
- TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal)
- {
- JS::Rooted<JSObject*> global(aCx, aGlobal);
- return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
- }
|