// Create channel for dictionary requests function makeChan(url) {
let chan = NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal: true,
contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
}).QueryInterface(Ci.nsIHttpChannel); return chan;
}
function channelOpenPromise(chan) { returnnew Promise(resolve => { function finish(req, buffer) {
resolve([req, buffer]);
}
chan.asyncOpen(new ChannelListener(finish, null, CL_ALLOW_UNKNOWN_CL));
});
}
// Verify dictionary is stored in cache function verifyDictionaryStored(url, shouldExist, callback) {
let lci = Services.loadContextInfo.custom(false, {
partitionKey: `(https,localhost)`,
});
asyncCheckCacheEntryPresence(url, "disk", shouldExist, lci, callback);
}
// Setup server endpoint that expects specific dictionary headers
async function registerDictionaryAwareEndpoint(
httpServer,
path,
responseContent
) { // We have to put all values and functions referenced in the handler into // this string which will be turned into a function for the handler, because // NodeHTTPSServer handlers can't access items in the local or global scopes of the // containing file
let func = ` // Log the request for analysis
global.requestLog[global.requestLog.length] = {
path: "${path}",
hasAvailableDict: request.headers['available-dictionary'] !== undefined,
availableDict: request.headers['available-dictionary'] || null
};
for (let test of patternMatchTests) {
let url = `https://localhost:${server.port()}${test.url}`;
let expectedDict = RETRIEVAL_TEST_DICTIONARIES[test.dictKey];
let expectedHash = await calculateDictionaryHash(expectedDict.content);
let chan = makeChan(url);
let [, data] = await channelOpenPromise(chan);
Assert.greater(data.length, 0, `Resource ${test.url} should have content`);
// Check request log
await sync_from_server();
let logEntry = requestLog.find(entry => entry.path === test.url); Assert.ok(
logEntry && logEntry.hasAvailableDict,
`Available-Dictionary header should be present for ${test.url}`
); if (logEntry && logEntry.hasAvailableDict) { Assert.ok(
logEntry.availableDict.includes(expectedHash),
`Available-Dictionary header should contain expected hash for ${test.url}`
);
}
}
});
// Test dictionary precedence when multiple patterns match
add_task(async function test_dictionary_precedence() { // Test URL that matches multiple patterns: /api/v1/users // Should match: "/api/v1/*" (most specific), "/api/*", "*" (wildcard) // Most specific pattern should take precedence
let url = `https://localhost:${server.port()}/api/v1/users`;
requestLog = [];
await sync_to_server();
let mostSpecificHash = await calculateDictionaryHash(
RETRIEVAL_TEST_DICTIONARIES.api_v1.content
);
let chan = makeChan(url);
let [, data] = await channelOpenPromise(chan);
Assert.equal(data, "API V1 USERS DATA", "Content should match");
// Check request log for precedence
await sync_from_server();
let logEntry = requestLog.find(entry => entry.path === "/api/v1/users"); Assert.ok(
logEntry && logEntry.hasAvailableDict, "Available-Dictionary header should be present for precedence test"
); if (logEntry && logEntry.hasAvailableDict) { // The most specific pattern (/api/v1/*) should be included // Implementation may include multiple matching dictionaries Assert.ok(
logEntry.availableDict.includes(mostSpecificHash), "Available-Dictionary header should contain most specific pattern hash"
);
}
});
// Test successful dictionary lookup and usage
add_task(async function test_dictionary_cache_hit() {
let url = `https://localhost:${server.port()}/api/generic`;
requestLog = [];
await sync_to_server();
let expectedHash = await calculateDictionaryHash(
RETRIEVAL_TEST_DICTIONARIES.api_generic.content
);
let chan = makeChan(url);
let [, data] = await channelOpenPromise(chan);
Assert.equal(data, "GENERIC API DATA", "Content should match");
// Verify dictionary lookup succeeded
await sync_from_server();
let logEntry = requestLog.find(entry => entry.path === "/api/generic"); Assert.ok(
logEntry && logEntry.hasAvailableDict, "Available-Dictionary header should be present for cache hit"
); if (logEntry && logEntry.hasAvailableDict) { Assert.ok(
logEntry.availableDict.includes(expectedHash), "Available-Dictionary header should contain expected hash for cache hit"
);
}
});
// Test Available-Dictionary header hash format compliance
add_task(async function test_dictionary_hash_format() { // Test that dictionary hashes follow IETF spec format: :base64hash:
let testDict = RETRIEVAL_TEST_DICTIONARIES.api_v1;
let calculatedHash = await calculateDictionaryHash(testDict.content);
// Verify hash is base64 format Assert.greater(calculatedHash.length, 0, "Hash should not be empty");
// Verify base64 pattern (rough check)
let base64Pattern = /^[A-Za-z0-9+/]*={0,2}$/; Assert.ok(base64Pattern.test(calculatedHash), "Hash should be valid base64");
// The hash format should be structured field byte sequence: :base64:
let structuredFieldFormat = `:${calculatedHash}:`; Assert.ok(
structuredFieldFormat.includes(calculatedHash), "Hash should follow structured field format"
);
});
// Test retrieval with multiple dictionary matches
add_task(async function test_multiple_dictionary_matches() { // Create a request that could match multiple dictionaries
let url = `https://localhost:${server.port()}/api/test`;
requestLog = [];
await sync_to_server();
await registerDictionaryAwareEndpoint(server, "/api/test", "API TEST DATA");
let apiGenericHash = await calculateDictionaryHash(
RETRIEVAL_TEST_DICTIONARIES.api_generic.content
);
let chan = makeChan(url);
let [, data] = await channelOpenPromise(chan);
Assert.equal(data, "API TEST DATA", "Content should match");
// Check for multiple dictionary hashes in Available-Dictionary header
await sync_from_server();
let logEntry = requestLog.find(entry => entry.path === "/api/test"); Assert.ok(
logEntry && logEntry.hasAvailableDict, "Available-Dictionary header should be present for multiple matches"
); if (logEntry && logEntry.hasAvailableDict) { // Could match both /api/* and * patterns - verify the longest pattern's hash is present // (IETF spec says the longest match should be used)
let hasApiGenericHash = logEntry.availableDict.includes(apiGenericHash); Assert.ok(
hasApiGenericHash, "Available-Dictionary header should contain at least one expected hash for multiple matches"
);
}
});
// Test case-insensitive Vary header removal (Bug 2010968)
add_task(async function test_vary_header_case_insensitive_removal() { // Test that RemoveFromVary removes Accept-Encoding regardless of case // Both "Accept-Encoding" and "accept-encoding" should be properly removed
// Test 1: Uppercase Accept-Encoding
let urlUpper = `https://localhost:${server.port()}/dict/vary-uppercase`;
let chanUpper = makeChan(urlUpper);
let [reqUpper, dataUpper] = await channelOpenPromise(chanUpper); Assert.equal(
dataUpper, "VARY_UPPERCASE_DICT_DATA", "Dictionary with uppercase Accept-Encoding should be fetched"
);
// Check the Vary header - Accept-Encoding should be removed
let varyUpper = reqUpper
.QueryInterface(Ci.nsIHttpChannel)
.getResponseHeader("Vary"); Assert.ok(
!varyUpper.toLowerCase().includes("accept-encoding"),
`Vary header should not contain Accept-Encoding (was: "${varyUpper}")`
); Assert.ok(
varyUpper.includes("User-Agent"), "Vary header should still contain User-Agent"
);
// Verify dictionary was stored
await new Promise(resolve => {
verifyDictionaryStored(urlUpper, true, resolve);
});
// Test 2: Lowercase accept-encoding
let urlLower = `https://localhost:${server.port()}/dict/vary-lowercase`;
let chanLower = makeChan(urlLower);
let [reqLower, dataLower] = await channelOpenPromise(chanLower); Assert.equal(
dataLower, "VARY_LOWERCASE_DICT_DATA", "Dictionary with lowercase accept-encoding should be fetched"
);
// Check the Vary header - accept-encoding should be removed
let varyLower = reqLower
.QueryInterface(Ci.nsIHttpChannel)
.getResponseHeader("Vary"); Assert.ok(
!varyLower.toLowerCase().includes("accept-encoding"),
`Vary header should not contain accept-encoding (was: "${varyLower}")`
); Assert.ok(
varyLower.includes("User-Agent"), "Vary header should still contain User-Agent"
);
// Verify dictionary was stored
await new Promise(resolve => {
verifyDictionaryStored(urlLower, true, resolve);
});
// Test 3: Mixed case AcCePt-EnCoDiNg
let urlMixed = `https://localhost:${server.port()}/dict/vary-mixedcase`;
let chanMixed = makeChan(urlMixed);
let [reqMixed, dataMixed] = await channelOpenPromise(chanMixed); Assert.equal(
dataMixed, "VARY_MIXEDCASE_DICT_DATA", "Dictionary with mixed case Accept-Encoding should be fetched"
);
// Check the Vary header - AcCePt-EnCoDiNg should be removed
let varyMixed = reqMixed
.QueryInterface(Ci.nsIHttpChannel)
.getResponseHeader("Vary"); Assert.ok(
!varyMixed.toLowerCase().includes("accept-encoding"),
`Vary header should not contain Accept-Encoding in any case (was: "${varyMixed}")`
); Assert.ok(
varyMixed.includes("User-Agent"), "Vary header should still contain User-Agent"
);
// Verify dictionary was stored
await new Promise(resolve => {
verifyDictionaryStored(urlMixed, true, resolve);
});
dump("**** Case-insensitive Vary header removal test complete\n");
});
// Cleanup
add_task(async function cleanup() { // Clear cache
let lci = Services.loadContextInfo.custom(false, {
partitionKey: `(https,localhost)`,
});
evict_cache_entries("all", lci);
});
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet am 2026-09-27)
¤
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 und die Messung sind noch experimentell.