John Davidson

php - Nginx configuration for Server Sent Event

0 comments
Message:


we're following a project with 3 docker containers:



  • PHP 7.4 (backend, CodeIgniter), which provides APIs

  • Next.js 12.1 (frontend)

  • nginx 1.19.1


We're trying to set up a live notification system.
Here's the backend code:



namespace App\Controllers;

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, Authorization");

header("Content-Type: text/event-stream");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Connection: keep-alive");

use CodeIgniter\API\ResponseTrait;
use CodeIgniter\RESTful\ResourceController;

class SSEtest extends ResourceController {
use ResponseTrait;

protected $format = 'json';

const TIME_TO_SLEEP_IN_SECS = 60;

public function run($id_user) {
while(ob_get_level() > 0) {
ob_end_flush();
}

@ini_set('output_buffering', 'Off');
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
@ob_end_clean();
set_time_limit(0);
ob_start();

$id = 0;
while(true) {
$id++;
$curDate = date(DATE_ISO8601);

$notifications = ['foo', 'bar'];

if( is_array($notifications) ) {
$tot = 0;
foreach($notifications AS $n) {
$tot += $n['num'];
}

$this->sendMsg('msg', ['id' => $id, 'time' => $curDate, 'cnt' => $notifications, 'total' => $tot], $id);
}
else {
$this->sendMsg('ping', ['id' => $id, 'time' => $curDate, 'cnt' => 'ping'], $id);
}

ob_flush();
flush();

if( connection_aborted() ) {
break;
}

sleep(self::TIME_TO_SLEEP_IN_SECS);

}
}

protected function sendMsg($event, $data, $id) {
echo 'event: '.$event.PHP_EOL;
echo 'data: '.json_encode($data).PHP_EOL;
echo 'id: '.$id.PHP_EOL;
//echo 'retry: 1000'.PHP_EOL; // Tempo in ms
//echo 'Time: '.date(DATE_ISO8601).PHP_EOL;
echo PHP_EOL;
}

}

And here's our frontend code in a component:


useEffect(() => {

let isMounted = true

const fetchNotifications = async () => {
if(!!user){
await fetchEventSource(API, {
method: "POST",
headers: {
Accept: "text/event-stream",
},
onopen(res) {
if (res.ok && res.status === 200) {
console.log("Connection made ", res);
}
else if (res.status >= 400 && res.status < 500 && res.status !== 429) {
console.log("Client side error ", res);
}
},
/*
event: ping
data: {"id":1,"time":"2022-05-10T11:32:24+0200","cnt":"ping"}
id: 1
retry: NULL
*/
onmessage(e) {
const parsedData = JSON.parse(e.data);
console.log(e, parsedData);
},
onclose() {
console.log("Connection closed by the server");
},
onerror(err) {
console.log("There was an error from server", err);
},
});
}
}

fetchNotifications()

return () => isMounted = false

}, [])

This is the error message we receive:


 There was an error from server TypeError: NetworkError when attempting to fetch resource. index.jsx:60:32

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/foo/bar. (Reason: CORS request did not succeed). Status code: (null).

We suspect the error is due to our nginx configuration:


server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;

index index.html index.htm index.debian-default.html index.php;

root /var/www/html/public;

error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;

set $virtualdir "";
set $realdir "";

if ($request_uri ~ ^/([^/]*)/.*$ ) {
set $virtualdir /$1;
}

if (-d "$document_root$virtualdir") {
set $realdir "${virtualdir}";
}

location / {
try_files $uri $uri/ $realdir/index.php?$args;
}

location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass api:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Any idea on how to solve the issue?
Thank you!



My Answer:
To enable Server Sent Events (SSE) in Nginx, you need to add the appropriate headers to allow cross-origin requests. You can do this by adding the following configuration to your Nginx server block:

nginx
location /sse {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Content-Type' 'text/event-stream';
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate';
add_header 'Connection' 'keep-alive';

proxy_pass http://backend_server; // Replace with your backend server URL
}


In this configuration, we have added the necessary headers for SSE and configured Nginx to proxy requests to the backend server where your PHP code is running.

Make sure to replace `http://backend_server` with the actual URL of your backend server.

After adding this configuration, restart Nginx for the changes to take effect. This should resolve the CORS issue and allow your frontend to connect to the SSE endpoint successfully.

Additionally, make sure that your PHP code is correctly sending the SSE events and data as shown in your backend code snippet.

Rate this post

4 of 5 based on 5173 votes

Comments




© 2024 Hayatsk.info - Personal Blogs Platform. All Rights Reserved.
Create blog  |  Privacy Policy  |  Terms & Conditions  |  Contact Us