fix: use full encoded topic iri in streaming http receiveFrom url template

This commit is contained in:
elf Pavlik
2024-08-11 16:28:03 -06:00
parent 4599bf413e
commit 5398c445a6
6 changed files with 21 additions and 10 deletions

View File

@@ -32,6 +32,7 @@
"@id": "urn:solid-server:default:StreamingHttp2023RequestHandler",
"@type": "StreamingHttpRequestHandler",
"streamMap": { "@id": "urn:solid-server:default:StreamingHttpMap" },
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"pathPrefix": { "@id": "urn:solid-server:default:variable:streamingHTTPReceiveFromPrefix" },
"generator": { "@id": "urn:solid-server:default:BaseNotificationGenerator" },
"serializer": { "@id": "urn:solid-server:default:BaseNotificationSerializer" },

View File

@@ -19,8 +19,8 @@ export class StreamingHttpMetadataWriter extends MetadataWriter {
}
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 = `${this.baseUrl}${this.pathPrefix}${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);

View File

@@ -28,6 +28,7 @@ export class StreamingHttpRequestHandler extends OperationHttpHandler {
public constructor(
private readonly streamMap: StreamingHttpMap,
private readonly baseUrl: string,
private readonly pathPrefix: string,
private readonly generator: NotificationGenerator,
private readonly serializer: NotificationSerializer,
@@ -39,7 +40,7 @@ export class StreamingHttpRequestHandler extends OperationHttpHandler {
}
public async handle({ operation, request }: OperationHttpHandlerInput): Promise<ResponseDescription> {
const topic = operation.target.path.replace(this.pathPrefix, '');
const topic = decodeURIComponent(operation.target.path.replace(this.baseUrl + this.pathPrefix, ''));
// Verify if the client is allowed to connect
const credentials = await this.credentialsExtractor.handleSafe(request);

View File

@@ -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);

View File

@@ -4,6 +4,7 @@ import {
} from '../../../../../src/server/notifications/StreamingHttpChannel2023/StreamingHttpMetadataWriter';
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/';
@@ -12,9 +13,10 @@ describe('A StreamingHttpMetadataWriter', (): void => {
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}"` });
});
});

View File

@@ -31,6 +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 baseUrl = 'http://example.com/';
const pathPrefix = '.notifications/StreamingHTTPChannel2023/';
const channel: NotificationChannel = {
id: 'id',
@@ -64,7 +65,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
beforeEach(async(): Promise<void> => {
operation = {
method: 'GET',
target: { path: 'http://example.com/.notifications/StreamingHTTPChannel2023/foo' },
target: { path: `http://example.com/.notifications/StreamingHTTPChannel2023/${encodeURIComponent(topic.path)}` },
body: new BasicRepresentation(),
preferences: {},
};
@@ -95,6 +96,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
handler = new StreamingHttpRequestHandler(
streamMap,
baseUrl,
pathPrefix,
generator,
serializer,
@@ -151,6 +153,7 @@ describe('A StreamingHttpRequestHandler', (): void => {
} as any;
handler = new StreamingHttpRequestHandler(
streamMap,
baseUrl,
pathPrefix,
generator,
serializer,