Bug 1487100 - Allow opening the input stream for original content when alt-data is available r=michal,luke
☠☠ backed out by 856c528a0983 ☠ ☠
authorValentin Gosu <valentin.gosu@gmail.com>
Wed, 17 Oct 2018 12:27:37 +0000
changeset 500274 7f9d03c29a6ffd82c1b5e17c14e27a2ae9d64434
parent 500273 dd1c31ea78c2b15d14750d137037a54d50719997
child 500275 b77bde54527692f87c31a60112d3cb57ec13298e
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal, luke
bugs1487100
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1487100 - Allow opening the input stream for original content when alt-data is available r=michal,luke In trying to use fetch with alt-data, we sometimes want the benefit of using alt-data but the JS consumer actually needs to use the original HTTP response from the server. To get around this problem, we introduce a new API - nsICacheInfoChannel.getOriginalInputStream(nsIInputStreamReceiver) that asyncly receives the input stream containing the HTTP response in the cache entry. Depends on D8071 Differential Revision: https://phabricator.services.mozilla.com/D8072
netwerk/base/nsICacheInfoChannel.idl
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/InterceptedHttpChannel.cpp
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/test/unit/test_alt-data_simple.js
--- a/netwerk/base/nsICacheInfoChannel.idl
+++ b/netwerk/base/nsICacheInfoChannel.idl
@@ -1,24 +1,31 @@
 /* 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 "nsISupports.idl"
 
 interface nsIOutputStream;
+interface nsIInputStream;
 
 %{C++
 namespace mozilla {
 template<typename... Elements> class Tuple;
 } // namespace mozilla
 %}
 
 [ref] native ConstPreferenceArray(const nsTArray<mozilla::Tuple<nsCString, nsCString>>);
 
+[scriptable, uuid(1fb8ccf2-5fa5-45ec-bc57-8c8022a5d0d3)]
+interface nsIInputStreamReceiver : nsISupports
+{
+  void onInputStreamReady(in nsIInputStream aStream);
+};
+
 [scriptable, uuid(72c34415-c6eb-48af-851f-772fa9ee5972)]
 interface nsICacheInfoChannel : nsISupports
 {
   /**
    * Get the number of times the cache entry has been opened. This attribute is
    * equivalent to nsICachingChannel.cacheToken.fetchCount.
    *
    * @throws NS_ERROR_NOT_AVAILABLE if the cache entry or the alternate data
@@ -112,16 +119,23 @@ interface nsICacheInfoChannel : nsISuppo
    * is returning.
    * Is empty string if no alternative data representation was requested, or
    * if the requested representation wasn't found in the cache.
    * Can only be called during or after OnStartRequest.
    */
   readonly attribute ACString alternativeDataType;
 
   /**
+   * Sometimes when the channel is delivering alt-data, we may want to somehow
+   * access the original content too. This method asynchronously opens the
+   * input stream and delivers it to the receiver.
+   */
+  void getOriginalInputStream(in nsIInputStreamReceiver aReceiver);
+
+  /**
    * Opens and returns an output stream that a consumer may use to save an
    * alternate representation of the data.
    * Must be called after the OnStopRequest that delivered the real data.
    * The consumer may choose to replace the saved alt representation.
    * Opening the output stream will fail if there are any open input streams
    * reading the already saved alt representation.
    *
    * @param type
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -3336,16 +3336,46 @@ HttpChannelChild::OpenAlternativeOutputS
                                                         this)) {
     return NS_ERROR_FAILURE;
   }
 
   stream.forget(_retval);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpChannelChild::GetOriginalInputStream(nsIInputStreamReceiver *aReceiver)
+{
+  if (aReceiver == nullptr) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!mIPCOpen) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  mInputStreamReceiver = aReceiver;
+  Unused << SendOpenOriginalCacheInputStream();
+
+  return NS_OK;
+}
+
+mozilla::ipc::IPCResult
+HttpChannelChild::RecvOriginalCacheInputStreamAvailable(const OptionalIPCStream& aStream)
+{
+  nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
+  nsCOMPtr<nsIInputStreamReceiver> receiver;
+  receiver.swap(mInputStreamReceiver);
+  if (receiver) {
+    receiver->OnInputStreamReady(stream);
+  }
+
+  return IPC_OK();
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIResumableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID)
 {
   LOG(("HttpChannelChild::ResumeAt [this=%p]\n", this));
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -177,16 +177,18 @@ protected:
   mozilla::ipc::IPCResult RecvSetPriority(const int16_t& aPriority) override;
 
   mozilla::ipc::IPCResult RecvAttachStreamFilter(Endpoint<extensions::PStreamFilterParent>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult RecvCancelDiversion() override;
 
   mozilla::ipc::IPCResult RecvCancelRedirected() override;
 
+  mozilla::ipc::IPCResult RecvOriginalCacheInputStreamAvailable(const OptionalIPCStream& aStream) override;
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual void DoNotifyListenerCleanup() override;
 
   virtual void DoAsyncAbort(nsresult aStatus) override;
 
   nsresult
   AsyncCall(void (HttpChannelChild::*funcPtr)(),
@@ -322,16 +324,18 @@ private:
 
   RequestHeaderTuples mClientSetRequestHeaders;
   RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
   nsCOMPtr<nsIInputStream> mSynthesizedInput;
   nsCOMPtr<nsIInterceptedBodyCallback> mSynthesizedCallback;
   nsCOMPtr<nsICacheInfoChannel> mSynthesizedCacheInfo;
   RefPtr<ChannelEventQueue> mEventQ;
 
+  nsCOMPtr<nsIInputStreamReceiver> mInputStreamReceiver;
+
   // Used to ensure atomicity of mBgChild and mBgInitFailCallback
   Mutex mBgChildMutex;
 
   // Associated HTTP background channel
   RefPtr<HttpBackgroundChannelChild> mBgChild;
 
   // Error handling procedure if failed to establish PBackground IPC
   nsCOMPtr<nsIRunnable> mBgInitFailCallback;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1768,16 +1768,36 @@ HttpChannelParent::RecvBytesRead(const i
     MOZ_ASSERT(mSuspendedForFlowControl);
     Unused << mChannel->Resume();
     mSuspendedForFlowControl = false;
   }
   mSendWindowSize += aCount;
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+HttpChannelParent::RecvOpenOriginalCacheInputStream()
+{
+  if (mIPCClosed) {
+    return IPC_OK();
+  }
+  AutoIPCStream autoStream;
+  if (mCacheEntry) {
+    nsCOMPtr<nsIInputStream> inputStream;
+    nsresult rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
+    if (NS_SUCCEEDED(rv)) {
+      PContentParent* pcp = Manager()->Manager();
+      Unused << autoStream.Serialize(inputStream, static_cast<ContentParent*>(pcp));
+    }
+  }
+
+  Unused << SendOriginalCacheInputStreamAvailable(autoStream.TakeOptionalValue());
+  return IPC_OK();
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIProgressEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnProgress(nsIRequest *aRequest,
                               nsISupports *aContext,
                               int64_t aProgress,
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -205,16 +205,17 @@ protected:
                                          const uint64_t& offset,
                                          const uint32_t& count) override;
   virtual mozilla::ipc::IPCResult RecvDivertOnStopRequest(const nsresult& statusCode) override;
   virtual mozilla::ipc::IPCResult RecvDivertComplete() override;
   virtual mozilla::ipc::IPCResult RecvCrossProcessRedirectDone(const nsresult& aResult) override;
   virtual mozilla::ipc::IPCResult RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
                                                                     const mozilla::ipc::PrincipalInfo& requestingPrincipal) override;
   virtual mozilla::ipc::IPCResult RecvBytesRead(const int32_t& aCount) override;
+  virtual mozilla::ipc::IPCResult RecvOpenOriginalCacheInputStream() override;
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   // Supporting function for ADivertableParentChannel.
   MOZ_MUST_USE nsresult ResumeForDiversion();
 
   // Asynchronously calls NotifyDiversionFailed.
   void FailDiversion(nsresult aErrorCode);
 
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -1343,16 +1343,25 @@ InterceptedHttpChannel::OpenAlternativeO
 {
   if (mSynthesizedCacheInfo) {
     return mSynthesizedCacheInfo->OpenAlternativeOutputStream(type, predictedSize, _retval);
   }
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
+InterceptedHttpChannel::GetOriginalInputStream(nsIInputStreamReceiver *aReceiver)
+{
+  if (mSynthesizedCacheInfo) {
+    return mSynthesizedCacheInfo->GetOriginalInputStream(aReceiver);
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
 InterceptedHttpChannel::GetCacheKey(uint32_t* key)
 {
   if (mSynthesizedCacheInfo) {
     return mSynthesizedCacheInfo->GetCacheKey(key);
   }
   return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -7,16 +7,17 @@
 
 include protocol PNecko;
 include protocol PStreamFilter;
 include InputStreamParams;
 include URIParams;
 include PBackgroundSharedTypes;
 include NeckoChannelParams;
 include IPCServiceWorkerDescriptor;
+include IPCStream;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
 
 namespace mozilla {
@@ -88,16 +89,19 @@ parent:
   // to remove any matching entry from the CORS preflight cache.
   async RemoveCorsPreflightCacheEntry(URIParams uri,
                                       PrincipalInfo requestingPrincipal);
 
   // After receiving this message, the parent calls SendDeleteSelf, and makes
   // sure not to send any more messages after that.
   async DeletingChannel();
 
+  // Called to get the input stream when altData was delivered.
+  async OpenOriginalCacheInputStream();
+
   // Tell the parent the amount bytes read by child for the e10s back pressure
   // flow control
   async BytesRead(int32_t count);
 
   async __delete__();
 
 child:
   async OnStartRequest(nsresult            channelStatus,
@@ -165,16 +169,17 @@ child:
   // console.
   async LogBlockedCORSRequest(nsString message, nsCString category);
 
   async AttachStreamFilter(Endpoint<PStreamFilterParent> aEndpoint);
 
   // See ADivertableParentChannel::CancelDiversion
   async CancelDiversion();
 
+  async OriginalCacheInputStreamAvailable(OptionalIPCStream stream);
 both:
   // After receiving this message, the parent also calls
   // SendFinishInterceptedRedirect, and makes sure not to send any more messages
   // after that. When receiving this message, the child will call
   // Send__delete__() and complete the steps required to finish the redirect.
   async FinishInterceptedRedirect();
 
   async SetPriority(int16_t priority);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -8344,16 +8344,33 @@ nsHttpChannel::OpenAlternativeOutputStre
     if (NS_SUCCEEDED(rv)) {
         // Clear this metadata flag in case it exists.
         // The caller of this method may set it again.
         cacheEntry->SetMetaDataElement("alt-data-from-child", nullptr);
     }
     return rv;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::GetOriginalInputStream(nsIInputStreamReceiver *aReceiver)
+{
+    if (aReceiver == nullptr) {
+        return NS_ERROR_INVALID_ARG;
+    }
+    nsCOMPtr<nsIInputStream> inputStream;
+
+    nsCOMPtr<nsICacheEntry> cacheEntry = mCacheEntry ? mCacheEntry
+                                                     : mAltDataCacheEntry;
+    if (cacheEntry) {
+        cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
+    }
+    aReceiver->OnInputStreamReady(inputStream);
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsICachingChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::GetCacheToken(nsISupports **token)
 {
     NS_ENSURE_ARG_POINTER(token);
--- a/netwerk/test/unit/test_alt-data_simple.js
+++ b/netwerk/test/unit/test_alt-data_simple.js
@@ -147,17 +147,25 @@ function readAltContent(request, buffer)
 {
   var cc = request.QueryInterface(Ci.nsICacheInfoChannel);
 
   Assert.equal(servedNotModified, true);
   Assert.equal(cc.alternativeDataType, altContentType);
   Assert.equal(buffer, altContent);
   check_has_alt_data_in_index(true);
 
-  requestAgain();
+  cc.getOriginalInputStream({
+    onInputStreamReady: function(aInputStream) {
+      executeSoon(function() {
+        let originalData = read_stream(aInputStream, aInputStream.available());
+        Assert.equal(originalData, responseContent);
+        requestAgain();
+      });
+    }
+  });
 }
 
 function requestAgain()
 {
   shouldPassRevalidation = false;
   var chan = make_channel(URL);
   var cc = chan.QueryInterface(Ci.nsICacheInfoChannel);
   cc.preferAlternativeDataType(altContentType, "");