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

Problem with share session. #20

Closed KrissMcCone closed 9 years ago

KrissMcCone commented 9 years ago

Hello I try to share sesssion by this tutorial: https://github.com/GeniusesOfSymfony/WebSocketBundle/blob/master/Resources/docs/SessionSetup.md

My configuration:

# Web Socket Configuration
gos_web_socket:
    server:
        port: 8080        #The port the socket server will listen on
        host: 0.0.0.0   #The host ip to bind to
        router:
            resources:
                - @AppChatBundle/Resources/config/pubsub/routing.yml
    client:
        firewall: secured_area
        session_handler: @session.handler.pdo
        storage:
            driver: @gos_web_socket.client_storage.driver.predis
            decorator: @gos_web_socket.client_storage.doctrine.decorator

# Doctrine cache        
doctrine_cache:
    providers:
        redis_cache:
            redis:
                host: 127.0.0.1
                port: 6379
                database: 3
        websocket_cache_client:
            type: redis
            alias: gos_web_socket.client_storage.driver.redis         
# SNC           
snc_redis:
    clients:
        ws_client:
            type: predis
            alias: client_storage.driver #snc_redis.client_storage.driver
            dsn: redis://127.0.0.1/2
            logging: %kernel.debug%
            options:
                profile: 2.2
                connection_timeout: 10
                read_write_timeout: 30

ServiceNotFoundException in CheckExceptionOnInvalidReferenceBehaviorPass.php line 58: The service "gos_web_scocket.client_storage.driver.predis" has a dependency on a non-existent service "snc_redis.cache".

I've installed SNC and Redis Bundle.

jjsaunier commented 9 years ago

Docs was wrong, apply this change and let me know if that work. Thx for reporting this issue !

https://github.com/GeniusesOfSymfony/WebSocketBundle/commit/c397212952a7d30fcb8c6b289dd5ff2a0a817b61

KrissMcCone commented 9 years ago
# Web Socket Configuration
gos_web_socket:
    server:
        port: 8080        #The port the socket server will listen on
        host: 0.0.0.0   #The host ip to bind to
        router:
            resources:
                - @AppChatBundle/Resources/config/pubsub/routing.yml
    client:
        firewall: secured_area
        session_handler: @session.handler.pdo
        storage:
            driver: @gos_web_socket.client_storage.driver.predis

# Doctrine cache        
doctrine_cache:
    providers:
        redis_cache:
            redis:
                host: 127.0.0.1
                port: 6379
                database: 3
        websocket_cache_client:
            type: redis
            alias: gos_web_socket.client_storage.driver.redis         
# SNC           
snc_redis:
    clients:
        cache:
            type: predis
            alias: cache #snc_redis.cache
            dsn: redis://127.0.0.1/2
            logging: %kernel.debug%
            options:
                connection_timeout: 0
                read_write_timeout: 30

Thank you! Now, there is my config, but still the error:

The service "gos_web_socket.client_storage" has a dependency on a non-existent service "gos_web_socket.client_storage.driver.predis".

jjsaunier commented 9 years ago

This time the docs is right :D

Checkout https://github.com/GeniusesOfSymfony/WebSocketBundle/blob/master/Resources/docs/SessionSetup.md#create-your-own-driver

NOTE : Predis driver class is included in GosWebSocketBundle, just register the service like below to use it.

services:
    gos_web_scocket.client_storage.driver.predis:
        class: Gos\Bundle\WebSocketBundle\Client\Driver\PredisDriver
        arguments:
            - @snc_redis.cache
KrissMcCone commented 9 years ago

Yes, I have bad service name linked. Thanks. :)

Fatal error: Call to a member function beginTransaction() on a non-object in /Applications/XAMPP/xamppfiles/htdocs/app/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php on line 445

[Symfony\Component\Debug\Exception\FatalErrorException]
Error: Call to a member function beginTransaction() on a non-object

Now I have error in symfony engine, lolz. Do you know sth about it?

jjsaunier commented 9 years ago

Yes, your PDO session handler is not correctly setup.

http://symfony.com/doc/master/cookbook/configuration/pdo_session_storage.html

KrissMcCone commented 9 years ago

I've configured this by this tutorial

session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - "mysql:host=%database_host%;dbname=%database_name%"
            - { db_username: %database_user%, db_password: %database_password% }

And probably everyting works, cos I can login and my session is saved in database.

jjsaunier commented 9 years ago

I will dig into it so

KrissMcCone commented 9 years ago

Thanks so : )

This error occurred in WebSocket console when I connected some user. :)

jjsaunier commented 9 years ago

Symfony PdoSessionHandler bug when dsn (instead of inject pdo instance directly) is used in event loop context.

I have write this part docs : https://github.com/GeniusesOfSymfony/WebSocketBundle/blob/master/Resources/docs/SessionSetup.md

KrissMcCone commented 9 years ago

Thank you. :)

[Predis\Connection\ConnectionException]
Operation timed out [tcp://0.0.0.0:6379]

Now this after connected some user. Hard to do...

jjsaunier commented 9 years ago

Redis is installed ? (BTW redis driver is optional and proposed to illustrate a concret exemple, you can leave by default to fallback on php driver)

KrissMcCone commented 9 years ago

Yes, some play with configs helps me. :) Thank you

https://github.com/GeniusesOfSymfony/WebSocketBundle/blob/master/Resources/docs/SessionSetup.md

-> Retrieve current user

You have double use Ratchet\ConnectionInterface; in the topic class. :)

jjsaunier commented 9 years ago

Nice catch ! Just fixed thank you !

May the docs on Session setup part is confused and orient people to use another driver to store client ?

KrissMcCone commented 9 years ago

I would add there informaton that you have to install redis server. Rest there is ok, i think. Good job! :) http://redis.io/

KrissMcCone commented 9 years ago

Probably works, but can't use it in topic. hahaha :P

use Gos\Bundle\WebSocketBundle\Client\WebSocketUserTrait;
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
use Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic;
use App\ChatBundle\Entity\ChatMessage;
use Doctrine\ORM\Mapping as ORM;

**
     * Constructor
     *
     * @param \Doctrine\ORM\EntityManager $em
     * @param ClientStorageInterface $clientStorage
     */
    public function __construct(\Doctrine\ORM\EntityManager $em, ClientStorageInterface $clientStorage)
    {
        $this->em = $em;
        $this->clientStorage = $clientStorage;
    }

Error:

[Symfony\Component\Debug\Exception\ContextErrorException]
Catchable Fatal Error: Argument 2 passed to App\ChatBundle\Topic\ChatTopic
::__construct() must be an instance of Gos\Bundle\WebSocketBundle\Client\Cl
ientStorageInterface, none given, called in /Applications/XAMPP/xamppfiles/
htdocs/app/app/cache/dev/appDevDebugProjectContainer.php on line 2366 an
d defined

I've tried to use full path in argument but didn't help.

jjsaunier commented 9 years ago

It's normal, you must inject $clientStorage service @gos_web_socket.client_storage into your Topic via the depency injection (like you inject your entity manager)

KrissMcCone commented 9 years ago

I'm really sorry but it still doesn't work.

Now, Fatal Error: Fatal Error: Call to undefined method App\ChatBundle\Topic\ChatTopic::getCurrentUser()

I have:

use Gos\Bundle\WebSocketBundle\Client\WebSocketUserTrait;

So the topic clas doesn't add method from trait, but why lol.

jjsaunier commented 9 years ago
class AcmeTopic implements TopicInterface
{
    use WebSocketUserTrait;
KrissMcCone commented 9 years ago

Ohhh, kill me better!!! :(

Now it works, but session isn't shared. I have to test it.

I also removed DoctrineCacheBundle and SncRedis. Default is the best. :D

jjsaunier commented 9 years ago

I also removed DoctrineCacheBundle and SncRedis. Default is the best. :D

Sure. Use it when you'll have 1000 concurrent users :dancer:

If the session is not shared it's due to your domain.

The domain between your websocket must be the same as your website. Because we use the cookie to auth the user.

Some examples :

Webserver | Websocket 127.0.0.1:80 | 127.0.0.1:8080 -> WORK 127.0.0.1:80 | localhost:8080 -> NOT WORK my-app.dev:80 | my-app.dev:8080 -> WORK my-app.dev:80 | 127.0.0.1:8080 -> NOT WORK

I hope that will help you.

KrissMcCone commented 9 years ago

[2015-05-25 16:52:04] websocket.INFO: Launching Ratchet on localhost:8080
http://localhost/app/web/app_dev.php/chat/open/82

$this->getCurrentUser($connection) return anon-5563377bbd5b7;

Should work 😡😡😡

jjsaunier commented 9 years ago

Your user is authenticated from symfony2 application ?

KrissMcCone commented 9 years ago

Yes. I copied PHPSESSID from firebug, and checked if the record exist in database and everything seems to be ok.

jjsaunier commented 9 years ago

Have you check the firewall name used in websocket bundle config is the same than your security.yml ?

KrissMcCone commented 9 years ago
firewalls:
        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_provider: form.csrf_provider
                use_referer: false
                success_handler: login_success_handler 

            logout:       true
            anonymous:    true

So porbably not xD

jjsaunier commented 9 years ago

Just take your example above, adapt since you update.

# Web Socket Configuration
gos_web_socket:
    server:
        port: 8080        #The port the socket server will listen on
        host: 0.0.0.0   #The host ip to bind to
        router:
            resources:
                - @AppChatBundle/Resources/config/pubsub/routing.yml
    client:
        firewall: main #HERE IS THE CHANGE
        session_handler: @session.handler.pdo
        storage:
            driver: @gos_web_socket.client_storage.driver.predis
KrissMcCone commented 9 years ago

Works perfectly. Thanks a lot. I hope that this topic helps another newbie in symfony like me.

jjsaunier commented 9 years ago

I will continue to improve the docs to be more explicit because the installation of this bundle is heavy and wrap a lot features bring by symfony.

ewertoncode commented 9 years ago

Hi! I yet has this problem:

PHP Fatal error: Call to a member function beginTransaction() on null in /var/www/APSEAB/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php on line 445 [2015-06-27 12:51:26] php.EMERGENCY: Fatal Error: Call to a member function beginTransaction() on null {"type":1,"file":"/var/www/APSEAB/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php","line":445,"level":-1,"stack":[]}

[Symfony\Component\Debug\Exception\FatalErrorException]
Error: Call to a member function beginTransaction() on null

I'm not use redis, my configs:

app/config.yml
services:
    session.handler.pdo:
       class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public:    false
        arguments:
            - "mysql:host=%database_host%;dbname=%database_name%"
            - { db_table: sessions, db_username: %database_user%, db_password: %database_password% }

Web Socket Configuration
gos_web_socket:
    server:
        port: 8080        #The port the socket server will listen on
        host: 127.0.0.1   #The host ip to bind to
        router:
            resources:
                - @TonChatBundle/Resources/config/pubsub/routing.yml
    client:
        firewall: main #can be an array of firewalls
        session_handler: @session.handler.pdo
service.yml
services:
    ton_chat.topic_chat_service:
        class: Ton\ChatBundle\Topic\ChatTopic
        tags:
            - { name: gos_web_socket.topic }
        arguments: ["@gos_web_socket.client_storage"]

    pdo:
        class: PDO
        arguments:
            dsn: mysql:host=%database_host%;dbname=%database_name%
            user: %database_user%
            password: %database_password%
        calls:
            - [ setAttribute, [3, 2] ] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION

    session.handler.pdo:
        class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        arguments: [@pdo, {lock_mode: 0}]

My server is runing in: 127.0.0.1:8000, and websoket in: 127.0.0.1:8080.

Can help me @ProPheT777 @KrissMcCone?

Tanks!

jjsaunier commented 9 years ago

Yes,

services:
session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
public: false
arguments:
- "mysql:host=%database_host%;dbname=%database_name%"
- { db_table: sessions, db_username: %database_user%, db_password: %database_password% }

This definition is wrong.

First of all you need to turn PDO as service like following:

pdo:
    class: PDO
    arguments:
        dsn: mysql:host=%database_host%;dbname=%database_name%
        user: %database_user%
        password: %database_password%
    calls:
        - [ setAttribute, [3, 2] ] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION

Because Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler require it.

and then declare the session handler service:

session.handler.pdo:
    class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
    arguments: [@pdo, {lock_mode: 0}]

and then it should work. Don't worry about redis, he is not required to run websocket in basic configuration.

ewertoncode commented 9 years ago

Thank's a lot man!!

Works now!!

hermandinho commented 8 years ago

having the same issue here

[Symfony\Component\Debug\Exception\FatalErrorException] Error: Call to a member function beginTransaction() on null

babukaivan commented 8 years ago

Hello, please help me, in onSubscribe method user always anon-**** $user = $this->clientManipulator->getClient($connection);
dump($connection->WebSocket->request->getCookies()); return me PHPSESSID from browser, session shared beetween web and websockets Use predis without doctrine cache

babukaivan commented 8 years ago

I found a problem, my User Session stored in Redis, and if (false !== $serializedToken = $connection->Session->get('security' . $firewall, false)) { always false.

zeckon commented 8 years ago

I have the same issue and traced it to the same point with @babukaivan . serializedToken is empty, Any ideas?

zeckon commented 8 years ago

Ok. I figured it out. In my config, i was using firewall "main" so my config was like

gos_web_socket: client: firewall: main

but actually i have more than 1 firewall and they use same context (which is 'main_auth'). After checking the session bag, I noticed the value is _security_main_auth not _security_main so I changed my config to

gos_web_socket: client: firewall: main_auth

and it works now.

KooliFiras commented 7 years ago

same problem !!! I used GosWebSocket and i had followed the configuartion step by step and every thing seemed to be ok but when i logged in i was authenticated as anonymous client what shall do ? below you're gonna find my project 's configuration and thank you in advance

// config.yml imports:

framework:

esi: ~

translator:      { fallbacks: ["%locale%"] }
secret:          "%secret%"
router:
    resource: "%kernel.root_dir%/config/routing.yml"
    strict_requirements: ~
form:            ~
csrf_protection: ~
validation:      { enable_annotations: true }
templating:
    engines: ['twig']
    #assets_version: SomeVersionScheme
default_locale:  "%locale%"
trusted_hosts:   ~
trusted_proxies: ~
#session:
    # handler_id set to null will use default session handler from php.ini
    #handler_id: session.handler.native_file
session:
    handler_id: session.handler.pdo

Twig Configuration

twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%"

Assetic Configuration

assetic: debug: "%kernel.debug%" use_controller: false bundles: [ ]

java: /usr/bin/java

filters:
    cssrewrite: ~
    #closure:
    #    jar: "%kernel.root_dir%/Resources/java/compiler.jar"
    #yui_css:
    #    jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"

Doctrine Configuration

doctrine: dbal: driver: "%database_driver%" host: "%database_host%" port: "%database_port%" dbname: "%database_name%" user: "%database_user%" password: "%database_password%" charset: UTF8

if using pdo_sqlite as your database driver:

    #   1. add the path in parameters.yml
    #     e.g. database_path: "%kernel.root_dir%/data/data.db3"
    #   2. Uncomment database_path in parameters.yml.dist
    #   3. Uncomment next line:
    #     path:     "%database_path%"

orm:
    auto_generate_proxy_classes: "%kernel.debug%"
    auto_mapping: true

Swiftmailer Configuration

swiftmailer: transport: "%mailer_transport%" host: "%mailer_host%" username: "%mailer_user%" password: "%mailer_password%" spool: { type: memory }

doctrine_cache: providers: redis_cache: redis: host: 127.0.0.1 port: 6379 database: 3 websocket_cache_client: type: redis alias: gos_web_socket.client_storage.driver.redis

app/config/config.yml

fos_user: db_driver: orm # other valid values are 'mongodb' and 'couchdb' firewall_name: main user_class: UserBundle\Entity\User from_email: address: "%mailer_user%" sender_name: "%mailer_user%"

gos_web_socket: server: port: 9010 #The port the socket server will listen on host: 127.0.0.1 #The host ip to bind to router: resources:

*** security.yml ** security: encoders: FOS\UserBundle\Model\UserInterface: bcrypt

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN

providers:
    fos_userbundle:
        id: fos_user.user_provider.username_email

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt|error)|css|images|js)/
        security: false
    main:
        pattern: ^/
        form_login:
            provider: fos_userbundle
            csrf_token_generator: security.csrf.token_manager
            # if you are using Symfony < 2.8, use the following config      instead:
            # csrf_provider: form.csrf_provider
            login_path: /fos/login
            check_path: /fos/login_check
            failure_path: /fos/login
        remember_me:
            secret:   '%secret%'

        logout:
          path: /fos/logout
          target: /fos/login
        anonymous:    true

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/, role: ROLE_ADMIN }
    - { path: ^/app/, role: [IS_AUTHENTICATED_REMEMBERED,IS_AUTHENTICATED_FULLY] }

** parameters.yml *****

This file is auto-generated during the composer install

parameters: database_driver: pdo_mysql database_host: 127.0.0.1 database_port: 3306 database_name: gos database_user: root database_password: null mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: test@test.tn mailer_password: test locale: en secret: 26458ad393bfa0ec28f50c091b41b0f89ca9f101

* service.yml *****

services: session.handler.pdo: class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler public: false arguments:

*** acmeTopic **** <?php

namespace AppBundle\Topic;

use Gos\Bundle\WebSocketBundle\Topic\TopicInterface; use Ratchet\ConnectionInterface; use Ratchet\Wamp\Topic; use Gos\Bundle\WebSocketBundle\Router\WampRequest; use Gos\Bundle\WebSocketBundle\Topic\PushableTopicInterface; use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;

class AcmeTopic implements TopicInterface , PushableTopicInterface {

    protected $clientManipulator;

/**
 * @param ClientManipulatorInterface $clientManipulator
 */
public function __construct(ClientManipulatorInterface $clientManipulator)
{
    $this->clientManipulator = $clientManipulator;
}

/**
 * @param Topic        $topic
 * @param WampRequest  $request
 * @param array|string $data
 * @param string       $provider The name of pusher who push the data
 */
public function onPush(Topic $topic, WampRequest $request, $data, $provider)
{
    $topic->broadcast($data);
}

/**
 * This will receive any Subscription requests for this topic.
 *
 * @param ConnectionInterface $connection
 * @param Topic $topic
 * @param WampRequest $request
 */

public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{

    $user = $this->clientManipulator->getClient($connection);
    $topic->broadcast(['msg'=> 'hello '. $user]);

}

.... }