WebPush
TsgcHTMLWebPush: Web Push for a browser that is not running or not on your page, in Delphi, C++ Builder and .NET. Available only in the Enterprise and All-Access editions.
TsgcHTMLWebPush: Web Push for a browser that is not running or not on your page, in Delphi, C++ Builder and .NET. Available only in the Enterprise and All-Access editions.
Web Push reaches a user whose browser is closed or showing another page. The page subscribes, your application stores the subscription through three events, and one call to Send delivers the notification. Available only in the Enterprise and All-Access editions.
TsgcHTMLWebPush (unit sgcHTML_WebPush)
No markup: encrypted push messages sent to the browser
Delphi, C++ Builder, .NET
Generate a VAPID key pair once and keep it. Assign the pair and a Subject, handle the storage events, set WebPush on the engine and WebPushEnabled on its template, then call Send.
// Enterprise and All-Access only: compiled when SGC_WEBPUSH is defined
uses
Data.DB, sgcHTML_WebPush, sgcHTMX_Engine_Server;
var
vPublic, vPrivate: string;
begin
// once, then keep both values in your own settings
TsgcHTMLWebPush.GenerateVAPIDKeys(vPublic, vPrivate);
FPush := TsgcHTMLWebPush.Create(Self);
FPush.VAPIDPublicKey := vPublic;
FPush.VAPIDPrivateKey := vPrivate;
FPush.Subject := 'mailto:support@example.com';
FPush.OnSaveSubscription := PushSave;
FPush.OnLoadSubscriptions := PushLoad;
FPush.OnDeleteSubscription := PushDelete;
FPush.OnSubscriptionExpired := PushExpired;
FPush.Enabled := True;
// oHTMX is a TsgcHTMX_Engine_Server
oHTMX.WebPush := FPush;
oHTMX.Template.WebPushEnabled := True;
// later, from anywhere in the application
FPush.Send('u1', 'Order shipped',
'Order 1042 left the warehouse', '/orders/1042');
end;
// the application owns the storage
procedure TForm1.PushSave(Sender: TObject;
const aUserID, aEndpoint, aP256dh, aAuth: string);
begin
SaveSubscription(aUserID, aEndpoint, aP256dh, aAuth);
end;
procedure TForm1.PushLoad(Sender: TObject; const aUserID: string;
const aList: TsgcHTMLWebPushSubscriptions);
var
oQuery: TDataSet;
begin
oQuery := OpenSubscriptions(aUserID);
try
// one Add per stored subscription of this user
while not oQuery.Eof do
begin
aList.Add(oQuery.FieldByName('endpoint').AsString,
oQuery.FieldByName('p256dh').AsString,
oQuery.FieldByName('auth').AsString);
oQuery.Next;
end;
finally
oQuery.Free;
end;
end;
procedure TForm1.PushDelete(Sender: TObject; const aUserID, aEndpoint: string);
begin
DeleteSubscription(aUserID, aEndpoint);
end;
procedure TForm1.PushExpired(Sender: TObject;
const aUserID, aEndpoint: string; aStatusCode: Integer);
begin
// 404 or 410: the push service dropped it for good
DeleteSubscription(aUserID, aEndpoint);
end;
// in the page, a click starts the permission prompt:
// <button data-sgc-webpush>Enable notifications</button>
// includes: sgcHTML_WebPush.hpp, sgcHTMX_Engine_Server.hpp
// Enterprise and All-Access only: compiled when SGC_WEBPUSH is defined
String vPublic, vPrivate;
// once, then keep both values in your own settings
TsgcHTMLWebPush::GenerateVAPIDKeys(vPublic, vPrivate);
FPush = new TsgcHTMLWebPush(this);
FPush->VAPIDPublicKey = vPublic;
FPush->VAPIDPrivateKey = vPrivate;
FPush->Subject = "mailto:support@example.com";
FPush->OnSaveSubscription = PushSave;
FPush->OnLoadSubscriptions = PushLoad;
FPush->OnDeleteSubscription = PushDelete;
FPush->OnSubscriptionExpired = PushExpired;
FPush->Enabled = true;
// oHTMX is a TsgcHTMX_Engine_Server
oHTMX->WebPush = FPush;
oHTMX->Template->WebPushEnabled = true;
// later, from anywhere in the application
FPush->Send("u1", "Order shipped",
"Order 1042 left the warehouse", "/orders/1042");
// the application owns the storage
void __fastcall TForm1::PushSave(TObject *Sender,
const UnicodeString aUserID, const UnicodeString aEndpoint,
const UnicodeString aP256dh, const UnicodeString aAuth)
{
SaveSubscription(aUserID, aEndpoint, aP256dh, aAuth);
}
void __fastcall TForm1::PushLoad(TObject *Sender, const UnicodeString aUserID,
TsgcHTMLWebPushSubscriptions *const aList)
{
// one Add per stored subscription of this user
for (const TStoredSubscription &row : LoadSubscriptions(aUserID))
aList->Add(row.Endpoint, row.P256dh, row.Auth);
}
// in the page, a click starts the permission prompt:
// <button data-sgc-webpush>Enable notifications</button>
using esegece.sgcWebSockets;
// once, then keep both values in your own settings
TsgcHTMLWebPush.GenerateVAPIDKeys(out var pub, out var priv);
var push = new TsgcHTMLWebPush();
push.VAPIDPublicKey = pub;
push.VAPIDPrivateKey = priv;
push.Subject = "mailto:support@example.com";
// the application owns the storage
push.OnSaveSubscription += (sender, aUserID, aEndpoint, aP256dh, aAuth) =>
SaveSubscription(aUserID, aEndpoint, aP256dh, aAuth);
push.OnLoadSubscriptions += (sender, aUserID, aList) =>
{
// one Add per stored subscription of this user
foreach (var row in LoadSubscriptions(aUserID))
aList.Add(row.Endpoint, row.P256dh, row.Auth);
};
push.OnDeleteSubscription += (sender, aUserID, aEndpoint) =>
DeleteSubscription(aUserID, aEndpoint);
push.OnSubscriptionExpired += (sender, aUserID, aEndpoint, aStatusCode) =>
DeleteSubscription(aUserID, aEndpoint); // 404 or 410: gone for good
push.Enabled = true;
// htmx is a TsgcHTMX_Engine_Server
htmx.WebPush = push;
htmx.Template.WebPushEnabled = true;
// later, from anywhere in the application
int delivered = push.Send("u1", "Order shipped",
"Order 1042 left the warehouse", "/orders/1042");
// in the page, a click starts the permission prompt:
// <button data-sgc-webpush>Enable notifications</button>
The members you reach for most often.
Web Push is available only in the Enterprise and All-Access editions. The unit compiles when SGC_WEBPUSH is defined, which sgcVer.inc sets for those two editions, and it also needs sgcHTML itself, which is sold independently. The notification inbox and the preferences table are part of sgcHTML and do not need it: only the push channel does.
GenerateVAPIDKeys is a class method that returns a new pair: the public half is the base64url application server key the browser subscribes with, the private half an EC key in PEM. Assign them to VAPIDPublicKey and VAPIDPrivateKey. Subject is the VAPID contact, a mailto: or https: URI. The standard Delphi build loads OpenSSL 3.x for the cryptography and selects that API itself; the .NET port uses managed cryptography and needs no OpenSSL.
The component keeps no subscription of its own. OnSaveSubscription(aUserID, aEndpoint, aP256dh, aAuth) stores one, OnLoadSubscriptions(aUserID, aList) fills aList with one Add(endpoint, p256dh, auth) per stored subscription of the user, and OnDeleteSubscription(aUserID, aEndpoint) removes one. Each entry is a TsgcHTMLWebPushSubscription.
Send(aUserID, aTitle, aBody, aURL, aIcon, aTag, aTTL, aUrgency) encrypts one notification per stored subscription of the user (RFC 8291) and POSTs it to the push service with a VAPID signature (RFC 8292). It returns how many subscriptions it delivered to, or 0 when Enabled is False. The sender, TsgcHTMLWebPushClient, reuses TsgcHTTP_API_WebPush_Client.
TTL defaults to 2419200 seconds (28 days) and Urgency is empty, which sends no urgency header. Pass aTTL or aUrgency to Send to override either for one notification.
When a push service answers 404 or 410, OnSubscriptionExpired(aUserID, aEndpoint, aStatusCode) fires so you can drop the subscription. Any other failure raises OnSendError(aUserID, aEndpoint, E) and the remaining subscriptions are still tried.
Once the component is assigned to the engine, a POST to SubscribeEndpoint (/push/subscribe) or UnsubscribeEndpoint (/push/unsubscribe) is answered for you: 204 when handled, 400 for an invalid subscription, 403 when no user resolves, 413 above 64 KB. Call HandleSubscribe(aJSON, aUserID) and HandleUnsubscribe yourself to route the POSTs your own way.
The user is never read from what the browser posts. With a TsgcHTMLAuth on the engine it is the user of the session cookie, and the CSRF token is required. Without one, OnResolveUser(aCookieHeader, aHeaders, aBody, aUserID, aAllowed) answers with your own user id or refuses. With neither, the request gets 403.
With Template.WebPushEnabled on, the engine copies the public key and endpoints into the template, serves the service worker at ServiceWorkerPath and the page carries the subscribe script. Put <button data-sgc-webpush> in the page so the permission prompt runs from a click; data-sgc-webpush="unsubscribe" turns it off. Browsers need https, or localhost while developing.
TsgcHTMLComponent_Notification chooses between the two. Assign Presence and WebPush, answer OnGetChannel with ncPush and call AddNotification(aUserID, aId, aTitle, aMessage, ...): a connected user gets the notification in the page, a user who is not connected gets one Send, so nobody is told twice. Without an OnGetChannel handler nothing is ever pushed.
| Online HelpFull API reference and usage guide for this component. | Open | |
| Web Push guideHow the pieces fit together: subscriptions, VAPID, the engine endpoints and the notification component. | Open | |
| All sgcHTML ComponentsBrowse the full feature matrix of 80+ components. | Open | |
| Download Free TrialThe 30-day trial ships the 60.HTML demo projects, including 17.FieldService, which uses Web Push. | Open | |
| PricingSingle, Team and Site licenses with full source code. | Open |