Page 1 of 2
SSE example
Posted: Tue Aug 11, 2020 5:44 am
by dakosimo
Is it possible to use Server-Sent-Events in Mod-harbour and Mercury ? How to define and use EventSource ?
I did not find any example in mod_harbour_samples.
On Internet there are samples for PHP, but I do not know how to realize it using Mercury. I made this view:
{{ View( 'head.view' ) }}
<body>
<h1>Testing SSE</h1>
<div id="result">
</div>
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("servtime.prg");
source.onmessage = function(event) {
document.getElementById("result").innerHTML += event.data + "<br>";
};
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>
</html>
In servtime.prg I have:
FUNCTION Servtime()
LOCAL stime = TIME()
AP_RPuts( stime )
RETURN NIL
Servtime.prg I put in document root, in place where index.prg exists also.
On display I only get Testing SSE as header text, but no time.
Thank you and Best regards.
Dako.
Re: SSE example
Posted: Tue Aug 11, 2020 10:12 am
by Antonio Linares
Dako,
It is not implemented yet and it may work on the mod_harbour fastCGI version only
We are studying the way to implement it
Re: SSE example
Posted: Tue Aug 11, 2020 10:25 am
by dakosimo
I will wait. It is not urgent.
Thank you.
Best regards.
Dako.
Re: SSE example
Posted: Wed May 24, 2023 2:11 pm
by ssbbs
dakosimo wrote:I will wait. It is not urgent.
Thank you.
Best regards.
Dako.
I've done the SSE functional test!!
https://www4.zzz.com.tw/phpBB3/viewtopic.php?f=2&t=358
Re: SSE example
Posted: Wed May 24, 2023 3:02 pm
by Otto
Hello,
May I ask if the method "printData()" in the class "TPER" is from your program? Do you run it on Mod Harbour?
Best regards,
Otto
Re: SSE example
Posted: Wed May 24, 2023 3:28 pm
by ssbbs
Otto wrote:Hello,
May I ask if the method "printData()" in the class "TPER" is from your program? Do you run it on Mod Harbour?
Best regards,
Otto
Yes!! I'm run on mod_harbour!!
The TPER is a CLASS I wrote for mod_harbour implementation in the backend.
Re: SSE example
Posted: Wed May 24, 2023 5:46 pm
by Otto
Hello,
Thank you. Do you have a sample code?
I am working on a chat application.
I didn't know about Server-Sent Events (SSE) until you mentioned it here.
SSE is a technology that enables real-time communication between a web browser and a server. It allows the server to send events or updates to the client (web page) over a single, long-lived HTTP connection.
SSE can be a more efficient and reliable approach compared to interval requests with AJAX, which is what I currently use.
Would you be willing to share your code?
Best regards,
Otto
Re: SSE example
Posted: Thu May 25, 2023 4:24 am
by ssbbs
Otto wrote:Hello,
Thank you. Do you have a sample code?
I am working on a chat application.
I didn't know about Server-Sent Events (SSE) until you mentioned it here.
SSE is a technology that enables real-time communication between a web browser and a server. It allows the server to send events or updates to the client (web page) over a single, long-lived HTTP connection.
SSE can be a more efficient and reliable approach compared to interval requests with AJAX, which is what I currently use.
Would you be willing to share your code?
Best regards,
Otto
The way mod_harbour is implemented is to insert HTML into the PRG.
The way I implement mod_harbour is to include PRG in HTML.
The implementation is just like PHP, so you have to modify the example code.
I test it and
httpd.exe cpu loading is 0%~0.5% only.
demo file download:
https://is.gd/lyVHyf
Code: Select all | Expand
<?prg
LOCAL cMethod := AP_Method()
LOCAL hParam := hb_hash()
LOCAL hData := hb_hash()
LOCAL i := 0,;
nTolRec := 100,;
nFunc := 0
LOCAL hRes := {;
'err_no'=>0; // 錯誤碼
,'errmsg'=>''; // 錯誤訊息
,'errtil'=>''; // 錯誤抬頭
,'lEnd'=>0; // 結束?<*0:No/1:Yes>
,'step'=>0; // 目前進度指標(every)
,'total'=>0; // 總筆數(total)
}
//
IF cMethod == 'POST'
hParam := AP_PostPairs()
ELSE
hParam := AP_GetPairs()
ENDIF
//
IF hb_hHasKey(hParam, 'func')
nFunc := Val(hParam['func'])
ELSE
nFunc := -1
ENDIF
// AP_RPuts('data method:'+cMethod,'<br>')
IF nFunc == 9
// hData := hb_jsondecode(hParam, 'data')
// 送出 SSE 專用表頭
AP_SetContentType( "Cache-Control: no-cache" ) // 關閉快取
AP_SetContentType( "text/event-stream;charset=UTF-8" ) // 送出表頭格式
//
hRes['step' ] := 0
hRes['total'] := 100
sendMsg( hb_jsonencode( hRes ) )
//
FOR i := 1 To 100
hRes['step' ] := i
hRes['total'] := nTolRec
sendMsg( hb_jsonencode( hRes ) )
NEXT i
hRes['lEnd'] := 1 // 利用此變數通知前端處理完畢!!
sendMsg( hb_jsonencode( hRes ))
RETURN
ENDIF
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>SSE test!!</title>
<meta name="generator" content="WYSIWYG Web Builder 18 - https://www.wysiwygwebbuilder.com">
<link href="test.css" rel="stylesheet">
<link href="index.css" rel="stylesheet">
<script src="jquery-3.6.4.min.js"></script>
<script>
function go(){
let url='index.prg';
let data={
a: 1
,b: 2
};
let param=url+'?func=9&data='+JSON.stringify(data);
alert('Go...');
source = new EventSource(param,{
withCredentials: false // Whether to allow cross domain requests, default: false
});
// Handle postback events 'message'
source.addEventListener('message',function(e){
let o=JSON.parse(e.data); // analyze
if(o.lEnd == 0){ // lEnd=1:End
// Screen display progress processing
let per=Math.round( o.step/o.total*100,2 ); // calculate percentage
$('#progress').val(per);
$('#progress_down').text( per+'%' );
}else{
source.close(); // Close SSE!! Don't send data back!!
//
if(o.err_no != 0){
alert('Failed to read data!!'+o.err_msg, 'Error!!');
return;
}else{
alert('success!!')
}
}
});
// Handle the event on start 'open'
source.addEventListener('open', function(e) {
//
});
// Events when errors are handled 'error'
source.addEventListener('error', function(e) {
if(e.target.readyState == EventSource.CLOSED){
alert('connect be close!!');
source.close(); // stop SSE
}else{
console.log('error!!');
}
});
// Handle system status events 'status'
source.addEventListener('status', function(e) {
console.log('System status is now: ' + e.data);
});
}
</script>
</head>
<body>
<input type="button" id="Button1" onclick="javascript:go();return false;" name="" value="Test" style="position:absolute;left:170px;top:143px;width:132px;height:52px;z-index:0;">
<!-- jquery -->
<label for="" id="progress_down" style="position:absolute;left:0px;top:100px;width:465px;height:35px;line-height:35px;z-index:2;">Text</label>
<span style="color:#000000;font-family:微軟正黑體;font-size:14px;"><strong id="progress_up"><br> Process wait...</strong></span>'
<br><progress id="progress" value="0" max="100" style="height:48px;width:100%;">
<br><label style="text-align:center;width:100%"></label>
</body>
</html>
<?prg
FUNC sendMsg(cRes)
AP_SetContentType( "text/event-stream;charset=UTF-8" ) // 送出表頭格式
AP_RWRITE( e"event: message\n" )
AP_RWRITE( e"id: "+hb_ntos( int( hb_TToMSec( hb_dateTime() )))+e"\n" )
AP_RWRITE( e"retry: 15000\n" ) // 一個整數值,指示斷開連接時的重新連接時間
AP_RWRITE( e"data: "+cRes+e"\n" )
AP_RWRITE( e"\n" ) // 跟上面 hb_eol() 組合成兩個 \n \n 代表送出完畢!!
Delay(50)
AP_RFLUSH()
RETURN NIL
FUNC Delay(nMSec)
LOCAL nSec := hb_TToMSec( hb_dateTime() )
Do While hb_TToMSec( hb_dateTime() ) - nSec < nMSec
// SysRefresh()
hb_releaseCPU()
EndDo
RETURN .T.
?>
add ap_rflush() function .c
Code: Select all | Expand
#include <http_protocol.h>
#include <hbapi.h>
#include <hbapiitm.h>
#include <hbvm.h>
request_rec * GetRequestRec( void );
HB_FUNC( AP_RFLUSH )
{
hb_retni( (int) ap_rflush( GetRequestRec() ) );
}
result:
Re: SSE example
Posted: Thu May 25, 2023 8:27 am
by Otto
Hello ssbbs,
Thank you very much for that.
I will study and test your code.
I'm glad to hear that you're using mod_harbour APACHE and that it's working fine for you.
I want to remember that this question has been bothering me a lot:
The way mod_harbour is implemented is by inserting HTML into the PRG.
The way I implement mod_harbour is by embedding PRG into HTML.
Everything with mod_harbour is so new, and there is so much to learn. But it's really fun.
Thanks again for your help.
Best regards from Austria.
LG
Otto
Re: SSE example
Posted: Thu May 25, 2023 10:32 am
by ssbbs
Now there is one more important question!
It takes 2xxms to load a .prg each time, but only 5xms for php with the same function.
Re: SSE example
Posted: Thu May 25, 2023 12:18 pm
by Otto
Hello ssbbs,
I believe it is important to appreciate the functionality of the server-side program. In my case, since I am using DBF, I require mod Harbour.
Currently, without SSE (Server-Sent Events), I utilize `setInterval()`, which is similar to `timer()` in Harbour. Then, I use `fetch` to check if there is any new data on the server.
Best regards,
Otto
myInterval = setInterval(LoadItems, 3000);
function LoadItems() {
let recipients = {
"recipient1": document.getElementById("nav_recip1").checked,
"miterledigt": document.getElementById("nav_miterledigt").checked,
};
fetch("chat.prg?items=" + ctime, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(recipients)
})
.then(res => {
if (res.ok) {
return res.text();
} else {
throw new Error(res.status + " " + res.statusText);
}
}).then(data => {
intervalcounter += 1
console.log("LoadItems data");
console.log(intervalcounter);
if (intervalcounter === 20) {
clearInterval(myInterval);
alert("Aktualisierung pausiert.");
intervalcounter = 0;
myInterval = setInterval(LoadItems, 3000);
}
if (data.startsWith("stop")) {
clearInterval(myInterval);
alert("beendet");
}
if (tmpdata != data) {
document.getElementById('browse').innerHTML = data;
scrollToBottom("browse");
tmpdata = data;
}
});
}
Re: SSE example
Posted: Thu May 25, 2023 2:49 pm
by ssbbs
No! What my mean is: the time it takes for the client to receive a .prg file from the host!!
Receiving index.prg file from host takes 262ms.
Re: SSE example
Posted: Thu May 25, 2023 3:59 pm
by Otto
Hello ssbbs,
I understand.
Can you please post the php code, too.
Best regards,
Otto
Re: SSE example
Posted: Fri May 26, 2023 4:29 am
by ssbbs
index_php.php:
Code: Select all | Expand
<?php
$hRes=(object)[
'err_no'=>0 // 錯誤碼
,'errmsg'=>'' // 錯誤訊息
,'errtil'=>'' // 錯誤抬頭
,'lEnd'=>0 // 結束?<*0:No/1:Yes>
,'step'=>0 // 目前進度指標(every)
,'total'=>0 // 總筆數(total)
];
$func=(Integer)(isset($_POST["func"]) ? $_POST["func"] : (isset($_GET["func"]) ? $_GET["func"] : -1 ));
if($func == 9){
// hData := hb_jsondecode(hParam, 'data')
// 送出 SSE 專用表頭
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
//
$hRes->step=0;
$hRes->total=100;
sendMsg( json_encode( $hRes ) );
//
for($i=1; $i<=100; $i++){
$hRes->step=i;
$hRes->total=nTolRec;
sendMsg( json_encode( $hRes ) );
}
$hRes->lEnd=1;
sendMsg( json_encode( $hRes ));
return;
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>SSE test!!(PHP)</title>
<meta name="generator" content="WYSIWYG Web Builder 18 - https://www.wysiwygwebbuilder.com">
<link href="test.css" rel="stylesheet">
<link href="index_php.css" rel="stylesheet">
<script src="jquery-3.6.4.min.js"></script>
<script>
function go(){
let url='index.prg';
let data={
a: 1
,b: 2
};
let param=url+'?func=9&data='+JSON.stringify(data);
alert('Go...');
source = new EventSource(param,{
withCredentials: false // Whether to allow cross domain requests, default: false
});
// Handle postback events 'message'
source.addEventListener('message',function(e){
let o=JSON.parse(e.data); // analyze
if(o.lEnd == 0){ // lEnd=1:End
// Screen display progress processing
let per=Math.round( o.step/o.total*100,2 ); // calculate percentage
$('#progress').val(per);
$('#progress_down').text( per+'%' );
}else{
source.close(); // Close SSE!! Don't send data back!!
//
if(o.err_no != 0){
alert('Failed to read data!!'+o.err_msg, 'Error!!');
return;
}else{
alert('success!!')
}
}
});
// Handle the event on start 'open'
source.addEventListener('open', function(e) {
//
});
// Events when errors are handled 'error'
source.addEventListener('error', function(e) {
if(e.target.readyState == EventSource.CLOSED){
alert('connect be close!!');
source.close(); // stop SSE
}else{
console.log('error!!');
}
});
// Handle system status events 'status'
source.addEventListener('status', function(e) {
console.log('System status is now: ' + e.data);
});
}
</script>
</head>
<body>
<input type="button" id="Button1" onclick="javascript:go();return false;" name="" value="Test" style="position:absolute;left:170px;top:143px;width:132px;height:52px;z-index:0;">
<!-- jquery -->
<label for="" id="progress_down" style="position:absolute;left:0px;top:100px;width:465px;height:35px;line-height:35px;z-index:2;">Text</label>
<span style="color:#000000;font-family:微軟正黑體;font-size:14px;"><strong id="progress_up"><br> Process wait...</strong></span>'
<br><progress id="progress" value="0" max="100" style="height:48px;width:100%;">
<br><label style="text-align:center;width:100%"></label>
</body>
</html>
<?php
function sendMsg($cRes){
echo "event: message".PHP_EOL;
echo "id: ".time().PHP_EOL;
echo "retry: 15000".PHP_EOL;
echo "data: $cRes".PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
usleep(200);
}
?>
index_php.css
Code: Select all | Expand
body
{
background-color: #FFFFFF;
color: #000000;
font-family: Arial;
font-weight: normal;
font-size: 13px;
line-height: 1.1875;
margin: 0;
padding: 0;
}
#Button1
{
border: 1px solid #2E6DA4;
border-radius: 4px;
background-color: transparent;
background-image: none;
color: #000000;
font-family: Arial;
font-weight: normal;
font-style: normal;
font-size: 16px;
padding: 1px 6px 1px 6px;
text-align: center;
-webkit-appearance: none;
margin: 0;
}
}
#Button1:focus
{
outline: 0;
}
#progress_down
{
border: 0px solid #CCCCCC;
border-radius: 4px;
background-color: transparent;
background-image: none;
color: #000000;
font-family: Arial;
font-weight: normal;
font-style: normal;
font-size: 16px;
margin: 0;
text-align: center;
vertical-align: top;
padding: 4px 4px 4px 4px;
}
#progress_down:focus
{
outline: 0;
}
result:
gif:
Re: SSE example
Posted: Fri May 26, 2023 6:22 am
by Otto
Hello ssbbs,
thank you.
With the prg version I get following error:
Error: Unterminated string ''
called from: __PP_PROCESS, line: 0
called from: ..\source\exec.prg, EXECUTE, line: 64
Source:
0062: a: 1
0063: ,b: 2
0064 => };
0065: let param=url+'?func=9&data='+JSON.stringify(data);
0066: alert('Go...');
php version is running but not showing the meter.
Maybe you can help me once more.
Best regards,
Otto