thenavisapp
Version:
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
604 lines (493 loc) • 15.7 kB
text/typescript
import {Buffer} from 'node:buffer';
import test from 'ava';
import type {Handler} from 'express';
import nock from 'nock';
import got, {MaxRedirectsError, RequestError} from '../source/index.js';
import withServer, {withHttpsServer} from './helpers/with-server.js';
const reachedHandler: Handler = (_request, response) => {
const body = 'reached';
response.writeHead(200, {
'content-length': body.length,
});
response.end(body);
};
const finiteHandler: Handler = (_request, response) => {
response.writeHead(302, {
location: '/',
});
response.end();
};
const relativeHandler: Handler = (_request, response) => {
response.writeHead(302, {
location: '/',
});
response.end();
};
const unixProtocol: Handler = (_request, response) => {
response.writeHead(302, {
location: 'unix:/var/run/docker.sock:/containers/json',
});
response.end();
};
const unixHostname: Handler = (_request, response) => {
response.writeHead(302, {
location: 'http://unix:/var/run/docker.sock:/containers/json',
});
response.end();
};
test('cannot redirect to UNIX protocol when UNIX sockets are enabled', withServer, async (t, server, got) => {
server.get('/protocol', unixProtocol);
server.get('/hostname', unixHostname);
const gotUnixSocketsEnabled = got.extend({enableUnixSockets: true});
t.true(gotUnixSocketsEnabled.defaults.options.enableUnixSockets);
await t.throwsAsync(gotUnixSocketsEnabled('protocol'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});
await t.throwsAsync(gotUnixSocketsEnabled('hostname'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});
});
test('cannot redirect to UNIX protocol when UNIX sockets are not enabled', withServer, async (t, server, got) => {
server.get('/protocol', unixProtocol);
server.get('/hostname', unixHostname);
const gotUnixSocketsDisabled = got.extend({enableUnixSockets: false});
t.false(gotUnixSocketsDisabled.defaults.options.enableUnixSockets);
await t.throwsAsync(gotUnixSocketsDisabled('protocol'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});
await t.throwsAsync(gotUnixSocketsDisabled('hostname'), {
message: 'Cannot redirect to UNIX socket',
instanceOf: RequestError,
});
});
test('follows redirect', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
const {body, redirectUrls} = await got('finite');
t.is(body, 'reached');
t.deepEqual(redirectUrls.map(String), [`${server.url}/`]);
});
test('does not follow redirect when followRedirect is a function and returns false', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
const {body, statusCode} = await got('finite', {followRedirect: () => false});
t.not(body, 'reached');
t.is(statusCode, 302);
});
test('follows redirect when followRedirect is a function and returns true', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
const {body, redirectUrls} = await got('finite', {followRedirect: () => true});
t.is(body, 'reached');
t.deepEqual(redirectUrls.map(String), [`${server.url}/`]);
});
test('followRedirect gets plainResponse and does not follow', withServer, async (t, server, got) => {
server.get('/temporary', (_request, response) => {
response.writeHead(307, {
location: '/redirect',
});
response.end();
});
const {statusCode} = await got('temporary', {followRedirect(response) {
t.is(response.headers.location, '/redirect');
return false;
}});
t.is(statusCode, 307);
});
test('follows 307, 308 redirect', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/temporary', (_request, response) => {
response.writeHead(307, {
location: '/',
});
response.end();
});
server.get('/permanent', (_request, response) => {
response.writeHead(308, {
location: '/',
});
response.end();
});
const temporaryBody = (await got('temporary')).body;
t.is(temporaryBody, 'reached');
const permBody = (await got('permanent')).body;
t.is(permBody, 'reached');
});
test('does not follow redirect when disabled', withServer, async (t, server, got) => {
server.get('/', finiteHandler);
t.is((await got({followRedirect: false})).statusCode, 302);
});
test('relative redirect works', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/relative', relativeHandler);
t.is((await got('relative')).body, 'reached');
});
test('throws on endless redirects - default behavior', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(302, {
location: server.url,
});
response.end();
});
const error = await t.throwsAsync<MaxRedirectsError>(got(''), {message: 'Redirected 10 times. Aborting.'});
t.deepEqual(error?.response.redirectUrls.map(String), Array.from({length: 10}).fill(`${server.url}/`));
t.is(error?.code, 'ERR_TOO_MANY_REDIRECTS');
});
test('custom `maxRedirects` option', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(302, {
location: server.url,
});
response.end();
});
const error = await t.throwsAsync<MaxRedirectsError>(got('', {maxRedirects: 5}), {message: 'Redirected 5 times. Aborting.'});
t.deepEqual(error?.response.redirectUrls.map(String), Array.from({length: 5}).fill(`${server.url}/`));
t.is(error?.code, 'ERR_TOO_MANY_REDIRECTS');
});
test('searchParams are not breaking redirects', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/relativeSearchParam', (request, response) => {
t.is(request.query.bang, '1');
response.writeHead(302, {
location: '/',
});
response.end();
});
t.is((await got('relativeSearchParam', {searchParams: 'bang=1'})).body, 'reached');
});
test('redirects GET and HEAD requests', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(308, {
location: '/',
});
response.end();
});
await t.throwsAsync(got.get(''), {
instanceOf: MaxRedirectsError,
code: 'ERR_TOO_MANY_REDIRECTS',
});
});
test('redirects POST requests', withServer, async (t, server, got) => {
server.post('/', (_request, response) => {
response.writeHead(308, {
location: '/',
});
response.end();
});
await t.throwsAsync(got.post({body: 'wow'}), {
instanceOf: MaxRedirectsError,
code: 'ERR_TOO_MANY_REDIRECTS',
});
});
test('redirects on 303 if GET or HEAD', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.head('/seeOther', (_request, response) => {
response.writeHead(303, {
location: '/',
});
response.end();
});
const {url, headers, request} = await got.head('seeOther');
t.is(url, `${server.url}/`);
t.is(headers['content-length'], 'reached'.length.toString());
t.is(request.options.method, 'HEAD');
});
test('removes body on GET redirect', withServer, async (t, server, got) => {
server.get('/', (request, response) => request.pipe(response));
server.post('/seeOther', (_request, response) => {
response.writeHead(303, {
location: '/',
});
response.end();
});
const {headers, body} = await got.post('seeOther', {body: 'hello'});
t.is(body, '');
t.is(headers['content-length'], '0');
});
test('redirects on 303 response even on post, put, delete', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.post('/seeOther', (_request, response) => {
response.writeHead(303, {
location: '/',
});
response.end();
});
const {url, body} = await got.post('seeOther', {body: 'wow'});
t.is(url, `${server.url}/`);
t.is(body, 'reached');
});
test('redirects from http to https work', withServer, async (t, serverHttp) => {
await withHttpsServer().exec(t, async (t, serverHttps, got) => {
serverHttp.get('/', (_request, response) => {
response.end('http');
});
serverHttps.get('/', (_request, response) => {
response.end('https');
});
serverHttp.get('/httpToHttps', (_request, response) => {
response.writeHead(302, {
location: serverHttps.url,
});
response.end();
});
t.is((await got('httpToHttps', {
prefixUrl: serverHttp.url,
})).body, 'https');
});
});
test('redirects from https to http work', withHttpsServer(), async (t, serverHttps, got) => {
await withServer.exec(t, async (t, serverHttp) => {
serverHttp.get('/', (_request, response) => {
response.end('http');
});
serverHttps.get('/', (_request, response) => {
response.end('https');
});
serverHttps.get('/httpsToHttp', (_request, response) => {
response.writeHead(302, {
location: serverHttp.url,
});
response.end();
});
t.is((await got('httpsToHttp', {
prefixUrl: serverHttps.url,
})).body, 'http');
});
});
test('redirects works with lowercase method', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/relative', relativeHandler);
const {body} = await got('relative', {method: 'head'});
t.is(body, '');
});
test('redirect response contains new url', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
const {url} = await got('finite');
t.is(url, `${server.url}/`);
});
test('redirect response contains old url', withServer, async (t, server, got) => {
server.get('/', reachedHandler);
server.get('/finite', finiteHandler);
const {requestUrl} = await got('finite');
t.is(requestUrl.toString(), `${server.url}/finite`);
});
test('redirect response contains UTF-8 with binary encoding', withServer, async (t, server, got) => {
server.get('/utf8-url-%C3%A1%C3%A9', reachedHandler);
server.get('/redirect-with-utf8-binary', (_request, response) => {
response.writeHead(302, {
location: Buffer.from((new URL('/utf8-url-áé', server.url)).toString(), 'utf8').toString('binary'),
});
response.end();
});
t.is((await got('redirect-with-utf8-binary')).body, 'reached');
});
test('redirect response contains UTF-8 with URI encoding', withServer, async (t, server, got) => {
server.get('/', (request, response) => {
t.is(request.query.test, 'it’s ok');
response.end('reached');
});
server.get('/redirect-with-uri-encoded-location', (_request, response) => {
response.writeHead(302, {
location: new URL('/?test=it’s+ok', server.url).toString(),
});
response.end();
});
t.is((await got('redirect-with-uri-encoded-location')).body, 'reached');
});
test('throws on invalid redirect URL', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(302, {
location: 'http://',
});
response.end();
});
await t.throwsAsync(got(''), {
code: 'ERR_INVALID_URL',
});
});
test('port is reset on redirect', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(307, {
location: 'http://localhost',
});
response.end();
});
nock('http://localhost').get('/').reply(200, 'ok');
const {body} = await got('');
t.is(body, 'ok');
});
test('body is reset on GET redirect', withServer, async (t, server, got) => {
server.post('/', (_request, response) => {
response.writeHead(303, {
location: '/',
});
response.end();
});
server.get('/', (_request, response) => {
response.end();
});
await got.post('', {
body: 'foobar',
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
},
],
},
});
await got.post('', {
json: {foo: 'bar'},
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
},
],
},
});
await got.post('', {
form: {foo: 'bar'},
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
},
],
},
});
});
test('body is passed on POST redirect', withServer, async (t, server, got) => {
server.post('/redirect', (_request, response) => {
response.writeHead(302, {
location: '/',
});
response.end();
});
server.post('/', (request, response) => {
request.pipe(response);
});
const {body} = await got.post('redirect', {
body: 'foobar',
hooks: {
beforeRedirect: [
options => {
t.is(options.body, 'foobar');
},
],
},
});
t.is(body, 'foobar');
});
test('method rewriting', withServer, async (t, server, got) => {
server.post('/redirect', (_request, response) => {
response.writeHead(302, {
location: '/',
});
response.end();
});
server.get('/', (_request, response) => {
response.end();
});
server.post('/temporaryRedirect', (_request, response) => {
response.writeHead(307, {
location: '/',
});
response.end();
});
server.post('/', (request, response) => {
request.pipe(response);
});
const {body} = await got.post('redirect', {
body: 'foobar',
methodRewriting: true,
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
},
],
},
});
t.is(body, '');
// Do not rewrite method on 307 or 308
const {body: temporaryRedirectBody} = await got.post('temporaryRedirect', {
body: 'foobar',
methodRewriting: true,
hooks: {
beforeRedirect: [
options => {
t.is(options.body, 'foobar');
},
],
},
});
t.is(temporaryRedirectBody, 'foobar');
});
test('clears username and password when redirecting to a different hostname', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(302, {
location: 'https://httpbin.org/anything',
});
response.end();
});
const {headers} = await got('', {
username: 'hello',
password: 'world',
}).json<{headers: Record<string, string | undefined>}>();
t.is(headers.Authorization, undefined);
});
test('clears the authorization header when redirecting to a different hostname', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.writeHead(302, {
location: 'https://httpbin.org/anything',
});
response.end();
});
const {headers} = await got('', {
headers: {
authorization: 'Basic aGVsbG86d29ybGQ=',
},
}).json<{headers: Record<string, string | undefined>}>();
t.is(headers.Authorization, undefined);
});
test('preserves userinfo on redirect to the same origin', withServer, async (t, server) => {
server.get('/redirect', (_request, response) => {
response.writeHead(303, {
location: `http://localhost:${server.port}/`,
});
response.end();
});
server.get('/', (request, response) => {
t.is(request.headers.authorization, 'Basic aGVsbG86d29ybGQ=');
response.end();
});
await got(`http://hello:world@localhost:${server.port}/redirect`);
});
test('clears the host header when redirecting to a different hostname', async t => {
nock('https://testweb.com').get('/redirect').reply(302, undefined, {location: 'https://webtest.com/'});
nock('https://webtest.com').get('/').reply(function (_uri, _body) {
return [200, this.req.getHeader('host')];
});
const resp = await got('https://testweb.com/redirect', {headers: {host: 'wrongsite.com'}});
t.is(resp.body, 'webtest.com');
});
test('correct port on redirect', withServer, async (t, server1, got) => {
await withServer.exec(t, async (t, server2) => {
server1.get('/redirect', (_request, response) => {
response.redirect(`http://${server2.hostname}:${server2.port}/`);
});
server1.get('/', (_request, response) => {
response.end('SERVER1');
});
server2.get('/', (_request, response) => {
response.end('SERVER2');
});
const response = await got(`${server1.url}/redirect`, {prefixUrl: ''});
t.is(response.body, 'SERVER2');
});
});