netwerk/protocol/ftp/FTPChannelParent.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=8 et tw=80 : */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "mozilla/net/FTPChannelParent.h"
     9 #include "nsFTPChannel.h"
    10 #include "nsNetUtil.h"
    11 #include "nsFtpProtocolHandler.h"
    12 #include "mozilla/ipc/InputStreamUtils.h"
    13 #include "mozilla/ipc/URIUtils.h"
    14 #include "mozilla/unused.h"
    15 #include "SerializedLoadContext.h"
    17 using namespace mozilla::ipc;
    19 #undef LOG
    20 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
    22 namespace mozilla {
    23 namespace net {
    25 FTPChannelParent::FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatus aOverrideStatus)
    26   : mIPCClosed(false)
    27   , mLoadContext(aLoadContext)
    28   , mPBOverride(aOverrideStatus)
    29   , mStatus(NS_OK)
    30   , mDivertingFromChild(false)
    31   , mDivertedOnStartRequest(false)
    32   , mSuspendedForDiversion(false)
    33 {
    34   nsIProtocolHandler* handler;
    35   CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
    36   NS_ASSERTION(handler, "no ftp handler");
    37 }
    39 FTPChannelParent::~FTPChannelParent()
    40 {
    41   gFtpHandler->Release();
    42 }
    44 void
    45 FTPChannelParent::ActorDestroy(ActorDestroyReason why)
    46 {
    47   // We may still have refcount>0 if the channel hasn't called OnStopRequest
    48   // yet, but we must not send any more msgs to child.
    49   mIPCClosed = true;
    50 }
    52 //-----------------------------------------------------------------------------
    53 // FTPChannelParent::nsISupports
    54 //-----------------------------------------------------------------------------
    56 NS_IMPL_ISUPPORTS(FTPChannelParent,
    57                   nsIStreamListener,
    58                   nsIParentChannel,
    59                   nsIInterfaceRequestor,
    60                   nsIRequestObserver)
    62 //-----------------------------------------------------------------------------
    63 // FTPChannelParent::PFTPChannelParent
    64 //-----------------------------------------------------------------------------
    66 //-----------------------------------------------------------------------------
    67 // FTPChannelParent methods
    68 //-----------------------------------------------------------------------------
    70 bool
    71 FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
    72 {
    73   switch (aArgs.type()) {
    74   case FTPChannelCreationArgs::TFTPChannelOpenArgs:
    75   {
    76     const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
    77     return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream());
    78   }
    79   case FTPChannelCreationArgs::TFTPChannelConnectArgs:
    80   {
    81     const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
    82     return ConnectChannel(cArgs.channelId());
    83   }
    84   default:
    85     NS_NOTREACHED("unknown open type");
    86     return false;
    87   }
    88 }
    90 bool
    91 FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
    92                               const uint64_t& aStartPos,
    93                               const nsCString& aEntityID,
    94                               const OptionalInputStreamParams& aUploadStream)
    95 {
    96   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
    97   if (!uri)
    98       return false;
   100 #ifdef DEBUG
   101   nsCString uriSpec;
   102   uri->GetSpec(uriSpec);
   103   LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
   104        this, uriSpec.get()));
   105 #endif
   107   nsresult rv;
   108   nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
   109   if (NS_FAILED(rv))
   110     return SendFailedAsyncOpen(rv);
   112   nsCOMPtr<nsIChannel> chan;
   113   rv = NS_NewChannel(getter_AddRefs(chan), uri, ios);
   114   if (NS_FAILED(rv))
   115     return SendFailedAsyncOpen(rv);
   117   mChannel = static_cast<nsFtpChannel*>(chan.get());
   119   if (mPBOverride != kPBOverride_Unset) {
   120     mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   121   }
   123   rv = mChannel->SetNotificationCallbacks(this);
   124   if (NS_FAILED(rv))
   125     return SendFailedAsyncOpen(rv);
   127   nsTArray<mozilla::ipc::FileDescriptor> fds;
   128   nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
   129   if (upload) {
   130     // contentType and contentLength are ignored
   131     rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
   132     if (NS_FAILED(rv))
   133       return SendFailedAsyncOpen(rv);
   134   }
   136   rv = mChannel->ResumeAt(aStartPos, aEntityID);
   137   if (NS_FAILED(rv))
   138     return SendFailedAsyncOpen(rv);
   140   rv = mChannel->AsyncOpen(this, nullptr);
   141   if (NS_FAILED(rv))
   142     return SendFailedAsyncOpen(rv);
   144   return true;
   145 }
   147 bool
   148 FTPChannelParent::ConnectChannel(const uint32_t& channelId)
   149 {
   150   nsresult rv;
   152   LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
   154   nsCOMPtr<nsIChannel> channel;
   155   rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
   156   if (NS_SUCCEEDED(rv))
   157     mChannel = static_cast<nsFtpChannel*>(channel.get());
   159   LOG(("  found channel %p, rv=%08x", mChannel.get(), rv));
   161   return true;
   162 }
   164 bool
   165 FTPChannelParent::RecvCancel(const nsresult& status)
   166 {
   167   if (mChannel)
   168     mChannel->Cancel(status);
   169   return true;
   170 }
   172 bool
   173 FTPChannelParent::RecvSuspend()
   174 {
   175   if (mChannel)
   176     mChannel->Suspend();
   177   return true;
   178 }
   180 bool
   181 FTPChannelParent::RecvResume()
   182 {
   183   if (mChannel)
   184     mChannel->Resume();
   185   return true;
   186 }
   188 bool
   189 FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
   190                                             const uint64_t& offset,
   191                                             const uint32_t& count)
   192 {
   193   if (NS_WARN_IF(!mDivertingFromChild)) {
   194     MOZ_ASSERT(mDivertingFromChild,
   195                "Cannot RecvDivertOnDataAvailable if diverting is not set!");
   196     FailDiversion(NS_ERROR_UNEXPECTED);
   197     return false;
   198   }
   200   // Drop OnDataAvailables if the parent was canceled already.
   201   if (NS_FAILED(mStatus)) {
   202     return true;
   203   }
   205   nsCOMPtr<nsIInputStream> stringStream;
   206   nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
   207                                       count, NS_ASSIGNMENT_DEPEND);
   208   if (NS_FAILED(rv)) {
   209     if (mChannel) {
   210       mChannel->Cancel(rv);
   211     }
   212     mStatus = rv;
   213     return true;
   214   }
   216   rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
   218   stringStream->Close();
   219   if (NS_FAILED(rv)) {
   220     if (mChannel) {
   221       mChannel->Cancel(rv);
   222     }
   223     mStatus = rv;
   224   }
   225   return true;
   226 }
   228 bool
   229 FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
   230 {
   231   if (NS_WARN_IF(!mDivertingFromChild)) {
   232     MOZ_ASSERT(mDivertingFromChild,
   233                "Cannot RecvDivertOnStopRequest if diverting is not set!");
   234     FailDiversion(NS_ERROR_UNEXPECTED);
   235     return false;
   236   }
   238   // Honor the channel's status even if the underlying transaction completed.
   239   nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
   241   // Reset fake pending status in case OnStopRequest has already been called.
   242   if (mChannel) {
   243     mChannel->ForcePending(false);
   244   }
   246   OnStopRequest(mChannel, nullptr, status);
   247   return true;
   248 }
   250 bool
   251 FTPChannelParent::RecvDivertComplete()
   252 {
   253   if (NS_WARN_IF(!mDivertingFromChild)) {
   254     MOZ_ASSERT(mDivertingFromChild,
   255                "Cannot RecvDivertComplete if diverting is not set!");
   256     FailDiversion(NS_ERROR_UNEXPECTED);
   257     return false;
   258   }
   260   nsresult rv = ResumeForDiversion();
   261   if (NS_WARN_IF(NS_FAILED(rv))) {
   262     FailDiversion(NS_ERROR_UNEXPECTED);
   263     return false;
   264   }
   266   return true;
   267 }
   269 //-----------------------------------------------------------------------------
   270 // FTPChannelParent::nsIRequestObserver
   271 //-----------------------------------------------------------------------------
   273 NS_IMETHODIMP
   274 FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
   275 {
   276   LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
   278   if (mDivertingFromChild) {
   279     MOZ_RELEASE_ASSERT(mDivertToListener,
   280                        "Cannot divert if listener is unset!");
   281     return mDivertToListener->OnStartRequest(aRequest, aContext);
   282   }
   284   nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
   285   MOZ_ASSERT(chan);
   286   NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
   288   int64_t contentLength;
   289   chan->GetContentLength(&contentLength);
   290   nsCString contentType;
   291   chan->GetContentType(contentType);
   293   nsCString entityID;
   294   nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
   295   MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
   296   if (resChan) {
   297     resChan->GetEntityID(entityID);
   298   }
   300   nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
   301   PRTime lastModified = 0;
   302   if (ftpChan) {
   303     ftpChan->GetLastModifiedTime(&lastModified);
   304   } else {
   305     // Temporary hack: if we were redirected to use an HTTP channel (ie FTP is
   306     // using an HTTP proxy), cancel, as we don't support those redirects yet.
   307     aRequest->Cancel(NS_ERROR_NOT_IMPLEMENTED);
   308   }
   310   URIParams uriparam;
   311   nsCOMPtr<nsIURI> uri;
   312   chan->GetURI(getter_AddRefs(uri));
   313   SerializeURI(uri, uriparam);
   315   if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
   316                                         lastModified, entityID, uriparam)) {
   317     return NS_ERROR_UNEXPECTED;
   318   }
   320   return NS_OK;
   321 }
   323 NS_IMETHODIMP
   324 FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
   325                                 nsISupports* aContext,
   326                                 nsresult aStatusCode)
   327 {
   328   LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%ul]\n",
   329        this, aStatusCode));
   331   if (mDivertingFromChild) {
   332     MOZ_RELEASE_ASSERT(mDivertToListener,
   333                        "Cannot divert if listener is unset!");
   334     return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
   335   }
   337   if (mIPCClosed || !SendOnStopRequest(aStatusCode)) {
   338     return NS_ERROR_UNEXPECTED;
   339   }
   341   return NS_OK;
   342 }
   344 //-----------------------------------------------------------------------------
   345 // FTPChannelParent::nsIStreamListener
   346 //-----------------------------------------------------------------------------
   348 NS_IMETHODIMP
   349 FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
   350                                   nsISupports* aContext,
   351                                   nsIInputStream* aInputStream,
   352                                   uint64_t aOffset,
   353                                   uint32_t aCount)
   354 {
   355   LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
   357   if (mDivertingFromChild) {
   358     MOZ_RELEASE_ASSERT(mDivertToListener,
   359                        "Cannot divert if listener is unset!");
   360     return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
   361                                               aOffset, aCount);
   362   }
   364   nsCString data;
   365   nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
   366   if (NS_FAILED(rv))
   367     return rv;
   369   if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
   370     return NS_ERROR_UNEXPECTED;
   372   return NS_OK;
   373 }
   375 //-----------------------------------------------------------------------------
   376 // FTPChannelParent::nsIParentChannel
   377 //-----------------------------------------------------------------------------
   379 NS_IMETHODIMP
   380 FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
   381 {
   382   // Do not need ptr to HttpChannelParentListener.
   383   return NS_OK;
   384 }
   386 NS_IMETHODIMP
   387 FTPChannelParent::Delete()
   388 {
   389   if (mIPCClosed || !SendDeleteSelf())
   390     return NS_ERROR_UNEXPECTED;
   392   return NS_OK;
   393 }
   395 //-----------------------------------------------------------------------------
   396 // FTPChannelParent::nsIInterfaceRequestor
   397 //-----------------------------------------------------------------------------
   399 NS_IMETHODIMP
   400 FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
   401 {
   402   // Only support nsILoadContext if child channel's callbacks did too
   403   if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
   404     NS_ADDREF(mLoadContext);
   405     *result = static_cast<nsILoadContext*>(mLoadContext);
   406     return NS_OK;
   407   }
   409   return QueryInterface(uuid, result);
   410 }
   412 //-----------------------------------------------------------------------------
   413 // FTPChannelParent::ADivertableParentChannel
   414 //-----------------------------------------------------------------------------
   415 nsresult
   416 FTPChannelParent::SuspendForDiversion()
   417 {
   418   MOZ_ASSERT(mChannel);
   419   if (NS_WARN_IF(mDivertingFromChild)) {
   420     MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
   421     return NS_ERROR_UNEXPECTED;
   422   }
   424   // Try suspending the channel. Allow it to fail, since OnStopRequest may have
   425   // been called and thus the channel may not be pending.
   426   nsresult rv = mChannel->Suspend();
   427   MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
   428   mSuspendedForDiversion = NS_SUCCEEDED(rv);
   430   // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
   431   // to the child.
   432   mDivertingFromChild = true;
   434   return NS_OK;
   435 }
   437 /* private, supporting function for ADivertableParentChannel */
   438 nsresult
   439 FTPChannelParent::ResumeForDiversion()
   440 {
   441   MOZ_ASSERT(mChannel);
   442   MOZ_ASSERT(mDivertToListener);
   443   if (NS_WARN_IF(!mDivertingFromChild)) {
   444     MOZ_ASSERT(mDivertingFromChild,
   445                "Cannot ResumeForDiversion if not diverting!");
   446     return NS_ERROR_UNEXPECTED;
   447   }
   449   if (mSuspendedForDiversion) {
   450     nsresult rv = mChannel->Resume();
   451     if (NS_WARN_IF(NS_FAILED(rv))) {
   452       FailDiversion(NS_ERROR_UNEXPECTED, true);
   453       return rv;
   454     }
   455     mSuspendedForDiversion = false;
   456   }
   458   // Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
   459   // keep us alive if there's more data to be delivered to listener.
   460   if (NS_WARN_IF(NS_FAILED(Delete()))) {
   461     FailDiversion(NS_ERROR_UNEXPECTED);
   462     return NS_ERROR_UNEXPECTED;   
   463   }
   464   return NS_OK;
   465 }
   467 void
   468 FTPChannelParent::DivertTo(nsIStreamListener *aListener)
   469 {
   470   MOZ_ASSERT(aListener);
   471   if (NS_WARN_IF(!mDivertingFromChild)) {
   472     MOZ_ASSERT(mDivertingFromChild,
   473                "Cannot DivertTo new listener if diverting is not set!");
   474     return;
   475   }
   477   if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
   478     FailDiversion(NS_ERROR_UNEXPECTED);
   479     return;
   480   }
   482   mDivertToListener = aListener;
   484   // Call OnStartRequest and SendDivertMessages asynchronously to avoid
   485   // reentering client context.
   486   NS_DispatchToCurrentThread(
   487     NS_NewRunnableMethod(this, &FTPChannelParent::StartDiversion));
   488   return;
   489 }
   491 void
   492 FTPChannelParent::StartDiversion()
   493 {
   494   if (NS_WARN_IF(!mDivertingFromChild)) {
   495     MOZ_ASSERT(mDivertingFromChild,
   496                "Cannot StartDiversion if diverting is not set!");
   497     return;
   498   }
   500   // Fake pending status in case OnStopRequest has already been called.
   501   if (mChannel) {
   502     mChannel->ForcePending(true);
   503   }
   505   // Call OnStartRequest for the "DivertTo" listener.
   506   nsresult rv = OnStartRequest(mChannel, nullptr);
   507   if (NS_FAILED(rv)) {
   508     if (mChannel) {
   509       mChannel->Cancel(rv);
   510     }
   511     mStatus = rv;
   512     return;
   513   }
   515   // After OnStartRequest has been called, tell FTPChannelChild to divert the
   516   // OnDataAvailables and OnStopRequest to this FTPChannelParent.
   517   if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
   518     FailDiversion(NS_ERROR_UNEXPECTED);
   519     return;
   520   }
   521 }
   523 class FTPFailDiversionEvent : public nsRunnable
   524 {
   525 public:
   526   FTPFailDiversionEvent(FTPChannelParent *aChannelParent,
   527                         nsresult aErrorCode,
   528                         bool aSkipResume)
   529     : mChannelParent(aChannelParent)
   530     , mErrorCode(aErrorCode)
   531     , mSkipResume(aSkipResume)
   532   {
   533     MOZ_RELEASE_ASSERT(aChannelParent);
   534     MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
   535   }
   536   NS_IMETHOD Run()
   537   {
   538     mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
   539     return NS_OK;
   540   }
   541 private:
   542   nsRefPtr<FTPChannelParent> mChannelParent;
   543   nsresult mErrorCode;
   544   bool mSkipResume;
   545 };
   547 void
   548 FTPChannelParent::FailDiversion(nsresult aErrorCode,
   549                                             bool aSkipResume)
   550 {
   551   MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
   552   MOZ_RELEASE_ASSERT(mDivertingFromChild);
   553   MOZ_RELEASE_ASSERT(mDivertToListener);
   554   MOZ_RELEASE_ASSERT(mChannel);
   556   NS_DispatchToCurrentThread(
   557     new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
   558 }
   560 void
   561 FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
   562                                         bool aSkipResume)
   563 {
   564   MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
   565   MOZ_RELEASE_ASSERT(mDivertingFromChild);
   566   MOZ_RELEASE_ASSERT(mDivertToListener);
   567   MOZ_RELEASE_ASSERT(mChannel);
   569   mChannel->Cancel(aErrorCode);
   571   mChannel->ForcePending(false);
   573   bool isPending = false;
   574   nsresult rv = mChannel->IsPending(&isPending);
   575   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   577   // Resume only we suspended earlier.
   578   if (mSuspendedForDiversion) {
   579     mChannel->Resume();
   580   }
   581   // Channel has already sent OnStartRequest to the child, so ensure that we
   582   // call it here if it hasn't already been called.
   583   if (!mDivertedOnStartRequest) {
   584     mChannel->ForcePending(true);
   585     mDivertToListener->OnStartRequest(mChannel, nullptr);
   586     mChannel->ForcePending(false);
   587   }
   588   // If the channel is pending, it will call OnStopRequest itself; otherwise, do
   589   // it here.
   590   if (!isPending) {
   591     mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
   592   }
   593   mDivertToListener = nullptr;
   594   mChannel = nullptr;
   596   if (!mIPCClosed) {
   597     unused << SendDeleteSelf();
   598   }
   599 }
   601 //---------------------
   602 } // namespace net
   603 } // namespace mozilla

mercurial