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

  1. Subscriber discovers the topics it needs to subscribe to in order to receive updates/content and discovers the hub(s) where it can subscribe.

  2. 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
  3. 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.

  4. Publisher notifies the hub of updates to the topic, and the content to deliver is identified.

  5. 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:

  1. Publishers
    • Register a topic at the Hub
    • Publish to the hub, indicating an update of the topic
  2. 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 KeyDefault ValueDescription
b7a.websub.hub.port9292The port to start the WebSub Hub Service on
b7a.websub.hub.leasetime86400The 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.remotepublishfalseWhether 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.topicregistrationtrueWhether 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.enablessltrueWhether 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

The object representing the WebSub Subscriber Service.

SignatureValidationFilter

Signature validation filter for WebSub services.

WebSubHub

Object representing a Ballerina WebSub Hub.

Endpoints Summary

Endpoint Description
Client

Object representing the WebSub Hub Client Endpoint.

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_PAYLOAD_KEY|TOPIC_ID_HEADER_AND_PAYLOAD|TOPIC_ID_HEADER?

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

http:Response Response with the link header added

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

http:FilterResult The result of the filter indicating whether or not proceeding can be allowed

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

WebSubHub The WebSubHub object representing the started up hub

Endpoint Client

Object representing the WebSub Hub Client Endpoint.

Field Name Data Type Default Value Description
hubUrl string

The URL of the target Hub to which requests need to be sent

  • <Client> subscribe(websub:SubscriptionChangeRequest subscriptionRequest) returns (SubscriptionChangeResponse | error)

    Sends a subscription request to a WebSub Hub.

    Parameter Name Data Type Default Value Description
    subscriptionRequest websub:SubscriptionChangeRequest

    The SubscriptionChangeRequest containing subscription details

    Return Type Description
    SubscriptionChangeResponse | error

    SubscriptionChangeResponse indicating subscription details, if the request was successful else error if an error occurred with the subscription request

  • <Client> unsubscribe(websub:SubscriptionChangeRequest unsubscriptionRequest) returns (SubscriptionChangeResponse | error)

    Sends an unsubscription request to a WebSub Hub.

    Parameter Name Data Type Default Value Description
    unsubscriptionRequest websub:SubscriptionChangeRequest

    The SubscriptionChangeRequest containing unsubscription details

    Return Type Description
    SubscriptionChangeResponse | error

    SubscriptionChangeResponse indicating unsubscription details, if the request was successful else error if an error occurred with the unsubscription request

  • <Client> registerTopic(string topic, string? secret) returns (error)

    Registers a topic in a Ballerina WebSub Hub against which subscribers can subscribe and the publisher will publish updates, with a secret which will be used in signature generation if specified.

    Parameter Name Data Type Default Value Description
    topic string

    The topic to register

    secret string? null

    The secret the publisher will use to generate a signature when publishing updates

    Return Type Description
    error

    error if an error occurred registering the topic

  • <Client> unregisterTopic(string topic, string? secret) returns (error)

    Unregisters a topic in a Ballerina WebSub Hub.

    Parameter Name Data Type Default Value Description
    topic string

    The topic to unregister

    secret string? null

    The secret the publisher used when registering the topic

    Return Type Description
    error

    error if an error occurred unregistering the topic

  • <Client> publishUpdate(string topic, json payload, string? secret, string signatureMethod, map<string>? headers) returns (error)

    Publishes an update to a remote Ballerina WebSub Hub.

    Parameter Name Data Type Default Value Description
    topic string

    The topic for which the update occurred

    payload json

    The update payload

    secret string? null

    The secret used when registering the topic

    signatureMethod string sha256

    The signature method to use to generate a secret

    headers map? null

    The headers, if any, that need to be set

    Return Type Description
    error

    error if an error occurred with the update

  • <Client> notifyUpdate(string topic, map<string>? headers) returns (error)

    Notifies a remote WebSub Hub that an update is available to fetch, for hubs that require publishing to happen as such.

    Parameter Name Data Type Default Value Description
    topic string

    The topic for which the update occurred

    headers map? null

    The headers, if any, that need to be set

    Return Type Description
    error

    error if an error occurred with the notification

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

The object representing the WebSub Subscriber Service.

  • <Service> getEndpoint() returns (Listener)

    Returns the WebSub Listener endpoint to which this service binds.

    Return Type Description
    Listener

    WebSub Listener endpoint

public type SignatureValidationFilter object

Signature validation filter for WebSub services.

  • <SignatureValidationFilter> filterRequest(ballerina.http:Request request, ballerina.http:FilterContext context) returns (FilterResult)

    Represents the filtering function that will be invoked on WebSub notification requests.

    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

    http:FilterResult The result of the filter indicating whether or not proceeding can be allowed

public type WebSubHub object

Object representing a Ballerina WebSub Hub.

Field Name Data Type Default Value Description
hubUrl string

The URL of the started up Ballerina WebSub Hub

  • <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