mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
refactor: Move WebSocket URL handling to utility functions
This commit is contained in:
parent
bc119dbd3e
commit
cb619415fa
@ -325,6 +325,7 @@ export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Emi
|
|||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
|
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
|
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
|
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
|
||||||
|
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Util';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocketMap';
|
export * from './server/notifications/WebSocketSubscription2021/WebSocketMap';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
|
export * from './server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { getLoggerFor } from '../../../logging/LogUtil';
|
|||||||
import { WebSocketServerConfigurator } from '../../WebSocketServerConfigurator';
|
import { WebSocketServerConfigurator } from '../../WebSocketServerConfigurator';
|
||||||
import type { SubscriptionStorage } from '../SubscriptionStorage';
|
import type { SubscriptionStorage } from '../SubscriptionStorage';
|
||||||
import type { WebSocket2021Handler } from './WebSocket2021Handler';
|
import type { WebSocket2021Handler } from './WebSocket2021Handler';
|
||||||
|
import { parseWebSocketRequest } from './WebSocket2021Util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for WebSocket connections and verifies if they are valid WebSocketSubscription2021 connections,
|
* Listens for WebSocket connections and verifies if they are valid WebSocketSubscription2021 connections,
|
||||||
@ -25,22 +26,18 @@ export class WebSocket2021Listener extends WebSocketServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async handleConnection(webSocket: WebSocket, upgradeRequest: IncomingMessage): Promise<void> {
|
protected async handleConnection(webSocket: WebSocket, upgradeRequest: IncomingMessage): Promise<void> {
|
||||||
// Base doesn't matter since we just want the path and query parameter
|
const { path, id } = parseWebSocketRequest(upgradeRequest);
|
||||||
const { pathname, searchParams } = new URL(upgradeRequest.url ?? '', 'http://example.com');
|
|
||||||
|
|
||||||
if (pathname !== this.path) {
|
if (path !== this.path) {
|
||||||
webSocket.send('Unknown WebSocket target.');
|
webSocket.send('Unknown WebSocket target.');
|
||||||
return webSocket.close();
|
return webSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = searchParams.get('auth');
|
if (!id) {
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
webSocket.send('Missing auth parameter from WebSocket URL.');
|
webSocket.send('Missing auth parameter from WebSocket URL.');
|
||||||
return webSocket.close();
|
return webSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = decodeURI(auth);
|
|
||||||
const info = await this.storage.get(id);
|
const info = await this.storage.get(id);
|
||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
import type { IncomingMessage } from 'http';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a WebSocket URL by converting an HTTP(S) URL into a WS(S) URL
|
||||||
|
* and adding the `auth` query parameter using the identifier.
|
||||||
|
* @param url - The HTTP(S) URL.
|
||||||
|
* @param id - The identifier to use as `auth` parameter.
|
||||||
|
*/
|
||||||
|
export function generateWebSocketUrl(url: string, id: string): string {
|
||||||
|
return `ws${url.slice('http'.length)}?auth=${encodeURIComponent(id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a {@link IncomingMessage} to extract both its path and the identifier used for authentication.
|
||||||
|
* The returned path is relative to the host.
|
||||||
|
*
|
||||||
|
* E.g., a request to `ws://example.com/foo/bar?auth=123456` would return `{ path: '/foo/bar', id: '123456' }`.
|
||||||
|
*
|
||||||
|
* @param request - The request to parse.
|
||||||
|
*/
|
||||||
|
export function parseWebSocketRequest(request: IncomingMessage): { path: string; id?: string } {
|
||||||
|
// Base doesn't matter since we just want the path and query parameter
|
||||||
|
const { pathname, searchParams } = new URL(request.url ?? '', 'http://example.com');
|
||||||
|
|
||||||
|
let auth: string | undefined;
|
||||||
|
if (searchParams.has('auth')) {
|
||||||
|
auth = decodeURIComponent(searchParams.get('auth')!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: pathname,
|
||||||
|
id: auth,
|
||||||
|
};
|
||||||
|
}
|
@ -11,6 +11,7 @@ import type { Subscription } from '../Subscription';
|
|||||||
import { SUBSCRIBE_SCHEMA } from '../Subscription';
|
import { SUBSCRIBE_SCHEMA } from '../Subscription';
|
||||||
import type { SubscriptionStorage } from '../SubscriptionStorage';
|
import type { SubscriptionStorage } from '../SubscriptionStorage';
|
||||||
import type { SubscriptionResponse, SubscriptionType } from '../SubscriptionType';
|
import type { SubscriptionResponse, SubscriptionType } from '../SubscriptionType';
|
||||||
|
import { generateWebSocketUrl } from './WebSocket2021Util';
|
||||||
|
|
||||||
const type = 'WebSocketSubscription2021';
|
const type = 'WebSocketSubscription2021';
|
||||||
const schema = SUBSCRIBE_SCHEMA.shape({
|
const schema = SUBSCRIBE_SCHEMA.shape({
|
||||||
@ -48,7 +49,7 @@ export class WebSocketSubscription2021 implements SubscriptionType<typeof schema
|
|||||||
const jsonld = {
|
const jsonld = {
|
||||||
'@context': [ CONTEXT_NOTIFICATION ],
|
'@context': [ CONTEXT_NOTIFICATION ],
|
||||||
type: this.type,
|
type: this.type,
|
||||||
source: `ws${this.path.slice('http'.length)}?auth=${encodeURI(info.id)}`,
|
source: generateWebSocketUrl(this.path, info.id),
|
||||||
};
|
};
|
||||||
const response = new BasicRepresentation(JSON.stringify(jsonld), APPLICATION_LD_JSON);
|
const response = new BasicRepresentation(JSON.stringify(jsonld), APPLICATION_LD_JSON);
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import type { IncomingMessage } from 'http';
|
||||||
|
import {
|
||||||
|
generateWebSocketUrl, parseWebSocketRequest,
|
||||||
|
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Util';
|
||||||
|
|
||||||
|
describe('WebSocket2021Util', (): void => {
|
||||||
|
describe('#generateWebSocketUrl', (): void => {
|
||||||
|
it('generates a WebSocket link with a query parameter.', async(): Promise<void> => {
|
||||||
|
expect(generateWebSocketUrl('http://example.com/', '123456')).toBe('ws://example.com/?auth=123456');
|
||||||
|
|
||||||
|
expect(generateWebSocketUrl('https://example.com/foo/bar', '123456'))
|
||||||
|
.toBe('wss://example.com/foo/bar?auth=123456');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#parseWebSocketRequest', (): void => {
|
||||||
|
it('parses the request.', async(): Promise<void> => {
|
||||||
|
const request: IncomingMessage = { url: '/foo/bar?auth=123%24456' } as any;
|
||||||
|
expect(parseWebSocketRequest(request)).toEqual({ path: '/foo/bar', id: '123$456' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty path and no id if the url parameter is undefined.', async(): Promise<void> => {
|
||||||
|
const request: IncomingMessage = {} as any;
|
||||||
|
expect(parseWebSocketRequest(request)).toEqual({ path: '/' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user