feat: Split up server creation and request handling

This allows us to decouple the WebSocket listening from the HTTP configs,
making these features completely orthogonal.
This commit is contained in:
Joachim Van Herwegen
2022-09-29 16:34:38 +02:00
parent 764ce3cc28
commit 4223dcf8a4
64 changed files with 949 additions and 694 deletions

View File

@@ -1,39 +1,36 @@
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 type { App } from '../../src/init/App';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import { splitCommaSeparated } from '../../src/util/StringUtil';
import { getPort } from '../util/Util';
import { getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, 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 app: App;
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);
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
[
getTestConfigPath('server-middleware.json'),
],
getDefaultVariables(port),
) as { app: App; factory: HttpServerFactory };
({ app } = instances);
server = await instances.factory.createServer();
server.listen(port);
});
afterAll(async(): Promise<void> => {
server.close();
// Even though the server was started separately, there might still be finalizers that need to be stopped
await app.stop();
});
it('sets a Vary header containing Accept.', async(): Promise<void> => {
@@ -75,7 +72,6 @@ describe('An http server with middleware', (): void => {
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;
@@ -90,12 +86,12 @@ describe('An http server with middleware', (): void => {
});
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);
const res = await request(server).options('/').set('origin', 'test.com').expect(204);
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 res = await request(server).options('/').expect(204);
const exposed = res.header['access-control-expose-headers'];
expect(splitCommaSeparated(exposed)).toContain('Accept-Patch');
expect(splitCommaSeparated(exposed)).toContain('Accept-Post');
@@ -103,45 +99,39 @@ describe('An http server with middleware', (): void => {
});
it('exposes the Last-Modified and ETag headers via CORS.', async(): Promise<void> => {
const res = await request(server).get('/').expect(200);
const res = await request(server).options('/').expect(204);
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 res = await request(server).options('/').expect(204);
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 res = await request(server).options('/').expect(204);
const exposed = res.header['access-control-expose-headers'];
expect(splitCommaSeparated(exposed)).toContain('Location');
});
it('exposes the WAC-Allow header via CORS.', async(): Promise<void> => {
const res = await request(server).get('/').expect(200);
const res = await request(server).options('/').expect(204);
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 res = await request(server).options('/').expect(204);
const exposed = res.header['access-control-expose-headers'];
expect(splitCommaSeparated(exposed)).toContain('Updates-Via');
});
it('exposes the Www-Authenticate header via CORS.', async(): Promise<void> => {
const res = await request(server).get('/').expect(200);
const res = await request(server).options('/').expect(204);
const exposed = res.header['access-control-expose-headers'];
expect(splitCommaSeparated(exposed)).toContain('Www-Authenticate');
});
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');
});
});