UNPKG

@solid/community-server

Version:

Community Solid Server: an open and modular implementation of the Solid specifications

74 lines 4.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamingHttpRequestHandler = void 0; const node_stream_1 = require("node:stream"); const policy_engine_1 = require("@solidlab/policy-engine"); const global_logger_factory_1 = require("global-logger-factory"); const OkResponseDescription_1 = require("../../../http/output/response/OkResponseDescription"); const BasicRepresentation_1 = require("../../../http/representation/BasicRepresentation"); const OperationHttpHandler_1 = require("../../OperationHttpHandler"); const GuardedStream_1 = require("../../../util/GuardedStream"); const IdentifierMap_1 = require("../../../util/map/IdentifierMap"); const ErrorUtil_1 = require("../../../util/errors/ErrorUtil"); const StreamUtil_1 = require("../../../util/StreamUtil"); const StreamingHttp2023Util_1 = require("./StreamingHttp2023Util"); /** * Handles request to Streaming HTTP receiveFrom endopints. * All allowed requests are stored in the {@link StreamingHttpMap} */ class StreamingHttpRequestHandler extends OperationHttpHandler_1.OperationHttpHandler { streamMap; route; generator; serializer; credentialsExtractor; permissionReader; authorizer; logger = (0, global_logger_factory_1.getLoggerFor)(this); constructor(streamMap, route, generator, serializer, credentialsExtractor, permissionReader, authorizer) { super(); this.streamMap = streamMap; this.route = route; this.generator = generator; this.serializer = serializer; this.credentialsExtractor = credentialsExtractor; this.permissionReader = permissionReader; this.authorizer = authorizer; } async handle({ operation, request }) { 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); await this.authorize(credentials, topic); const stream = (0, GuardedStream_1.guardStream)(new node_stream_1.PassThrough()); this.streamMap.add(topic, stream); stream.on('error', () => this.streamMap.deleteEntry(topic, stream)); stream.on('close', () => this.streamMap.deleteEntry(topic, stream)); const channel = (0, StreamingHttp2023Util_1.generateChannel)({ path: topic }); // Send initial notification try { const notification = await this.generator.handle({ channel, topic: { path: topic } }); const representation = await this.serializer.handleSafe({ channel, notification }); // Ensure that the whole notification gets sent in a single chunk const chunk = await (0, StreamUtil_1.readableToString)(representation.data); stream.write(chunk); } catch (error) { this.logger.error(`Problem emitting initial notification: ${(0, ErrorUtil_1.createErrorMessage)(error)}`); } // Pre-established channels use Turtle const representation = new BasicRepresentation_1.BasicRepresentation(topic, operation.target, channel.accept); return new OkResponseDescription_1.OkResponseDescription(representation.metadata, stream); } async authorize(credentials, topic) { const requestedModes = new IdentifierMap_1.IdentifierSetMultiMap([[{ path: topic }, policy_engine_1.PERMISSIONS.Read]]); this.logger.debug(`Retrieved required modes: ${[...requestedModes.entrySets()].join(',')}`); const availablePermissions = await this.permissionReader.handleSafe({ credentials, requestedModes }); this.logger.debug(`Available permissions are ${[...availablePermissions.entries()].join(',')}`); await this.authorizer.handleSafe({ credentials, requestedModes, availablePermissions }); this.logger.debug(`Authorization succeeded, creating notification channel`); } } exports.StreamingHttpRequestHandler = StreamingHttpRequestHandler; //# sourceMappingURL=StreamingHttpRequestHandler.js.map