Bug 1397627 - Fetch API and other components should pass the length of the stream to necko when known, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 08 Sep 2017 16:06:26 +0200
changeset 429259 6933c6396bda51a32059f7bbd24349ff9cda2eaf
parent 429258 c08a59cb75a34422a3fdeb287cbfa954df117be8
child 429260 523bfd08926983194d46be8c8886114f513b9727
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1397627
milestone57.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 1397627 - Fetch API and other components should pass the length of the stream to necko when known, r=smaug
dom/cache/TypeUtils.cpp
dom/fetch/FetchDriver.cpp
dom/fetch/InternalRequest.cpp
dom/fetch/InternalRequest.h
dom/fetch/InternalResponse.h
dom/fetch/Request.cpp
dom/fetch/Request.h
dom/fetch/Response.h
dom/file/ipc/IPCBlobInputStream.cpp
dom/flyweb/HttpServer.cpp
dom/workers/ServiceWorkerPrivate.cpp
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -338,17 +338,17 @@ TypeUtils::ToInternalRequest(const Cache
   internalRequest->Headers()->Fill(*internalHeaders, result);
   MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
 
   internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
   MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
 
-  internalRequest->SetBody(stream);
+  internalRequest->SetBody(stream, -1);
 
   return internalRequest.forget();
 }
 
 already_AddRefed<Request>
 TypeUtils::ToRequest(const CacheRequest& aIn)
 {
   RefPtr<InternalRequest> internalRequest = ToInternalRequest(aIn);
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -364,22 +364,25 @@ FetchDriver::HttpFetch()
     // void string if no header was set.
 #ifdef DEBUG
     bool hasContentTypeHeader =
       mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result);
     MOZ_ASSERT(!result.Failed());
     MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
 #endif // DEBUG
 
+    int64_t bodyLength;
     nsCOMPtr<nsIInputStream> bodyStream;
-    mRequest->GetBody(getter_AddRefs(bodyStream));
+    mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
     if (bodyStream) {
       nsAutoCString method;
       mRequest->GetMethod(method);
-      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
+      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
+                                               bodyLength, method,
+                                               false /* aStreamHasHeaders */);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // If preflight is required, start a "CORS preflight fetch"
   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
   // implementation is handled by the http channel calling into
   // nsCORSListenerProxy. We just inform it which unsafe headers are included
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -25,16 +25,17 @@ InternalRequest::GetRequestConstructorCo
 {
   MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty when copied from constructor.");
   RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement(),
                                                      mFragment);
   copy->SetMethod(mMethod);
   copy->mHeaders = new InternalHeaders(*mHeaders);
   copy->SetUnsafeRequest();
   copy->mBodyStream = mBodyStream;
+  copy->mBodyLength = mBodyLength;
   copy->mForceOriginHeader = true;
   // The "client" is not stored in our implementation. Fetch API users should
   // use the appropriate window/document/principal and other Gecko security
   // mechanisms as appropriate.
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
   copy->mReferrer = mReferrer;
   copy->mReferrerPolicy = mReferrerPolicy;
@@ -74,16 +75,17 @@ InternalRequest::Clone()
     mBodyStream.swap(replacementBody);
   }
   return clone.forget();
 }
 InternalRequest::InternalRequest(const nsACString& aURL,
                                  const nsACString& aFragment)
   : mMethod("GET")
   , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
+  , mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE)
   , mContentPolicyType(nsIContentPolicy::TYPE_FETCH)
   , mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
   , mReferrerPolicy(ReferrerPolicy::_empty)
   , mEnvironmentReferrerPolicy(net::RP_Unset)
   , mMode(RequestMode::No_cors)
   , mCredentialsMode(RequestCredentials::Omit)
   , mResponseTainting(LoadTainting::Basic)
   , mCacheMode(RequestCache::Default)
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -457,30 +457,35 @@ public:
 
   void
   UnsetSameOriginDataURL()
   {
     mSameOriginDataURL = false;
   }
 
   void
-  SetBody(nsIInputStream* aStream)
+  SetBody(nsIInputStream* aStream, int64_t aBodyLength)
   {
     // A request's body may not be reset once set.
     MOZ_ASSERT_IF(aStream, !mBodyStream);
     mBodyStream = aStream;
+    mBodyLength = aBodyLength;
   }
 
   // Will return the original stream!
   // Use a tee or copy if you don't want to erase the original.
   void
-  GetBody(nsIInputStream** aStream)
+  GetBody(nsIInputStream** aStream, int64_t* aBodyLength = nullptr)
   {
     nsCOMPtr<nsIInputStream> s = mBodyStream;
     s.forget(aStream);
+
+    if (aBodyLength) {
+      *aBodyLength = mBodyLength;
+    }
   }
 
   // The global is used as the client for the new object.
   already_AddRefed<InternalRequest>
   GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const;
 
   bool
   WasCreatedByFetchEvent() const
@@ -549,16 +554,17 @@ private:
   static bool
   IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType);
 
   nsCString mMethod;
   // mURLList: a list of one or more fetch URLs
   nsTArray<nsCString> mURLList;
   RefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBodyStream;
+  int64_t mBodyLength;
 
   nsContentPolicyType mContentPolicyType;
 
   // Empty string: no-referrer
   // "about:client": client (default)
   // URL: an URL
   nsString mReferrer;
   ReferrerPolicy mReferrerPolicy;
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -207,17 +207,17 @@ public:
         Type() == ResponseType::Opaqueredirect) {
       *aStream = nullptr;
       if (aBodySize) {
         *aBodySize = UNKNOWN_BODY_SIZE;
       }
       return;
     }
 
-    return GetUnfilteredBody(aStream, aBodySize);
+    GetUnfilteredBody(aStream, aBodySize);
   }
 
   void
   SetBody(nsIInputStream* aBody, int64_t aBodySize)
   {
     if (mWrappedResponse) {
       return mWrappedResponse->SetBody(aBody, aBodySize);
     }
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -575,21 +575,21 @@ Request::Constructor(const GlobalObject&
   }
 
   if (aInit.mBody.WasPassed()) {
     const Nullable<fetch::OwningBodyInit>& bodyInitNullable = aInit.mBody.Value();
     if (!bodyInitNullable.IsNull()) {
       const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
       nsCOMPtr<nsIInputStream> stream;
       nsAutoCString contentTypeWithCharset;
-      uint64_t contentLengthUnused;
+      uint64_t contentLength = 0;
       aRv = ExtractByteStreamFromBody(bodyInit,
                                       getter_AddRefs(stream),
                                       contentTypeWithCharset,
-                                      contentLengthUnused);
+                                      contentLength);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
 
       nsCOMPtr<nsIInputStream> temporaryBody = stream;
 
       if (!contentTypeWithCharset.IsVoid() &&
           !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
@@ -599,32 +599,32 @@ Request::Constructor(const GlobalObject&
 
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
 
       request->ClearCreatedByFetchEvent();
 
       if (hasCopiedBody) {
-        request->SetBody(nullptr);
+        request->SetBody(nullptr, 0);
       }
 
-      request->SetBody(temporaryBody);
+      request->SetBody(temporaryBody, contentLength);
     }
   }
 
   RefPtr<Request> domRequest = new Request(global, request, signal);
   domRequest->SetMimeType();
 
   if (aInput.IsRequest()) {
     RefPtr<Request> inputReq = &aInput.GetAsRequest();
     nsCOMPtr<nsIInputStream> body;
     inputReq->GetBody(getter_AddRefs(body));
     if (body) {
-      inputReq->SetBody(nullptr);
+      inputReq->SetBody(nullptr, 0);
       inputReq->SetBodyUsed(aGlobal.Context(), aRv);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
     }
   }
   return domRequest.forget();
 }
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -121,23 +121,29 @@ public:
   InternalHeaders*
   GetInternalHeaders() const
   {
     return mRequest->Headers();
   }
 
   Headers* Headers_();
 
-  void
-  GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
-
   using FetchBody::GetBody;
 
   void
-  SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
+  GetBody(nsIInputStream** aStream, int64_t* aBodyLength = nullptr)
+  {
+    mRequest->GetBody(aStream, aBodyLength);
+  }
+
+  void
+  SetBody(nsIInputStream* aStream, int64_t aBodyLength)
+  {
+    mRequest->SetBody(aStream, aBodyLength);
+  }
 
   static already_AddRefed<Request>
   Constructor(const GlobalObject& aGlobal, const RequestOrUSVString& aInput,
               const RequestInit& aInit, ErrorResult& rv);
 
   nsIGlobalObject* GetParentObject() const
   {
     return mOwner;
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -100,17 +100,20 @@ public:
   GetPrincipalInfo() const
   {
     return mInternalResponse->GetPrincipalInfo();
   }
 
   Headers* Headers_();
 
   void
-  GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
+  GetBody(nsIInputStream** aStream, int64_t* aBodyLength = nullptr)
+  {
+    mInternalResponse->GetBody(aStream, aBodyLength);
+  }
 
   using FetchBody::GetBody;
 
   static already_AddRefed<Response>
   Error(const GlobalObject& aGlobal);
 
   static already_AddRefed<Response>
   Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus, ErrorResult& aRv);
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -153,16 +153,24 @@ IPCBlobInputStream::Available(uint64_t* 
   if (mState == eInit || mState == ePending) {
     *aLength = mActor->Size();
     return NS_OK;
   }
 
   if (mState == eRunning) {
     MOZ_ASSERT(mRemoteStream);
 
+    // This will go away eventually: an async input stream can return 0 in
+    // Available(), but this is not currently fully supported in the rest of
+    // gecko.
+    if (!mAsyncRemoteStream) {
+      *aLength = mActor->Size();
+      return NS_OK;
+    }
+
     nsresult rv = EnsureAsyncRemoteStream();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(mAsyncRemoteStream);
     return mAsyncRemoteStream->Available(aLength);
   }
--- a/dom/flyweb/HttpServer.cpp
+++ b/dom/flyweb/HttpServer.cpp
@@ -682,17 +682,17 @@ HttpServer::Connection::ConsumeLine(cons
                                getter_AddRefs(output),
                                0,          // Segment size
                                UINT32_MAX, // Unlimited buffer size
                                false,      // not nonBlockingInput
                                true);      // nonBlockingOutput
       NS_ENSURE_SUCCESS(rv, rv);
 
       mCurrentRequestBody = do_QueryInterface(output);
-      mPendingReq->SetBody(input);
+      mPendingReq->SetBody(input, -1);
     } else {
       LOG_V("HttpServer::Connection::ConsumeLine(%p) - No body", this);
       mState = eRequestLine;
     }
 
     mPendingRequests.AppendElement(PendingRequest(mPendingReq, nullptr));
 
     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Fire OnRequest", this);
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1598,17 +1598,17 @@ private:
                                                               mCacheMode,
                                                               mRequestMode,
                                                               mRequestRedirect,
                                                               mRequestCredentials,
                                                               NS_ConvertUTF8toUTF16(mReferrer),
                                                               mReferrerPolicy,
                                                               mContentPolicyType,
                                                               mIntegrity);
-    internalReq->SetBody(mUploadStream);
+    internalReq->SetBody(mUploadStream, -1);
     // For Telemetry, note that this Request object was created by a Fetch event.
     internalReq->SetCreatedByFetchEvent();
 
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(globalObj.GetAsSupports());
     if (NS_WARN_IF(!global)) {
       return false;
     }