/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set expandtab ts=4 sw=2 sts=2 cin: */ /* 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/. */
// HttpLog.h should generally be included first #include"HttpLog.h"
// True if the local cache should be bypassed when processing a request. #define BYPASS_LOCAL_CACHE(loadFlags, isPreferCacheLoadOverBypass) \
((loadFlags) & (nsIRequest::LOAD_BYPASS_CACHE | \
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE) && \
!(((loadFlags) & nsIRequest::LOAD_FROM_CACHE) && \
(isPreferCacheLoadOverBypass)))
// Computes and returns a SHA1 hash of the input buffer. The input buffer // must be a null-terminated string.
nsresult Hash(constchar* buf, nsACString& hash) {
nsresult rv;
// We only treat 3xx responses as redirects if they have a Location header and // the status code is in a whitelist. bool nsHttpChannel::WillRedirect(const nsHttpResponseHead& response) { return IsRedirectStatus(response.Status()) &&
response.HasHeader(nsHttp::Location);
}
if (LOG_ENABLED()) {
nsCString webExtension;
this->GetPropertyAsACString(u"cancelledByExtension"_ns, webExtension); if (!webExtension.IsEmpty()) {
LOG(("channel [%p] cancelled by extension [id=%s]", this,
webExtension.get()));
}
}
if (mAuthProvider) {
DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
ReleaseMainThreadOnlyReferences(); if (gHttpHandler) {
gHttpHandler->RemoveHttpChannel(mChannelId);
}
}
void nsHttpChannel::ReleaseMainThreadOnlyReferences() { if (NS_IsMainThread()) { // Already on main thread, let dtor to // take care of releasing references return;
}
MOZ_DIAGNOSTIC_ASSERT(
!mEarlyHintObserver, "Early hint observer should have been released in ReleaseListeners()");
arrayToRelease.AppendElement(mEarlyHintObserver.forget());
MOZ_DIAGNOSTIC_ASSERT(
!mChannelClassifier, "Channel classifier should have been released in ReleaseListeners()");
arrayToRelease.AppendElement(
mChannelClassifier.forget().downcast<nsIURIClassifierCallback>());
MOZ_DIAGNOSTIC_ASSERT(
!mWarningReporter, "Warning reporter should have been released in ReleaseListeners()");
arrayToRelease.AppendElement(mWarningReporter.forget());
auto prefEnabledForCurrentContainer = [&]() {
uint32_t containerId = mLoadInfo->GetOriginAttributes().mUserContextId; // Make sure that the default container ID is 0
static_assert(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID == 0);
LOG(("Pref for %s is %d\n", prefName.get(), enabled));
return enabled;
};
#endif// defined(XP_WIN) || defined(XP_MACOSX)
#ifdef XP_WIN
// If Windows 10 SSO is enabled, we potentially add auth // information to secure top level loads (DOCUMENTs) and iframes // (SUBDOCUMENTs) that aren't anonymous or private browsing. if (StaticPrefs::network_http_windows_sso_enabled() &&
mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) &&
!mPrivateBrowsing) {
ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); if ((type == ExtContentPolicy::TYPE_DOCUMENT ||
type == ExtContentPolicy::TYPE_SUBDOCUMENT) &&
prefEnabledForCurrentContainer()) {
AddWindowsSSO(this);
}
} #endif
#ifdef XP_MACOSX
auto isUriMSAuthority = [&]() {
nsAutoCString endPoint;
nsresult rv = mURI->GetHost(endPoint); if (!NS_SUCCEEDED(rv)) { returnfalse;
}
LOG(("endPoint is %s\n", endPoint.get()));
// Returns NS_OK if performRequests is called in MicrosoftEntraSSOUtils // This temporarily stops the channel setup for the delegate. if (NS_SUCCEEDED(rv)) { return rv;
}
}
}
void nsHttpChannel::SetPriorityHeader() {
nsAutoCString userSetPriority;
Unused << GetRequestHeader("Priority"_ns, userSetPriority); if (!userSetPriority.IsEmpty()) { // If the Priority header is set by the user, do not override it. return;
}
// Check if request was cancelled during suspend AFTER on-modify-request if (mCanceled) { return mStatus;
}
// Check to see if we should redirect this channel elsewhere by // nsIHttpChannel.redirectTo API request if (mAPIRedirectTo) { return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
}
// Note that we are only setting the "Upgrade-Insecure-Requests" request // header for *all* navigational requests instead of all requests as // defined in the spec, see: // https://www.w3.org/TR/upgrade-insecure-requests/#preference
ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
if (type == ExtContentPolicy::TYPE_DOCUMENT ||
type == ExtContentPolicy::TYPE_SUBDOCUMENT) {
rv = SetRequestHeader("Upgrade-Insecure-Requests"_ns, "1"_ns, false);
NS_ENSURE_SUCCESS(rv, rv);
}
if (LoadAuthRedirectedChannel()) { // This channel is a result of a redirect due to auth retry // We have already checked for HSTS upgarde in the redirecting channel. // We can safely skip those checks return ContinueOnBeforeConnect(false, rv);
}
SecFetch::AddSecFetchHeader(this);
// Check to see if we should redirect this channel to the unstripped URI. To // revert the query stripping if the loading channel is in the content // blocking allow list. if (ContentBlockingAllowList::Check(this)) {
nsCOMPtr<nsIURI> unstrippedURI;
mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
if (unstrippedURI) { return AsyncCall(&nsHttpChannel::HandleAsyncRedirectToUnstrippedURI);
}
}
nsCOMPtr<nsIPrincipal> resultPrincipal; if (!mURI->SchemeIs("https")) {
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( this, getter_AddRefs(resultPrincipal));
}
// Check if we already know about the HSTS status of the host
nsISiteSecurityService* sss = gHttpHandler->GetSSService();
NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); bool isSecureURI;
OriginAttributes originAttributes; if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
originAttributes)) { return NS_ERROR_FAILURE;
}
rv = sss->IsSecureURI(mURI, originAttributes, &isSecureURI);
NS_ENSURE_SUCCESS(rv, rv); // Save that on the loadInfo so it can later be consumed by // SecurityInfo.sys.mjs
mLoadInfo->SetHstsStatus(isSecureURI);
RefPtr<mozilla::dom::BrowsingContext> bc;
mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); // If bypassing the cache and we're forced offline // we can just return the error here. if (bc && bc->Top()->GetForceOffline() &&
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) { return NS_ERROR_OFFLINE;
}
// At this point it is no longer possible to call // HttpBaseChannel::UpgradeToSecure.
StoreUpgradableToSecure(false); bool shouldUpgrade = LoadUpgradeToSecure(); if (mURI->SchemeIs("http")) {
OriginAttributes originAttributes; if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
originAttributes)) { return NS_ERROR_FAILURE;
}
if (!shouldUpgrade) { // Make sure http channel is released on main thread. // See bug 1539148 for details.
nsMainThreadPtrHandle<nsHttpChannel> self( new nsMainThreadPtrHolder<nsHttpChannel>( "nsHttpChannel::OnBeforeConnect::self", this)); auto resultCallback = [self(self)](bool aResult, nsresult aStatus) {
MOZ_ASSERT(NS_IsMainThread());
bool willCallback = false;
rv = NS_ShouldSecureUpgrade(
mURI, mLoadInfo, resultPrincipal, LoadAllowSTS(), originAttributes,
shouldUpgrade, std::move(resultCallback), willCallback); // If the request gets upgraded because of the HTTPS-Only mode, but no // event listener has been registered so far, we want to do that here.
uint32_t httpOnlyStatus = mLoadInfo->GetHttpsOnlyStatus(); if (httpOnlyStatus &
nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) {
RefPtr<nsHTTPSOnlyStreamListener> httpsOnlyListener = new nsHTTPSOnlyStreamListener(mListener, mLoadInfo);
mListener = httpsOnlyListener;
// Returns true if the network connectivity checker indicated // that HTTPS records can be resolved on this network - false otherwise. // When TRR is enabled, we always return true, as resolving HTTPS // records don't depend on the network. staticbool canUseHTTPSRRonNetwork(bool& aTRREnabled) { // Respect the pref. if (StaticPrefs::network_dns_force_use_https_rr()) {
aTRREnabled = true; returntrue;
}
aTRREnabled = false;
if (nsCOMPtr<nsIDNSService> dns = mozilla::components::DNS::Service()) {
nsIDNSService::ResolverMode mode; // If the browser is currently using TRR/DoH, then it can // definitely resolve HTTPS records. if (NS_SUCCEEDED(dns->GetCurrentTrrMode(&mode))) { if (mode == nsIDNSService::MODE_TRRFIRST) {
RefPtr<TRRService> trr = TRRService::Get(); if (trr && trr->IsConfirmed()) {
aTRREnabled = true;
}
} elseif (mode == nsIDNSService::MODE_TRRONLY) {
aTRREnabled = true;
} if (aTRREnabled) { returntrue;
}
}
}
if (RefPtr<NetworkConnectivityService> ncs =
NetworkConnectivityService::GetSingleton()) {
nsINetworkConnectivityService::ConnectivityState state; if (NS_SUCCEEDED(ncs->GetDNS_HTTPS(&state)) &&
state == nsINetworkConnectivityService::NOT_AVAILABLE) { returnfalse;
}
} returntrue;
}
auto shouldSkipUpgradeWithHTTPSRR = [&]() -> bool { if (mCaps & NS_HTTP_DISALLOW_HTTPS_RR) { returntrue;
}
// Skip using HTTPS RR to upgrade when this is not a top-level load and the // loading principal is http. if ((mLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) &&
(mLoadInfo->GetLoadingPrincipal() &&
mLoadInfo->GetLoadingPrincipal()->SchemeIs("http"))) { returntrue;
}
// If the network connectivity checker indicates the network is // blocking HTTPS requests, then we should skip them so we don't // needlessly wait for a timeout. bool trrEnabled = false; if (!canUseHTTPSRRonNetwork(trrEnabled)) { returntrue;
}
// Don't block the channel when TRR is not used. if (!trrEnabled) { returntrue;
}
auto dnsStrategy = GetProxyDNSStrategy(); if (dnsStrategy != ProxyDNSStrategy::ORIGIN) { returntrue;
}
if (shouldSkipUpgradeWithHTTPSRR()) {
StoreUseHTTPSSVC(false); // If the website does not want to use HTTPS RR, we should set // NS_HTTP_DISALLOW_HTTPS_RR. This is for avoiding HTTPS RR being used by // the transaction.
DisallowHTTPSRR(mCaps); return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
}
if (aShouldUpgrade && !mURI->SchemeIs("https")) { // only set HTTPS_RR to be responsbile for the upgrade in the loadinfo // if it actually was responsible, otherwise the correct flag is // already present in the loadinfo. if (aUpgradeWithHTTPSRR) {
mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::HTTPS_RR);
} return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
}
// ensure that we are using a valid hostname if (!net_IsValidDNSHost(nsDependentCString(mConnectionInfo->Origin()))) { return NS_ERROR_UNKNOWN_HOST;
}
if (mUpgradeProtocolCallback) { // Websockets can run over HTTP/2, but other upgrades can't. if (mUpgradeProtocol.EqualsLiteral("websocket") &&
StaticPrefs::network_http_http2_websockets()) { // Need to tell the conn manager that we're ok with http/2 even with // the allow keepalive bit not set. That bit needs to stay off, // though, in case we end up having to fallback to http/1.1 (where // we absolutely do want to disable keepalive).
mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE;
} else {
mCaps |= NS_HTTP_DISALLOW_SPDY;
} // Upgrades cannot use HTTP/3.
mCaps |= NS_HTTP_DISALLOW_HTTP3; // Because NS_HTTP_STICKY_CONNECTION breaks HTTPS RR fallabck mecnahism, we // can not use HTTPS RR for upgrade requests.
DisallowHTTPSRR(mCaps);
}
if (LoadIsTRRServiceChannel()) {
mCaps |= NS_HTTP_LARGE_KEEPALIVE;
DisallowHTTPSRR(mCaps);
}
if (mTransactionSticky) {
MOZ_ASSERT(LoadAuthRedirectedChannel()); // this means this is a redirected channel channel due to auth retry and a // connection based auth scheme was used // we have a reference to the old-transaction with sticky connection which // we need to use
mCaps |= NS_HTTP_STICKY_CONNECTION;
}
class MOZ_STACK_CLASS AddResponseHeadersToResponseHead final
: public nsIHttpHeaderVisitor { public: explicit AddResponseHeadersToResponseHead(nsHttpResponseHead* aResponseHead)
: mResponseHead(aResponseHead) {}
nsresult nsHttpChannel::HandleOverrideResponse() { // Start building a response with the data from mOverrideResponse.
mResponseHead = MakeUnique<nsHttpResponseHead>();
// Apply override response status code and status text.
uint32_t statusCode;
nsresult rv = mOverrideResponse->GetResponseStatus(&statusCode);
NS_ENSURE_SUCCESS(rv, rv);
if (WillRedirect(*mResponseHead)) { // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here, // to avoid event dispatching latency.
LOG(("Skipping read of overridden response redirect entity\n")); return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
}
// Handle Set-Cookie headers as if the response was from networking.
CookieVisitor cookieVisitor(mResponseHead.get());
SetCookieHeaders(cookieVisitor.CookieHeaders());
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(this, parentChannel); if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
httpParent->SetCookieHeaders(cookieVisitor.CookieHeaders());
}
// If mOverrideResponse is set, bypass the rest of the connection and reply // immediately with a response built using the data from mOverrideResponse. if (mOverrideResponse) { return HandleOverrideResponse();
}
// Don't allow resuming when cache must be used if (LoadResuming() && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
LOG(("Resuming from cache is not supported yet")); return NS_ERROR_DOCUMENT_NOT_CACHED;
}
// Step 8.18 of HTTP-network-or-cache fetch // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
nsAutoCString rangeVal; if (NS_SUCCEEDED(GetRequestHeader("Range"_ns, rangeVal))) {
SetRequestHeader("Accept-Encoding"_ns, "identity"_ns, true);
}
if (mRequestHead.IsPost() || mRequestHead.IsPatch()) { // If the post id is already set then this is an attempt to replay // a post/patch transaction via the cache. Otherwise, we need a unique // post id for this transaction. if (mPostID == 0) {
mPostID = gHttpHandler->GenerateUniqueID();
}
if (StaticPrefs::network_http_idempotencyKey_enabled() &&
!mRequestHead.HasHeader(nsHttp::Idempotency_Key)) { // check if we need to add // idempotency-key header // See Bug 1830022 for more details
nsAutoCString key;
gHttpHandler->GenerateIdempotencyKeyForPost(mPostID, mLoadInfo, key);
MOZ_ALWAYS_SUCCEEDS(
mRequestHead.SetHeader(nsHttp::Idempotency_Key, key, false));
}
}
#ifdef MOZ_WIDGET_ANDROID bool val = false; if (nsIOService::ShouldAddAdditionalSearchHeaders(mURI, &val)) {
SetRequestHeader("X-Search-Subdivision"_ns, val ? "1"_ns : "0"_ns, false);
} #endif
// Consider opening a TCP connection right away.
SpeculativeConnect();
// open a cache entry for this channel...
rv = OpenCacheEntry(mURI->SchemeIs("https"));
// do not continue if asyncOpenCacheEntry is in progress if (AwaitingCacheCallbacks()) {
LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n", this));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
if (mNetworkTriggered && mWaitingForProxy) { // Someone has called TriggerNetwork(), meaning we are racing the // network with the cache.
mWaitingForProxy = false; return ContinueConnect();
}
return NS_OK;
}
if (NS_FAILED(rv)) {
LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv))); // if this channel is only allowed to pull from the cache, then // we must fail if we were unable to open a cache entry. if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { return NS_ERROR_DOCUMENT_NOT_CACHED;
} // otherwise, let's just proceed without using the cache.
}
if (mRaceCacheWithNetwork && ((mCacheEntry && !CachedContentIsValid() &&
(mDidReval || LoadCachedContentIsPartial())) ||
mIgnoreCacheEntry)) { // We won't send the conditional request because the unconditional // request was already sent (see bug 1377223).
AccumulateCategorical(
Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
}
// When racing, if OnCacheEntryAvailable is called before AsyncOpenURI // returns, then we may not have started reading from the cache. // If the content is valid, we should attempt to do so, as technically the // cache has won the race. if (mRaceCacheWithNetwork && CachedContentIsValid()) {
Unused << ReadFromCache();
}
return TriggerNetwork();
}
nsresult nsHttpChannel::ContinueConnect() { // If we need to start a CORS preflight, do it now! // Note that it is important to do this before the early returns below. if (!LoadIsCorsPreflightDone() && LoadRequireCORSPreflight()) {
MOZ_ASSERT(!mPreflightChannel);
nsresult rv = nsCORSListenerProxy::StartCORSPreflight( this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel)); return rv;
}
MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(), "CORS preflight must have been finished by the time we " "do the rest of ContinueConnect");
// we may or may not have a cache entry at this point if (mCacheEntry) { // read straight from the cache if possible... if (CachedContentIsValid()) { // If we're forced offline, and set to bypass the cache, return offline. if (bc && bc->Top()->GetForceOffline() &&
BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) { return NS_ERROR_OFFLINE;
}
return rv;
} if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { // the cache contains the requested resource, but it must be // validated before we can reuse it. since we are not allowed // to hit the net, there's nothing more to do. the document // is effectively not in the cache.
LOG((" !CachedContentIsValid() && mLoadFlags & LOAD_ONLY_FROM_CACHE")); return NS_ERROR_DOCUMENT_NOT_CACHED;
}
} elseif (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
LOG((" !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE")); return NS_ERROR_DOCUMENT_NOT_CACHED;
}
if (!mDNSBlockingPromise.IsEmpty()) {
LOG((" waiting for DNS prefetch"));
// Transaction is passed only from auth retry for which we will definitely // not block on DNS to alter the origin server name for IP; it has already // been done.
MOZ_ASSERT(!aTransWithStickyConn);
MOZ_ASSERT(mDNSBlockingThenable);
rv = mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump)); if (NS_FAILED(rv)) { return rv;
}
uint32_t suspendCount = mSuspendCount; if (LoadAsyncResumePending()) {
LOG(
(" Suspend()'ing transaction pump once because of async resume pending" ", sc=%u, pump=%p, this=%p",
suspendCount, mTransactionPump.get(), this));
++suspendCount;
} while (suspendCount--) {
mTransactionPump->Suspend();
}
return NS_OK;
}
void nsHttpChannel::SpeculativeConnect() { // Before we take the latency hit of dealing with the cache, try and // get the TCP (and SSL) handshakes going so they can overlap.
// don't speculate if we are offline, when doing http upgrade (i.e. // websockets bootstrap), or if we can't do keep-alive (because then we // couldn't reuse the speculative connection anyhow).
RefPtr<mozilla::dom::BrowsingContext> bc;
mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
// LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network. // LOAD_FROM_CACHE is unlikely to hit network, so skip preconnects for it. if (mLoadFlags &
(LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | LOAD_NO_NETWORK_IO)) { return;
}
// since this event is handled asynchronously, it is possible that this // channel could have been canceled, in which case there would be no point // in processing the redirect. if (NS_SUCCEEDED(mStatus)) {
PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
rv = AsyncProcessRedirection(mResponseHead->Status()); if (NS_FAILED(rv)) {
PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect); // TODO: if !DoNotRender3xxBody(), render redirect body instead. // But first we need to cache 3xx bodies (bug 748510)
rv = ContinueHandleAsyncRedirect(rv);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
} else {
rv = ContinueHandleAsyncRedirect(mStatus);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) { if (NS_FAILED(rv)) { // If AsyncProcessRedirection fails, then we have to send out the // OnStart/OnStop notifications.
LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
if (redirectsEnabled) { // TODO: stop failing original channel if redirect vetoed?
mStatus = rv;
DoNotifyListener();
// Blow away cache entry if we couldn't process the redirect // for some reason (the cache entry might be corrupt). if (mCacheEntry) {
mCacheEntry->AsyncDoom(nullptr);
}
} else {
DoNotifyListener();
}
}
CloseCacheEntry(true);
StoreIsPending(false);
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
return NS_OK;
}
void nsHttpChannel::HandleAsyncNotModified() {
MOZ_ASSERT(!mCallOnResume, "How did that happen?");
if (mSuspendCount) {
LOG(("Waiting until resume to do async not-modified [this=%p]\n", this));
mCallOnResume = [](nsHttpChannel* self) {
self->HandleAsyncNotModified(); return NS_OK;
}; return;
}
if (StaticPrefs::network_http_priority_header_enabled()) {
SetPriorityHeader();
}
// If we're racing cache with network, conditional or byte range header // could be added in OnCacheEntryCheck. We cannot send conditional request // without having the entry, so we need to remove the headers here and // ignore the cache entry in OnCacheEntryAvailable. if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) { if (mDidReval) {
LOG((" Removing conditional request headers"));
UntieValidationRequest();
mDidReval = false;
mIgnoreCacheEntry = true;
}
if (LoadCachedContentIsPartial()) {
LOG((" Removing byte range request headers"));
UntieByteRangeRequest();
StoreCachedContentIsPartial(false);
mIgnoreCacheEntry = true;
}
if (!LoadAllowSpdy()) {
mCaps |= NS_HTTP_DISALLOW_SPDY;
} if (!LoadAllowHttp3()) {
mCaps |= NS_HTTP_DISALLOW_HTTP3;
} if (LoadBeConservative()) {
mCaps |= NS_HTTP_BE_CONSERVATIVE;
}
if (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) {
mCaps |= NS_HTTP_LOAD_ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT;
}
if (nsContentUtils::ShouldResistFingerprinting(this,
RFPTarget::HttpUserAgent)) {
mCaps |= NS_HTTP_USE_RFP;
}
// Use the URI path if not proxying (transparent proxying such as proxy // CONNECT does not count here). Also figure out what HTTP version to use.
nsAutoCString buf, path;
nsCString* requestURI;
// This is the normal e2e H1 path syntax "/index.html"
rv = mURI->GetPathQueryRef(path); if (NS_FAILED(rv)) { return rv;
}
// path may contain UTF-8 characters, so ensure that they're escaped. if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
buf)) {
requestURI = &buf;
} else {
requestURI = &path;
}
// trim off the #ref portion if any...
int32_t ref1 = requestURI->FindChar('#'); if (ref1 != kNotFound) {
requestURI->SetLength(ref1);
}
if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
mRequestHead.SetVersion(gHttpHandler->HttpVersion());
} else {
mRequestHead.SetPath(*requestURI);
// RequestURI should be the absolute uri H1 proxy syntax // "http://foo/index.html" so we will overwrite the relative version in // requestURI
rv = mURI->GetUserPass(buf); if (NS_FAILED(rv)) return rv; if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
strncmp(mSpec.get(), "https:", 6) == 0)) {
nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI);
rv = tempURI->GetAsciiSpec(path); if (NS_FAILED(rv)) return rv;
requestURI = &path;
} else {
requestURI = &mSpec;
}
// trim off the #ref portion if any...
int32_t ref2 = requestURI->FindChar('#'); if (ref2 != kNotFound) {
requestURI->SetLength(ref2);
}
// set the request time for cache expiration calculations
mRequestTime = NowInSeconds();
StoreRequestTimeInitialized(true);
// if doing a reload, force end-to-end if (mLoadFlags & LOAD_BYPASS_CACHE) { // We need to send 'Pragma:no-cache' to inhibit proxy caching even if // no proxy is configured since we might be talking with a transparent // proxy, i.e. one that operates at the network level. See bug #14772. // But we should not touch Pragma if Cache-Control is already set // (https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A3) if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
} // If we're configured to speak HTTP/1.1 then also send 'Cache-control: // no-cache'. But likewise don't touch Cache-Control if it's already set. if (mRequestHead.Version() >= HttpVersion::v1_1 &&
!mRequestHead.HasHeader(nsHttp::Cache_Control)) {
rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
} elseif (mLoadFlags & VALIDATE_ALWAYS) { // We need to send 'Cache-Control: max-age=0' to force each cache along // the path to the origin server to revalidate its own entry, if any, // with the next cache or server. See bug #84847. // // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache' // // But don't send the headers if they're already set: // https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A2 if (mRequestHead.Version() >= HttpVersion::v1_1) { if (!mRequestHead.HasHeader(nsHttp::Cache_Control)) {
rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true);
}
} else { if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
}
}
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
if (!mEntityID.IsEmpty()) { // Also, we want an error if this resource changed in the meantime // Format of the entity id is: escaped_etag/size/lastmod
nsCString::const_iterator start, end, slash;
mEntityID.BeginReading(start);
mEntityID.EndReading(end);
mEntityID.BeginReading(slash);
if (mWebTransportSessionEventListener) {
mCaps |= NS_HTTP_STICKY_CONNECTION;
}
return NS_OK;
}
nsresult nsHttpChannel::InitTransaction() {
nsresult rv; // create wrapper for this channel's notification callbacks
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
getter_AddRefs(callbacks));
// create the transaction object if (nsIOService::UseSocketProcess()) { if (NS_WARN_IF(!gIOService->SocketProcessReady())) { return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<SocketProcessParent> socketProcess =
SocketProcessParent::GetSingleton(); if (!socketProcess->CanSend()) { return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(this, parentChannel);
RefPtr<DocumentLoadListener> documentChannelParent =
do_QueryObject(parentChannel); // See HttpTransactionChild::CanSendODAToContentProcessDirectly() and // nsHttpChannel::CallOnStartRequest() for the reason why we need to know if // this is a document load. We only send ODA directly to child process for // non document loads.
RefPtr<HttpTransactionParent> transParent = new HttpTransactionParent(!!documentChannelParent);
LOG1(("nsHttpChannel %p created HttpTransactionParent %p\n", this,
transParent.get()));
// Since OnStopRequest could be sent to child process from socket process // directly, we need to store these two values in HttpTransactionChild and // forward to child process until HttpTransactionChild::OnStopRequest is // called.
transParent->SetRedirectTimestamp(mRedirectStartTimeStamp,
mRedirectEndTimeStamp);
if (socketProcess) {
MOZ_ALWAYS_TRUE(
socketProcess->SendPHttpTransactionConstructor(transParent));
}
mTransaction = transParent;
} else {
mTransaction = new nsHttpTransaction();
LOG1(("nsHttpChannel %p created nsHttpTransaction %p\n", this,
mTransaction.get()));
}
// Save the mapping of channel id and the channel. We need this mapping for // nsIHttpActivityObserver.
gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.