/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et tw=80 : */ /* 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/. */
/* RFC 7838 Alternative Services http://httpwg.org/http-extensions/opsec.html note that connections currently do not do mixed-scheme (the I attribute in the ConnectionInfo prevents it) but could, do not honor tls-commit and should not, and always require authentication
*/
namespace mozilla { namespace net {
// function places true in outIsHTTPS if scheme is https, false if // http, and returns an error if neither. originScheme passed into // alternate service should already be normalized to those lower case // strings by the URI parser (and so there is an assert)- this is an extra // check. static nsresult SchemeIsHTTPS(const nsACString& originScheme, bool& outIsHTTPS) {
outIsHTTPS = originScheme.EqualsLiteral("https");
if (!outIsHTTPS && !originScheme.EqualsLiteral("http")) {
MOZ_ASSERT(!originScheme.LowerCaseEqualsLiteral("https") &&
!originScheme.LowerCaseEqualsLiteral("http"), "The scheme should already be lowercase"); return NS_ERROR_UNEXPECTED;
} return NS_OK;
}
if (StaticPrefs::network_http_altsvc_proxy_checks() &&
!AcceptableProxy(proxyInfo)) {
LOG(("AltSvcMapping::ProcessHeader ignoring due to proxy\n")); return;
}
bool isHTTPS; if (NS_FAILED(SchemeIsHTTPS(originScheme, isHTTPS))) { return;
} if (!isHTTPS && !gHttpHandler->AllowAltSvcOE()) {
LOG(("Alt-Svc Response Header for http:// origin but OE disabled\n")); return;
}
if (!pairIndex) { if (currentName.EqualsLiteral("clear")) {
clearEntry = true;
--numEntriesInHeader; // Only want to keep track of actual alt-svc // maps, not clearing break;
}
// h2=[hostname]:443 or h3-xx=[hostname]:port // XX is current version we support and it is define in nsHttp.h.
alpnRank = IsAlpnSupported(currentName);
npnToken = currentName;
if (clearEntry) {
nsCString suffix;
originAttributes.CreateSuffix(suffix);
LOG(("Alt Svc clearing mapping for %s:%d:%s", originHost.get(),
originPort, suffix.get()));
gHttpHandler->AltServiceCache()->ClearHostMapping(originHost, originPort,
originAttributes); continue;
}
if (NS_FAILED(NS_CheckPortSafety(portno, originScheme.get()))) {
LOG(("Alt Svc doesn't allow port %d, ignoring", portno)); continue;
}
// unescape modifies a c string in place, so afterwards // update nsCString length
nsUnescape(npnToken.BeginWriting());
npnToken.SetLength(strlen(npnToken.BeginReading())); bool isHttp3 = net::IsHttp3(alpnRank);
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo(); if (!(npnToken.Equals(spdyInfo->VersionString) &&
StaticPrefs::network_http_http2_enabled()) &&
!(isHttp3 && nsHttpHandler::IsHttp3Enabled() &&
!gHttpHandler->IsHttp3Excluded(hostname.IsEmpty() ? originHost
: hostname))) {
LOG(("Alt Svc unknown protocol %s, ignoring", npnToken.get())); continue;
}
LOG(("AltSvcMapping created npnToken=%s", npnToken.get()));
RefPtr<AltSvcMapping> mapping = new AltSvcMapping(
gHttpHandler->AltServiceCache()->GetStoragePtr(),
gHttpHandler->AltServiceCache()->StorageEpoch(), originScheme,
originHost, originPort, username, privateBrowsing,
NowInSeconds() + maxage, hostname, portno, npnToken, originAttributes,
isHttp3, alpnRank); if (mapping->TTL() <= 0) {
LOG(("Alt Svc invalid map"));
mapping = nullptr; // since this isn't a parse error, let's clear any existing mapping // as that would have happened if we had accepted the parameters.
gHttpHandler->AltServiceCache()->ClearHostMapping(originHost, originPort,
originAttributes);
} else { if (isHttp3) {
h3Mappings.AppendElement(std::move(mapping));
} else {
otherMappings.AppendElement(std::move(mapping));
}
}
}
if (numEntriesInHeader) { // Ignore headers that were just "alt-svc: clear"
Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_ENTRIES_PER_HEADER,
numEntriesInHeader);
}
}
if (mAlternateHost.IsEmpty()) {
mAlternateHost = mOriginHost;
}
if ((mAlternatePort == mOriginPort) &&
mAlternateHost.EqualsIgnoreCase(mOriginHost.get()) && !mIsHttp3) { // Http2 on the same host:port does not make sense because we are // connecting to the same end point over the same protocol (TCP) as with // original host. On the other hand, for Http3 alt-svc can be hosted on // the same host:port because protocol(UDP vs. TCP) is always different and // we are not connecting to the same end point.
LOG(("Alt Svc is also origin Svc - ignoring\n"));
mExpiresAt = 0; // invalid
}
// http:// without the mixed-scheme attribute needs to be segmented in the // connection manager connection information hash with this attribute if (!mHttps && !mMixedScheme) {
ci->SetInsecureScheme(true);
}
ci->SetPrivate(mPrivate);
ci.forget(outCI);
}
void AltSvcMapping::Serialize(nsCString& out) { // Be careful, when serializing new members, add them to the end of this list.
out = mHttps ? "https:"_ns : "http:"_ns;
out.Append(mOriginHost);
out.Append(':');
out.AppendInt(mOriginPort);
out.Append(':');
out.Append(mAlternateHost);
out.Append(':');
out.AppendInt(mAlternatePort);
out.Append(':');
out.Append(mUsername);
out.Append(':');
out.Append(mPrivate ? 'y' : 'n');
out.Append(':');
out.AppendInt(mExpiresAt);
out.Append(':');
out.Append(mNPNToken);
out.Append(':');
out.Append(mValidated ? 'y' : 'n');
out.Append(':');
out.AppendInt(mStorageEpoch);
out.Append(':');
out.Append(mMixedScheme ? 'y' : 'n');
out.Append(':');
nsAutoCString suffix;
mOriginAttributes.CreateSuffix(suffix);
out.Append(suffix);
out.Append(':');
out.Append(""_ns); // Formerly topWindowOrigin. Now unused empty string.
out.Append('|'); // Be careful, the top window origin may contain colons!
out.Append('n'); // Formerly mIsolated. Now always 'n'. Should remove someday
out.Append(':');
out.Append(mIsHttp3 ? 'y' : 'n');
out.Append(':'); // Add code to serialize new members here!
}
if (mTriedToWrite && reason == NS_BASE_STREAM_CLOSED) { // The normal course of events is to cause the transaction to fail with // CLOSED on a write - so that's a success that means the HTTP/2 session // is setup.
reason = NS_OK;
}
if (NS_FAILED(reason) || !mRunning || !mConnection) {
LOG(("AltSvcTransaction::MaybeValidate %p Failed due to precondition", this)); returnfalse;
}
// insist on >= http/2
HttpVersion version = mConnection->Version();
LOG(("AltSvcTransaction::MaybeValidate() %p version %d\n", this, static_cast<int32_t>(version))); if ((!mIsHttp3 && (version != HttpVersion::v2_0)) ||
(mIsHttp3 && (version != HttpVersion::v3_0))) {
LOG(
("AltSvcTransaction::MaybeValidate %p Failed due to protocol version" " expacted %s.", this, mIsHttp3 ? "Http3" : "Http2")); returnfalse;
}
mWaiting--; // another channel is complete if (!mWaiting) { // there are all complete!
nsAutoCString mAlternateCT, mOriginCT;
mTransactionOrigin->mChannel->GetContentType(mOriginCT);
mTransactionAlternate->mChannel->GetContentType(mAlternateCT);
nsCOMPtr<nsIWellKnownOpportunisticUtils> uu =
do_CreateInstance(NS_WELLKNOWNOPPORTUNISTICUTILS_CONTRACTID); bool accepted = false;
if (!mTransactionOrigin->mStatusOK) {
LOG(("WellKnownChecker::Done %p origin was not 200 response code\n", this));
} elseif (!mTransactionAlternate->mAuthOK) {
LOG(("WellKnownChecker::Done %p alternate was not TLS authenticated\n", this));
} elseif (!mTransactionAlternate->mStatusOK) {
LOG(("WellKnownChecker::Done %p alternate was not 200 response code\n", this));
} elseif (!mTransactionAlternate->mVersionOK) {
LOG(("WellKnownChecker::Done %p alternate was not at least h2 or h3\n", this));
} elseif (!mTransactionAlternate->mWKResponse.Equals(
mTransactionOrigin->mWKResponse)) {
LOG(
("WellKnownChecker::Done %p alternate and origin " ".wk representations don't match\norigin: %s\alternate:%s\n", this, mTransactionOrigin->mWKResponse.get(),
mTransactionAlternate->mWKResponse.get()));
} elseif (!mAlternateCT.Equals(mOriginCT)) {
LOG(
("WellKnownChecker::Done %p alternate and origin content types " "dont match\n", this));
} elseif (!mAlternateCT.EqualsLiteral("application/json")) {
LOG(("WellKnownChecker::Done %p .wk content type is %s\n", this,
mAlternateCT.get()));
} elseif (!uu) {
LOG(("WellKnownChecker::Done %p json parser service unavailable\n", this));
} else {
accepted = true;
}
if (accepted) {
MOZ_ASSERT(!mMapping->HTTPS()); // https:// does not use .wk
NS_IMETHODIMP
TransactionObserver::OnStartRequest(nsIRequest* aRequest) {
MOZ_ASSERT(NS_IsMainThread()); // only consider the first 32KB.. because really.
mWKResponse.SetCapacity(MAX_WK); return NS_OK;
}
auto initTask = [&]() {
MOZ_ASSERT(NS_IsMainThread());
// nsIDataStorage gives synchronous access to a memory based hash table // that is backed by disk where those writes are done asynchronously // on another thread
nsCOMPtr<nsIDataStorageManager> dataStorageManager(
do_GetService("@mozilla.org/security/datastoragemanager;1")); if (!dataStorageManager) {
LOG(("AltSvcCache::EnsureStorageInited WARN NO STORAGE MANAGER\n")); return;
}
nsresult rv = dataStorageManager->Get(
nsIDataStorageManager::AlternateServices, getter_AddRefs(mStorage)); if (NS_FAILED(rv) || !mStorage) {
LOG(("AltSvcCache::EnsureStorageInited WARN NO STORAGE\n")); return;
}
initialized = true;
mStorageEpoch = NowInSeconds();
};
if (NS_IsMainThread()) {
initTask(); return;
}
nsCOMPtr<nsIEventTarget> main = GetMainThreadSerialEventTarget(); if (!main) { return;
}
if (NS_IsMainThread()) { bool isReady;
nsresult rv = mStorage->IsReady(&isReady); if (NS_FAILED(rv)) {
LOG(("AltSvcCache::LookupMapping %p mStorage->IsReady failed\n", this)); return nullptr;
} if (!isReady) {
LOG(("AltSvcCache::LookupMapping %p skip when storage is not ready\n", this)); return nullptr;
}
}
nsAutoCString val;
nsresult rv =
mStorage->Get(key,
privateBrowsing ? nsIDataStorage::DataType::Private
: nsIDataStorage::DataType::Persistent,
val); if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
LOG(("AltSvcCache::LookupMapping %p mStorage->Get failed \n", this)); return nullptr;
} if (rv == NS_ERROR_NOT_AVAILABLE || val.IsEmpty()) {
LOG(("AltSvcCache::LookupMapping %p MISS\n", this)); return nullptr;
}
RefPtr<AltSvcMapping> mapping = new AltSvcMapping(mStorage, mStorageEpoch, val); if (!mapping->Validated() && (mapping->StorageEpoch() != mStorageEpoch)) { // this was an in progress validation abandoned in a different session // rare edge case will not detect session change - that's ok as only impact // will be loss of alt-svc to this origin for this session.
LOG(("AltSvcCache::LookupMapping %p invalid hit - MISS\n", this));
(void)mStorage->Remove(key, mapping->Private()
? nsIDataStorage::DataType::Private
: nsIDataStorage::DataType::Persistent); return nullptr;
}
if (mapping->IsHttp3() &&
(!nsHttpHandler::IsHttp3Enabled() ||
!gHttpHandler->IsHttp3VersionSupported(mapping->NPNToken()) ||
gHttpHandler->IsHttp3Excluded(mapping->AlternateHost()))) { // If Http3 is disabled or the version not supported anymore, remove the // mapping.
(void)mStorage->Remove(key, mapping->Private()
? nsIDataStorage::DataType::Private
: nsIDataStorage::DataType::Persistent); return nullptr;
}
if (existing && existing->Validated()) { if (existing->RouteEquals(map)) { // update expires in storage // if this is http:// then a ttl can only be extended via .wk, so ignore // this header path unless it is making things shorter if (existing->HTTPS()) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p map %p updates ttl of " "%p\n", this, map, existing.get()));
existing->SetExpiresAt(map->GetExpiresAt());
} else { if (map->GetExpiresAt() < existing->GetExpiresAt()) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p map %p reduces ttl of " "%p\n", this, map, existing.get()));
existing->SetExpiresAt(map->GetExpiresAt());
} else {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p map %p tries to extend " "%p but" " cannot as without .wk\n", this, map, existing.get()));
}
}
Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_MAPPING_CHANGED_TARGET, false); return;
}
// new alternate. start new validation
LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p may overwrite %p\n", this, map, existing.get()));
Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_MAPPING_CHANGED_TARGET, true);
}
if (existing && !existing->Validated()) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p map %p ignored because %p " "still in progress\n", this, map, existing.get())); return;
}
if (map->IsHttp3()) { bool isDirectOrNoProxy = pi ? pi->IsDirect() : true; if (!isDirectOrNoProxy) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p map %p ignored h3 because " "proxy is in use %p\n", this, map, existing.get())); return;
}
}
// start new validation, but don't overwrite a valid existing mapping unless // this completes successfully
MOZ_ASSERT(!map->Validated()); if (!existing) {
map->Sync();
} else {
map->SetSyncOnlyOnSuccess(true);
}
if (map->HTTPS()) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p validation via " "speculative connect started\n", this)); // for https resources we only establish a connection
nsCOMPtr<nsIInterfaceRequestor> callbacks = new AltSvcOverride(aCallbacks);
RefPtr<AltSvcMappingValidator> validator = new AltSvcMappingValidator(map);
RefPtr<SpeculativeTransaction> transaction; if (nsIOService::UseSocketProcess()) {
RefPtr<AltSvcTransactionParent> parent = new AltSvcTransactionParent(ci, aCallbacks, caps, validator); if (!parent->Init()) { return;
}
transaction = parent;
} else {
transaction = new AltSvcTransaction<AltSvcMappingValidator>(
ci, aCallbacks, caps, validator, map->IsHttp3());
}
nsresult rv =
gHttpHandler->SpeculativeConnect(ci, callbacks, caps, transaction); if (NS_FAILED(rv)) {
LOG(
("AltSvcCache::UpdateAltServiceMapping %p " "speculative connect failed with code %08x\n", this, static_cast<uint32_t>(rv)));
}
} else { // for http:// resources we fetch .well-known too
nsAutoCString origin("http://"_ns);
// Check whether origin is an ipv6 address. In that case we need to add // '[]'. if (map->OriginHost().FindChar(':') != kNotFound) {
origin.Append('[');
origin.Append(map->OriginHost());
origin.Append(']');
} else {
origin.Append(map->OriginHost());
} if (map->OriginPort() != NS_HTTP_DEFAULT_PORT) {
origin.Append(':');
origin.AppendInt(map->OriginPort());
}
NS_IMETHODIMP
AltSvcOverride::GetAllow1918(bool* allow) { // normally we don't do speculative connects to 1918.. and we use // speculative connects for the mapping validation, so override // that default here for alt-svc
*allow = true; return NS_OK;
}
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.