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:
smayya 2024-07-04 12:24:46 +00:00
Родитель 1f2c55e2f7
Коммит 380c1efb91
10 изменённых файлов: 142 добавлений и 50 удалений

Просмотреть файл

@ -46,6 +46,7 @@
#include "BodyExtractor.h"
#include "FetchChild.h"
#include "FetchUtil.h"
#include "FetchObserver.h"
#include "InternalRequest.h"
#include "InternalResponse.h"
@ -603,6 +604,28 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
// keepalive is set to true, route the request through PFetch
// We plan to route all main-thread fetch request through PFetch.
// 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 =
FetchChild::CreateForMainThread(p, signalImpl, observer);
if (!actor) {

Просмотреть файл

@ -5,6 +5,7 @@
#include "FetchChild.h"
#include "FetchLog.h"
#include "FetchObserver.h"
#include "FetchUtil.h"
#include "InternalResponse.h"
#include "Request.h"
#include "Response.h"
@ -228,6 +229,7 @@ RefPtr<FetchChild> FetchChild::CreateForWorker(
RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) {
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
FETCH_LOG(("FetchChild::CreateForWorker [%p]", aWorkerPrivate));
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
@ -254,6 +256,8 @@ RefPtr<FetchChild> FetchChild::CreateForMainThread(
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
actor->mIsKeepAliveRequest = true;
FETCH_LOG(("FetchChild::CreateForMainThread actor[%p]", actor.get()));
return actor;
}
@ -398,6 +402,12 @@ void FetchChild::RunAbortAlgorithm() {
void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) {
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->Aborted()) {
Unused << SendAbortFetchOp();
@ -446,6 +456,22 @@ void FetchChild::Shutdown() {
void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
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;
mFetchObserver = nullptr;
mSignalImpl = nullptr;

Просмотреть файл

@ -83,6 +83,7 @@ class FetchChild final : public PFetchChild, public AbortFollower {
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
bool mIsKeepAliveRequest{false};
uint64_t mKeepaliveRequestSize{0};
RefPtr<Promise> mPromise;
RefPtr<AbortSignalImpl> mSignalImpl;
RefPtr<FetchObserver> mFetchObserver;

Просмотреть файл

@ -76,6 +76,52 @@ static bool PushOverLine(nsACString::const_iterator& aStart,
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
bool FetchUtil::ExtractHeader(nsACString::const_iterator& aStart,
nsACString::const_iterator& aEnd,

Просмотреть файл

@ -24,6 +24,8 @@ class Document;
class InternalRequest;
class WorkerPrivate;
#define FETCH_KEEPALIVE_MAX_SIZE 65536
class FetchUtil final {
private:
FetchUtil() = delete;
@ -76,6 +78,23 @@ class FetchUtil final {
* untyped 'size_t' instead of Gecko 'nsresult'.
*/
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

Просмотреть файл

@ -8,6 +8,7 @@
#define mozilla_dom_InternalRequest_h
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/InternalResponse.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/RequestBinding.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) {
mBodyBlobURISpec = aBlobURISpec;
}
@ -434,7 +437,7 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
nsCString mBodyBlobURISpec;
nsString mBodyLocalPath;
nsCOMPtr<nsIInputStream> mBodyStream;
int64_t mBodyLength;
int64_t mBodyLength{InternalResponse::UNKNOWN_BODY_SIZE};
nsCString mPreferredAlternativeDataType;

Просмотреть файл

@ -66,6 +66,13 @@ interface nsILoadGroup : nsIRequest
*/
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
* LOAD_BACKGROUND bit set).

Просмотреть файл

@ -659,6 +659,19 @@ nsLoadGroup::GetRequests(nsISimpleEnumerator** aRequests) {
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
nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
SetGroupObserver(aObserver, false);

Просмотреть файл

@ -99,6 +99,9 @@ class nsLoadGroup : public nsILoadGroup,
bool mExternalRequestContext{false};
bool mNotifyObserverAboutBackgroundRequests{false};
// size of requests with keepalive flag for this load group
uint64_t mPendingKeepaliveRequestSize{0};
/* Telemetry */
mozilla::TimeStamp mDefaultRequestCreationTime;
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]