forked from tornado-packages/archive-monorepo
6006120e60
Signed-off-by: T-Hax <>
190 lines
7.4 KiB
TypeScript
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
|
|
// *
|