mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Use full encoded topic iri in streaming http receiveFrom url template
* fix: use full encoded topic iri in streaming http receiveFrom url template * clean up urls and routing
This commit is contained in:
parent
4599bf413e
commit
3e8365bb26
@ -1,6 +1,7 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
|
||||
"import": [
|
||||
"css:config/http/notifications/base/description.json",
|
||||
"css:config/http/notifications/base/handler.json",
|
||||
"css:config/http/notifications/base/http.json",
|
||||
"css:config/http/notifications/base/storage.json",
|
||||
|
@ -2,16 +2,16 @@
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
|
||||
"@graph": [
|
||||
{
|
||||
"comment": "Path prefix used by streaming HTTP receiveFrom endpoints",
|
||||
"@id": "urn:solid-server:default:variable:streamingHTTPReceiveFromPrefix",
|
||||
"valueRaw": ".notifications/StreamingHTTPChannel2023/"
|
||||
"@id": "urn:solid-server:default:StreamingHTTP2023Route",
|
||||
"@type": "RelativePathInteractionRoute",
|
||||
"base": { "@id": "urn:solid-server:default:NotificationRoute" },
|
||||
"relativePath": "/StreamingHTTPChannel2023/"
|
||||
},
|
||||
{
|
||||
"comment": "Creates updatesViaStreamingHttp2023 Link relations",
|
||||
"@id": "urn:solid-server:default:StreamingHttpMetadataWriter",
|
||||
"@type": "StreamingHttpMetadataWriter",
|
||||
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||
"pathPrefix": { "@id": "urn:solid-server:default:variable:streamingHTTPReceiveFromPrefix" }
|
||||
"route": { "@id": "urn:solid-server:default:StreamingHTTP2023Route" }
|
||||
},
|
||||
{
|
||||
"comment": "Allows discovery of the corresponding streaming HTTP channel",
|
||||
@ -32,7 +32,7 @@
|
||||
"@id": "urn:solid-server:default:StreamingHttp2023RequestHandler",
|
||||
"@type": "StreamingHttpRequestHandler",
|
||||
"streamMap": { "@id": "urn:solid-server:default:StreamingHttpMap" },
|
||||
"pathPrefix": { "@id": "urn:solid-server:default:variable:streamingHTTPReceiveFromPrefix" },
|
||||
"route": { "@id": "urn:solid-server:default:StreamingHTTP2023Route" },
|
||||
"generator": { "@id": "urn:solid-server:default:BaseNotificationGenerator" },
|
||||
"serializer": { "@id": "urn:solid-server:default:BaseNotificationSerializer" },
|
||||
"credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { getLoggerFor } from '../../../logging/LogUtil';
|
||||
import type { HttpResponse } from '../../HttpResponse';
|
||||
import { addHeader } from '../../../util/HeaderUtil';
|
||||
import { joinUrl } from '../../../util/PathUtil';
|
||||
import type { InteractionRoute } from '../../../identity/interaction/routing/InteractionRoute';
|
||||
import type { RepresentationMetadata } from '../../../http/representation/RepresentationMetadata';
|
||||
import { MetadataWriter } from '../../../http/output/metadata/MetadataWriter';
|
||||
|
||||
@ -12,15 +14,14 @@ export class StreamingHttpMetadataWriter extends MetadataWriter {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
public constructor(
|
||||
private readonly baseUrl: string,
|
||||
private readonly pathPrefix: string,
|
||||
private readonly route: InteractionRoute,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
|
||||
const resourcePath = input.metadata.identifier.value.replace(this.baseUrl, '');
|
||||
const receiveFrom = `${this.baseUrl}${this.pathPrefix}${resourcePath}`;
|
||||
const encodedUrl = encodeURIComponent(input.metadata.identifier.value);
|
||||
const receiveFrom = joinUrl(this.route.getPath(), encodedUrl);
|
||||
const link = `<${receiveFrom}>; rel="http://www.w3.org/ns/solid/terms#updatesViaStreamingHttp2023"`;
|
||||
this.logger.debug('Adding updatesViaStreamingHttp2023 to the Link header');
|
||||
addHeader(input.response, 'Link', link);
|
||||
|
@ -7,6 +7,7 @@ import { AccessMode } from '../../../authorization/permissions/Permissions';
|
||||
import { OkResponseDescription } from '../../../http/output/response/OkResponseDescription';
|
||||
import type { ResponseDescription } from '../../../http/output/response/ResponseDescription';
|
||||
import { BasicRepresentation } from '../../../http/representation/BasicRepresentation';
|
||||
import type { InteractionRoute } from '../../../identity/interaction/routing/InteractionRoute';
|
||||
import { getLoggerFor } from '../../../logging/LogUtil';
|
||||
import type { OperationHttpHandlerInput } from '../../OperationHttpHandler';
|
||||
import { OperationHttpHandler } from '../../OperationHttpHandler';
|
||||
@ -28,7 +29,7 @@ export class StreamingHttpRequestHandler extends OperationHttpHandler {
|
||||
|
||||
public constructor(
|
||||
private readonly streamMap: StreamingHttpMap,
|
||||
private readonly pathPrefix: string,
|
||||
private readonly route: InteractionRoute,
|
||||
private readonly generator: NotificationGenerator,
|
||||
private readonly serializer: NotificationSerializer,
|
||||
private readonly credentialsExtractor: CredentialsExtractor,
|
||||
@ -39,7 +40,8 @@ export class StreamingHttpRequestHandler extends OperationHttpHandler {
|
||||
}
|
||||
|
||||
public async handle({ operation, request }: OperationHttpHandlerInput): Promise<ResponseDescription> {
|
||||
const topic = operation.target.path.replace(this.pathPrefix, '');
|
||||
const encodedUrl = operation.target.path.replace(this.route.getPath(), '');
|
||||
const topic = decodeURIComponent(encodedUrl);
|
||||
|
||||
// Verify if the client is allowed to connect
|
||||
const credentials = await this.credentialsExtractor.handleSafe(request);
|
||||
|
@ -17,6 +17,7 @@ import namedNode = DataFactory.namedNode;
|
||||
|
||||
const port = getPort('StreamingHTTPChannel2023');
|
||||
const baseUrl = `http://localhost:${port}/`;
|
||||
const pathPrefix = '.notifications/StreamingHTTPChannel2023';
|
||||
|
||||
const rootFilePath = getTestFolder('StreamingHTTPChannel2023');
|
||||
const stores: [string, any][] = [
|
||||
@ -38,13 +39,16 @@ async function readChunk(reader: ReadableStreamDefaultReader): Promise<Store> {
|
||||
return new Store(parser.parse(notification));
|
||||
}
|
||||
|
||||
function endpoint(topic: string): string {
|
||||
return joinUrl(baseUrl, pathPrefix, encodeURIComponent(topic));
|
||||
}
|
||||
|
||||
describe.each(stores)('A server supporting StreamingHTTPChannel2023 using %s', (name, { configs, teardown }): void => {
|
||||
let app: App;
|
||||
let store: ResourceStore;
|
||||
const webId = 'http://example.com/card/#me';
|
||||
const topic = joinUrl(baseUrl, '/foo');
|
||||
const pathPrefix = '.notifications/StreamingHTTPChannel2023';
|
||||
const receiveFrom = joinUrl(baseUrl, pathPrefix, '/foo');
|
||||
const receiveFrom = endpoint(topic);
|
||||
|
||||
beforeAll(async(): Promise<void> => {
|
||||
const variables = {
|
||||
@ -246,7 +250,7 @@ describe.each(stores)('A server supporting StreamingHTTPChannel2023 using %s', (
|
||||
|
||||
it('prevents connecting to channels of restricted topics.', async(): Promise<void> => {
|
||||
const restricted = joinUrl(baseUrl, '/restricted');
|
||||
const restrictedReceiveFrom = joinUrl(baseUrl, pathPrefix, '/restricted');
|
||||
const restrictedReceiveFrom = endpoint(restricted);
|
||||
await store.setRepresentation({ path: restricted }, new BasicRepresentation('new', 'text/plain'));
|
||||
|
||||
// Only allow our WebID to read
|
||||
@ -285,7 +289,7 @@ describe.each(stores)('A server supporting StreamingHTTPChannel2023 using %s', (
|
||||
|
||||
it('emits container notifications if contents get added or removed.', async(): Promise<void> => {
|
||||
const resource = joinUrl(baseUrl, '/resource');
|
||||
const baseReceiveFrom = joinUrl(baseUrl, pathPrefix, '/');
|
||||
const baseReceiveFrom = endpoint(joinUrl(baseUrl, '/'));
|
||||
|
||||
// Connecting to the base URL, which is the parent container
|
||||
const streamingResponse = await fetch(baseReceiveFrom);
|
||||
|
@ -2,19 +2,24 @@ import { createResponse } from 'node-mocks-http';
|
||||
import {
|
||||
StreamingHttpMetadataWriter,
|
||||
} from '../../../../../src/server/notifications/StreamingHttpChannel2023/StreamingHttpMetadataWriter';
|
||||
import {
|
||||
AbsolutePathInteractionRoute,
|
||||
} from '../../../../../src/identity/interaction/routing/AbsolutePathInteractionRoute';
|
||||
import { RepresentationMetadata } from '../../../../../src/http/representation/RepresentationMetadata';
|
||||
import type { HttpResponse } from '../../../../../src/server/HttpResponse';
|
||||
import type { ResourceIdentifier } from '../../../../../src/http/representation/ResourceIdentifier';
|
||||
|
||||
describe('A StreamingHttpMetadataWriter', (): void => {
|
||||
const baseUrl = 'http://example.org/';
|
||||
const pathPrefix = '.notifications/StreamingHTTPChannel2023/';
|
||||
const writer = new StreamingHttpMetadataWriter(baseUrl, pathPrefix);
|
||||
const path = 'http://example.org/.notifications/StreamingHTTPChannel2023/';
|
||||
const route = new AbsolutePathInteractionRoute(path);
|
||||
const writer = new StreamingHttpMetadataWriter(route);
|
||||
const rel = 'http://www.w3.org/ns/solid/terms#updatesViaStreamingHttp2023';
|
||||
|
||||
it('adds the correct link header.', async(): Promise<void> => {
|
||||
const topic: ResourceIdentifier = { path: 'http://example.com/foo' };
|
||||
const response = createResponse() as HttpResponse;
|
||||
const metadata = new RepresentationMetadata({ path: 'http://example.org/foo/bar/baz' });
|
||||
const metadata = new RepresentationMetadata(topic);
|
||||
await expect(writer.handle({ response, metadata })).resolves.toBeUndefined();
|
||||
expect(response.getHeaders()).toEqual({ link: `<http://example.org/.notifications/StreamingHTTPChannel2023/foo/bar/baz>; rel="${rel}"` });
|
||||
expect(response.getHeaders()).toEqual({ link: `<http://example.org/.notifications/StreamingHTTPChannel2023/${encodeURIComponent(topic.path)}>; rel="${rel}"` });
|
||||
});
|
||||
});
|
||||
|
@ -16,8 +16,8 @@ import { getLoggerFor } from '../../../../../src/logging/LogUtil';
|
||||
import {
|
||||
StreamingHttpRequestHandler,
|
||||
} from '../../../../../src/server/notifications/StreamingHttpChannel2023/StreamingHttpRequestHandler';
|
||||
import { AbsolutePathInteractionRoute, StreamingHttpMap } from '../../../../../src';
|
||||
import type { NotificationGenerator, NotificationSerializer } from '../../../../../src';
|
||||
import { StreamingHttpMap } from '../../../../../src';
|
||||
import type { Notification } from '../../../../../src/server/notifications/Notification';
|
||||
import { flushPromises } from '../../../../util/Util';
|
||||
|
||||
@ -31,7 +31,7 @@ jest.mock('../../../../../src/logging/LogUtil', (): any => {
|
||||
describe('A StreamingHttpRequestHandler', (): void => {
|
||||
const logger: jest.Mocked<Logger> = getLoggerFor('mock') as any;
|
||||
const topic: ResourceIdentifier = { path: 'http://example.com/foo' };
|
||||
const pathPrefix = '.notifications/StreamingHTTPChannel2023/';
|
||||
const path = 'http://example.com/.notifications/StreamingHTTPChannel2023/';
|
||||
const channel: NotificationChannel = {
|
||||
id: 'id',
|
||||
topic: topic.path,
|
||||
@ -52,6 +52,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
|
||||
const request: HttpRequest = {} as any;
|
||||
const response: HttpResponse = {} as any;
|
||||
let representation: BasicRepresentation;
|
||||
let route: AbsolutePathInteractionRoute;
|
||||
let streamMap: StreamingHttpMap;
|
||||
let operation: Operation;
|
||||
let generator: jest.Mocked<NotificationGenerator>;
|
||||
@ -64,12 +65,14 @@ describe('A StreamingHttpRequestHandler', (): void => {
|
||||
beforeEach(async(): Promise<void> => {
|
||||
operation = {
|
||||
method: 'GET',
|
||||
target: { path: 'http://example.com/.notifications/StreamingHTTPChannel2023/foo' },
|
||||
target: { path: `${path}${encodeURIComponent(topic.path)}` },
|
||||
body: new BasicRepresentation(),
|
||||
preferences: {},
|
||||
};
|
||||
representation = new BasicRepresentation(chunk, 'text/plain');
|
||||
|
||||
route = new AbsolutePathInteractionRoute(path);
|
||||
|
||||
streamMap = new StreamingHttpMap();
|
||||
|
||||
generator = {
|
||||
@ -95,7 +98,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
|
||||
|
||||
handler = new StreamingHttpRequestHandler(
|
||||
streamMap,
|
||||
pathPrefix,
|
||||
route,
|
||||
generator,
|
||||
serializer,
|
||||
credentialsExtractor,
|
||||
@ -151,7 +154,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
|
||||
} as any;
|
||||
handler = new StreamingHttpRequestHandler(
|
||||
streamMap,
|
||||
pathPrefix,
|
||||
route,
|
||||
generator,
|
||||
serializer,
|
||||
credentialsExtractor,
|
||||
|
Loading…
x
Reference in New Issue
Block a user