mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
docs: Add notification architecture documentation
This commit is contained in:
parent
b1f7a6a8b1
commit
7b6ddfa272
@ -21,6 +21,8 @@ flowchart LR
|
||||
StaticAssetHandler("<strong>StaticAssetHandler</strong><br>StaticAssetHandler")
|
||||
SetupHandler("<strong>SetupHandler</strong><br><i>HttpHandler</i>")
|
||||
OidcHandler("<strong>OidcHandler</strong><br><i>HttpHandler</i>")
|
||||
NotificationHttpHandler("<strong>NotificationHttpHandler</strong><br><i>HttpHandler</i>")
|
||||
StorageDescriptionHandler("<strong>StorageDescriptionHandler</strong><br><i>HttpHandler</i>")
|
||||
AuthResourceHttpHandler("<strong>AuthResourceHttpHandler</strong><br><i>HttpHandler</i>")
|
||||
IdentityProviderHttpHandler("<strong>IdentityProviderHttpHandler</strong><br><i>HttpHandler</i>")
|
||||
LdpHandler("<strong>LdpHandler</strong><br><i>HttpHandler</i>")
|
||||
@ -28,7 +30,9 @@ flowchart LR
|
||||
|
||||
StaticAssetHandler --> SetupHandler
|
||||
SetupHandler --> OidcHandler
|
||||
OidcHandler --> AuthResourceHttpHandler
|
||||
OidcHandler --> NotificationHttpHandler
|
||||
NotificationHttpHandler --> StorageDescriptionHandler
|
||||
StorageDescriptionHandler --> AuthResourceHttpHandler
|
||||
AuthResourceHttpHandler --> IdentityProviderHttpHandler
|
||||
IdentityProviderHttpHandler --> LdpHandler
|
||||
```
|
||||
@ -66,6 +70,19 @@ to the Solid-OIDC [specification](https://solid.github.io/solid-oidc/).
|
||||
The OIDC component is configured to work on the `/.oidc/` subpath,
|
||||
so this handler catches all those requests and sends them to the internal OIDC library that is used.
|
||||
|
||||
## NotificationHttpHandler
|
||||
|
||||
The `urn:solid-server:default:NotificationHttpHandler` catches all notification subscription requests.
|
||||
By default these are requests targeting `/.notifications/`.
|
||||
Which specific subscription type is targeted is then based on the next part of the URL.
|
||||
|
||||
## StorageDescriptionHandler
|
||||
|
||||
The `urn:solid-server:default:StorageDescriptionHandler` returns the relevant RDF data
|
||||
for requests targeting a storage description resource.
|
||||
It does this by knowing which URL suffix is used for such resources,
|
||||
and verifying that the associated container is an actual storage container.
|
||||
|
||||
## AuthResourceHttpHandler
|
||||
|
||||
The `urn:solid-server:default:AuthResourceHttpHandler` is identical
|
||||
|
@ -108,25 +108,32 @@ to add any custom initializers that need to run.
|
||||
|
||||
The `ServerInitializer` is the initializer that finally starts up the server by listening to the relevant port,
|
||||
once all the initialization described above is finished.
|
||||
This is an example of a component that differs based on some of the choices made during configuration.
|
||||
It takes as input 2 components: a `HttpServerFactory` and a `ServerListener`.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
ServerInitializer("<strong>ServerInitializer</strong><br>ServerInitializer")
|
||||
ServerInitializer --> WebSocketServerFactory("<strong>ServerFactory</strong><br>WebSocketServerFactory")
|
||||
WebSocketServerFactory --> BaseHttpServerFactory("<br>BaseHttpServerFactory")
|
||||
BaseHttpServerFactory --> HttpHandler("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
||||
ServerInitializer --> ServerInitializerArgs
|
||||
|
||||
ServerInitializer2("<strong>ServerInitializer</strong><br>ServerInitializer")
|
||||
ServerInitializer2 ---> BaseHttpServerFactory2("<strong>ServerFactory</strong><br>BaseHttpServerFactory")
|
||||
BaseHttpServerFactory2 --> HttpHandler2("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
||||
subgraph ServerInitializerArgs[" "]
|
||||
direction LR
|
||||
ServerFactory("<strong>ServerFactory</strong><br>BaseServerFactory")
|
||||
ServerListener("<strong>ServerListener</strong><br>ParallelHandler")
|
||||
end
|
||||
|
||||
ServerListener --> HandlerServerListener("<strong>HandlerServerListener</strong><br>HandlerServerListener")
|
||||
|
||||
HandlerServerListener --> HttpHandler("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
||||
```
|
||||
|
||||
Depending on if the configurations necessary for websockets are imported or not,
|
||||
the `urn:solid-server:default:ServerFactory` identifier will point to a different resource.
|
||||
There will always be a `BaseHttpServerFactory` that starts the HTTP(S) server,
|
||||
but there might also be a `WebSocketServerFactory` wrapped around it to handle websocket support.
|
||||
Although not indicated here, the parameters for initializing the `BaseHttpServerFactory`
|
||||
might also differ in case an HTTPS configuration is imported.
|
||||
The `HttpServerFactory` is responsible for starting a server on a given port.
|
||||
Depending on the configuration this can be an HTTP or an HTTPS server.
|
||||
The created server emits events when it receives requests.
|
||||
|
||||
The `HttpHandler` it takes as input is responsible for how [HTTP requests get resolved](http-handler.md).
|
||||
A `ServerListener` is a class that takes the created server as input and attaches a listener to interpret events.
|
||||
One listener that is always used is the `urn:solid-server:default:HandlerServerListener`,
|
||||
which calls an `HttpHandler` [to resolve HTTP requests](http-handler.md).
|
||||
|
||||
Sometimes it is necessary to add additional listeners,
|
||||
these can then be added to the `urn:solid-server:default:ServerListener` as it is a `ParallellHandler`.
|
||||
An example of this is when WebSockets are used [to handle notifications](notifications.md).
|
||||
|
172
documentation/markdown/architecture/features/notifications.md
Normal file
172
documentation/markdown/architecture/features/notifications.md
Normal file
@ -0,0 +1,172 @@
|
||||
# Notifications
|
||||
|
||||
This section covers the architecture used to support Notifications protocol
|
||||
as described in <https://solidproject.org/TR/notifications-protocol>.
|
||||
|
||||
There are 3 core architectural components to this that each have separate entry points:
|
||||
|
||||
* Exposing metadata to allow discovery of the subscription type.
|
||||
* Handling subscriptions targeting a resource.
|
||||
* Emitting notifications when there is activity on a resource.
|
||||
|
||||
## Discovery
|
||||
|
||||
Discovery is done through the storage description resource(s).
|
||||
The server returns the same triples for every such resource
|
||||
as the notification subscription URL is always located in the root of the server.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
StorageDescriptionHandler("<br>StorageDescriptionHandler")
|
||||
StorageDescriptionHandler --> StorageDescriber("<strong>StorageDescriber</strong><br>ArrayUnionHandler")
|
||||
StorageDescriber --> StorageDescriberArgs
|
||||
|
||||
subgraph StorageDescriberArgs[" "]
|
||||
direction LR
|
||||
NotificationDescriber("<br>NotificationDescriber")
|
||||
NotificationDescriber2("<br>NotificationDescriber")
|
||||
end
|
||||
```
|
||||
|
||||
The server uses a `StorageDescriptionHandler` to generate the necessary RDF data
|
||||
and to handle content negotiation.
|
||||
To generate the data we have multiple `StorageDescriber`s,
|
||||
whose results get merged together in an `ArrayUnionHandler`.
|
||||
|
||||
One specific instance of a `StorageDescriber` is a `NotificationSubcriber`,
|
||||
that contains all the necessary presets to describe a notification subscription type.
|
||||
When adding a new subscription type,
|
||||
a new instance of such a class should be added to the `urn:solid-server:default:StorageDescriber`.
|
||||
|
||||
## Subscription
|
||||
|
||||
To subscribe, a client has to send a specific JSON-LD request to the URl found during discovery.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
NotificationTypeHandler("<strong>NotificationTypeHandler</strong><br>WaterfallHandler")
|
||||
NotificationTypeHandler --> NotificationTypeHandlerArgs
|
||||
|
||||
subgraph NotificationTypeHandlerArgs[" "]
|
||||
direction LR
|
||||
OperationRouterHandler("<br>OperationRouterHandler") --> NotificationSubscriber("<br>NotificationSubscriber")
|
||||
NotificationSubscriber --> SubscriptionType("<br><i>SubscriptionType</i>")
|
||||
OperationRouterHandler2("<br>OperationRouterHandler") --> NotificationSubscriber2("<br>NotificationSubscriber")
|
||||
NotificationSubscriber2 --> SubscriptionType2("<br><i>SubscriptionType</i>")
|
||||
end
|
||||
```
|
||||
|
||||
Every subscription type should have a subscription URL relative to the root notification URL,
|
||||
which in our configs is set to `/.notifications/`.
|
||||
For every type there is then a `OperationRouterHandler` that accepts requests to that specific URL,
|
||||
after which a `NotificationSubscriber` handles all checks related to subscribing,
|
||||
for which it uses a `SubscriptionType` that contains all the information necessary for a specific type.
|
||||
If the subscription is valid and has authorization, the results will be saved in a `SubscriptionStorage`.
|
||||
|
||||
## Activity
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
ListeningActivityHandler("<strong>ListeningActivityHandler</strong><br>ListeningActivityHandler")
|
||||
ListeningActivityHandler --> ListeningActivityHandlerArgs
|
||||
|
||||
subgraph ListeningActivityHandlerArgs[" "]
|
||||
SubscriptionStorage("<strong>SubscriptionStorage</strong><br><i>SubscriptionStorage</i>")
|
||||
ResourceStore("<strong>ResourceStore</strong><br><i>ActivityEmitter</i>")
|
||||
NotificationHandler("<strong>NotificationHandler</strong><br>WaterfallHandler")
|
||||
end
|
||||
|
||||
NotificationHandler --> NotificationHandlerArgs
|
||||
subgraph NotificationHandlerArgs[" "]
|
||||
direction TB
|
||||
NotificationHandler1("<br><i>NotificationHandler</i>")
|
||||
NotificationHandler2("<br><i>NotificationHandler</i>")
|
||||
end
|
||||
```
|
||||
|
||||
An `ActivityEmitter` is a class that emits events every time data changes in the server.
|
||||
The `MonitoringStore` is an implementation of this in the server.
|
||||
The `ListeningActivityHandler` is the class that listens to these events
|
||||
and makes sure relevant notifications get sent out.
|
||||
|
||||
It will pull the relevant subscriptions from the storage and call the stored `NotificationHandler` for each of time.
|
||||
For every subscription type, a `NotificationHandler` should be added to the `WaterfallHandler`
|
||||
that handles notifications for the specific type.
|
||||
|
||||
## WebSocketSubscription2021
|
||||
|
||||
To add support for WebSocketSubscription2021 notifications,
|
||||
components were added as described in the documentation above.
|
||||
|
||||
For discovery a `NotificationDescriber` was added with the corresponding settings.
|
||||
|
||||
As `SubscriptionType` there is a specific `WebSocketSubscription2021` that contains all the necessary information.
|
||||
|
||||
### Handling notifications
|
||||
|
||||
As `NotificationHandler` the following architecture is used:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
TypedNotificationHandler("<br>TypedNotificationHandler")
|
||||
TypedNotificationHandler --> ComposedNotificationHandler("<br>ComposedNotificationHandler")
|
||||
ComposedNotificationHandler --> ComposedNotificationHandlerArgs
|
||||
|
||||
subgraph ComposedNotificationHandlerArgs[" "]
|
||||
direction LR
|
||||
BaseNotificationGenerator("<strong>BaseNotificationGenerator</strong><br><i>NotificationGenerator</i>")
|
||||
BaseNotificationSerializer("<strong>BaseNotificationSerializer</strong><br><i>NotificationSerializer</i>")
|
||||
WebSocket2021Emitter("<strong>WebSocket2021Emitter</strong><br>WebSocket2021Emitter")
|
||||
BaseNotificationGenerator --> BaseNotificationSerializer --> WebSocket2021Emitter
|
||||
end
|
||||
```
|
||||
|
||||
A `TypedNotificationHandler` is a handler that can be used to filter out subscriptions for a specific type,
|
||||
making sure only WebSocketSubscription2021 subscriptions will be handled.
|
||||
|
||||
A `ComposedNotificationHandler` combines 3 interfaces to handle the notifications:
|
||||
|
||||
* A `NotificationGenerator` converts the information into a Notification object.
|
||||
* A `NotificationSerializer` converts a Notification object into a serialized Representation.
|
||||
* A `NotificationEmitter` takes a Representation and sends it out in a way specific to that subscription type.
|
||||
|
||||
`urn:solid-server:default:BaseNotificationGenerator` is a generator that fills in the default Notification template,
|
||||
and also caches the result so it can be reused by multiple subscriptions.
|
||||
|
||||
`urn:solid-server:default:BaseNotificationSerializer` converts the Notification to a JSON-LD representation
|
||||
and handles any necessary content negotiation based on the `accept` notification feature.
|
||||
|
||||
A `WebSocket2021Emitter` is a specific emitter that checks the current open WebSockets
|
||||
if they correspond to the subscription.
|
||||
|
||||
### WebSockets
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
WebSocket2021Listener("<strong>WebSocket2021Listener</strong><br>WebSocket2021Listener")
|
||||
WebSocket2021Listener --> WebSocket2021ListenerArgs
|
||||
|
||||
subgraph WebSocket2021ListenerArgs[" "]
|
||||
direction LR
|
||||
SubscriptionStorage("<strong>SubscriptionStorage</strong><br>SubscriptionStorage")
|
||||
SequenceHandler("<br>SequenceHandler")
|
||||
end
|
||||
|
||||
SequenceHandler --> SequenceHandlerArgs
|
||||
|
||||
subgraph SequenceHandlerArgs[" "]
|
||||
direction TB
|
||||
WebSocket2021Storer("<strong>WebSocket2021Storer</strong><br>WebSocket2021Storer")
|
||||
WebSocket2021StateHandler("<strong>WebSocket2021StateHandler</strong><br>BaseStateHandler")
|
||||
end
|
||||
```
|
||||
|
||||
To detect and store WebSocket connections, the `WebSocket2021Listener` is added as a listener to the HTTP server.
|
||||
For all WebSocket connections that get opened, it verifies if they correspond to an existing subscription.
|
||||
If yes the information gets sent out to its stored `WebSocket2021Handler`.
|
||||
|
||||
In this case this is a `SequenceHandler` which contains a `WebSocket2021Storer` and a `BaseStateHandler`.
|
||||
The `WebSocket2021Storer` will store the WebSocket in the same map used by the `WebSocket2021Emitter`,
|
||||
so that class can later on emit events as mentioned above.
|
||||
The state handler will make sure that a notification gets sent out if the subscription has a `state` feature request,
|
||||
as defined in the notification specification.
|
@ -61,3 +61,4 @@ Below are the sections that go deeper into the features of the server and how th
|
||||
* [How the server is initialized and started](features/initialization.md)
|
||||
* [How HTTP requests are handled](features/http-handler.md)
|
||||
* [How the server handles a standard Solid request](features/protocol/overview.md)
|
||||
* [How Notification components are configured](features/notifications.md)
|
||||
|
@ -96,6 +96,7 @@ nav:
|
||||
- Parsing: architecture/features/protocol/parsing.md
|
||||
- Authorization: architecture/features/protocol/authorization.md
|
||||
- Resource Store: architecture/features/protocol/resource-store.md
|
||||
- Notifications: architecture/features/notifications.md
|
||||
- Contributing:
|
||||
- Pull requests: contributing/making-changes.md
|
||||
- Releases: contributing/release.md
|
||||
|
Loading…
x
Reference in New Issue
Block a user