archive-monorepo/@tornado/xhr2-cookies/test/headers.spec.ts
T-Hax 6006120e60
Set up monorepo
Signed-off-by: T-Hax <>
2023-05-03 20:35:27 +00:00

190 lines
7.4 KiB
TypeScript

import * as ava from 'ava';
import { XMLHttpRequest } from '../xml-http-request';
import { HttpServer } from './helpers/server';
function contextualize<T>(getContext: () => T): ava.RegisterContextual<T> {
ava.test.beforeEach(t => {
Object.assign(t.context, getContext());
});
return ava.test;
}
const test = contextualize(() => ({
xhr: new XMLHttpRequest()
}));
test.before(async () => {
await HttpServer.serverStarted;
XMLHttpRequest.nodejsSet({
baseUrl: HttpServer.testUrl().replace('https://', 'http://')
});
});
test.beforeEach(t => {
t.context.xhr = new XMLHttpRequest();
});
test('#setRequestHeader with allowed headers should send the headers', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/headers`);
xhr.responseType = 'text';
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('X-Answer', '42');
xhr.setRequestHeader('X-Header-Name', 'value');
await new Promise(resolve => {
xhr.onload = () => {
t.regex(xhr.responseText, /^\{.*\}$/, 'response text looks like JSON');
const headers = JSON.parse(xhr.responseText);
t.true(headers.hasOwnProperty('authorization'), 'headers have authorization header');
t.is(headers.authorization, 'lol', 'authorization header is correct');
t.true(headers.hasOwnProperty('x-answer'), 'headers have x-answer header');
t.is(headers['x-answer'], '42', 'x-answer header is correct');
t.true(headers.hasOwnProperty('x-header-name'), 'headers have x-header-name header');
t.is(headers['x-header-name'], 'value', 'x-header-name header is correct');
resolve();
};
xhr.send('');
});
});
test('#setRequestHeader with a mix of allowed and forbidden headers should only send the allowed headers', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/headers`);
xhr.responseType = 'text';
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('Proxy-Authorization', 'evil:kitten');
xhr.setRequestHeader('Sec-Breach', 'yes please');
xhr.setRequestHeader('Host', 'www.google.com');
xhr.setRequestHeader('Origin', 'https://www.google.com');
xhr.setRequestHeader('X-Answer', '42');
await new Promise(resolve => {
xhr.onload = () => {
t.regex(xhr.responseText, /^\{.*\}$/, 'response text looks like JSON');
const headers = JSON.parse(xhr.responseText);
t.true(headers.hasOwnProperty('authorization'), 'headers have authorization header');
t.is(headers['authorization'], 'lol', 'authorization header is correct');
t.false(headers.hasOwnProperty('proxy-authorization'), 'headers do not have proxy-authorization header');
t.false(headers.hasOwnProperty('sec-breach'), 'headers do not have sec-breach header');
t.notRegex(headers['origin'] || '', /www\.google\.com/, 'header "origin" should not contain www.google.com');
t.notRegex(headers['host'] || '', /www\.google\.com/, 'header "host" should not contain www.google.com');
t.true(headers.hasOwnProperty('x-answer'), 'headers have x-answer header');
t.is(headers['x-answer'], '42', 'x-answer header is correct');
resolve();
};
xhr.send('');
});
});
test('#setRequestHeader with repeated headers should send all headers', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/headers`);
xhr.responseType = 'text';
xhr.setRequestHeader('Authorization', 'troll');
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('X-Answer', '42');
await new Promise(resolve => {
xhr.onload = () => {
t.regex(xhr.responseText, /^\{.*\}$/, 'response text looks like JSON');
const headers = JSON.parse(xhr.responseText);
t.true(headers.hasOwnProperty('authorization'), 'headers have authorization header');
t.is(headers['authorization'], 'troll, lol, lol', 'authorization header is correct');
t.true(headers.hasOwnProperty('x-answer'), 'headers have x-answer header');
t.is(headers['x-answer'], '42', 'x-answer header is correct');
resolve();
};
xhr.send('');
});
});
test('#setRequestHeader with no headers should set the protected headers correctly', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/headers`);
xhr.responseType = 'text';
xhr.setRequestHeader('Authorization', 'troll');
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('Authorization', 'lol');
xhr.setRequestHeader('X-Answer', '42');
await new Promise(resolve => {
xhr.onload = () => {
t.regex(xhr.responseText, /^\{.*\}$/, 'response text looks like JSON');
const headers = JSON.parse(xhr.responseText);
t.true(headers.hasOwnProperty('connection'), 'headers have connection header');
t.is(headers['connection'], 'keep-alive', 'connection header is correct');
t.true(headers.hasOwnProperty('host'), 'headers have host header');
t.is(headers['host'], `localhost:${HttpServer.port}`, 'host header is correct');
t.true(headers.hasOwnProperty('user-agent'), 'headers have user-agent header');
t.regex(headers['user-agent'], /^Mozilla\//, 'user-agent header is correct');
resolve();
};
xhr.send('');
});
});
test('#getResponseHeader returns accessible headers, returns null for private headers, has headers on HEADERS_RECEIVED readyState', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/get-headers`);
const headerJson = `{
"Accept-Ranges": "bytes",
"Content-Type": "application/xhr2; charset=utf-1337",
"Set-Cookie": "UserID=JohnDoe; Max-Age=3600; Version=1",
"X-Header": "one, more, value"
}`;
await new Promise(resolve => {
xhr.onloadend = () => {
t.is(xhr.getResponseHeader('AccEPt-RANgeS'), 'bytes', 'AccEPt-RANgeS works correctly');
t.is(xhr.getResponseHeader('content-Type'), 'application/xhr2; charset=utf-1337', 'content-Type works correctly');
t.is(xhr.getResponseHeader('X-Header'), 'one, more, value', 'X-Header works correctly');
t.is(xhr.getResponseHeader('set-cookie'), null, 'set-cookie works correctly');
resolve();
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== XMLHttpRequest.HEADERS_RECEIVED) { return; }
t.is(xhr.getResponseHeader('AccEPt-RANgeS'), 'bytes', 'AccEPt-RANgeS works correctly when HEADERS_RECEIVED ready state');
};
xhr.send(headerJson);
});
});
test('#getAllResponseHeaders contains accessible headers, does not contain private headers, has headers on HEADERS_RECEIVED readyState', async t => {
const xhr = t.context.xhr;
xhr.open('POST', `http://localhost:${HttpServer.port}/_/get-headers`);
const headerJson = `{
"Accept-Ranges": "bytes",
"Content-Type": "application/xhr2; charset=utf-1337",
"Set-Cookie": "UserID=JohnDoe; Max-Age=3600; Version=1",
"X-Header": "one, more, value"
}`;
await new Promise(resolve => {
xhr.onloadend = () => {
const headers = xhr.getAllResponseHeaders();
t.regex(headers, /(\A|\r\n)accept-ranges: bytes(\r\n|\Z)/mi);
t.regex(headers, /(\A|\r\n)content-type: application\/xhr2; charset=utf-1337(\r\n|\Z)/mi);
t.regex(headers, /(\A|\r\n)X-Header: one, more, value(\r\n|\Z)/mi);
t.notRegex(headers, /(\A|\r\n)set-cookie:/mi);
resolve();
};
xhr.onreadystatechange = () => {
if (xhr.readyState !== XMLHttpRequest.HEADERS_RECEIVED) { return; }
const headers = xhr.getAllResponseHeaders();
t.regex(headers, /(\A|\r\n)accept-ranges: bytes(\r\n|\Z)/mi);
};
xhr.send(headerJson);
});
});
// TODO:
// * set request header after request opened should throw InvalidStateError
// *