ballerina/websub package
Package overview
This package contains an implementation of the W3C WebSub recommendation, which facilitates a push-based content delivery/notification mechanism between publishers and subscribers.
This implementation supports introducing all WebSub components: subscribers, publishers, and hubs.
- Subscriber - A party interested in receiving update notifications for particular topics.
- Publisher - A party that advertises topics to which interested parties subscribe in order to receive notifications on occurrence of events.
- Hub - A party that accepts subscription requests from subscribers and delivers content to the subscribers when the topic is updated by the topic's publisher.
Basic Flow with WebSub
-
Subscriber discovers the topics it needs to subscribe to in order to receive updates/content and discovers the hub(s) where it can subscribe.
-
Subscriber sends a subscription request to a hub, specifying the topic it needs to receive notifications for along with other subscription parameters, such as:
- The callback URL where content is expected to be delivered
- (Optional) A lease seconds value indicating how long the subscriber wants the subscription to stay active
- (Optional) A secret to use for authenticated content distribution
-
The hub sends an intent verification request to the specified callback URL, and if the response indicates verification (by echoing a challenge specified in the request) by the subscriber, the subscription is added for the topic at the hub.
-
Publisher notifies the hub of updates to the topic, and the content to deliver is identified.
-
The hub delivers the identified content to the subscribers of the topic.
Features
This package allows introducing a WebSub Subscriber Service with onIntentVerification
, which accepts HTTP GET requests for intent verification, and onNotification
, which accepts HTTP POST requests for notifications. The WebSub Subscriber Service provides the following capabilities:
- Subscription Requests are sent at service start time for the hub and topic, which are either specified as annotations or discovered based on the resource URL specified as an annotation.
- Auto Intent Verification against the topic specified as an annotation, or discovered based on the resource URL specified as an annotation, if
onIntentVerification
is not specified. - Signature Validation for authenticated content distribution if a secret is specified for the subscription.
A WebSub compliant hub based on the Ballerina Message Broker is also available for use as a remote hub or to be used by publishers who want to have their own internal hub. Ballerina's WebSub Hub honors specified lease periods and supports authenticated content distribution.
Ballerina WebSub publishers can use utility functions to add WebSub link headers indicating the hub and topic URLs, which facilitates WebSub discovery.
A hub client endpoint is also made available to publishers and subscribers to perform the following:
- Publishers
- Register a topic at the Hub
- Publish to the hub, indicating an update of the topic
- Subscribers
- Subscribe/Unsubscribe to topics at a hub
Samples
This sample demonstrates a Subscriber Service with subscribeOnStartUp
set to true, which will result in a
subscription request being sent to the specified hub for the specified topic, with the specified lease seconds value
and the specified secret for authenticated content distribution.
Since an onIntentVerification
resource is not included, intent verification for subscription and unsubscription
requests would happen automatically, if the topic specified in the request matches that specified as an annotation or
that discovered for the annotated resource URL.
import ballerina/log;
import ballerina/websub;
endpoint websub:Listener websubEP {
port: 8181
};
@websub:SubscriberServiceConfig {
path: "/websub",
subscribeOnStartUp: true,
topic: "<TOPIC_URL>",
hub: "<HUB_URL>",
leaseSeconds: 3600,
secret: "<SECRET>"
}
service websubSubscriber bind websubEP {
onNotification(websub:Notification notification) {
log:printInfo("WebSub Notification Received: " + notification.payload.toString());
}
}
Explicit intent verification can be done by introducing an onIntentVerification
resource.
import ballerina/log;
import ballerina/http;
import ballerina/websub;
endpoint websub:Listener websubEP {
port: 8181
};
@websub:SubscriberServiceConfig {
path: "/websub",
subscribeOnStartUp: true,
topic: "<TOPIC_URL>",
hub: "<HUB_URL>",
leaseSeconds: 3600,
secret: "<SECRET>"
}
service websubSubscriber bind websubEP {
onIntentVerification(endpoint caller, websub:IntentVerificationRequest request) {
http:Response response = new;
// Insert logic to build subscription/unsubscription intent verification response
caller->respond(response) but {
error e => log:printError("Error responding to intent verification request", err = e)
};
}
onNotification(websub:Notification notification) {
log:printInfo("WebSub Notification Received: " + notification.payload.toString());
}
}
Functions are made available on the websub:IntentVerificationRequest
to build a subscription or unsubscription
verification response, specifying the topic to verify intent against:
http:Response response = request.buildSubscriptionVerificationResponse(topic = "<TOPIC_TO_VERIFY_FOR>");
http:Response response = request.buildUnsubscriptionVerificationResponse(topic = "<TOPIC_TO_VERIFY_FOR>");
Ballerina publishers can start up and publish directly to the Ballerina WebSub hub.
import ballerina/log;
import ballerina/runtime;
import ballerina/websub;
function main(string... args) {
log:printInfo("Starting up the Ballerina Hub Service");
websub:WebSubHub webSubHub = websub:startUpBallerinaHub(port = 9191);
var registrationResponse = webSubHub.registerTopic("<TOPIC_URL>");
match (registrationResponse) {
error webSubError => log:printError("Error occurred registering topic: " + webSubError.message);
() => log:printInfo("Topic registration successful!");
}
// Make the publisher wait until the subscriber subscribes at the hub.
runtime:sleep(20000);
log:printInfo("Publishing update to internal Hub");
var publishResponse = webSubHub.publishUpdate("<TOPIC_URL>", {"action": "publish", "mode": "internal-hub"});
match (publishResponse) {
error webSubError => log:printError("Error notifying hub: " + webSubError.message);
() => log:printInfo("Update notification successful!");
}
// Make sure the service is running until the subscriber receives the update notification.
runtime:sleep(5000);
}
Ballerina publishers can also use the hub client endpoint to register topics at Ballerina WebSub hubs and publish/notify updates to the remote hubs.
import ballerina/log;
import ballerina/runtime;
import ballerina/websub;
endpoint websub:Client websubHubClientEP {
url: "https://localhost:9191/websub/hub"
};
function main(string... args) {
var registrationResponse = websubHubClientEP->registerTopic("<TOPIC_URL>");
match (registrationResponse) {
error webSubError => log:printError("Error occurred registering topic: " + webSubError.message);
() => log:printInfo("Topic registration successful!");
}
// Make the publisher wait until the subscriber subscribes at the hub.
runtime:sleep(10000);
log:printInfo("Publishing update to remote Hub");
var publishResponse = websubHubClientEP->publishUpdate("<TOPIC_URL>", {"action": "publish", "mode": "remote-hub"});
match (publishResponse) {
error webSubError => log:printError("Error notifying hub: " + webSubError.message);
() => log:printInfo("Update notification successful!");
}
}
The hub client endpoint can also be used by subscribers to send subscription and unsubscription requests explicitly.
import ballerina/log;
import ballerina/websub;
endpoint websub:Client websubHubClientEP {
url: "<HUB_URL>"
};
function main(string... args) {
// Send subscription request for a subscriber service.
websub:SubscriptionChangeRequest subscriptionRequest = { topic: "<TOPIC_URL>",
callback: "<CALLBACK_URL>",
secret: "<SECRET>" };
var response = websubHubClientEP->subscribe(subscriptionRequest);
match (response) {
websub:SubscriptionChangeResponse subscriptionChangeResponse => {
log:printInfo("Subscription Request successful at Hub [" + subscriptionChangeResponse.hub
+ "] for Topic [" + subscriptionChangeResponse.topic + "]");
}
error e => {
log:printError("Error occurred with Subscription Request", err = e);
}
}
// Send unsubscription request for the subscriber service.
websub:SubscriptionChangeRequest unsubscriptionRequest = { topic: "<TOPIC_URL>",
callback: "<CALLBACK_URL>" };
response = websubHubClientEP->unsubscribe(unsubscriptionRequest);
match (response) {
websub:SubscriptionChangeResponse subscriptionChangeResponse => {
log:printInfo("Unsubscription Request successful at Hub [" + subscriptionChangeResponse.hub
+ "] for Topic [" + subscriptionChangeResponse.topic + "]");
}
error e => {
log:printError("Error occurred with Unsubscription Request", err = e);
}
}
}
Configuration Parameters
The Ballerina WebSub implementation allows specifying the following properties/parameters via the Ballerina Config API.
Configuration Key | Default Value | Description |
---|---|---|
b7a.websub.hub.port | 9292 | The port to start the WebSub Hub Service on |
b7a.websub.hub.leasetime | 86400 | The default lease period, if not specified in a request |
b7a.websub.hub.signaturemethod | "SHA256" | The signature method to use for authenticated content distribution |
b7a.websub.hub.remotepublish | false | Whether publishing updates against the topics in the hub could be done by remote publishers via HTTP requests with hub.mode set to publish |
b7a.websub.hub.topicregistration | true | Whether a topic needs to be registered at the hub for publishers to publish updates against the topic and for subscribers to send subscription requests for the topic |
b7a.websub.hub.enablessl | true | Whether the Hub service should be exposed over HTTPS |
Annotations
Name | Attachement Points | Data Type | Description |
---|---|---|---|
SubscriberServiceConfig | service | SubscriberServiceConfiguration | WebSub Subscriber Configuration for the service, indicating subscription related parameters. |
Records Summary
Record | Description | ||
---|---|---|---|
HubClientEndpointConfig | Record representing the configuration parameters for the WebSub Hub Client Endpoint. |
||
Notification | Record representing the WebSub Content Delivery Request received. |
||
SubscriberServiceConfiguration | Configuration for a WebSubSubscriber service. |
||
SubscriberServiceEndpointConfiguration | Object representing the configuration for the WebSub Subscriber Service Endpoint. |
||
SubscriptionChangeRequest | Record representing a WebSub subscription change request. |
||
SubscriptionChangeResponse | Record representing subscription/unsubscription details if a subscription/unsubscription request is successful. |
Objects Summary
Object | Description | ||
---|---|---|---|
IntentVerificationRequest | Object representing an intent verification request received. |
||
Service | |||
SignatureValidationFilter | Signature validation filter for WebSub services. |
||
WebSubHub | Object representing a Ballerina WebSub Hub. |
Functions Summary
Return Type | Function and Description | ||
---|---|---|---|
Response | addWebSubLinkHeader(ballerina.http:Response response, string[] hubs, string topic) Function to add link headers to a response to allow WebSub discovery. |
||
FilterResult | interceptWebSubRequest(ballerina.http:Request request, ballerina.http:FilterContext context) The function called to validate signature for content received by WebSub services. |
||
WebSubHub | startUpBallerinaHub(int? port) Starts up the Ballerina Hub. |
public type HubClientEndpointConfig
Record representing the configuration parameters for the WebSub Hub Client Endpoint.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
url | string | The URL of the target Hub |
|
secureSocket | ballerina.http:SecureSocket? | SSL/TLS related options for the underlying HTTP Client |
|
auth | ballerina.http:AuthConfig? | Authentication mechanism for the underlying HTTP Client |
|
followRedirects | ballerina.http:FollowRedirects? | HTTP redirect related configuration |
public type Notification
Record representing the WebSub Content Delivery Request received.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
payload | json | The JSON payload of the notification received |
|
request | ballerina.http:Request | The HTTP POST request received as the notification |
public type SubscriberServiceConfiguration
Configuration for a WebSubSubscriber service.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
endpoints | websub:Listener[] | Array of endpoints the service would be attached to |
|
path | string | Path of the WebSubSubscriber service |
|
subscribeOnStartUp | boolean | Boolean indicating whether a subscription request is expected to be sent on start up |
|
resourceUrl | string | The resource URL for which discovery will be initiated to identify hub and topic if not specified |
|
hub | string | The hub at which the subscription should be registered |
|
topic | string | The topic for which this WebSub subscriber (callback) should be registered |
|
leaseSeconds | int | The period for which the subscription is expected to be active |
|
secret | string | The secret to be used for authenticated content distribution |
|
callback | string | The callback to use when registering, if unspecified host:port/path will be used |
|
auth | ballerina.http:AuthConfig? | The auth configuration to use when subscribing at the hub |
|
secureSocket | ballerina.http:SecureSocket? | The secure socket configuration to use when subscribing at the hub |
|
followRedirects | ballerina.http:FollowRedirects? | The HTTP redirect related configuration specifying if subscription requests should be sent to redirected hubs/topics |
public type SubscriberServiceEndpointConfiguration
Object representing the configuration for the WebSub Subscriber Service Endpoint.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
host | string | The configuration for the endpoint |
|
port | int | The underlying HTTP service endpoint |
|
secureSocket | ballerina.http:ServiceSecureSocket? | The SSL configurations for the service endpoint |
|
topicIdentifier | TOPIC_ID_HEADER|TOPIC_ID_PAYLOAD_KEY|TOPIC_ID_HEADER_AND_PAYLOAD? | The identifier based on which dispatching should happen for custom subscriber services |
|
topicHeader | string? | The header to consider if required with dispatching for custom services |
|
topicPayloadKeys | string[]? | The payload keys to consider if required with dispatching for custom services |
|
topicResourceMap | map | The mapping between topics and resources if required for custom services |
public type SubscriptionChangeRequest
Record representing a WebSub subscription change request.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
topic | string | The topic for which the subscription/unsubscription request is sent |
|
callback | string | The callback which should be registered/unregistered for the subscription/unsubscription request is sent |
|
leaseSeconds | int | The lease period for which the subscription is expected to be active |
|
secret | string | The secret to be used for authenticated content distribution with this subscription |
public type SubscriptionChangeResponse
Record representing subscription/unsubscription details if a subscription/unsubscription request is successful.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
hub | string | The hub at which the subscription/unsubscription was successful |
|
topic | string | The topic for which the subscription/unsubscription was successful |
|
response | ballerina.http:Response | The response from the hub to the subscription/unsubscription requests |
public function addWebSubLinkHeader(ballerina.http:Response response, string[] hubs, string topic) returns (Response)
Function to add link headers to a response to allow WebSub discovery.
Parameter Name | Data Type | Default Value | Description |
---|---|---|---|
response | ballerina.http:Response | The response being sent |
|
hubs | string[] | The hubs the publisher advertises as the hubs that it publishes updates to |
|
topic | string | The topic to which subscribers need to subscribe to, to receive updates for the resource |
Return Type | Description | ||
---|---|---|---|
Response |
|
public function interceptWebSubRequest(ballerina.http:Request request, ballerina.http:FilterContext context) returns (FilterResult)
The function called to validate signature for content received by WebSub services.
Parameter Name | Data Type | Default Value | Description |
---|---|---|---|
request | ballerina.http:Request | The request being intercepted |
|
context | ballerina.http:FilterContext | The filter context |
Return Type | Description | ||
---|---|---|---|
FilterResult |
|
public function startUpBallerinaHub(int? port) returns (WebSubHub)
Starts up the Ballerina Hub.
Parameter Name | Data Type | Default Value | Description |
---|---|---|---|
port | int? | null | The port to start up the hub on |
Return Type | Description | ||
---|---|---|---|
WebSubHub |
|
public type IntentVerificationRequest object
Object representing an intent verification request received.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
mode | string | The mode specified whether intent is being verified for subscription or unsubscription |
|
topic | string | The for which intent is being verified for subscription or unsubscription |
|
challenge | string | The challenge to be echoed to verify intent to subscribe/unsubscribe |
|
leaseSeconds | int | The lease seconds period for which a subscription will be active if intent verification is being done for subscription |
|
request | ballerina.http:Request | The HTTP request received for intent verification |
-
<IntentVerificationRequest> buildSubscriptionVerificationResponse(string? topic) returns (Response)
Builds the response for the request, verifying intention to subscribe, if the topic matches that expected.
Parameter Name Data Type Default Value Description topic string? null The topic for which subscription should be accepted, if not specified the annotated topic will be used
Return Type Description Response http:Response
The response to the hub verifying/denying intent to subscribe -
<IntentVerificationRequest> buildUnsubscriptionVerificationResponse(string? topic) returns (Response)
Builds the response for the request, verifying intention to unsubscribe, if the topic matches that expected.
Parameter Name Data Type Default Value Description topic string? null The topic for which unsubscription should be accepted, if not specified the annotated topic will be used
Return Type Description Response http:Response
The response to the hub verifying/denying intent to unsubscribe
public type Service object
-
<Service> getEndpoint() returns (Listener)
Return Type Description Listener
public type SignatureValidationFilter object
Signature validation filter for WebSub services.
-
<SignatureValidationFilter> filterRequest(ballerina.http:Request request, ballerina.http:FilterContext context) returns (FilterResult)
Parameter Name Data Type Default Value Description request ballerina.http:Request context ballerina.http:FilterContext Return Type Description FilterResult
public type WebSubHub object
Object representing a Ballerina WebSub Hub.
Field Name | Data Type | Default Value | Description |
---|---|---|---|
hubUrl | string |
-
<WebSubHub> publishUpdate(string topic, json payload) returns (error)
Publishes an update against the topic in the initialized Ballerina Hub.
Parameter Name Data Type Default Value Description topic string The topic for which the update should happen
payload json The update payload
Return Type Description error error
if the hub is not initialized or does not represent the internal hub -
<WebSubHub> registerTopic(string topic) returns (error)
Registers a topic in the Ballerina Hub.
Parameter Name Data Type Default Value Description topic string The topic to register
Return Type Description error error
if an error occurred with registration -
<WebSubHub> unregisterTopic(string topic) returns (error)
Unregisters a topic in the Ballerina Hub.
Parameter Name Data Type Default Value Description topic string The topic to unregister
Return Type Description error error
if an error occurred with unregistration