GeniusesOfSymfony / WebSocketBundle

:part_alternation_mark: Websocket server for Symfony applications (powered by Ratchet), includes a Autobahn.JS based JavaScript client
MIT License
608 stars 140 forks source link

Disconnected for Connection lost #211

Open jokari4242 opened 7 years ago

jokari4242 commented 7 years ago

Hi!

Thanks for this nice bundle,

I have a pb, like a timeout or i don't know...

When the page is loaded, everything works well, user are authentified, and can suscribe to room.

When a little period of inactivity or when i'm using back navigator function, i have this message, in console of developper tools:

Disconnected for Connection lost - scheduled 1th reconnect to occur in 5 second(s). with code 6

And when i'm publish something :

Uncaught Autobahn not connected

Here, the script use for connect client to websocket

    {#WEBSOCKET ONLY IF CONNECTED#}
    {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
        {{ ws_client() }}

        <script>
            var _WS_URI = "ws://{{ uri }}:{{ gos_web_socket_server_port }}";
            var websocket = WS.connect(_WS_URI);

            websocket.on("socket/connect", function(session){
                var event = CustomEvent('web-socket:ready', { 'detail': { session: session } } );

                window.dispatchEvent(event);
            });

            websocket.on("socket/disconnect", function(error){
                console.log("Disconnected for " + error.reason + " with code " + error.code);

            });

            (function () {
                function CustomEvent ( event, params ) {
                    params = params || { bubbles: false, cancelable: false, detail: undefined };
                    var evt = document.createEvent( 'CustomEvent' );
                    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
                    return evt;
                }

                CustomEvent.prototype = window.Event.prototype;

                window.CustomEvent = CustomEvent;
            })();

        </script>

        {{  render(controller('jokari4242ChatBundle:Chat:getChat'))  }}
    {% endif %}

If someone can help me, thanks a lot :-)

MicioBaus commented 7 years ago

Anyone was able to solve this issue?

trandangtri commented 7 years ago

I got the same problem and have no idea to fix this one.

murman-lexus commented 7 years ago

Hello. what messages you have in log of your WebsocketServer?

sobha-sudheesh commented 6 years ago

Hi, anyone got a fix to this issue?

haskasu commented 6 years ago

I am having the same issue. does anyone knows what it is?

murman-lexus commented 6 years ago

@haskasu Hello. what messages you have in log of your WebsocketServer?

murman-lexus commented 6 years ago

@haskasu try this https://github.com/GeniusesOfSymfony/WebSocketBundle/issues/197#issuecomment-264655610

MicioBaus commented 6 years ago

I think the ping should be from the client. The server should close any inactive connection after a timeout, to avoid running out of resources.

var pingpong = function (){
    session.publish( myroute , {message: "ping"});
};

var intervalvar = setInterval(pingpong, timeout_in_milliseconds);
Evgen14 commented 6 years ago

Hi, anyone was able to solve this issue?

murman-lexus commented 6 years ago

Hi, anyone was able to solve this issue?

try this #197 (comment)

StanislavUngr commented 5 years ago

This seems to be also happening to me since Evgen14 reported it. No changes to code was made and it just stopped working... Any solution so far?

klodoma commented 5 years ago

Related to #351. Does anyone has this running on Symfony 4?

mbabker commented 5 years ago

The demo app was upgraded to run Symfony 4.2 over the weekend and personally I've got an app in production I upgraded to Symfony 4.2 last Friday (upgraded from 3.4 to 4.1 4 months ago). So whatever issue you're running into is not related to Symfony version support.

murtho commented 5 years ago

I'm also running this bundle (v1.8.13) in a Symfony 4.1.7 application without problems

klodoma commented 5 years ago

The demo app was upgraded to run Symfony 4.2 over the weekend and personally I've got an app in production I upgraded to Symfony 4.2 last Friday (upgraded from 3.4 to 4.1 4 months ago). So whatever issue you're running into is not related to Symfony version support.

The demo app uses the dev-master version of the bundle. That way it works. With 1.8* it doesn't.

I've created this project that I can't get running: https://github.com/klodoma/symfony4-websocket

composer update
php bin/console server:run
php bin/console gos:websocket:server

Any help/ideas are appreciated!

mbabker commented 5 years ago

There's also a 1.x branch on the demo app corresponding with the 1.x branch on this repo.

klodoma commented 5 years ago

There's also a 1.x branch on the demo app corresponding with the 1.x branch on this repo.

Didn't noticed that. I'll have a look. Thanks!

klodoma commented 5 years ago

Ok, I've solved it for my case. I had these missing in services.yml:


    # RPC handlers need to have the `gos_web_socket.rpc` tag, so by convention we will tag all handlers in the directory
    AppBundle\Rpc\:
        resource: '../../src/AppBundle/Rpc'
        tags:
            - { name: gos_web_socket.rpc }

    # RPC handlers need to have the `gos_web_socket.topic` tag, so by convention we will tag all handlers in the directory
    AppBundle\Topic\:
        resource: '../../src/AppBundle/Topic'
        tags:
            - { name: gos_web_socket.topic }
mbabker commented 5 years ago

Yeah, that would make sense (1.x doesn't support autoconfiguration of the RPC and Topic services).

Something I need to port from my current setup is adding logging and a little more error trapping to the RpcDispatcher since right now that can be a bit of a black box if it fails (and I think in your case it was the RPC calls that was causing your disconnect). If you're comfortable with it, you can use a compiler pass to replace the dispatcher service until that happens.

<?php declare(strict_types=1);

namespace App\Websocket\DependencyInjection\CompilerPass;

use App\Websocket\Server\Dispatcher\RpcDispatcher;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class ReplaceRpcDispatcherPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        // Change to our custom RPC Dispatcher and add appropriate method calls
        if ($container->hasDefinition('gos_web_socket.rpc.dispatcher')) {
            $container->getDefinition('gos_web_socket.rpc.dispatcher')
                ->setClass(RpcDispatcher::class)
                ->addMethodCall('setLogger', [new Reference('monolog.logger.websocket')]);
        }
    }
}
<?php declare(strict_types = 1);

namespace App\Websocket\Server\Dispatcher;

use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use Gos\Bundle\WebSocketBundle\RPC\RpcResponse;
use Gos\Bundle\WebSocketBundle\Server\App\Dispatcher\RpcDispatcherInterface;
use Gos\Bundle\WebSocketBundle\Server\App\Registry\RpcRegistry;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Ratchet\ConnectionInterface;

class RpcDispatcher implements RpcDispatcherInterface, LoggerAwareInterface
{
    use LoggerAwareTrait;

    /**
     * @var RpcRegistry
     */
    private $rpcRegistry;

    public function __construct(RpcRegistry $rpcRegistry)
    {
        $this->rpcRegistry = $rpcRegistry;
    }

    public function dispatch(ConnectionInterface $conn, $id, $topic, WampRequest $request, array $params)
    {
        $callback = $request->getRoute()->getCallback();

        try {
            $procedure = $this->rpcRegistry->getRpc($callback);
        } catch (\Throwable $e) {
            // Log the error
            $this->logger->error(
                sprintf(
                    'Could not find RPC handler in registry for callback "%s".',
                    $callback
                ),
                [
                    'exception' => $e,
                ]
            );

            $conn->callError(
                $id,
                $topic,
                $e->getMessage(),
                [
                    'code'    => $e->getCode(),
                    'rpc'     => $topic,
                    'params'  => $params,
                    'request' => $request,
                ]
            );

            return;
        }

        $method = $this->toCamelCase($request->getAttributes()->get('method'));

        if (!method_exists($procedure, $method)) {
            // Log the error
            $this->logger->error(
                sprintf(
                    'Method "%s" not found in %s',
                    $method,
                    get_class($procedure)
                ),
                [
                    'called_method' => $request->getAttributes()->get('method'),
                ]
            );

            $conn->callError(
                $id,
                $topic,
                'Could not execute RPC callback, method not found',
                [
                    'code'    => 404,
                    'rpc'     => $topic,
                    'params'  => $params,
                    'request' => $request,
                ]
            );

            return;
        }

        try {
            $result = call_user_func([$procedure, $method], $conn, $request, $params);
        } catch (\Throwable $e) {
            // Log the error
            $this->logger->error(
                'Websocket error processing RPC function.',
                [
                    'exception' => $e,
                    'rpc'       => $topic,
                ]
            );

            $conn->callError(
                $id,
                $topic,
                $e->getMessage(),
                [
                    'code'    => $e->getCode(),
                    'rpc'     => $topic,
                    'params'  => $params,
                    'request' => $request,
                ]
            );

            return;
        }

        if ($result === null) {
            $this->logger->error(
                sprintf(
                    'Null return from RPC handler %s::%s()',
                    get_class($procedure),
                    $method
                )
            );

            $conn->callError(
                $id,
                $topic,
                'RPC Error',
                [
                    'code'    => 500,
                    'rpc'     => $topic,
                    'params'  => $params,
                    'request' => $request,
                ]
            );

            return;
        }

        if ($result instanceof RpcResponse) {
            $result = $result->getData();
        } elseif (!is_array($result)) {
            $result = [$result];
        }

        $conn->callResult($id, $result);
    }

    private function toCamelCase(?string $str): string
    {
        return preg_replace_callback(
            '/_([a-z])/',
            function ($c): string {
                return strtoupper($c[1]);
            },
            $str
        );
    }
}