mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00

Implemented new StringUtil helper functions: splitCommaSeparated, sanitizeUrlPart, isValidFileName. Added helper functions to HeaderUtil: matchesAuthorizationScheme, hasScheme. Added unit tests for the new helper functions. Refactored codebase to use helper functions instead of regexes if applicable.
148 lines
6.1 KiB
TypeScript
148 lines
6.1 KiB
TypeScript
import type { Server } from 'http';
|
|
import request from 'supertest';
|
|
import type { BaseHttpServerFactory } from '../../src/server/BaseHttpServerFactory';
|
|
import type { HttpHandlerInput } from '../../src/server/HttpHandler';
|
|
import { HttpHandler } from '../../src/server/HttpHandler';
|
|
import { splitCommaSeparated } from '../../src/util/StringUtil';
|
|
import { getPort } from '../util/Util';
|
|
import { getTestConfigPath, instantiateFromConfig } from './Config';
|
|
|
|
const port = getPort('Middleware');
|
|
|
|
class SimpleHttpHandler extends HttpHandler {
|
|
public async handle(input: HttpHandlerInput): Promise<void> {
|
|
input.response.writeHead(200, { location: '/' });
|
|
input.response.end('Hello World');
|
|
}
|
|
}
|
|
|
|
describe('An http server with middleware', (): void => {
|
|
let server: Server;
|
|
|
|
beforeAll(async(): Promise<void> => {
|
|
const factory = await instantiateFromConfig(
|
|
'urn:solid-server:default:HttpServerFactory',
|
|
getTestConfigPath('server-middleware.json'),
|
|
{
|
|
'urn:solid-server:default:LdpHandler': new SimpleHttpHandler(),
|
|
'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
|
|
'urn:solid-server:default:variable:showStackTrace': true,
|
|
},
|
|
) as BaseHttpServerFactory;
|
|
server = factory.startServer(port);
|
|
});
|
|
|
|
afterAll(async(): Promise<void> => {
|
|
server.close();
|
|
});
|
|
|
|
it('sets a Vary header containing Accept.', async(): Promise<void> => {
|
|
const res = await request(server).get('/');
|
|
expect(res.header).toEqual(expect.objectContaining({
|
|
vary: expect.stringMatching(/(^|,)\s*Accept\s*(,|$)/iu),
|
|
}));
|
|
});
|
|
|
|
it('sets a Vary header containing Authorization.', async(): Promise<void> => {
|
|
const res = await request(server).get('/');
|
|
expect(res.header).toEqual(expect.objectContaining({
|
|
vary: expect.stringMatching(/(^|,)\s*Authorization\s*(,|$)/iu),
|
|
}));
|
|
});
|
|
|
|
it('sets a Vary header containing Origin.', async(): Promise<void> => {
|
|
const res = await request(server).get('/');
|
|
expect(res.header).toEqual(expect.objectContaining({
|
|
vary: expect.stringMatching(/(^|,)\s*Origin\s*(,|$)/iu),
|
|
}));
|
|
});
|
|
|
|
it('sends server identification in the X-Powered-By header.', async(): Promise<void> => {
|
|
const res = await request(server).get('/');
|
|
expect(res.header).toEqual(expect.objectContaining({
|
|
'x-powered-by': 'Community Solid Server',
|
|
}));
|
|
});
|
|
|
|
it('returns all relevant headers for an OPTIONS request.', async(): Promise<void> => {
|
|
const res = await request(server)
|
|
.options('/')
|
|
.set('Access-Control-Allow-Credentials', 'true')
|
|
.set('Access-Control-Request-Headers', 'content-type')
|
|
.set('Access-Control-Request-Method', 'POST')
|
|
.set('Host', 'test.com')
|
|
.expect(204);
|
|
expect(res.header).toEqual(expect.objectContaining({
|
|
'access-control-allow-origin': '*',
|
|
'access-control-allow-headers': 'content-type',
|
|
'updates-via': 'wss://example.pod/',
|
|
'x-powered-by': 'Community Solid Server',
|
|
}));
|
|
const { vary } = res.header;
|
|
expect(vary).toMatch(/(^|,)\s*Accept\s*(,|$)/iu);
|
|
expect(vary).toMatch(/(^|,)\s*Authorization\s*(,|$)/iu);
|
|
expect(vary).toMatch(/(^|,)\s*Origin\s*(,|$)/iu);
|
|
const corsMethods = res.header['access-control-allow-methods'].split(',')
|
|
.map((method: string): string => method.trim());
|
|
const allowedMethods = [ 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE' ];
|
|
expect(corsMethods).toEqual(expect.arrayContaining(allowedMethods));
|
|
expect(corsMethods).toHaveLength(allowedMethods.length);
|
|
});
|
|
|
|
it('specifies CORS origin header if an origin was supplied.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').set('origin', 'test.com').expect(200);
|
|
expect(res.header).toEqual(expect.objectContaining({ 'access-control-allow-origin': 'test.com' }));
|
|
});
|
|
|
|
it('exposes the Accept-[Method] header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('Accept-Patch');
|
|
expect(splitCommaSeparated(exposed)).toContain('Accept-Post');
|
|
expect(splitCommaSeparated(exposed)).toContain('Accept-Put');
|
|
});
|
|
|
|
it('exposes the Last-Modified and ETag headers via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('ETag');
|
|
expect(splitCommaSeparated(exposed)).toContain('Last-Modified');
|
|
});
|
|
|
|
it('exposes the Link header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('Link');
|
|
});
|
|
|
|
it('exposes the Location header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('Location');
|
|
});
|
|
|
|
it('exposes the MS-Author-Via header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('MS-Author-Via');
|
|
});
|
|
|
|
it('exposes the WAC-Allow header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('WAC-Allow');
|
|
});
|
|
|
|
it('exposes the Updates-Via header via CORS.', async(): Promise<void> => {
|
|
const res = await request(server).get('/').expect(200);
|
|
const exposed = res.header['access-control-expose-headers'];
|
|
expect(splitCommaSeparated(exposed)).toContain('Updates-Via');
|
|
});
|
|
|
|
it('sends incoming requests to the handler.', async(): Promise<void> => {
|
|
const response = request(server).get('/').set('Host', 'test.com');
|
|
expect(response).toBeDefined();
|
|
await response.expect(200).expect('Hello World');
|
|
});
|
|
});
|