Commit b63c5f27 authored by Mario's avatar Mario

Polling fallback to server sent events. Polling is the default. SSE must be...

Polling fallback to server sent events. Polling is the default. SSE must be enabled in /admin/site > Advanced > Enable SSE Notifications if desired.
parent 685c569e
Pipeline #362605 passed with stage
in 3 minutes and 42 seconds
......@@ -62,6 +62,9 @@ class Site {
$from_email = ((array_key_exists('from_email',$_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . \App::get_hostname());
$from_email_name = ((array_key_exists('from_email_name',$_POST) && trim($_POST['from_email_name'])) ? trim($_POST['from_email_name']) : \Zotlabs\Lib\System::get_site_name());
$sse_enabled = ((x($_POST,'sse_enabled')) ? true : false);
$verifyssl = ((x($_POST,'verifyssl')) ? True : False);
$proxyuser = ((x($_POST,'proxyuser')) ? notags(trim($_POST['proxyuser'])) : '');
$proxy = ((x($_POST,'proxy')) ? notags(trim($_POST['proxy'])) : '');
......@@ -151,6 +154,9 @@ class Site {
set_config('system','no_community_page', $no_community_page);
set_config('system','no_utf', $no_utf);
set_config('system','sse_enabled', $sse_enabled);
set_config('system','verifyssl', $verifyssl);
set_config('system','proxyuser', $proxyuser);
set_config('system','proxy', $proxy);
......@@ -331,6 +337,8 @@ class Site {
'$directory_server' => (($dir_choices) ? array('directory_server', t("Directory Server URL"), get_config('system','directory_server'), t("Default directory server"), $dir_choices) : null),
'$sse_enabled' => array('sse_enabled', t('Enable SSE Notifications'), get_config('system', 'sse_enabled', 0), t('If disabled, traditional polling will be used. Warning: this setting might not be suited for shared hosting.')),
'$proxyuser' => array('proxyuser', t("Proxy user"), get_config('system','proxyuser'), ""),
'$proxy' => array('proxy', t("Proxy URL"), get_config('system','proxy'), ""),
'$timeout' => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")),
......
......@@ -14,6 +14,7 @@ class Sse extends Controller {
public static $ob_hash;
public static $sse_id;
public static $vnotify;
public static $sse_enabled;
function init() {
......@@ -49,18 +50,86 @@ class Sse extends Controller {
$sys = get_sys_channel();
$sleep_seconds = 3;
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
header("Connection: keep-alive");
header("X-Accel-Buffering: no");
self::$sse_enabled = get_config('system', 'sse_enabled', 0);
while(true) {
if(self::$sse_enabled) {
/**
* Update chat presence indication (if applicable)
*/
// Server Sent Events
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
header("Connection: keep-alive");
header("X-Accel-Buffering: no");
while(true) {
if(! self::$sse_id) {
// Update chat presence indication
$r = q("select cp_id, cp_room from chatpresence where cp_xchan = '%s' and cp_client = '%s' and cp_room = 0 limit 1",
dbesc(self::$ob_hash),
dbesc($_SERVER['REMOTE_ADDR'])
);
$basic_presence = false;
if($r) {
$basic_presence = true;
q("update chatpresence set cp_last = '%s' where cp_id = %d",
dbesc(datetime_convert()),
intval($r[0]['cp_id'])
);
}
if(! $basic_presence) {
q("insert into chatpresence ( cp_xchan, cp_last, cp_status, cp_client)
values( '%s', '%s', '%s', '%s' ) ",
dbesc(self::$ob_hash),
dbesc(datetime_convert()),
dbesc('online'),
dbesc($_SERVER['REMOTE_ADDR'])
);
}
}
XConfig::Load(self::$ob_hash);
$result = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []);
$lock = XConfig::Get(self::$ob_hash, 'sse', 'lock');
if($result && !$lock) {
echo "event: notifications\n";
echo 'data: ' . json_encode($result);
echo "\n\n";
XConfig::Set(self::$ob_hash, 'sse', 'notifications', []);
unset($result);
}
// always send heartbeat to detect disconnected clients
echo "event: heartbeat\n";
echo 'data: {}';
echo "\n\n";
ob_end_flush();
flush();
if(connection_status() != CONNECTION_NORMAL || connection_aborted()) {
//TODO: this does not seem to be triggered
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE);
break;
}
sleep($sleep_seconds);
}
}
else {
// Fallback to traditional polling
if(! self::$sse_id) {
// Update chat presence indication
$r = q("select cp_id, cp_room from chatpresence where cp_xchan = '%s' and cp_client = '%s' and cp_room = 0 limit 1",
dbesc(self::$ob_hash),
dbesc($_SERVER['REMOTE_ADDR'])
......@@ -90,29 +159,11 @@ class Sse extends Controller {
$lock = XConfig::Get(self::$ob_hash, 'sse', 'lock');
if($result && !$lock) {
echo "event: notifications\n";
echo 'data: ' . json_encode($result);
echo "\n\n";
XConfig::Set(self::$ob_hash, 'sse', 'notifications', []);
unset($result);
}
// always send heartbeat to detect disconnected clients
echo "event: heartbeat\n";
echo 'data: {}';
echo "\n\n";
ob_end_flush();
flush();
if(connection_status() != CONNECTION_NORMAL || connection_aborted()) {
//TODO: this does not seem to be triggered
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE);
break;
json_return_and_die($result);
}
sleep($sleep_seconds);
killme();
}
......
......@@ -1207,6 +1207,7 @@ class App {
'$metas' => self::$meta->get(),
'$plugins' => $x['header'],
'$update_interval' => $interval,
'$sse_enabled' => get_config('system', 'sse_enabled', 0),
'$head_css' => head_get_css(),
'$head_js' => head_get_js(),
'$linkrel' => head_get_links(),
......
......@@ -31,6 +31,7 @@ var sse_offset = 0;
var sse_type;
var sse_partial_result = false;
var sse_rmids = [];
var sse_fallback_interval;
var page_cache = {};
......@@ -94,37 +95,56 @@ $(document).ready(function() {
jQuery.timeago.settings.allowFuture = true;
if(typeof(window.SharedWorker) === 'undefined') {
// notifications with multiple tabs open will not work very well in this scenario
var evtSource = new EventSource('/sse');
evtSource.addEventListener('notifications', function(e) {
var obj = JSON.parse(e.data);
sse_handleNotifications(obj, false, false);
}, false);
if(sse_enabled) {
if(typeof(window.SharedWorker) === 'undefined') {
// notifications with multiple tabs open will not work very well in this scenario
var evtSource = new EventSource('/sse');
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
sse_offset = 0;
sse_bs_init();
}
}, false);
evtSource.addEventListener('notifications', function(e) {
var obj = JSON.parse(e.data);
sse_handleNotifications(obj, false, false);
}, false);
}
else {
var myWorker = new SharedWorker('/view/js/sse_worker.js', localUser);
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
sse_offset = 0;
sse_bs_init();
}
}, false);
myWorker.port.onmessage = function(e) {
obj = e.data;
console.log(obj);
sse_handleNotifications(obj, false, false);
}
else {
var myWorker = new SharedWorker('/view/js/sse_worker.js', localUser);
myWorker.onerror = function(e) {
myWorker.port.close();
myWorker.port.onmessage = function(e) {
obj = e.data;
console.log(obj);
sse_handleNotifications(obj, false, false);
}
myWorker.onerror = function(e) {
myWorker.port.close();
}
myWorker.port.start();
}
}
else {
if (!document.hidden)
sse_fallback_interval = setInterval(sse_fallback, updateInterval);
myWorker.port.start();
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
clearInterval(sse_fallback_interval);
}
else {
sse_offset = 0;
sse_bs_init();
sse_fallback_interval = setInterval(sse_fallback, updateInterval);
}
}, false);
}
$('.notification-link').on('click', { replace: true, followup: false }, sse_bs_notifications);
......@@ -224,6 +244,8 @@ $(document).ready(function() {
cache_next_page();
});
});
function getConversationSettings() {
......@@ -1763,8 +1785,6 @@ function sse_bs_init() {
}
function sse_bs_counts() {
if(sse_bs_active)
return;
......@@ -2022,3 +2042,15 @@ function sse_setNotificationsStatus() {
}
}
function sse_fallback() {
$.get('/sse', function(obj) {
if(! obj)
return;
console.log('sse fallback');
console.log(obj);
sse_handleNotifications(obj, false, false);
});
}
......@@ -95,6 +95,7 @@
</div>
<h3>{{$advanced}}</h3>
{{include file="field_checkbox.tpl" field=$sse_enabled}}
{{include file="field_input.tpl" field=$imagick_path}}
{{include file="field_input.tpl" field=$proxy}}
{{include file="field_input.tpl" field=$proxyuser}}
......
......@@ -9,6 +9,7 @@
{{$plugins}}
<script>
var updateInterval = {{$update_interval}};
var sse_enabled = {{$sse_enabled}};
var localUser = {{if $local_channel}}{{$local_channel}}{{else}}false{{/if}};
var zid = {{if $zid}}'{{$zid}}'{{else}}null{{/if}};
var justifiedGalleryActive = false;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment