Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverb not connectable locally via WebSocket (404) #202

Closed
medero opened this issue May 21, 2024 · 6 comments
Closed

Reverb not connectable locally via WebSocket (404) #202

medero opened this issue May 21, 2024 · 6 comments
Assignees

Comments

@medero
Copy link

medero commented May 21, 2024

Reverb Version

v1.0.0-beta10

Laravel Version

11.7.0

PHP Version

8.3.7

Description

I tried creating a fresh laravel project to reproduce and upon starting reverb with the defaults (0.0.0.0:8080) it gives a 404 GET on Chrome/Firefox even overriding the host with 127.0.0.1 or such. Alternatively tried node with ipv4/ipv6 and those both reliably work and return a 101.

Even with the --debug flag to start reverb there's nothing in laravel logs. Artisan successfully starts and I can see the "Welcome" page on port 8000 (web port) so Laravel seems to work at a base level, just not reverb.

When reverb is not running, I get NS_ERROR_CONNECTION_REFUSED. With a bogus host, I get NS_ERROR_UNKNOWN_HOST

When reverb is running I get a 404, so it means it's still hitting Laravel.

Screen Shot 2024-05-21 at 1 32 37 AM Screen Shot 2024-05-21 at 1 35 32 AM

Just to sanity check, .env has these values by default (cleared out app id, app key, app secret):

BROADCAST_CONNECTION=reverb
REVERB_APP_ID=
REVERB_APP_KEY=
REVERB_APP_SECRET=
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http

broadcasting.php:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Broadcaster
    |--------------------------------------------------------------------------
    |
    | This option controls the default broadcaster that will be used by the
    | framework when an event needs to be broadcast. You may set this to
    | any of the connections defined in the "connections" array below.
    |
    | Supported: "reverb", "pusher", "ably", "redis", "log", "null"
    |
    */

    'default' => env('BROADCAST_CONNECTION', 'null'),

    /*
    |--------------------------------------------------------------------------
    | Broadcast Connections
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the broadcast connections that will be used
    | to broadcast events to other systems or over WebSockets. Samples of
    | each available type of connection are provided inside this array.
    |
    */

    'connections' => [

        'reverb' => [
            'driver' => 'reverb',
            'key' => env('REVERB_APP_KEY'),
            'secret' => env('REVERB_APP_SECRET'),
            'app_id' => env('REVERB_APP_ID'),
            'options' => [
                'host' => env('REVERB_HOST'),
                'port' => env('REVERB_PORT', 443),
                'scheme' => env('REVERB_SCHEME', 'https'),
                'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
            ],
            'client_options' => [
                // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
            ],
        ],

        '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('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
                'port' => env('PUSHER_PORT', 443),
                'scheme' => env('PUSHER_SCHEME', 'https'),
                'encrypted' => true,
                'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
            ],
            'client_options' => [
                // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
            ],
        ],

        'ably' => [
            'driver' => 'ably',
            'key' => env('ABLY_KEY'),
        ],

        'log' => [
            'driver' => 'log',
        ],

        'null' => [
            'driver' => 'null',
        ],

    ],

];

And reverb.php has allowed origins set to *:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Reverb Server
    |--------------------------------------------------------------------------
    |
    | This option controls the default server used by Reverb to handle
    | incoming messages as well as broadcasting message to all your
    | connected clients. At this time only "reverb" is supported.
    |
    */

    'default' => env('REVERB_SERVER', 'reverb'),

    /*
    |--------------------------------------------------------------------------
    | Reverb Servers
    |--------------------------------------------------------------------------
    |
    | Here you may define details for each of the supported Reverb servers.
    | Each server has its own configuration options that are defined in
    | the array below. You should ensure all the options are present.
    |
    */

    'servers' => [

        'reverb' => [
            'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
            'port' => env('REVERB_SERVER_PORT', 8080),
            'hostname' => env('REVERB_HOST'),
            'options' => [
                'tls' => [],
            ],
            'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
            'scaling' => [
                'enabled' => env('REVERB_SCALING_ENABLED', false),
                'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
                'server' => [
                    'url' => env('REDIS_URL'),
                    'host' => env('REDIS_HOST', '127.0.0.1'),
                    'port' => env('REDIS_PORT', '6379'),
                    'username' => env('REDIS_USERNAME'),
                    'password' => env('REDIS_PASSWORD'),
                    'database' => env('REDIS_DB', '0'),
                ],
            ],
            'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
            'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Reverb Applications
    |--------------------------------------------------------------------------
    |
    | Here you may define how Reverb applications are managed. If you choose
    | to use the "config" provider, you may define an array of apps which
    | your server will support, including their connection credentials.
    |
    */

    'apps' => [

        'provider' => 'config',

        'apps' => [
            [
                'key' => env('REVERB_APP_KEY'),
                'secret' => env('REVERB_APP_SECRET'),
                'app_id' => env('REVERB_APP_ID'),
                'options' => [
                    'host' => env('REVERB_HOST'),
                    'port' => env('REVERB_PORT', 443),
                    'scheme' => env('REVERB_SCHEME', 'https'),
                    'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
                ],
                'allowed_origins' => ['*'],
                'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
                'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
            ],
        ],

    ],

];

System details:

  • Mac M1
  • composer 2.7.2
  • PHP installed via brew
  • No docker, no vagrant for this setup locally (no proxy/server config)
  • No SSL setup locally, just running artisan serve and reverb:start

Steps To Reproduce

Laravel steps to reproduce:

  1. composer create-project laravel/laravel test-websocket ^11.0
  2. cd test-websocket
  3. php artisan install:broadcasting
Screen Shot 2024-05-21 at 1 19 16 AM Screen Shot 2024-05-21 at 1 19 16 AM

Would you like to install Laravel Reverb? [Yes]

Would you like to install and build the Node dependencies required for broadcasting? [Yes]

  1. Before I run reverb:start, I check that 8080 isn't in use with lsof -i :8080

  2. php artisan reverb:start --debug

    INFO Starting server on 0.0.0.0:8080 (localhost).

Screen Shot 2024-05-21 at 1 27 34 AM
  1. Now, lsof -i :8080 reports that php is listening on a TCP 8080 port:

php **PID** meder **FDVALUE** IPv4 **DEVICEID** 0t0 TCP *:http-alt (LISTEN)

In Chrome and Firefox:

I get:

Screen Shot 2024-05-21 at 12 45 26 AM

In Firefox's network tab:
Screen Shot 2024-05-21 at 12 53 24 AM

Working node alternative with ipv6 and ipv4:

// server.js
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

console.log('WebSocket server is running on ws://0.0.0.0:8080');

Which lsof -i :8080 confirms node is listening on the 8080 TCP port:
node **PID** meder **FDVALUE** IPv6 **DEVICEID** 0t0 TCP *:http-alt (LISTEN)
I get a 101 response:

Screen Shot 2024-05-21 at 12 54 30 AM

Shortly after seeing the lsof output I saw ipv4 php used and ipv6 node used, to which I matched node and it still works, via:

const WebSocket = require('ws');

const server = new WebSocket.Server({ host: '127.0.0.1', port: 8080 }, () => {
  console.log('WebSocket server is listening on ws://127.0.0.1:8080');
});

node **PID** meder **FDVALUE** IPv4 **DEVICEID** 0t0 TCP localhost:http-alt (LISTEN)

Screenshot:
Screen Shot 2024-05-21 at 1 57 03 AM

Attempt specifying php artisan reverb:start --host=127.0.0.1 didn't work either

Creating a test controller for a 200 OK/green path worked

php artisan make:controller WebSocketTestController

class WebSocketTestController extends Controller
{
    public function test()
    {
        return response()->json(['message' => 'WebSocket test endpoint']);
    }
}

To rule out application wide 404 issues:

curl http://localhost:8000/websocket-test

{"message":"WebSocket test endpoint"}
@medero medero changed the title Reverb not connectable locally Reverb not connectable locally via WebSocket May 21, 2024
@medero medero changed the title Reverb not connectable locally via WebSocket Reverb not connectable locally via WebSocket (404) May 21, 2024
@joedixon
Copy link
Collaborator

To confirm, have you cleared out the following REVERB_ prefixed enviroment variables just for the purposes of not wanting to give away any sensitive information in the issue, or are they also empty in your app?

REVERB_APP_ID=
REVERB_APP_KEY=
REVERB_APP_SECRET=

@joedixon joedixon self-assigned this May 21, 2024
@medero
Copy link
Author

medero commented May 21, 2024

Correct - I blanked those out before sharing but they had pregenerated values.

Actually, I think I found the issue in that it's hitting the 404 in vendor/laravel/reverb/src/Servers/Reverb/Http/Router.php:53 due to no matched routes?

 38     /**
 39      * Dispatch the request to the appropriate controller.
 40      */
 41     public function dispatch(RequestInterface $request, Connection $connection): mixed
 42     {
 43         $uri = $request->getUri();
 44         $context = $this->matcher->getContext();
 45 
 46         $context->setMethod($request->getMethod());
 47         $context->setHost($uri->getHost());
 48 
 49         try {
 50             $route = $this->matcher->match($uri->getPath());
 51         } catch (MethodNotAllowedException $e) {
 52             return $this->close($connection, 405, 'Method not allowed.', ['Allow' => $e->getAllowedMethods()]);
 53         } catch (ResourceNotFoundException $e) {
 54                     Log::error('Resource not found for URI: ' . $uri->getPath(), [
 55                         'uri' => $uri->getPath(),
 56                         'host' => $uri->getHost(),
 57                         'method' => $request->getMethod(),
 58                     ]);
 59             return $this->close($connection, 404, 'Not Found.');
 60         }
 61 
 62         $controller = $this->controller($route);
 63 
 64         if ($this->isWebSocketRequest($request)) {
 65             $wsConnection = $this->attemptUpgrade($request, $connection);
 66 
 67             return $controller($request, $wsConnection, ...Arr::except($route, ['_controller', '_route']));
 68         }

So it never reaches that if ($this->isWebSocketRequest($request)) call. I added the above Log::error and this is the log:

[2024-05-21 09:25:27] local.ERROR: Resource not found for URI: / {"uri":"/","host":"localhost","method":"GET"}

Is this because it binds to specific routes that Echo/the Laravel frontend generates when interacting with web sockets?

EDIT: Pretty sure this is it.

channels.php is untouched:

<?php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

And I have the root route predefined in web.php - or is it trying to grab web socket specific routes?

Route::get('/', function () {
    return view('welcome');
});

@medero
Copy link
Author

medero commented May 21, 2024

Ah, I have to do an app specific call, eg new WebSocket('localhost:8080/app/[app_id]') since I'm calling it from React Native

@joedixon
Copy link
Collaborator

Hey @medero that's right. You need to tell the Reverb server which app you wish to connect to.

@POMXARK
Copy link

POMXARK commented May 23, 2024

https://beyondco.de/docs/laravel-websockets/advanced-usage/custom-websocket-handlers

I'm also looking for a way to set up a similar classic access point. and I don’t understand how to connect an external frontend to a channel without ws://localhost/ws/chat:6001 wss://localhost/ws/chat:6001 and how to test it in postman

this https://laravel.com/docs/11.x/reverb#ssl

@Jesse-Dan
Copy link

'{"event": "pusher:subscribe", "data": {"channel": "test-c"}}'

@POMXARK use this to subscribe first and then set broadcasts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants