summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/ftp/FTPChannelChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/ftp/FTPChannelChild.cpp')
-rw-r--r--netwerk/protocol/ftp/FTPChannelChild.cpp541
1 files changed, 541 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp
new file mode 100644
index 0000000000..63edaff4cd
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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/net/NeckoChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "nsContentUtils.h"
+#include "nsFtpProtocolHandler.h"
+#include "nsIBrowserChild.h"
+#include "nsStringStream.h"
+#include "nsNetUtil.h"
+#include "base/compiler_specific.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "SerializedLoadContext.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIURIMutator.h"
+#include "nsContentSecurityManager.h"
+#include "mozilla/ScopeExit.h"
+
+using mozilla::dom::ContentChild;
+using namespace mozilla::ipc;
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+namespace net {
+
+FTPChannelChild::FTPChannelChild(nsIURI* aUri)
+ : mIPCOpen(false),
+ mEventQ(new ChannelEventQueue(static_cast<nsIFTPChannel*>(this))),
+ mCanceled(false),
+ mSuspendCount(0),
+ mIsPending(false),
+ mLastModifiedTime(0),
+ mStartPos(0),
+ mSuspendSent(false) {
+ LOG(("Creating FTPChannelChild @%p\n", this));
+ // grab a reference to the handler to ensure that it doesn't go away.
+ NS_ADDREF(gFtpHandler);
+ SetURI(aUri);
+
+ // We could support thread retargeting, but as long as we're being driven by
+ // IPDL on the main thread it doesn't buy us anything.
+ DisallowThreadRetargeting();
+}
+
+FTPChannelChild::~FTPChannelChild() {
+ LOG(("Destroying FTPChannelChild @%p\n", this));
+ gFtpHandler->Release();
+}
+
+void FTPChannelChild::AddIPDLReference() {
+ MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
+ mIPCOpen = true;
+ AddRef();
+}
+
+void FTPChannelChild::ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
+ mIPCOpen = false;
+ Release();
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild, nsBaseChannel, nsIFTPChannel,
+ nsIUploadChannel, nsIResumableChannel,
+ nsIProxiedChannel, nsIChildChannel)
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::GetLastModifiedTime(PRTime* aLastModifiedTime) {
+ *aLastModifiedTime = mLastModifiedTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetLastModifiedTime(PRTime aLastModifiedTime) {
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) {
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ mStartPos = aStartPos;
+ mEntityID = aEntityID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetEntityID(nsACString& aEntityID) {
+ aEntityID = mEntityID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) { DROP_DEAD(); }
+
+NS_IMETHODIMP FTPChannelChild::GetHttpProxyConnectResponseCode(
+ int32_t* aResponseCode) {
+ DROP_DEAD();
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetUploadStream(nsIInputStream* aStream,
+ const nsACString& aContentType,
+ int64_t aContentLength) {
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ mUploadStream = aStream;
+ // NOTE: contentLength is intentionally ignored here.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetUploadStream(nsIInputStream** aStream) {
+ NS_ENSURE_ARG_POINTER(aStream);
+ *aStream = mUploadStream;
+ NS_IF_ADDREF(*aStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::AsyncOpen(nsIStreamListener* aListener) {
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv =
+ nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
+
+ NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(
+ !static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_ARG_POINTER(listener);
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+ // Port checked in parent, but duplicate here so we can return with error
+ // immediately, as we've done since before e10s.
+ rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
+ // because in the child ipdl,
+ // a typedef URI is defined...
+ if (NS_FAILED(rv)) return rv;
+
+ mozilla::dom::BrowserChild* browserChild = nullptr;
+ nsCOMPtr<nsIBrowserChild> iBrowserChild;
+ NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
+ NS_GET_IID(nsIBrowserChild),
+ getter_AddRefs(iBrowserChild));
+ GetCallback(iBrowserChild);
+ if (iBrowserChild) {
+ browserChild =
+ static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
+ }
+
+ mListener = listener;
+
+ // add ourselves to the load group.
+ if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
+
+ mozilla::ipc::AutoIPCStream autoStream;
+ autoStream.Serialize(mUploadStream,
+ static_cast<ContentChild*>(gNeckoChild->Manager()));
+
+ uint32_t loadFlags = 0;
+ GetLoadFlags(&loadFlags);
+
+ FTPChannelOpenArgs openArgs;
+ SerializeURI(nsBaseChannel::URI(), openArgs.uri());
+ openArgs.startPos() = mStartPos;
+ openArgs.entityID() = mEntityID;
+ openArgs.uploadStream() = autoStream.TakeOptionalValue();
+ openArgs.loadFlags() = loadFlags;
+
+ nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
+ rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This must happen before the constructor message is sent.
+ SetupNeckoTarget();
+
+ gNeckoChild->SendPFTPChannelConstructor(
+ this, browserChild, IPC::SerializedLoadContext(this), openArgs);
+
+ // The socket transport layer in the chrome process now has a logical ref to
+ // us until OnStopRequest is called.
+ AddIPDLReference();
+
+ mIsPending = true;
+ mWasOpened = true;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::IsPending(bool* aResult) {
+ *aResult = mIsPending;
+ return NS_OK;
+}
+
+nsresult FTPChannelChild::OpenContentStream(bool aAsync,
+ nsIInputStream** aStream,
+ nsIChannel** aChannel) {
+ MOZ_CRASH("FTPChannel*Child* should never have OpenContentStream called!");
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::PFTPChannelChild
+//-----------------------------------------------------------------------------
+
+mozilla::ipc::IPCResult FTPChannelChild::RecvOnStartRequest(
+ const nsresult& aChannelStatus, const int64_t& aContentLength,
+ const nsCString& aContentType, const PRTime& aLastModified,
+ const nsCString& aEntityID, const URIParams& aURI) {
+ LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this));
+
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this, [self = UnsafePtr<FTPChannelChild>(this), aChannelStatus,
+ aContentLength, aContentType, aLastModified, aEntityID, aURI]() {
+ self->DoOnStartRequest(aChannelStatus, aContentLength, aContentType,
+ aLastModified, aEntityID, aURI);
+ }));
+ return IPC_OK();
+}
+
+void FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI) {
+ mDuringOnStart = true;
+ RefPtr<FTPChannelChild> self = this;
+ auto clearDuringFlag =
+ mozilla::MakeScopeExit([self] { self->mDuringOnStart = false; });
+
+ LOG(("FTPChannelChild::DoOnStartRequest [this=%p]\n", this));
+
+ if (!mCanceled && NS_SUCCEEDED(mStatus)) {
+ mStatus = aChannelStatus;
+ }
+
+ mContentLength = aContentLength;
+ SetContentType(aContentType);
+ mLastModifiedTime = aLastModified;
+ mEntityID = aEntityID;
+
+ nsCString spec;
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ nsresult rv = uri->GetSpec(spec);
+ if (NS_SUCCEEDED(rv)) {
+ // Changes nsBaseChannel::URI()
+ rv = NS_MutateURI(mURI).SetSpec(spec).Finalize(mURI);
+ if (NS_FAILED(rv)) {
+ Cancel(rv);
+ }
+ } else {
+ Cancel(rv);
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ rv = mListener->OnStartRequest(this);
+ if (NS_FAILED(rv)) Cancel(rv);
+}
+
+mozilla::ipc::IPCResult FTPChannelChild::RecvOnDataAvailable(
+ const nsresult& aChannelStatus, const nsCString& aData,
+ const uint64_t& aOffset, const uint32_t& aCount) {
+ LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this));
+
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this, [self = UnsafePtr<FTPChannelChild>(this), aChannelStatus, aData,
+ aOffset, aCount]() {
+ self->DoOnDataAvailable(aChannelStatus, aData, aOffset, aCount);
+ }));
+
+ return IPC_OK();
+}
+
+void FTPChannelChild::DoOnDataAvailable(const nsresult& aChannelStatus,
+ const nsCString& aData,
+ const uint64_t& aOffset,
+ const uint32_t& aCount) {
+ LOG(("FTPChannelChild::DoOnDataAvailable [this=%p]\n", this));
+
+ if (!mCanceled && NS_SUCCEEDED(mStatus)) {
+ mStatus = aChannelStatus;
+ }
+
+ if (mCanceled) {
+ return;
+ }
+
+ // NOTE: the OnDataAvailable contract requires the client to read all the data
+ // in the inputstream. This code relies on that ('data' will go away after
+ // this function). Apparently the previous, non-e10s behavior was to actually
+ // support only reading part of the data, allowing later calls to read the
+ // rest.
+ nsCOMPtr<nsIInputStream> stringStream;
+ nsresult rv =
+ NS_NewByteInputStream(getter_AddRefs(stringStream),
+ Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
+ if (NS_FAILED(rv)) {
+ Cancel(rv);
+ return;
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ rv = mListener->OnDataAvailable(this, stringStream, aOffset, aCount);
+ if (NS_FAILED(rv)) {
+ Cancel(rv);
+ }
+ stringStream->Close();
+}
+
+mozilla::ipc::IPCResult FTPChannelChild::RecvOnStopRequest(
+ const nsresult& aChannelStatus, const nsCString& aErrorMsg,
+ const bool& aUseUTF8) {
+ LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aChannelStatus)));
+
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this, [self = UnsafePtr<FTPChannelChild>(this), aChannelStatus, aErrorMsg,
+ aUseUTF8]() {
+ self->DoOnStopRequest(aChannelStatus, aErrorMsg, aUseUTF8);
+ }));
+ return IPC_OK();
+}
+
+void FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus,
+ const nsCString& aErrorMsg,
+ bool aUseUTF8) {
+ LOG(("FTPChannelChild::DoOnStopRequest [this=%p status=%" PRIx32 "]\n", this,
+ static_cast<uint32_t>(aChannelStatus)));
+
+ if (!mCanceled) mStatus = aChannelStatus;
+
+ { // Ensure that all queued ipdl events are dispatched before
+ // we initiate protocol deletion below.
+ mIsPending = false;
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ (void)mListener->OnStopRequest(this, aChannelStatus);
+
+ mListener = nullptr;
+
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
+ }
+ }
+
+ // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if
+ // IPDL holds the last reference. Don't rely on |this| existing after here!
+ Send__delete__(this);
+}
+
+mozilla::ipc::IPCResult FTPChannelChild::RecvFailedAsyncOpen(
+ const nsresult& aStatusCode) {
+ LOG(("FTPChannelChild::RecvFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aStatusCode)));
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this, [self = UnsafePtr<FTPChannelChild>(this), aStatusCode]() {
+ self->DoFailedAsyncOpen(aStatusCode);
+ }));
+ return IPC_OK();
+}
+
+void FTPChannelChild::DoFailedAsyncOpen(const nsresult& aStatusCode) {
+ LOG(("FTPChannelChild::DoFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(aStatusCode)));
+ mStatus = aStatusCode;
+
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
+ }
+
+ if (mListener) {
+ mListener->OnStartRequest(this);
+ mIsPending = false;
+ mListener->OnStopRequest(this, aStatusCode);
+ } else {
+ mIsPending = false;
+ }
+
+ mListener = nullptr;
+
+ if (mIPCOpen) {
+ Send__delete__(this);
+ }
+}
+
+mozilla::ipc::IPCResult FTPChannelChild::RecvDeleteSelf() {
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this,
+ [self = UnsafePtr<FTPChannelChild>(this)]() { self->DoDeleteSelf(); }));
+ return IPC_OK();
+}
+
+void FTPChannelChild::DoDeleteSelf() {
+ if (mIPCOpen) {
+ Send__delete__(this);
+ }
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Cancel(nsresult aStatus) {
+ LOG(("FTPChannelChild::Cancel [this=%p]\n", this));
+ if (mCanceled) {
+ return NS_OK;
+ }
+
+ mCanceled = true;
+ mStatus = aStatus;
+ if (mIPCOpen) {
+ SendCancel(aStatus);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Suspend() {
+ NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+
+ LOG(("FTPChannelChild::Suspend [this=%p]\n", this));
+
+ // SendSuspend only once, when suspend goes from 0 to 1.
+ if (!mSuspendCount++) {
+ SendSuspend();
+ mSuspendSent = true;
+ }
+ mEventQ->Suspend();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Resume() {
+ NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+
+ LOG(("FTPChannelChild::Resume [this=%p]\n", this));
+
+ // SendResume only once, when suspend count drops to 0.
+ if (!--mSuspendCount && mSuspendSent) {
+ SendResume();
+ }
+ mEventQ->Resume();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsIChildChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::ConnectParent(uint32_t aId) {
+ NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(
+ !static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
+ NS_ERROR_FAILURE);
+
+ LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this));
+
+ mozilla::dom::BrowserChild* browserChild = nullptr;
+ nsCOMPtr<nsIBrowserChild> iBrowserChild;
+ NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
+ NS_GET_IID(nsIBrowserChild),
+ getter_AddRefs(iBrowserChild));
+ GetCallback(iBrowserChild);
+ if (iBrowserChild) {
+ browserChild =
+ static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
+ }
+
+ // This must happen before the constructor message is sent.
+ SetupNeckoTarget();
+
+ // The socket transport in the chrome process now holds a logical ref to us
+ // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
+ AddIPDLReference();
+
+ FTPChannelConnectArgs connectArgs(aId);
+
+ if (!gNeckoChild->SendPFTPChannelConstructor(
+ this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
+ LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this));
+
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+ mIsPending = true;
+ mWasOpened = true;
+ mListener = aListener;
+
+ // add ourselves to the load group.
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+
+ // We already have an open IPDL connection to the parent. If on-modify-request
+ // listeners or load group observers canceled us, let the parent handle it
+ // and send it back to us naturally.
+ return NS_OK;
+}
+
+void FTPChannelChild::SetupNeckoTarget() {
+ if (mNeckoTarget) {
+ return;
+ }
+ nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
+ mNeckoTarget =
+ nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network);
+ if (!mNeckoTarget) {
+ return;
+ }
+
+ gNeckoChild->SetEventTargetForActor(this, mNeckoTarget);
+}
+
+} // namespace net
+} // namespace mozilla