/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=4 sw=2 sts=2 et 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"
// find out how many atoms we have #define HTTP_ATOM(_name, _value) Unused_##_name, enum { #include"nsHttpAtomList.h"
NUM_HTTP_ATOMS
}; #undef HTTP_ATOM
// This is set to true in DestroyAtomTable so we don't try to repopulate the // table if ResolveAtom gets called during shutdown for some reason. static Atomic<bool> sTableDestroyed{false};
// We put the atoms in a hash table for speedy lookup.. see ResolveAtom. namespace nsHttp {
nsresult CreateAtomTable(
nsTHashtable<nsCStringASCIICaseInsensitiveHashKey>& base) { if (sTableDestroyed) { return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
} // fill the table with our known atoms const nsHttpAtomLiteral* atoms[] = { #define HTTP_ATOM(_name, _value) &(_name), #include"nsHttpAtomList.h" #undef HTTP_ATOM
};
if (!base.IsEmpty()) { return NS_OK;
} for (constauto* atom : atoms) {
Unused << base.PutEntry(atom->val(), fallible);
}
LOG(("Added static atoms to atomTable")); return NS_OK;
}
// this function may be called from multiple threads
nsHttpAtom ResolveAtom(const nsACString& str) { if (str.IsEmpty()) { return {};
}
auto atomTable = sAtomTable.Lock();
if (atomTable.ref().IsEmpty()) { if (sTableDestroyed) {
NS_WARNING("ResolveAtom called during shutdown"); return {};
}
NS_WARNING("ResolveAtom called before CreateAtomTable"); if (NS_FAILED(CreateAtomTable(atomTable.ref()))) { return {};
}
}
// Check if we already have an entry in the table auto* entry = atomTable.ref().GetEntry(str); if (entry) { return nsHttpAtom(entry->GetKey());
}
LOG(("Putting %s header into atom table", nsPromiseFlatCString(str).get())); // Put the string in the table. If it works create the atom.
entry = atomTable.ref().PutEntry(str, fallible); if (entry) { return nsHttpAtom(entry->GetKey());
} return {};
}
// static bool IsReasonableHeaderValue(const nsACString& s) { // Header values MUST NOT contain line-breaks. RFC 2616 technically // permits CTL characters, including CR and LF, in header values provided // they are quoted. However, this can lead to problems if servers do not // interpret quoted strings properly. Disallowing CR and LF here seems // reasonable and keeps things simple. We also disallow a null byte. const nsACString::char_type* end = s.EndReading(); for (const nsACString::char_type* i = s.BeginReading(); i != end; ++i) { if (*i == '\r' || *i == '\n' || *i == '\0') { returnfalse;
}
} returntrue;
}
char* end = nullptr;
errno = 0; // Clear errno to make sure its value is set by strtoll
int64_t value = strtoll(input, &end, /* base */ 10);
// Fail if: - the parsed number overflows. // - the end points to the start of the input string. // - we parsed a negative value. Consumers don't expect that. if (errno != 0 || end == input || value < 0) {
LOG(("nsHttp::ParseInt64 value=%" PRId64 " errno=%d", value, errno)); returnfalse;
}
// Check isForcedValid to see if it is possible to skip validation. // Don't skip validation if we have serious reason to believe that this // content is invalid (it's expired). // See netwerk/cache2/nsICacheEntry.idl for details if (isForcedValid && (!cachedResponseHead->ExpiresInPast() ||
!cachedResponseHead->MustValidateIfExpired())) {
LOG(("NOT validating based on isForcedValid being true.\n")); returnfalse;
}
// If the LOAD_FROM_CACHE flag is set, any cached data can simply be used if (loadFlags & nsIRequest::LOAD_FROM_CACHE || allowStaleCacheContent) {
LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n")); returnfalse;
}
// If the VALIDATE_ALWAYS flag is set, any cached data won't be used until // it's revalidated with the server. if (((loadFlags & nsIRequest::VALIDATE_ALWAYS) ||
forceValidateCacheContent) &&
!isImmutable) {
LOG(("Validating based on VALIDATE_ALWAYS load flag\n")); returntrue;
}
// Even if the VALIDATE_NEVER flag is set, there are still some cases in // which we must validate the cached response with the server. if (loadFlags & nsIRequest::VALIDATE_NEVER) {
LOG(("VALIDATE_NEVER set\n")); // if no-store validate cached response (see bug 112564) if (cachedResponseHead->NoStore()) {
LOG(("Validating based on no-store logic\n")); returntrue;
}
LOG(("NOT validating based on VALIDATE_NEVER load flag\n")); returnfalse;
}
// check if validation is strictly required... if (cachedResponseHead->MustValidate()) {
LOG(("Validating based on MustValidate() returning TRUE\n")); returntrue;
}
// possibly serve from cache for a custom If-Match/If-Unmodified-Since // conditional request if (customConditionalRequest && !requestHead.HasHeader(nsHttp::If_Match) &&
!requestHead.HasHeader(nsHttp::If_Unmodified_Since)) {
LOG(("Validating based on a custom conditional request\n")); returntrue;
}
// previously we also checked for a query-url w/out expiration // and didn't do heuristic on it. but defacto that is allowed now. // // Check if the cache entry has expired...
bool doValidation = true;
uint32_t now = NowInSeconds();
uint32_t age = 0;
nsresult rv = cachedResponseHead->ComputeCurrentAge(now, now, &age); if (NS_FAILED(rv)) { returntrue;
}
if (cacheControlRequest.NoCache()) {
LOG((" validating, no-cache request"));
doValidation = true;
} elseif (cacheControlRequest.MaxStale(&maxStaleRequest)) {
uint32_t staleTime = age > freshness ? age - freshness : 0;
doValidation = staleTime > maxStaleRequest;
LOG((" validating=%d, max-stale=%u requested", doValidation,
maxStaleRequest));
} elseif (cacheControlRequest.MaxAge(&maxAgeRequest)) { // The input information for age and freshness calculation are in seconds. // Hence, the internal logic can't have better resolution than seconds too. // To make max-age=0 case work even for requests made in less than a second // after the last response has been received, we use >= for compare. This // is correct because of the rounding down of the age calculated value.
doValidation = age >= maxAgeRequest;
LOG((" validating=%d, max-age=%u requested", doValidation, maxAgeRequest));
} elseif (cacheControlRequest.MinFresh(&minFreshRequest)) {
uint32_t freshTime = freshness > age ? freshness - age : 0;
doValidation = freshTime < minFreshRequest;
LOG((" validating=%d, min-fresh=%u requested", doValidation,
minFreshRequest));
} elseif (now < expiration) {
doValidation = false;
LOG((" not validating, expire time not in the past"));
} elseif (cachedResponseHead->MustValidateIfExpired()) {
doValidation = true;
} elseif (cachedResponseHead->StaleWhileRevalidate(now, expiration) &&
StaticPrefs::network_http_stale_while_revalidate_enabled()) {
LOG((" not validating, in the stall-while-revalidate window"));
doValidation = false; if (performBackgroundRevalidation) {
*performBackgroundRevalidation = true;
}
} elseif (loadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) { // If the cached response does not include expiration infor- // mation, then we must validate the response, despite whether // or not this is the first access this session. This behavior // is consistent with existing browsers and is generally expected // by web authors. if (freshness == 0) {
doValidation = true;
} else {
doValidation = fromPreviousSession;
}
} else {
doValidation = true;
}
LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v")); return doValidation;
}
nsresult GetHttpResponseHeadFromCacheEntry(
nsICacheEntry* entry, nsHttpResponseHead* cachedResponseHead) {
nsCString buf; // A "original-response-headers" metadata element holds network original // headers, i.e. the headers in the form as they arrieved from the network. We // need to get the network original headers first, because we need to keep // them in order.
nsresult rv = entry->GetMetaDataElement("original-response-headers",
getter_Copies(buf)); if (NS_SUCCEEDED(rv)) {
rv = cachedResponseHead->ParseCachedOriginalHeaders((char*)buf.get()); if (NS_FAILED(rv)) {
LOG((" failed to parse original-response-headers\n"));
}
}
buf.Adopt(nullptr); // A "response-head" metadata element holds response head, e.g. response // status line and headers in the form Firefox uses them internally (no // dupicate headers, etc.).
rv = entry->GetMetaDataElement("response-head", getter_Copies(buf));
NS_ENSURE_SUCCESS(rv, rv);
// Parse string stored in a "response-head" metadata element. // These response headers will be merged with the orignal headers (i.e. the // headers stored in a "original-response-headers" metadata element).
rv = cachedResponseHead->ParseCachedHead(buf.get());
NS_ENSURE_SUCCESS(rv, rv);
buf.Adopt(nullptr);
if (!responseHead) { return NS_ERROR_UNEXPECTED;
}
*aContentLength = responseHead->ContentLength();
return NS_OK;
}
void DetermineFramingAndImmutability(nsICacheEntry* entry,
nsHttpResponseHead* responseHead, bool isHttps, bool* weaklyFramed, bool* isImmutable) {
nsCString framedBuf;
nsresult rv =
entry->GetMetaDataElement("strongly-framed", getter_Copies(framedBuf)); // describe this in terms of explicitly weakly framed so as to be backwards // compatible with old cache contents which dont have strongly-framed makers
*weaklyFramed = NS_SUCCEEDED(rv) && framedBuf.EqualsLiteral("0");
*isImmutable = !*weaklyFramed && isHttps && responseHead->Immutable();
}
nsCString ConvertRequestHeadToString(nsHttpRequestHead& aRequestHead, bool aHasRequestBody, bool aRequestBodyHasHeaders, bool aUsingConnect) { // Make sure that there is "Content-Length: 0" header in the requestHead // in case of POST and PUT methods when there is no requestBody and // requestHead doesn't contain "Transfer-Encoding" header. // // RFC1945 section 7.2.2: // HTTP/1.0 requests containing an entity body must include a valid // Content-Length header field. // // RFC2616 section 4.4: // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests // containing a message-body MUST include a valid Content-Length header // field unless the server is known to be HTTP/1.1 compliant. if ((aRequestHead.IsPost() || aRequestHead.IsPut()) && !aHasRequestBody &&
!aRequestHead.HasHeader(nsHttp::Transfer_Encoding)) {
DebugOnly<nsresult> rv =
aRequestHead.SetHeader(nsHttp::Content_Length, "0"_ns);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
nsCString reqHeaderBuf;
reqHeaderBuf.Truncate();
// make sure we eliminate any proxy specific headers from // the request if we are using CONNECT
aRequestHead.Flatten(reqHeaderBuf, aUsingConnect);
if (!aRequestBodyHasHeaders || !aHasRequestBody) {
reqHeaderBuf.AppendLiteral("\r\n");
}
return reqHeaderBuf;
}
void NotifyActiveTabLoadOptimization() { if (gHttpHandler) {
gHttpHandler->NotifyActiveTabLoadOptimization();
}
}
nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode) { // In proxy CONNECT case, we treat every response code except 200 as an error. // Even if the proxy server returns other 2xx codes (i.e. 206), this function // still returns an error code.
MOZ_ASSERT(aStatusCode != 200);
nsresult rv; switch (aStatusCode) { case 300: case 301: case 302: case 303: case 307: case 308: // Bad redirect: not top-level, or it's a POST, bad/missing Location, // or ProcessRedirect() failed for some other reason. Legal // redirects that fail because site not available, etc., are handled // elsewhere, in the regular codepath.
rv = NS_ERROR_CONNECTION_REFUSED; break; // Squid sends 404 if DNS fails (regular 404 from target is tunneled) case 404: // HTTP/1.1: "Not Found" // RFC 2616: "some deployed proxies are known to return 400 or // 500 when DNS lookups time out." (Squid uses 500 if it runs // out of sockets: so we have a conflict here). case 400: // HTTP/1.1 "Bad Request" case 500: // HTTP/1.1: "Internal Server Error"
rv = NS_ERROR_UNKNOWN_HOST; break; case 401:
rv = NS_ERROR_PROXY_UNAUTHORIZED; break; case 402:
rv = NS_ERROR_PROXY_PAYMENT_REQUIRED; break; case 403:
rv = NS_ERROR_PROXY_FORBIDDEN; break; case 405:
rv = NS_ERROR_PROXY_METHOD_NOT_ALLOWED; break; case 406:
rv = NS_ERROR_PROXY_NOT_ACCEPTABLE; break; case 407: // ProcessAuthentication() failed (e.g. no header)
rv = NS_ERROR_PROXY_AUTHENTICATION_FAILED; break; case 408:
rv = NS_ERROR_PROXY_REQUEST_TIMEOUT; break; case 409:
rv = NS_ERROR_PROXY_CONFLICT; break; case 410:
rv = NS_ERROR_PROXY_GONE; break; case 411:
rv = NS_ERROR_PROXY_LENGTH_REQUIRED; break; case 412:
rv = NS_ERROR_PROXY_PRECONDITION_FAILED; break; case 413:
rv = NS_ERROR_PROXY_REQUEST_ENTITY_TOO_LARGE; break; case 414:
rv = NS_ERROR_PROXY_REQUEST_URI_TOO_LONG; break; case 415:
rv = NS_ERROR_PROXY_UNSUPPORTED_MEDIA_TYPE; break; case 416:
rv = NS_ERROR_PROXY_REQUESTED_RANGE_NOT_SATISFIABLE; break; case 417:
rv = NS_ERROR_PROXY_EXPECTATION_FAILED; break; case 421:
rv = NS_ERROR_PROXY_MISDIRECTED_REQUEST; break; case 425:
rv = NS_ERROR_PROXY_TOO_EARLY; break; case 426:
rv = NS_ERROR_PROXY_UPGRADE_REQUIRED; break; case 428:
rv = NS_ERROR_PROXY_PRECONDITION_REQUIRED; break; case 429:
rv = NS_ERROR_PROXY_TOO_MANY_REQUESTS; break; case 431:
rv = NS_ERROR_PROXY_REQUEST_HEADER_FIELDS_TOO_LARGE; break; case 451:
rv = NS_ERROR_PROXY_UNAVAILABLE_FOR_LEGAL_REASONS; break; case 501:
rv = NS_ERROR_PROXY_NOT_IMPLEMENTED; break; case 502:
rv = NS_ERROR_PROXY_BAD_GATEWAY; break; case 503: // Squid returns 503 if target request fails for anything but DNS. /* User sees: "Failed to Connect: * Firefox can't establish a connection to the server at * www.foo.com. Though the site seems valid, the browser * was unable to establish a connection."
*/
rv = NS_ERROR_CONNECTION_REFUSED; break; // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to // do here: picking target timeout, as DNS covered by 400/404/500 case 504:
rv = NS_ERROR_PROXY_GATEWAY_TIMEOUT; break; case 505:
rv = NS_ERROR_PROXY_VERSION_NOT_SUPPORTED; break; case 506:
rv = NS_ERROR_PROXY_VARIANT_ALSO_NEGOTIATES; break; case 510:
rv = NS_ERROR_PROXY_NOT_EXTENDED; break; case 511:
rv = NS_ERROR_PROXY_NETWORK_AUTHENTICATION_REQUIRED; break; default:
rv = NS_ERROR_PROXY_CONNECTION_REFUSED; break;
}
if (StaticPrefs::network_http_http2_enabled()) {
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo(); if (aAlpn.Equals(spdyInfo->VersionString)) { return SupportedAlpnRank::HTTP_2;
}
}
if (aAlpn.LowerCaseEqualsASCII("http/1.1")) { return SupportedAlpnRank::HTTP_1_1;
}
return SupportedAlpnRank::NOT_SUPPORTED;
}
// NSS Errors which *may* have been triggered by the use of 0-RTT in the // presence of badly behaving middleboxes. We may re-attempt the connection // without early data. bool PossibleZeroRTTRetryError(nsresult aReason) { return (aReason ==
psm::GetXPCOMFromNSSError(SSL_ERROR_PROTOCOL_VERSION_ALERT)) ||
(aReason == psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_MAC_ALERT)) ||
(aReason ==
psm::GetXPCOMFromNSSError(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT));
}
uint8_t Http3ErrorToWebTransportError(uint64_t aErrorCode) { // Ensure the code is within the valid range. if (aErrorCode < kWebTransportErrorCodeStart ||
aErrorCode > kWebTransportErrorCodeEnd) { return 0;
}
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.