walkor / workerman

An asynchronous event driven PHP socket framework. Supports HTTP, Websocket, SSL and other custom protocols.
http://www.workerman.net
MIT License
11.17k stars 2.27k forks source link

Why SSE doesn't work in Firefox? #1067

Closed shizzic closed 1 week ago

shizzic commented 1 week ago

Shortly (as i can be in this issue). Workerman's code:

public function __construct()
    {
        $this->context = array(
            'ssl' => array(
                'local_cert' => Yii::getAlias('@cert') . '/' . $this->getCertName('crtca'),
                'local_pk' => Yii::getAlias('@cert') . '/' . $this->getCertName('key'),
                'peer_certificate_chain' => Yii::getAlias('@cert') . '/' . $this->getCertName('crt'),
                'cafile' => Yii::getAlias('@cert') . '/' . $this->getCertName('ca'),
                'capath' => Yii::getAlias('@cert') . '/',

                'capture_peer_cert' => true,
                'capture_peer_cert_chain' => true,
                'SNI_enabled' => true,
                'security_level' => 0,
                'allow_self_signed' => true,
                'verify_peer' => false,
                'verify_peer_name' => false,
            )
        );
    }

    public function buildServer($port)
    {
        $this->worker = new Worker('http://0.0.0.0:' . $port, $this->context);
        $this->worker->name = 'sse';
        $this->worker->count = 1;
        $this->worker->transport = 'ssl';
        $this->worker->reloadable = true;

        $this->worker->onMessage = function ($connection, $request) {
            try {
                $connection->onClose = function (TcpConnection $connection) {
                    if (array_key_exists($connection->id, $this->info))
                        $this->removeConnection($connection->id);
                };

                if ($request->header('origin'))
                    $access_token = $this->getAccessToken($request->header('origin'), $request->cookie());

                if ($request->header('accept') === 'text/event-stream' && isset($access_token)) {
                    $user = User::find()->where(['access_token' => $access_token])->one();

                    if ($user) {
                        $response = new Response(
                            200,
                            [
                                'Access-Control-Allow-Origin' => rtrim($request->header('Referer'), '/'), // http://localhost:8080 (presented origin)
                                'Access-Control-Allow-Credentials' => 'true',
                                'Connection' => 'Upgrade',
                                'Cache-Control' => 'no-cache',
                                'Content-Type' => 'text/event-stream'
                            ],
                            "\n\r"
                        );
                        $connection->send($response);
                } else
                    $connection->destroy();
            }
        };

        return $this->worker;
    }

The thing is, presented code (simplified ofc) works in all browsers on chromium and doesn't on Firefox (clones included). Moreover, if i try to connect to sse on Firefox, than network shows nothing, just blank spaces everywhere (headers, cookies, request, response) all of em. There are 2 keys in config, which are verify_peer and verify_peer_name and if i set em to true, than i see headers and basically everything in Firefox (still no established connection btw) and with true chromium based browsers can't conenct to.

Funny thing is, with false (normal i guess, it works at least) i can print_r() anyting in stdoutFile even AFTER new Response send. So it's connected, and should return 200 status response, but not... (chromium works just fine just to remind).

Am i missing somethind, what do i do wrong, is it my problem, is it Firefox issue, can i do anything about it? Certificate is alright, i have chains (workerman's config included above). You can check it with any ssl certificate checker It's a test domain anyway.

walkor commented 1 week ago

https://manual.workerman.net/doc/en/http/SSE.html Here is an example of SSE. Please test if this example works. If it does, try adding your configuration and business code, which might help quickly identify the issue.

shizzic commented 1 week ago

https://manual.workerman.net/doc/en/http/SSE.html Here is an example of SSE. Please test if this example works. If it does, try adding your configuration and business code, which might help quickly identify the issue.

Commented my whole code, paste your example from docs like this:

$this->worker->onMessage = function (TcpConnection $connection, Request $request) {
            if ($request->header('accept') === 'text/event-stream') {
                $connection->send(new Response(200, [
                    'Access-Control-Allow-Origin' => rtrim($request->header('Referer'), '/'),
                    'Access-Control-Allow-Credentials' => 'true',
                    'Content-Type' => 'text/event-stream',
                ], "\r\n"));

                $timer_id = Timer::add(2, function () use ($connection, &$timer_id) {
                    if ($connection->getStatus() !== TcpConnection::STATUS_ESTABLISHED) {
                        Timer::del($timer_id);
                        return;
                    }

                    $connection->send(new ServerSentEvents(['event' => 'message', 'data' => 'hello', 'id' => 1]));
                });

                return;
            }

            $connection->send('ok');
        };

Needed to specify allow-origin, bacause my front and back are seperated. '*' doesn't work, it's need to be specified with specific names, that's why i trim Referer header. Results are the same, chrome works fine (i get EventStream responses as messages every 2 seconds) and firefox doesn't (every network tab: headers, cookie, response, request, timings are blank).

shizzic commented 1 week ago

Sorry, turns out Firefox doesn't have good network client for tracing SSE in browser's devtools. It worked all along, i just needed to console.log messages i got. All that time i thought i didn't have a connection in the first place.