Server-sent events not working in Google Chrome - javascript

I am trying to update a specific element in my HTML document using server-sent events. I am using the W3Schools example with parameters changed to fit my needs. When this did not work out of the box, I tried this question's solution, which is to include charset=utf-8 in my Content-Type header. Still no updates to the HTML. I then realized I was getting the following error:
EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection.
Here is the relevant JavaScript in my .js file:
var source = new EventSource("warehouse.php");
source.onmessage = function(event) {
document.getElementById("processing").innerHTML += event.data + "<br>";
};
In my PHP file, I have the following:
if (isset($args->products)) {
orderProducts($args->products);
}
function orderProducts($products) {
$bigResponse = outgoingData(constant('bigURL'), $products[0]);
$littleResponse = outgoingData(constant('lilURL'), $products[3]);
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache');
echo "data: Order has been placed.";
flush();
}
I am completely stumped. What am I missing? How do I fix this error?

Try sending a json encoded data part/element back to the browser.
Lets see if I can remember correctly... It's out of my mind, so it isn't tested!
PHP Side:
// Set SSE headers which are send along first output.
header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-cache');
$sseId = 0; //Increment this value on each next message
$event = 'eventName'; //Name of the event which triggers same named event-handler in js.
//$data needs to be json encoded. It can be a value of any type.
$data = array ('status' => 'Progress', 'message' => 'My message or html');
//Send SSE Message to browser
echo 'id: ' . $sseId++ . PHP_EOL; //Id of message
if (!is_null($event)) {
echo 'event: ' . $event . PHP_EOL; //Event Name to trigger eventhandler
}
retry: 10000 . PHP_EOL; //Define custom reconnection time. (Default to 3s when not specified)
echo 'data: ' . json_encode($data) . PHP_EOL; //Data to send to eventhandler
//Note: When sending html, you might need to encode with flags: JSON_HEX_QUOT | JSON_HEX_TAG
echo PHP_EOL;
ob_flush();
flush();
JavaScript side:
var es = new EventSource('sse.php');
es.addEventListener('eventName', function(event) {
var returnData = JSON.parse(event.data);
var myElement = document.getElementById("processing");
switch (returnData.status) {
case 'Error':
es.close();
myElement.innerHTML = 'An error occured!';
break;
case 'Done':
es.close();
myElement.innerHTML = 'Finished!';
break;
case 'Progress':
myElement.innerHTML += returnData.message + "<br>";
break;
}
});
es.onerror = function() {
console.log("EventSource failed.");
};
In this case I make use of 1 (named) event only where the data element contains a status on which js reacts.
You can also define multiple event-names to be used as a status and define corresponding event-handlers or define a handler which is triggered on each message received.
es.onmessage = function(event) {
//This handler fires on each message received.
console.log("Incoming message.");
};
If you have server-side script running longer then 3s, specify a longer retry time in the message sent (See code at PHP side). Otherwise the browser will reconnect after 3 seconds which might run your script from the beginning and abandon the previous run.

Related

Server Sent Event gives error in client but no error message

I'm trying to get to grips with SSE with the help of https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events.
Although everything works fine, I get an error on every server event. The error object doesn't contain the actual error (or I can't find it) but I'm not comfortable ignoring the error.
This is my client code (js):
const evtSource = new EventSource("http://ssedemo/server/ssedemo.php");
evtSource.addEventListener("ping", function(event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
newElement.innerHTML = "message: " + event.data;
eventList.appendChild(newElement);
});
evtSource.onerror = function(err) {
console.error("EventSource failed:", err);
};
And serverside (php) - abbreviated code from the example in the link
date_default_timezone_set("Europe/Amsterdam");
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
$curDate = date(DATE_ISO8601);
echo "event: ping\n";
echo 'data: This is a Ping at time ' . $curDate . "\n\n";
ob_end_flush();
flush();
I see the events, everything works but in the console I get this error:
There's loads of info in there but I can't find the actual error.
Where should I look for the error? Am I missing something?

Using server sent event to get an array from mysql in php

I am trying to use server sent event to get information from the database and update on my html page, immediately a new message enters, these are my codes
function update()
{
if(typeof(EventSource) !="undefined")
{
var source=new EventSource("update.php");
source.onmessage=function(event)
{
$scope.my_messages=JSON.parse(event.data)
}
}
}
update();
<div ng-repeat="x in my_messages">
<div>{{x.sender_username}}</div>
<div>{{x.title}}</div>
<p>{{x.messages}}</p>
</div>
at the update.php,I have this
<?php
include 'first.php';
$newUser= new Participant();
$newUser->connect();
$newUser->call_new_message();
?>
At first.php, I have this
public function call_new_message()
{
$sender=$_SESSION['username'];
$receiver=$_SESSION['receiverUsername'];
$a = mysqli_query($this->con, "SELECT * from messages_tb WHERE sender_username= '$sender' and receiver_username='$receiver' ");
if ($a) {
$s=$a->fetch_all(MYSQLI_ASSOC);
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo"data:".json_encode($s)."\n\n";
flush();
// echo json_encode($s);
}
else{
echo "An error occurred";
}
}
It worked well except that I expected it to update data in the divs in the html page immediately a new message comes in the messages_tb but it's not working like that, I have to refresh the page before I can get the new message in the div. Please, help out. thanks
I guess adding $scope.$apply() in on scope().message handler should work for you.
source.onmessage=function(event)
{
$scope.my_messages=JSON.parse(event.data);
$scope.$apply();
}
Reason is, angular might not be aware of plain java script updating the data. so forcefully you can tell angular to update the scope.

Server Sent Events on Wordpress

How can I achieve server sent events in WordPress. I have searched a lot about it but not able to figure out how can I implement it. The problem I am facing is SSE only takes header as 'Content-Type:text/plain'. How can I sent some event to js script only when some changes occur in my database through WordPress function?
If I use 'Content-Type:text/plain' in WordPress function whole website will turn into text output.
I am aware that Wordpress has heartbeat API but I don't want that. Currently, I am using ajax pooling to achieve the same task but it is taking a lot of resources.
you can make a custom REST API end point and make something like that:
public function prepare_rest_api_demo_response( $request ){
$args = array(
'numberposts' => 1
);
$last_id = '';
$serverTime = time();
$latest_posts = get_posts( $args );
$last_id = $latest_posts['0']->ID;
do {
$latest_posts = get_posts( $args );
if( $latest_posts['0']->ID > $last_id ){
$response = new WP_REST_Response($this->compile_sendMsg_in_apiendpoint_for_SSE($serverTime,$latest_posts,$last_id), 200);
break;
}
// break the loop if the client aborted the connection (closed the page)
if ( connection_aborted() ) {
$response = new WP_REST_Response('data: {connection: "closed"}'."\n\n", 503);
break;
}
sleep(2);
// If we didn't use a while loop, the browser would essentially do polling
// every ~3seconds. Using the while, we keep the connection open and only make
// one request.
} while(true);
$response->header( 'Content-Type', 'text/event-stream' );
$response->header( 'Cache-Control', 'no-cache' );
//$response->header( 'Access-Control-Allow-Origin', '*' );
$response->header( 'Accept', 'application/json' );
return $response;
}
and this function for compile the response in the correct way
private function compile_sendMsg_in_apiendpoint_for_SSE($id, $msg, $lastitemid) {
$msg = json_encode($msg);
echo "id: $id" . "\n";
echo "retry: 1500" . "\n";
//echo "event: nameofevent" . "\n"; // if you need some name for event uncomment that line
echo "data: $msg". PHP_EOL;//use \n instead of PHP_EOL for add another line data: if is the same data object https://www.html5rocks.com/en/tutorials/eventsource/basics/#toc-js-api
echo PHP_EOL;
ob_flush();
flush();
}

crawling page does only work for 3%

I am trying to crawl a full section of a website, but the problem is that the data that I need is not there from the start. Is there anyway to get the data from the website with PHP?
this is the link: https://www.iamsterdam.com/nl/uit-in-amsterdam/uit/agenda and this is the section I need:
After my post was set to duplicate I tried this
https://stackoverflow.com/a/28506533/7007968 but is also doesn't work so I need a other solucion this is what I tried:
get-website.php
$phantom_script= 'get-website.js';
$response = exec ('phantomjs ' . $phantom_script);
echo $response;
get-website.js
var webPage = require('webpage');
var page = webPage.create();
page.open('https://www.iamsterdam.com/nl/uit-in-amsterdam/uit', function(status) {
console.log(page.content);
phantom.exit();
});
this is all I get back (around 3% of the page):
</div><div id="ads"></div><script src="https://analytics.twitter.com/i/adsct?p_id=Twitter&p_user_id=0&txn_id=nvk6a&events=%5B%5B%22pageview%22%2Cnull%5D%5D&tw_sale_amount=0&tw_order_quantity=0&tw_iframe_status=0&tpx_cb=twttr.conversion.loadPixels" type="text/javascript"></script></body></html>
So I have the feeling that i am getting closer this is what I after a lot of searching:
var webPage = require('webpage');
var page = webPage.create();
var settings = {
operation: "POST",
encoding: "utf8",
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify({
DateFilter: 04112016,
LastMinuteTickets: 0,
PageId: "3418a37d-b907-4c80-9d67-9fec68d96568",
Skip: 0,
Take: 12,
ViewMode: 1
})
};
page.open('https://www.iamsterdam.com/api/AgendaApi/', settings, function(status) {
console.log(page.content);
phantom.exit();
});
But what I get back doesn't look good:
Message":"An error has occurred.","ExceptionMessage":"Page could not be found","ExceptionType":"System.ApplicationException","StackTrace":" at Axendo.SC.AM.Iamsterdam.Controllers.Api.AgendaApiController.GetResultsInternal(RequestModel requestModel)\r\n at lambda_method(Closure , Object , Object[] )\r\n
etc.
I hope somewann can help me,
Addressing your main question about 3%.
You use exec incorrectly. When used like this
$response = exec ('phantomjs ' . $phantom_script);
$response will containt the last line of what was printed in terminal during execution of a given command. Because you did console.log(page.contents); the last line of HTML document was placed into $response variable.
The correct use of exec would be
exec ('phantomjs ' . $phantom_script, $response);
This way the result will be placed into $response variable as an array, with each line an element of the array. Then, if you just want to get html, you can do
$html = implode("\n", $response);
But a more simple and correct way is to use the specific function for the task:
passthru ('phantomjs ' . $phantom_script);
passthru executes a function and returns recieved data unmodified, straight to the output.
So if you want to contain it to a variable, do:
ob_start();
passthru ('phantomjs ' . $phantom_script);
$html = ob_get_clean();

Server Sent Events : Not working

I am using HTML5 Server-Sent Events.
Actually I need to show notification (new record enter and which are unread) that's when any new record is insert in database (php/mysql).
So for testing purpose I just tried with count of total row. But I am getting this error message in my local-host:
Firefox can't establish a connection to the server at http://localhost/project/folder/servevent/demo_sse.php.
The line is:
var source = new EventSource("demo_sse.php");
I have tried this:
index.php
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("demo_sse.php");
source.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data;
};
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
<div id="result"></div>
demo_sse.php
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$db = mysql_connect("localhost", "root", ""); // your host, user, password
if(!$db) { echo mysql_error(); }
$select_db = mysql_select_db("testdatase"); // database name
if(!$select_db) { echo mysql_error(); }
$time = " SELECT count( id ) AS ct FROM `product` ";
$result = mysql_query($time);
$resa = mysql_fetch_assoc($result);
echo $resa['ct'];
flush();
?>
Please let me know what going wrong.
I know for notification we can use Ajax with some interval time, but I don't want such thing. As I have N number of records and which may slow my resources.
According to this,
There are several 'rules' that need to be met, and yours is lacking at this point:
Output the data to send (Always start with "data: ")
It is somehow like:
echo "data: {$resa['ct']}\n\n";
Setting a header to text/event-stream worked for me:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
// Rest of PHP code
Please modify the following code snippet according to your requirements
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
// infinite loop
while (1) {
// output the current timestamp; REPLACE WITH YOUR FUNCTIONALITY
$time = date('r');
echo "data: Server time: {$time}\n\n"; // 2 new line characters
ob_end_flush();
flush();
sleep(2); // wait for 2 seconds
}
?>
I tested this code snippet myself; it's working for me. If you have any query, let me know.

Categories

Resources