blob: 9bbad4f4019af08228b58b2543d4d38c8366579c [file] [log] [blame]
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('/resources/testharness-helpers.js');
importScripts('/serviceworker/resources/test-helpers.js');
importScripts('/fetch/resources/fetch-test-options.js');
}
function getContentType(headers) {
var content_type = '';
for (var header of headers) {
if (header[0] == 'content-type')
content_type = header[1];
}
return content_type;
}
// token [RFC 2616]
// "token = 1*<any CHAR except CTLs or separators>"
// All octets are tested except for those >= 0x80.
var INVALID_TOKENS = [
'',
// CTL
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f',
// separators
' ', '"', '(', ')', ',', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\',
']', '{', '}',
// non-CHAR
'\x80', '\xff', '\u0100', '\u3042',
// Strings that contain characters above.
'a(b', 'invalid name', 'invalid \r name', 'invalid \n name',
'invalid\r\n name', 'invalid \0 name',
'test\r', 'test\n', 'test\r\n', 'test\0',
'\0'.repeat(100000), '<'.repeat(100000), '\r\n'.repeat(50000),
'x'.repeat(100000) + '\0'];
// Method names.
// A method name must match token in RFC 2616:
// Fetch API Spec: https://fetch.spec.whatwg.org/#concept-method
var INVALID_METHOD_NAMES = INVALID_TOKENS;
// Spec: https://fetch.spec.whatwg.org/#forbidden-method
// "A forbidden method is a method that is a byte case-insensitive match for
// one of `CONNECT`, `TRACE`, and `TRACK`."
var FORBIDDEN_METHODS = ['TRACE', 'TRACK', 'CONNECT',
'trace', 'track', 'connect'];
// Spec: https://fetch.spec.whatwg.org/#concept-method-normalize
// "To normalize a method, if it is a byte case-insensitive match for
// `DELETE`, `GET`, `HEAD`, `OPTIONS`, `POST`, or `PUT`, byte uppercase it"
var TO_BE_NORMALIZED_METHOD_NAMES = [
'delete', 'get', 'head', 'options', 'post', 'put'];
var OTHER_VALID_METHOD_NAMES = [
'!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~',
'0123456789', 'PATCH', 'MKCOL', 'CUSTOM', 'X-FILES', 'p0sT', 'AZaz',
'x'.repeat(100000)];
var VALID_TOKENS = FORBIDDEN_METHODS
.concat(TO_BE_NORMALIZED_METHOD_NAMES)
.concat(OTHER_VALID_METHOD_NAMES);
// Header names and values.
// Header names are divided into the following mutually-exclusive categories:
// - not a name (INVALID_HEADER_NAMES)
// - forbidden header names (FORBIDDEN_HEADER_NAMES)
// - forbidden response header names (FORBIDDEN_RESPONSE_HEADER_NAMES)
// - names of simple headers, except for "Content-Type" (SIMPLE_HEADER_NAMES)
// - "Content-Type" (CONTENT_TYPE): This can be
// a simple header if the header value is in SIMPLE_HEADER_CONTENT_TYPE_VALUES
// or not if the header value is in NON_SIMPLE_HEADER_CONTENT_TYPE_VALUES.
// - others (NON_SIMPLE_HEADER_NAMES)
// A header name must match token in RFC 2616.
// Fetch API Spec: https://fetch.spec.whatwg.org/#concept-header-name
var INVALID_HEADER_NAMES = INVALID_TOKENS;
var INVALID_HEADER_VALUES = [
'test \r data', 'test \n data', 'test \0 data',
'test\r\n data',
'test\r', 'test\n', 'test\r\n', 'test\0',
'\0'.repeat(100000), '\r\n'.repeat(50000), 'x'.repeat(100000) + '\0'];
var FORBIDDEN_HEADER_NAMES =
['Accept-Charset', 'Accept-Encoding', 'Access-Control-Request-Headers',
'Access-Control-Request-Method', 'Connection', 'Content-Length',
'Cookie', 'Cookie2', 'Date', 'DNT', 'Expect', 'Host', 'Keep-Alive',
'Origin', 'Referer', 'TE', 'Trailer', 'Transfer-Encoding', 'Upgrade',
'User-Agent', 'Via', 'Proxy-', 'Sec-', 'Proxy-FooBar', 'Sec-FooBar'];
var FORBIDDEN_RESPONSE_HEADER_NAMES =
['Set-Cookie', 'Set-Cookie2',
'set-cookie', 'set-cookie2',
'set-cOokie', 'set-cOokie2',
'sEt-cookie', 'sEt-cookie2'];
var SIMPLE_HEADER_NAMES = ['Accept', 'Accept-Language', 'Content-Language'];
var CONTENT_TYPE = 'Content-Type';
var NON_SIMPLE_HEADER_NAMES = ['X-Fetch-Test', 'X-Fetch-Test2'];
var SIMPLE_HEADER_CONTENT_TYPE_VALUES =
['application/x-www-form-urlencoded',
'multipart/form-data',
// MIME types are case-insensitive.
'multiPart/foRm-data',
// MIME-type parameters are ignored when determining simple headers.
'multiPart/foRm-data;charset=utf-8',
'text/plain'];
var NON_SIMPLE_HEADER_CONTENT_TYPE_VALUES = ['foo/bar'];
// ResponseInit's statusText must match Reason-Phrase.
// https://fetch.spec.whatwg.org/#dom-response Step 2.
var INVALID_REASON_PHRASE = [
// \x00-\x1F (except for \t) and \x7f are invalid.
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f',
// non-ByteString.
'\u0100', '\u3042',
// Strings that contain characters above.
'invalid \r reason-phrase', 'invalid \n reason-phrase',
'invalid \0 reason-phrase', 'invalid\r\n reason-phrase',
'test\r', 'test\n', 'test\r\n', 'test\0',
'\0'.repeat(100000), '\r\n'.repeat(50000),
'x'.repeat(100000) + '\0'];
var VALID_REASON_PHRASE = [
'\t', ' ', '"', '(', ')', ',', '/', ':', ';', '<', '=', '>', '?', '@', '[',
'\\', ']', '{', '}',
'!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~',
// non-CHAR
'\x80', '\xff',
// Valid strings.
'', '0123456789', '404 Not Found', 'HTTP/1.1 404 Not Found', 'AZ\u00ffaz',
'x'.repeat(100000)];
var INVALID_URLS =
['http://',
'https://',
'http://ex%00ample.com',
'http://ex%0dample.com',
'http://ex%0aample.com',
'http://ex%08ample.com',
'http://ex\x00ample.com'];
function size(headers) {
var count = 0;
for (var header of headers) {
++count;
}
return count;
}
function testBlockMixedContent(mode) {
promise_test(function(t) {
return Promise.resolve()
.then(function() {
// Test 1: Must fail: blocked as mixed content.
return fetch(BASE_URL + 'test1-' + mode, {mode: mode})
.then(t.unreached_func('Test 1: Must be blocked (' +
mode + ', HTTPS->HTTP)'),
function() {});
})
.then(function() {
// Block mixed content in redirects.
// Test 2: Must fail: original fetch is not blocked but
// redirect is blocked.
return fetch(HTTPS_REDIRECT_URL +
encodeURIComponent(BASE_URL + 'test2-' + mode),
{mode: mode})
.then(t.unreached_func('Test 2: Must be blocked (' +
mode + ', HTTPS->HTTPS->HTTP)'),
function() {});
})
.then(function() {
// Test 3: Must fail: original fetch is blocked.
return fetch(REDIRECT_URL +
encodeURIComponent(HTTPS_BASE_URL + 'test3-' + mode),
{mode: mode})
.then(t.unreached_func('Test 3: Must be blocked (' +
mode + ', HTTPS->HTTP->HTTPS)'),
function() {});
})
.then(function() {
// Test 4: Must success.
// Test that the mixed contents above are not rejected due to
return fetch(HTTPS_REDIRECT_URL +
encodeURIComponent(HTTPS_BASE_URL + 'test4-' + mode),
{mode: mode})
.then(function(res) {assert_equals(res.status, 200); },
t.unreached_func('Test 4: Must success (' +
mode + ', HTTPS->HTTPS->HTTPS)'));
})
.then(function() {
// Test 5: Must success if mode is not 'same-origin'.
// Test that the mixed contents above are not rejected due to
// CORS check.
return fetch(HTTPS_OTHER_REDIRECT_URL +
encodeURIComponent(HTTPS_BASE_URL + 'test5-' + mode),
{mode: mode})
.then(function(res) {
if (mode === 'same-origin') {
assert_unreached(
'Test 5: Cross-origin HTTPS request must fail: ' +
'mode = ' + mode);
}
},
function() {
if (mode !== 'same-origin') {
assert_unreached(
'Test 5: Cross-origin HTTPS request must success: ' +
'mode = ' + mode);
}
});
});
}, 'Block fetch() as mixed content (' + mode + ')');
}
function add_referrer_tests(tests) {
for (var test of tests) {
var url = test[0];
var referrer = test[1];
var expected = test[2];
promise_test(((url, referrer, expected, t) => {
var request = new Request(url, {referrer: referrer, mode: 'cors'});
return fetch(new Request(url, request)).then(res => {
return res.json();
}).then(json => {
assert_equals(json.referrer, expected, 'referrer');
});
}).bind(undefined, url, referrer, expected),
'referrer test: url = ' + url + ', referrer = ' + referrer);
}
}