Bug 1871378 - update Fetch algorithm to restrict Fetch body Size for keepalive request. r=necko-reviewers,edenchuang,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D214245
This commit is contained in:
@@ -46,6 +46,7 @@
|
|||||||
|
|
||||||
#include "BodyExtractor.h"
|
#include "BodyExtractor.h"
|
||||||
#include "FetchChild.h"
|
#include "FetchChild.h"
|
||||||
|
#include "FetchUtil.h"
|
||||||
#include "FetchObserver.h"
|
#include "FetchObserver.h"
|
||||||
#include "InternalRequest.h"
|
#include "InternalRequest.h"
|
||||||
#include "InternalResponse.h"
|
#include "InternalResponse.h"
|
||||||
@@ -603,6 +604,28 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
|
|||||||
// keepalive is set to true, route the request through PFetch
|
// keepalive is set to true, route the request through PFetch
|
||||||
// We plan to route all main-thread fetch request through PFetch.
|
// We plan to route all main-thread fetch request through PFetch.
|
||||||
// See Bug 1897129.
|
// See Bug 1897129.
|
||||||
|
|
||||||
|
uint64_t bodyLength =
|
||||||
|
internalRequest->BodyLength() > 0 ? internalRequest->BodyLength() : 0;
|
||||||
|
|
||||||
|
nsCOMPtr<nsILoadGroup> loadGroup =
|
||||||
|
FetchUtil::GetLoadGroupFromGlobal(aGlobal);
|
||||||
|
|
||||||
|
if (loadGroup && !FetchUtil::IncrementPendingKeepaliveRequestSize(
|
||||||
|
loadGroup, bodyLength)) {
|
||||||
|
p->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
|
||||||
|
return p.forget();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!loadGroup) {
|
||||||
|
// if there is no load group for this request ensure that the request
|
||||||
|
// size does not exceed FETCH_KEEPALIVE_MAX_SIZE
|
||||||
|
if (bodyLength > FETCH_KEEPALIVE_MAX_SIZE) {
|
||||||
|
p->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<FetchChild> actor =
|
RefPtr<FetchChild> actor =
|
||||||
FetchChild::CreateForMainThread(p, signalImpl, observer);
|
FetchChild::CreateForMainThread(p, signalImpl, observer);
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "FetchChild.h"
|
#include "FetchChild.h"
|
||||||
#include "FetchLog.h"
|
#include "FetchLog.h"
|
||||||
#include "FetchObserver.h"
|
#include "FetchObserver.h"
|
||||||
|
#include "FetchUtil.h"
|
||||||
#include "InternalResponse.h"
|
#include "InternalResponse.h"
|
||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
#include "Response.h"
|
#include "Response.h"
|
||||||
@@ -228,6 +229,7 @@ RefPtr<FetchChild> FetchChild::CreateForWorker(
|
|||||||
RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) {
|
RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
|
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
|
||||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
FETCH_LOG(("FetchChild::CreateForWorker [%p]", aWorkerPrivate));
|
||||||
|
|
||||||
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
|
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
|
||||||
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
|
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
|
||||||
@@ -254,6 +256,8 @@ RefPtr<FetchChild> FetchChild::CreateForMainThread(
|
|||||||
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
|
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
|
||||||
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
|
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
|
||||||
actor->mIsKeepAliveRequest = true;
|
actor->mIsKeepAliveRequest = true;
|
||||||
|
FETCH_LOG(("FetchChild::CreateForMainThread actor[%p]", actor.get()));
|
||||||
|
|
||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +402,12 @@ void FetchChild::RunAbortAlgorithm() {
|
|||||||
|
|
||||||
void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) {
|
void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) {
|
||||||
FETCH_LOG(("FetchChild::DoFetchOp [%p]", this));
|
FETCH_LOG(("FetchChild::DoFetchOp [%p]", this));
|
||||||
|
// we need to store this for keepalive request
|
||||||
|
// as we need to update the load group during actor termination
|
||||||
|
if (mIsKeepAliveRequest) {
|
||||||
|
mKeepaliveRequestSize =
|
||||||
|
aArgs.request().bodySize() > 0 ? aArgs.request().bodySize() : 0;
|
||||||
|
}
|
||||||
if (mSignalImpl) {
|
if (mSignalImpl) {
|
||||||
if (mSignalImpl->Aborted()) {
|
if (mSignalImpl->Aborted()) {
|
||||||
Unused << SendAbortFetchOp();
|
Unused << SendAbortFetchOp();
|
||||||
@@ -446,6 +456,22 @@ void FetchChild::Shutdown() {
|
|||||||
|
|
||||||
void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
|
void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
|
||||||
FETCH_LOG(("FetchChild::ActorDestroy [%p]", this));
|
FETCH_LOG(("FetchChild::ActorDestroy [%p]", this));
|
||||||
|
// for keepalive request decrement the pending keepalive count
|
||||||
|
if (mIsKeepAliveRequest) {
|
||||||
|
// we only support keepalive for main thread fetch requests
|
||||||
|
// See Bug 1901759
|
||||||
|
// For workers we need to dispatch a runnable to the main thread for
|
||||||
|
// updating the loadgroup
|
||||||
|
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(mPromise->GetGlobalObject());
|
||||||
|
nsCOMPtr<nsILoadGroup> loadGroup =
|
||||||
|
FetchUtil::GetLoadGroupFromGlobal(mPromise->GetGlobalObject());
|
||||||
|
if (loadGroup) {
|
||||||
|
FetchUtil::DecrementPendingKeepaliveRequestSize(loadGroup,
|
||||||
|
mKeepaliveRequestSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
mPromise = nullptr;
|
mPromise = nullptr;
|
||||||
mFetchObserver = nullptr;
|
mFetchObserver = nullptr;
|
||||||
mSignalImpl = nullptr;
|
mSignalImpl = nullptr;
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ class FetchChild final : public PFetchChild, public AbortFollower {
|
|||||||
|
|
||||||
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
||||||
bool mIsKeepAliveRequest{false};
|
bool mIsKeepAliveRequest{false};
|
||||||
|
uint64_t mKeepaliveRequestSize{0};
|
||||||
RefPtr<Promise> mPromise;
|
RefPtr<Promise> mPromise;
|
||||||
RefPtr<AbortSignalImpl> mSignalImpl;
|
RefPtr<AbortSignalImpl> mSignalImpl;
|
||||||
RefPtr<FetchObserver> mFetchObserver;
|
RefPtr<FetchObserver> mFetchObserver;
|
||||||
|
|||||||
@@ -76,6 +76,52 @@ static bool PushOverLine(nsACString::const_iterator& aStart,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool FetchUtil::IncrementPendingKeepaliveRequestSize(
|
||||||
|
nsILoadGroup* aLoadGroup, const uint64_t aBodyLength) {
|
||||||
|
uint64_t pendingKeepaliveRequestSize = 0;
|
||||||
|
MOZ_ASSERT(aLoadGroup);
|
||||||
|
|
||||||
|
aLoadGroup->GetTotalKeepAliveBytes(&pendingKeepaliveRequestSize);
|
||||||
|
pendingKeepaliveRequestSize += aBodyLength;
|
||||||
|
|
||||||
|
if (pendingKeepaliveRequestSize > FETCH_KEEPALIVE_MAX_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aLoadGroup->SetTotalKeepAliveBytes(pendingKeepaliveRequestSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FetchUtil::DecrementPendingKeepaliveRequestSize(
|
||||||
|
nsILoadGroup* aLoadGroup, const uint64_t aBodyLength) {
|
||||||
|
MOZ_ASSERT(aLoadGroup);
|
||||||
|
|
||||||
|
uint64_t pendingKeepaliveRequestSize = 0;
|
||||||
|
aLoadGroup->GetTotalKeepAliveBytes(&pendingKeepaliveRequestSize);
|
||||||
|
MOZ_ASSERT(pendingKeepaliveRequestSize >= aBodyLength);
|
||||||
|
pendingKeepaliveRequestSize -= aBodyLength;
|
||||||
|
aLoadGroup->SetTotalKeepAliveBytes(pendingKeepaliveRequestSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
nsCOMPtr<nsILoadGroup> FetchUtil::GetLoadGroupFromGlobal(
|
||||||
|
nsIGlobalObject* aGlobalObject) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
nsCOMPtr<nsILoadGroup> loadGroup = nullptr;
|
||||||
|
auto* innerWindow = aGlobalObject->GetAsInnerWindow();
|
||||||
|
|
||||||
|
if (innerWindow) {
|
||||||
|
Document* doc = innerWindow->GetExtantDoc();
|
||||||
|
if (doc) {
|
||||||
|
loadGroup = doc->GetDocumentLoadGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadGroup;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool FetchUtil::ExtractHeader(nsACString::const_iterator& aStart,
|
bool FetchUtil::ExtractHeader(nsACString::const_iterator& aStart,
|
||||||
nsACString::const_iterator& aEnd,
|
nsACString::const_iterator& aEnd,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class Document;
|
|||||||
class InternalRequest;
|
class InternalRequest;
|
||||||
class WorkerPrivate;
|
class WorkerPrivate;
|
||||||
|
|
||||||
|
#define FETCH_KEEPALIVE_MAX_SIZE 65536
|
||||||
|
|
||||||
class FetchUtil final {
|
class FetchUtil final {
|
||||||
private:
|
private:
|
||||||
FetchUtil() = delete;
|
FetchUtil() = delete;
|
||||||
@@ -76,6 +78,23 @@ class FetchUtil final {
|
|||||||
* untyped 'size_t' instead of Gecko 'nsresult'.
|
* untyped 'size_t' instead of Gecko 'nsresult'.
|
||||||
*/
|
*/
|
||||||
static void ReportJSStreamError(JSContext* aCx, size_t aErrorCode);
|
static void ReportJSStreamError(JSContext* aCx, size_t aErrorCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements fetch spec
|
||||||
|
* https://fetch.spec.whatwg.org/#http-network-or-cache-fetch for
|
||||||
|
* bounding the keepalive request size
|
||||||
|
*/
|
||||||
|
static bool IncrementPendingKeepaliveRequestSize(nsILoadGroup* aLoadGroup,
|
||||||
|
const uint64_t aBodyLength);
|
||||||
|
|
||||||
|
static void DecrementPendingKeepaliveRequestSize(nsILoadGroup* aLoadGroup,
|
||||||
|
const uint64_t aBodyLength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper to fetch loadgroup from the global object
|
||||||
|
*/
|
||||||
|
static nsCOMPtr<nsILoadGroup> GetLoadGroupFromGlobal(
|
||||||
|
nsIGlobalObject* aGlobal);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#define mozilla_dom_InternalRequest_h
|
#define mozilla_dom_InternalRequest_h
|
||||||
|
|
||||||
#include "mozilla/dom/HeadersBinding.h"
|
#include "mozilla/dom/HeadersBinding.h"
|
||||||
|
#include "mozilla/dom/InternalResponse.h"
|
||||||
#include "mozilla/dom/InternalHeaders.h"
|
#include "mozilla/dom/InternalHeaders.h"
|
||||||
#include "mozilla/dom/RequestBinding.h"
|
#include "mozilla/dom/RequestBinding.h"
|
||||||
#include "mozilla/dom/SafeRefPtr.h"
|
#include "mozilla/dom/SafeRefPtr.h"
|
||||||
@@ -306,6 +307,8 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t BodyLength() const { return mBodyLength; }
|
||||||
|
|
||||||
void SetBodyBlobURISpec(nsACString& aBlobURISpec) {
|
void SetBodyBlobURISpec(nsACString& aBlobURISpec) {
|
||||||
mBodyBlobURISpec = aBlobURISpec;
|
mBodyBlobURISpec = aBlobURISpec;
|
||||||
}
|
}
|
||||||
@@ -434,7 +437,7 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
|
|||||||
nsCString mBodyBlobURISpec;
|
nsCString mBodyBlobURISpec;
|
||||||
nsString mBodyLocalPath;
|
nsString mBodyLocalPath;
|
||||||
nsCOMPtr<nsIInputStream> mBodyStream;
|
nsCOMPtr<nsIInputStream> mBodyStream;
|
||||||
int64_t mBodyLength;
|
int64_t mBodyLength{InternalResponse::UNKNOWN_BODY_SIZE};
|
||||||
|
|
||||||
nsCString mPreferredAlternativeDataType;
|
nsCString mPreferredAlternativeDataType;
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ interface nsILoadGroup : nsIRequest
|
|||||||
*/
|
*/
|
||||||
readonly attribute nsISimpleEnumerator requests;
|
readonly attribute nsISimpleEnumerator requests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sum total of content length of all pending keepalive
|
||||||
|
* requests in the fetch group group.
|
||||||
|
* See https://fetch.spec.whatwg.org/#ref-for-request-keepalive-flag%E2%91%A0
|
||||||
|
*/
|
||||||
|
attribute unsigned long long totalKeepAliveBytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the count of "active" requests (ie. requests without the
|
* Returns the count of "active" requests (ie. requests without the
|
||||||
* LOAD_BACKGROUND bit set).
|
* LOAD_BACKGROUND bit set).
|
||||||
|
|||||||
@@ -659,6 +659,19 @@ nsLoadGroup::GetRequests(nsISimpleEnumerator** aRequests) {
|
|||||||
return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest));
|
return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsLoadGroup::GetTotalKeepAliveBytes(uint64_t* aTotalKeepAliveBytes) {
|
||||||
|
MOZ_ASSERT(aTotalKeepAliveBytes);
|
||||||
|
*aTotalKeepAliveBytes = mPendingKeepaliveRequestSize;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsLoadGroup::SetTotalKeepAliveBytes(uint64_t aTotalKeepAliveBytes) {
|
||||||
|
mPendingKeepaliveRequestSize = aTotalKeepAliveBytes;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
|
nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
|
||||||
SetGroupObserver(aObserver, false);
|
SetGroupObserver(aObserver, false);
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ class nsLoadGroup : public nsILoadGroup,
|
|||||||
bool mExternalRequestContext{false};
|
bool mExternalRequestContext{false};
|
||||||
bool mNotifyObserverAboutBackgroundRequests{false};
|
bool mNotifyObserverAboutBackgroundRequests{false};
|
||||||
|
|
||||||
|
// size of requests with keepalive flag for this load group
|
||||||
|
uint64_t mPendingKeepaliveRequestSize{0};
|
||||||
|
|
||||||
/* Telemetry */
|
/* Telemetry */
|
||||||
mozilla::TimeStamp mDefaultRequestCreationTime;
|
mozilla::TimeStamp mDefaultRequestCreationTime;
|
||||||
uint32_t mTimedRequests{0};
|
uint32_t mTimedRequests{0};
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
[request-keepalive-quota.html]
|
|
||||||
[A Keep-Alive fetch() with a body over the Quota Limit should reject.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A Keep-Alive fetch() should not be allowed if the Quota is used up.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A Keep-Alive fetch() should return only its allocated Quota upon promise resolution.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?slow-2]
|
|
||||||
[Request Keepalive Quota Tests]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?fast]
|
|
||||||
[Request Keepalive Quota Tests]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?slow-3]
|
|
||||||
[Request Keepalive Quota Tests]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?slow-1]
|
|
||||||
[Request Keepalive Quota Tests]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?include=slow-2]
|
|
||||||
[A Keep-Alive fetch() should return only its allocated Quota upon promise resolution.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?include=fast]
|
|
||||||
[A Keep-Alive fetch() with a body over the Quota Limit should reject.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?include=slow-3]
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [TIMEOUT, OK]
|
|
||||||
[A Keep-Alive fetch() should not be allowed if the Quota is used up.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
||||||
[request-keepalive-quota.html?include=slow-1]
|
|
||||||
Reference in New Issue
Block a user