beyondcode / laravel-websockets

Websockets for Laravel. Done right.
https://beyondco.de/docs/laravel-websockets
MIT License
5.07k stars 617 forks source link

How to use this properly with Laravel Homestead #128

Closed 33sKamal closed 4 years ago

33sKamal commented 5 years ago

i tried many ways and i couldn't use this package with my project located in Hoemstead Any Help please

litan1106 commented 5 years ago

I tried many methods too but I ended up with a custom site type.

For example:

Homestead.yaml

sites:

serve-laravel-websockets.sh

https://docs.beyondco.de/laravel-websockets/1.0/basic-usage/ssl.html#usage-with-a-reverse-proxy-like-nginx

config/websockets.php

'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'host' => env('WS_HOST', 'websockets.test),
        'port' => env('WS_PORT', 443),
        'scheme' => env('WS_SCHEME', 'https'),
        'enable_client_messages' => false,
    ],
],

window.Echo = new Echo({ broadcaster: "pusher", key: process.env.MIX_PUSHER_APP_KEY, wsHost: process.env.MIX_WS_HOST, wsPort: process.env.MIX_WS_PORT, wssPort: process.env.MIX_WS_PORT, enabledTransports: ["ws", "wss"],

33sKamal commented 5 years ago

i'm sorry i don't get your idea

litan1106 commented 5 years ago

You can read more on Alex's article.

amacsmith commented 5 years ago

@litan1106 I was able to use that article for deploying to my forge sites and it works great. However, locally I am having issues with it. I want to host the websocket server on the same instance as my web app and that seems to work fine on forge. However, I have made my local homestead box SSL and because we utilize webrtc in our project it is a must that utilize even locally a HTTPS domain to obtain the permissions necessary to create a webrtc call. So how locally without the use of a sub domain can I achieve that? Also could you possibly document your ngnix config as well as your homestead.yaml that is utilizing a SSL configuration? Thanks in advanced. Also let me know if I can provide any information.

amacsmith commented 5 years ago

@33sKamal I was able to configure an implementation that allows me to use homestead and then deploy to remote servers. It is not perfect however it allows me to simply configure a certain set of environment variables to get it work once they have been configured correctly.

websocket.php:

<?php

use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize;

return [

    /*
     * This package comes with multi tenancy out of the box. Here you can
     * configure the different apps that can use the webSockets server.
     *
     * Optionally you can disable client events so clients cannot send
     * messages to each other via the webSockets.
     */
    'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'enable_client_messages' => false,
            'enable_statistics' => true,
        ],
    ],

    /*
     * This class is responsible for finding the apps. The default provider
     * will use the apps defined in this config file.
     *
     * You can create a custom provider by implementing the
     * `AppProvider` interface.
     */
    'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,

    /*
     * This array contains the hosts of which you want to allow incoming requests.
     * Leave this empty if you want to accept requests from all hosts.
     */
    'allowed_origins' => [
        //
    ],

    /*
     * The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
     */
    'max_request_size_in_kb' => 250,

    /*
     * This path will be used to register the necessary routes for the package.
     */
    'path' => 'websockets',

    /*
     * Dashboard Routes Middleware
     *
     * These middleware will be assigned to every dashboard route, giving you
     * the chance to add your own middleware to this list or change any of
     * the existing middleware. Or, you can simply stick with this list.
     */
    'middleware' => [
        'web',
        Authorize::class,
    ],

    'statistics' => [
        /*
         * This model will be used to store the statistics of the WebSocketsServer.
         * The only requirement is that the model should extend
         * `WebSocketsStatisticsEntry` provided by this package.
         */
        'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,

        /*
         * Here you can specify the interval in seconds at which statistics should be logged.
         */
        'interval_in_seconds' => 20,

        /*
         * When the clean-command is executed, all recorded statistics older than
         * the number of days specified here will be deleted.
         */
        'delete_statistics_older_than_days' => 60,

        /*
         * Use an DNS resolver to make the requests to the statistics logger
         * default is to resolve everything to 127.0.0.1.
         */
        'perform_dns_lookup' => false,
    ],

    /*
     * Define the optional SSL context for your WebSocket connections.
     * You can see all available options at: http://php.net/manual/en/context.ssl.php
     */
    'ssl' => [
        /*
         * Path to local certificate file on filesystem. It must be a PEM encoded file which
         * contains your certificate and private key. It can optionally contain the
         * certificate chain of issuers. The private key also may be contained
         * in a separate file specified by local_pk.
         */
        'local_cert' => null,

        /*
         * Path to local private key file on filesystem in case of separate files for
         * certificate (local_cert) and private key.
         */
        'local_pk' => null,

        /*
         * Passphrase for your local_cert file.
         */
        'passphrase' => null,
    ],

    /*
     * Channel Manager
     * This class handles how channel persistence is handled.
     * By default, persistence is stored in an array by the running webserver.
     * The only requirement is that the class should implement
     * `ChannelManager` interface provided by this package.
     */
    'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,
];

broadcast.php (pusher block):

'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster'       => env('PUSHER_APP_CLUSTER'),
                'host'          => env('WEBSOCKET_BROADCAST_HOST'),
                'port'          => env('WEBSOCKET_BROADCAST_PORT'),
                'scheme'        => env('WEBSOCKET_SCHEME'),
                'encrypted'     => env('WEBSOCKET_ENCRYPTED')
            ],
        ],

bootstrap.js (pusher and echo config block):

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

let useTLSOverride = process.env.MIX_WEBSOCKET_USE_TLS == "true" ? true : false
if( !useTLSOverride){
    window.Pusher.Runtime.getProtocol = function() {return 'http:';}
}

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    wsHost: window.location.hostname,
    wsPort:  process.env.MIX_WEBSOCKET_PORT_WS,
    wssPort:  process.env.MIX_WEBSOCKET_PORT_WSS,
    disableStats: false,
    encrypted: process.env.MIX_WEBSOCKET_ENCRYPTED == "true" ? true : false,
    enabledTransports: ['ws', 'wss'],
});

env (environment variables that names could be changed to be more semantic. Also there are probably extra):

PUSHER_APP_ID=xxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxx
PUSHER_APP_CLUSTER=us2

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MIX_APP_DEBUG="${APP_DEBUG}"

WEBSOCKET_PORT_WSS=6001
WEBSOCKET_PORT_WS=6001
WEBSOCKET_ENCRYPTED=false
WEBSOCKET_USE_TLS=false
WEBSOCKET_SCHEME=http

WEBSOCKET_BROADCAST_PORT=6001
WEBSOCKET_BROADCAST_HOST=127.0.0.1

MIX_WEBSOCKET_PORT="${WEBSOCKET_PORT}"
MIX_WEBSOCKET_ENCRYPTED="${WEBSOCKET_ENCRYPTED}"
MIX_WEBSOCKET_PORT_WS="${WEBSOCKET_PORT_WS}"
MIX_WEBSOCKET_PORT_WSS="${WEBSOCKET_PORT_WSS}"
MIX_WEBSOCKET_USE_TLS="${WEBSOCKET_USE_TLS}"

Also I followed the guide posted by @litan1106 authored by Alex Bouma https://github.com/beyondcode/laravel-websockets/issues/128#issuecomment-472865163

This probably isn't the best solution but it worked for me. Let me know if you have any questions.

xlcrr commented 5 years ago

Got it working

https://stackoverflow.com/questions/55076399/laravel-websocket-in-homestead-not-working/56370064#56370064

redactuk commented 5 years ago

Followed this and many other articles, but whatever I try I'm hit with: "WebSocket connection to 'ws://home24.local:6001/app/abc?protocol=7&client=js&version=4.3.1&flash=false' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED"

xlcrr commented 5 years ago

@redactuk I've experienced the same error, I think your Echo config in bootstrap.js might not configured properly. Do you want to post your code here so we can take a look at it ?

redactuk commented 5 years ago

@xlcrr Thanks for reply. For now I'm only trying to get this working on local homestead environment. I've tried multiple combinations of settings including those above, including your solution linked.

Main thing I'm unsure about is what the wsHost value in bootstrap.js and broadcasting.php should be, as seen multiple different variations. I connect to homestead using 192.168.10.10, so should these values be that IP or 127.0.0.1? or 'home24.local' ? And presumably whatever I use should also be one used in a new nginx config proxy_pass entry as you indicated?

xlcrr commented 5 years ago

@redactuk If you are using homestead as a VM your IP is not 127.0.0.1, it is 192.168.10.10 however you must add that extra server block to your nginx config, and when you are booting up php artisan websockets:serve, make sure to include the --host=192.168.10.10 flag

here is my config

bootstrap.js

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: window.PUSHER_APP_KEY,
    wsHost: window.location.hostname,
    wsPort:  window.APP_DEBUG ? 6001 : 6002,
    wssPort:  window.APP_DEBUG ? 6001 : 6002,
    disableStats: false,
    encrypted: ! window.APP_DEBUG,
    enabledTransports: ['ws', 'wss'],
});

app.blade.php (root html file)

<head>
    ...
    <script>
        window.PUSHER_APP_KEY = '{{ config('broadcasting.connections.pusher.key') }}';
        window.APP_DEBUG      = '{{ config('app.debug') ? 'true' : 'false' }}';
    </script>
     ...
</head>

broadcast.php

    'connections' => [

        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster'    => env('PUSHER_APP_CLUSTER'),
                'encrypted'  => env('WEBSOCKET_ENCRYPTED'),
                'host'       => env('WEBSOCKET_BROADCAST_HOST'),
                'port'       => env('WEBSOCKET_BROADCAST_PORT'),
                'scheme'     => env('WEBSOCKET_SCHEME')
            ],
        ],

.env

WEBSOCKET_PORT_WSS=6001
WEBSOCKET_PORT_WS=6001
WEBSOCKET_ENCRYPTED=false
WEBSOCKET_USE_TLS=false
WEBSOCKET_SCHEME=http

WEBSOCKET_BROADCAST_PORT=6001
WEBSOCKET_BROADCAST_HOST=192.168.10.10

It also takes 3 terminal windows to run

  1. horizon
  2. websockets:serve --host=192.168.10.10
  3. tinker to fire events

Hope this helps?

redactuk commented 5 years ago

@xlcrr Ok thanks. I've updated all as above, reboted vm and done npm run dev, but actioning 2. I get:

RuntimeException : Failed to listen on "tcp://192.168.10.10:6001": The requested address is not valid in its context.

Also, why do I need 1.horizon and 3. tinker?

xlcrr commented 5 years ago

@redactuk Useage of tinker and horizon depends how you want to use websockets.

For testing websockets, I am broadcasting events to the client with tinker

$ php artisan tinker

$ event (new \App\Events\Message("test"));

The Message event implements ShouldBroadcast;

However, laravel puts these jobs onto a queue, and you need a worker to process the job.

eg $ php artisan queue:work or $ php artisan horizon allows laravel to process the job asynchronously.

Alternatively you can implement ShouldBroadcastNow, which is process synchronously (immediately) however if you are dealing with a lot of processes in production, this could slow down your app.

Not sure about that runtime exception... if you open a local terminal window and ping yourdomain.test, do you get something like .. ?

PING mydomain.test (192.168.10.10): 56 data bytes
64 bytes from 192.168.10.10: icmp_seq=0 ttl=64 time=1.077 ms
64 bytes from 192.168.10.10: icmp_seq=1 ttl=64 time=0.231 ms
redactuk commented 5 years ago

@xlcrr Ok, understood regarding horizon and tinker. Ping is:

'Pinging home24.local [192.168.10.10] with 32 bytes of data: Reply from 192.168.10.10: bytes=32 time<1ms TTL=64 Reply from 192.168.10.10: bytes=32 time<1ms TTL=64 Reply from 192.168.10.10: bytes=32 time<1ms TTL=64 Reply from 192.168.10.10: bytes=32 time<1ms TTL=64

Ping statistics for 192.168.10.10: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms '

redactuk commented 5 years ago

Without trace, full error message is:

` RuntimeException : Failed to listen on "tcp://192.168.10.10:6001": The requested address is not valid in its context.

at C:\sites\home24\vendor\react\socket\src\TcpServer.php:164 160| \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN, 161| \stream_context_create(array('socket' => $context)) 162| ); 163| if (false === $this->master) {

164| throw new \RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno); 165| } 166| \stream_set_blocking($this->master, 0); 167| 168| $this->resume(); `

xlcrr commented 5 years ago

@redactuk I'm not familiar with that error sorry !

Make sure you are running php artisan websockets:serve --host=192.168.10.10 and visit yourdomain.test/laravel-websockets and connect to "Your App" which should be visible from the downdown list

redactuk commented 5 years ago

@xlcrr no worries. I'll do some digging to see if I can solve that one. thanks for you help.

redactuk commented 5 years ago

For the benefit of any other novices like me reading this, I have been running homestead under windows and using git bash to access my windows website project folder (c:/code/home24), from where I run composer (as php is also installed locally for use in a wamp environment for other non-homestead projects), npm etc..., so I had been trying to run "php artisan websockets:serve" from there as well. This morning I connected to my VM using vagrant ssh and tried running 'php artisan websockets:serve' from home/code/home24 and it worked - using standard setup as per websockets docs.

So it seems I misunderstand how this was supposed to work. i.e. 'php artisan websockets:serve' needs to be run from within your VM in your website project folder?

amacsmith commented 4 years ago

So it seems I misunderstand how this was supposed to work. i.e. 'php artisan websockets:serve' needs to be run from within your VM in your website project folder?

Correct, really any artisan command should be ran within your vm on windows. However, run webpack scripts from native windows inside project root.

jeremykenedy commented 3 years ago

You can also just ssh into your vm and then run php artisan websockets:serve from your projects root folder inside the vm.