SSE example

mod_harbour is an Apache module that allows to run PRGs directly on the web !!!

SSE example

Postby dakosimo » Tue Aug 11, 2020 5:44 am

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.
dakosimo
 
Posts: 9
Joined: Sun Nov 27, 2016 8:24 am

Re: SSE example

Postby Antonio Linares » Tue Aug 11, 2020 10:12 am

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
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42099
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: SSE example

Postby dakosimo » Tue Aug 11, 2020 10:25 am

I will wait. It is not urgent.
Thank you.
Best regards.

Dako.
dakosimo
 
Posts: 9
Joined: Sun Nov 27, 2016 8:24 am

Re: SSE example

Postby ssbbs » Wed May 24, 2023 2:11 pm

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
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Wed May 24, 2023 3:02 pm

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
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Re: SSE example

Postby ssbbs » Wed May 24, 2023 3:28 pm

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.
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Wed May 24, 2023 5:46 pm

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
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Re: SSE example

Postby ssbbs » Thu May 25, 2023 4:24 am

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 view

<?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&#058;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 view

#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:
Image
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Thu May 25, 2023 8:27 am

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
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Re: SSE example

Postby ssbbs » Thu May 25, 2023 10:32 am

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.
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Thu May 25, 2023 12:18 pm

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;
}
});
}
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Re: SSE example

Postby ssbbs » Thu May 25, 2023 2:49 pm

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.

Image
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Thu May 25, 2023 3:59 pm

Hello ssbbs,
I understand.
Can you please post the php code, too.

Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Re: SSE example

Postby ssbbs » Fri May 26, 2023 4:29 am

index_php.php:
Code: Select all  Expand view

<?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&#058;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 view

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:
Image

gif:
Image
line ID: ssbbstw
WeChat ID: ssbbstw
User avatar
ssbbs
 
Posts: 104
Joined: Mon Oct 17, 2005 3:03 am
Location: Taiwan

Re: SSE example

Postby Otto » Fri May 26, 2023 6:22 am

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
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Next

Return to mod_harbour

Who is online

Users browsing this forum: No registered users and 15 guests