Ratchet websocket SSL connect?

Created on 7 Jun 2013  路  18Comments  路  Source: ratchetphp/Ratchet

Hi cboden!

I have a ratchet chat server file


use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;
use MyAppChat\Chat;
    require dirname(__DIR__) . '/vendor/autoload.php';
    $server = IoServer::factory(
        new WsServer(
            new Chat()
        )
      , 26666
    );
    $server->run();

i using JS Websocket connect success throw http

if ("WebSocket" in window) {
    var ws = new WebSocket("ws://ratchet.mydomain.org:8888");
    ws.onopen = function() {
        // Web Socket is connected. You can send data by send() method.
        ws.send("message to send");
    };
    ws.onmessage = function (evt) { 
        var received_msg = evt.data;
    };
    ws.onclose = function() { 
        // websocket is closed. 
    };
} else {
  // the browser doesn't support WebSocket.
}

I will install SSL in my webserver and try connect via SSL but failed

if ("WebSocket" in window) {
    var ws = new WebSocket("wss://ratchet.mydomain.org:8888");
    ws.onopen = function() {
        // Web Socket is connected. You can send data by send() method.
        ws.send("message to send");
    };
    ws.onmessage = function (evt) { 
        var received_msg = evt.data;
    };
    ws.onclose = function() { 
        // websocket is closed. 
    };
} else {
  // the browser doesn't support WebSocket.
}

My question is how can using ratchet for websocket SSL connection?

Thanks

question

Most helpful comment

Hello again,

Fisrt I need explain that I have install Lets Encrypt certificate and, I think that, for this reason, mi conf file is named 000-default-le-ssl.conf.

You have the same file? Your file must see like this more or less:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        <Directory /var/www/html/>
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <IfModule mod_dir.c>
            DirectoryIndex index.php index.pl index.cgi index.html index.xhtml index.htm
        </IfModule>

SSLCertificateFile /etc/letsencrypt/live/yourdomain.xxx/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.xxx/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
ServerName yourdomain.xxx
ProxyPass /wss2/ ws://yourdomain.xxx:8888/
</VirtualHost>
</IfModule>

You can see that I have put 8888, the same port that I must put in the chat-server.php file later.

I have a DigitalOcean's Droplet with Lamp.

I supose that the steps are similars in others distributions of linux.

The steps that I have made are (after install Lets Encrypt):

1) Create a folder for websocket files. For example /var/www/html/websocket
2) Goto inside this folder.
3) Install composer:
3.1 php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
3.2 php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
3.3 php composer-setup.php
3.4 php -r "unlink('composer-setup.php');"

4) Install Ratchet WebSocket (I use this link http://socketo.me/docs/install). Type the next command inside of websocket folder:
4.1 php composer.phar require cboden/ratchet

In this moment you should see that, inside websocket folder, have the next files:
composer.json
composer.lock
composer.phar
vendor

5) Create, inside websocket folder, the folders src and bin and, inside src, create a MyApp folder (this steps is obtain from this url: http://socketo.me/docs/hello-world)

Then, you have the next files/folders:
composer.json
composer.lock
composer.phar
vendor
src/MyApp
bin

6) Edit composer.json file: type nano composer.json in the console. Your file must see similar to:

{
    "autoload": {
        "psr-0": {
            "MyApp": "src"
        }
    },
    "require": {
        "cboden/ratchet": "0.3.*"
    }
}

The steps 7 and 8 are extracted from Ratchet web.

7) Create an file named server-chat.php inside websocket/bin folder. Your file must see similar to:

<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

    require dirname(__DIR__) . '/vendor/autoload.php';

    $server = IoServer::factory(
        new HttpServer(
            new WsServer(
                new Chat()
            )
        ),
        8888
    );

    $server->run();

I have put 888, the same port that the ProxyPass line.

8) Create an file named Chat.php inside websocket/src/MyApp folder. Your file must see similar to:

<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";

        $conn->close();
    }
}

9) Type the next comand inside websocket folder: php composer.phar install. This command execute the composer.json. If you dont type this command and run chat-server.php you will see errors in the execution.

10) Create a index.php file inside the websocket folder. This file is only to try your websocket connection. This file should contain the code that I have put in the last post (HTML and JavaScript)

11) Type the next command inside websocket folder: php bin/server-chat.php

If you dont have problems the console is waiting.

Now open in your web browser the path of websocket, for example: http://www.yourdomain.xxx/websocket/ and you must see the content of index.php

If you have copy/paste my javascript and html code you can see the "Conectado!" message inside a input text.

The attached zip have all files from my project. This work with wss and ws.

websocket.zip

I think that is all! This work for me. I hope that this can help you.

Regards!

Edit:

If you want to do permanent the chat-server.php you can type this command inside the websocket/bin folder:

nohup php chat-server.php &

If you can stop the web socket you can type:

ps aux | less | grep ratchet

Find the pid process and kill it.

If after all this, you can not connect, try to type this command (only if not work):

iptables -A INPUT -p tcp --dport 8888 --jump ACCEPT

All 18 comments

The best solution would be to use Nginx as your web server. Have Nginx listen on port 80 for incoming connections and have it handle your SSL. Nginx will forward incoming connections to PHP-FPM for your regular website and if it detects a connection is a WebSocket connection have it proxy to your running Ratchet application on a port of your choice. Your javascript could then connect via wss://mydomain.org

Nginx SSL docs
Nginx WebSocket docs

Closing this issue in favour of Mailing List thread since this is more documentation than it is a bug.

And what about if I'm using Apache 2.2.5.
Any options ?

You could try mod_proxy_wstunnel

I am using IIS for this and used URL rewriting but still getting 400 bad request error :(
any help?

$server = IoServer::factory( new HttpServer( new WsServer( new Chat() ) ), 8888, SERVER_IP );

SSL is installed on port 8844

Under https configured project in IIS, i have added a rewrite rule
IIS - URL Rewriting Inbound Rule Action
http://000.000.00.000:8888/{R1}

For me, the solutions was:

Open /etc/apache2/sites-enabled
nano 000-default-le-ssl.conf and add ProxyPass /wss2/ ws://mydomain.es:8888/

In javascript new WebSocket("wss://mydomain/wss2/:8888");

I hope this can be usefull for somebody.

regards.

Updated.

why use new WebSocket("wss://mydomain/wss2/8888"); ?

Sorry! I copy-paste,

Is WebSocket("wss://mydomain/wss2/:8888").

8888 is the port.

This is my javascript to try (not is a optimal code, is only for tries)

<script>

    jQuery(document).ready(function($){
        var WebSocketKK = {
            CONECTANDO: 0,
            CONECTADO : 1,
            CERRANDO  : 2,
            CERRADO   : 3,
            url: "yourdomain/wss2/", 
            puerto: "8888",
            protocolo: "wss",
            conn : null,
            finalUrl: "",
            init: function(){
                $("#estado-websocket").val("DESCONECTADO");
                this.finalUrl = this.protocolo+"://"+this.url;
                if (parseInt(this.puerto)>0){
                    this.finalUrl= this.finalUrl+":"+this.puerto;
                }

                if (this.conn==null){
                    this.conectarWebSocket();
                }

                this.setManejadores();

                $("#ip").val(this.url);
                $("#puerto").val(this.puerto);
        },
        setManejadores: function(){
            $("#envia-mensaje-prueba").click(function(){
                WebSocketKK.enviarMensaje(WebSocketKK);
            });

            $("#conectar-websocket").click(WebSocketKK.conectarWebSocket);
            $("#desconectar-websocket").click(WebSocketKK.desconectarWebSocket);

            $("#ip").on("change", function(){
                WebSocketKK.url = $("#ip").val();

                WebSocketKK.finalUrl = WebSocketKK.protocolo+"://"+WebSocketKK.url;
                if (parseInt(WebSocketKK.puerto)>0){
                    WebSocketKK.finalUrl= WebSocketKK.finalUrl+":"+WebSocketKK.puerto;
                }

            });

             $("#puerto").on("change", function(){
                WebSocketKK.puerto = $("#puerto").val();

                WebSocketKK.finalUrl = WebSocketKK.protocolo+"://"+WebSocketKK.url;
                if (parseInt(WebSocketKK.puerto)>0){
                    WebSocketKK.finalUrl= WebSocketKK.finalUrl+":"+WebSocketKK.puerto;
                }

            });

            $("#protocolo").on("change", function(){
                WebSocketKK.protocolo = $("#protocolo").val();

                WebSocketKK.finalUrl = WebSocketKK.protocolo+"://"+WebSocketKK.url;
                if (parseInt(WebSocketKK.puerto)>0){
                    WebSocketKK.finalUrl= WebSocketKK.finalUrl+":"+WebSocketKK.puerto;
                }

            });

        },
        conectarWebSocket: function(){
            var $self = WebSocketKK;


            $("#estado-websocket").val("CONECTANDO");

            if (typeof $self.conn!="undefined" && $self.conn!=null && $self.is(WebSocketKK.CONECTADO)){
                $("#estado-websocket").val("YA EST脕S CONECTADO!");
                return false;
            }

            $self.conn = new WebSocket($self.finalUrl);

            $self.conn.onopen = function(e){
                $("#estado-websocket").val("CONECTADO!");
                console.log("Conexi贸n establecida con el WebSocket", e);
            };

            $self.conn.onmessage = function(e){
                /*e have a key named data. Is a JSON object with date, from and msg keys.*/
                console.log("Mensaje recibido: "+e.data, e);
                var mensaje = JSON.parse(e.data);
                $("#dialogo").append(
                    $("<p/>").html("("+mensaje.date+") "+mensaje.from+": "+mensaje.msg)
                )
            }

            $self.conn.onclose = function(e){
                console.log("Conexi贸n cerrada: ", e);
                $("#estado-websocket").val("DESCONECTADO");
            }


            $self.conn.onerror = function(e){
                console.log("Se ha producido un error: ", e);
                $("#estado-websocket").val("ERROR");
            }
        },
        desconectarWebSocket: function(){
            var $self = WebSocketKK;
            console.log("Desconectando websocket");
            if ($self.is($self.CONECTADO)){
                $self.conn.close();
            }else{
                $("#estado-websocket").val("NO ESTA CONECTADO");
            }

        },
        is:function(estado){
            var $self = WebSocketKK;
            return estado===$self.conn.readyState;
        },
        enviarMensaje: function($self){
            var mensaje = $("#mensaje").val();

            if (!$self.is($self.CONECTADO)){
                $("#estado-websocket").val("PRIMERO CONECTA");
                return false;
            }

            if (mensaje.length>0){
                $self.conn.send(mensaje);
                $("#dialogo").append(
                    $("<p/>").html("T脷: "+mensaje)
                );
            }else{
                $("#estado-websocket").val("MENSAJE VACIO");
            }
        }

    };

        WebSocketKK.init();
    });

</script>

`

I have tried to paste the html but github post not allow the html tags. Then I have replace the < and > with #.

`#select id="protocolo"#
#option value="wss"#WSS#/option#
#option value="ws"#WS#/option#

/select

input type="text" id="ip"

input type="text" id="puerto"

input type="text" disabled id="estado-websocket"

button id="conectar-websocket"#Conectar#/button

button id="desconectar-websocket"#Desconectar#/button

input type="text" id="mensaje"

button id="envia-mensaje-prueba"#Enviar#/button#`

Maybe can be usefull for somebody.

Bye.

Thanks for your reply

I have tried this but doesn't works:

sites-enabled/000-default-le-ssl.conf:
ProxyPass /wss2/ ws://test.fasys.it:8888/

my ratchet socket php file:
$app = new Ratchet\App('test.fasys.it', '8888', '0.0.0.0');

and js client:
wss://test.fasys.it/wss2/:8888/chat

also tried:
wss://test.fasys.it/wss2/

I think that the correct way is the second options (wss://test.fasys.it/wss2/) but you must to add the port (8888), then, de final url for websocket is (wss://test-fasys.it/wss2/:8000.

I need try but I think that if you want pass parameters (chat) at url you must change the ProxyPass.

In this moment I'm working but this afternoon I write a post with the steps that I have did to configure my DigitalOcean's Droplet and the PHP code of my example. Maybe can help you.

PD: Sorry for my english :D

regards.

Thanks you so much. I really appreciate your help.
I have tried but nothing. Without SSL it works, with SSL not.

Hello again,

Fisrt I need explain that I have install Lets Encrypt certificate and, I think that, for this reason, mi conf file is named 000-default-le-ssl.conf.

You have the same file? Your file must see like this more or less:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        <Directory /var/www/html/>
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <IfModule mod_dir.c>
            DirectoryIndex index.php index.pl index.cgi index.html index.xhtml index.htm
        </IfModule>

SSLCertificateFile /etc/letsencrypt/live/yourdomain.xxx/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.xxx/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
ServerName yourdomain.xxx
ProxyPass /wss2/ ws://yourdomain.xxx:8888/
</VirtualHost>
</IfModule>

You can see that I have put 8888, the same port that I must put in the chat-server.php file later.

I have a DigitalOcean's Droplet with Lamp.

I supose that the steps are similars in others distributions of linux.

The steps that I have made are (after install Lets Encrypt):

1) Create a folder for websocket files. For example /var/www/html/websocket
2) Goto inside this folder.
3) Install composer:
3.1 php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
3.2 php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
3.3 php composer-setup.php
3.4 php -r "unlink('composer-setup.php');"

4) Install Ratchet WebSocket (I use this link http://socketo.me/docs/install). Type the next command inside of websocket folder:
4.1 php composer.phar require cboden/ratchet

In this moment you should see that, inside websocket folder, have the next files:
composer.json
composer.lock
composer.phar
vendor

5) Create, inside websocket folder, the folders src and bin and, inside src, create a MyApp folder (this steps is obtain from this url: http://socketo.me/docs/hello-world)

Then, you have the next files/folders:
composer.json
composer.lock
composer.phar
vendor
src/MyApp
bin

6) Edit composer.json file: type nano composer.json in the console. Your file must see similar to:

{
    "autoload": {
        "psr-0": {
            "MyApp": "src"
        }
    },
    "require": {
        "cboden/ratchet": "0.3.*"
    }
}

The steps 7 and 8 are extracted from Ratchet web.

7) Create an file named server-chat.php inside websocket/bin folder. Your file must see similar to:

<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

    require dirname(__DIR__) . '/vendor/autoload.php';

    $server = IoServer::factory(
        new HttpServer(
            new WsServer(
                new Chat()
            )
        ),
        8888
    );

    $server->run();

I have put 888, the same port that the ProxyPass line.

8) Create an file named Chat.php inside websocket/src/MyApp folder. Your file must see similar to:

<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";

        $conn->close();
    }
}

9) Type the next comand inside websocket folder: php composer.phar install. This command execute the composer.json. If you dont type this command and run chat-server.php you will see errors in the execution.

10) Create a index.php file inside the websocket folder. This file is only to try your websocket connection. This file should contain the code that I have put in the last post (HTML and JavaScript)

11) Type the next command inside websocket folder: php bin/server-chat.php

If you dont have problems the console is waiting.

Now open in your web browser the path of websocket, for example: http://www.yourdomain.xxx/websocket/ and you must see the content of index.php

If you have copy/paste my javascript and html code you can see the "Conectado!" message inside a input text.

The attached zip have all files from my project. This work with wss and ws.

websocket.zip

I think that is all! This work for me. I hope that this can help you.

Regards!

Edit:

If you want to do permanent the chat-server.php you can type this command inside the websocket/bin folder:

nohup php chat-server.php &

If you can stop the web socket you can type:

ps aux | less | grep ratchet

Find the pid process and kill it.

If after all this, you can not connect, try to type this command (only if not work):

iptables -A INPUT -p tcp --dport 8888 --jump ACCEPT

After installing lets Encrypt
Will some one answer to this issue Click Here

@blackerfin @cboden

@blackerfin In addition to the above config, I'd like to add:

Make sure the appropriate Apache modules are enabled. That includes _proxy_ and _proxy_wstunnel_.

if you are using laravel library cboden/ratchet for socket then go to app/Console/Commands/WebSocketServer.php Replace handle function with these lines of code and use your certification file path 'local_cert' and 'local_pk'


public function handle()
{

    $loop   = Factory::create();
    $webSock = new SecureServer(
        new Server('0.0.0.0:8090', $loop),
        $loop,
        array(
            'local_cert'        => '/opt/ssl/chamberstock_com.crt', // path to your cert
            'local_pk'          => '/opt/ssl/server.key', // path to your server private key
            'allow_self_signed' => TRUE, // Allow self signed certs (should be false in production)
            'verify_peer' => FALSE
        )
    );
    // Ratchet magic
    $webServer = new IoServer(
        new HttpServer(
            new WsServer(
                new WebSocketController()
            )
        ),
        $webSock
    );
    $loop->run();

}

Hello,
I was able to solve the problem by creating a reverse proxy
access the apache2 configuration file related to the site,
cd / etc / apache2 / sites-enabled /
nano 000-default.conf
adding the line:
ProxyPass / wss2 / ws: //yourdomain.com: 8383 /
at the end of the tag

in the javascript script.js file
configure the link to access the websocket as follows:
聽 var conn = new WebSocket ('wss: //yourdomain.com/wss2/: 8383 / chat');

in this example the entire code is only in the chat folder

Was this page helpful?
0 / 5 - 0 ratings

Related issues

grappetite-ali picture grappetite-ali  路  5Comments

dschissler picture dschissler  路  3Comments

maliknaik16 picture maliknaik16  路  8Comments

Hemant3105 picture Hemant3105  路  6Comments

Erseni picture Erseni  路  5Comments