I developed a real time chat app with Laravel Websockets/Websockets API on localhost + Self Signed Certificate to use a SSL in local development. Everything works up to this point. I uploaded everything to the test server + setup supervisord to run both queue and websockets:serve (except the self signed certificate). Since we used AWS Certificate Manager to setup SSL, there is no copy of private key and certificate anywhere. Because of that, I am unable to run websockets in test server.
Below is my setup (very basic)
/etc/httpd/conf.d/project.conf
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.example.com
DocumentRoot "/var/www/html/project/core/public"
<Directory "/var/www/html/project/core">
Options Indexes FollowSymLinks Includes ExecCGI
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
ErrorLog "logs/project/errors.log"
CustomLog "logs/project/access.log" common
</VirtualHost>
/etc/supervisor.conf
[program:laravel-queue]
command=php /var/www/html/project/core/artisan queue:work --sleep=3 --tries=3
process_name=%(program_name)s
numprocs=1
autostart=true
autorestart=true
startsecs=10
startretries=3
user=ec2-user
redirect_stderr=true
stdout_logfile=/var/www/html/project/core/laravel-queue.log
[program:laravel-websockets]
command=php /var/www/html/project/core/artisan websockets:serve --host=127.0.0.1
process_name=%(program_name)s
numprocs=1
autostart=true
autorestart=true
startsecs=10
startretries=3
user=ec2-user
redirect_stderr=true
stdout_logfile=/var/www/html/project/core/laravel-websockets.log
broadcasting.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'),
'useTLS' => true,
'encrypted' => true,
'host' => '127.0.0.1',
'port' => env('LARAVEL_WEBSOCKETS_PORT'),
'scheme' => 'https',
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
],
],
],
websockets.php
'ssl' => [
'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),
'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),
'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
.env
PUSHER_APP_ID=12345
PUSHER_APP_KEY=ABCDEFG
PUSHER_APP_SECRET=HIJKLMNOP
PUSHER_APP_CLUSTER=mt1
LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=
LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=
LARAVEL_WEBSOCKETS_SSL_PASSPHRASE=""
LARAVEL_WEBSOCKETS_PORT=6001
MIX_LARAVEL_WEBSOCKETS_PORT="${LARAVEL_WEBSOCKETS_PORT}"
Echo Initialization
const Echo = new initEcho({
broadcaster: "pusher",
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
wsHost: window.location.hostname,
wsPort: process.env.MIX_LARAVEL_WEBSOCKETS_PORT,
wssPort: process.env.MIX_LARAVEL_WEBSOCKETS_PORT,
forceTLS: true,
encrypted: true,
enabledTransports: ["ws", "wss"],
auth: {
headers: {
Authorization:
"Bearer " + access_token,
Accept: "application/json",
},
},
});
Echo.connector.pusher.connection.strategy.transports.ws.transport.manager.livesLeft =
Infinity;
Echo.connector.pusher.connection.strategy.transports.wss.transport.manager.livesLeft =
Infinity;
Echo.connector.pusher.connection.bind("state_change", function (states) {
// state change
});
const channel = Echo.join("chat-message")
.here(() => {
console.log("chat channel");
})
.joining((event) => {
// console.log({ event }, "joining");
})
.leaving((event) => {
// console.log({ event }, "leaving");
})
.listenForWhisper("typing", (event) => {
// console.log({ event }, "listenForWhisper");
})
.listen(".chat-message", (event) => {
// console.log({ event }, "listen");
});
EDIT
A lot of SO Q&A said something about setting up Application Load Balancers, SSL Termination, etc (Don't really know about this) so we tried to do the following based on this aws document and changed 443 to 6001 (ws port number) and protocol to either http
or https
. Still the same issue.
UPDATE We also tried to do this aws add HTTPS listener. Still the same unable to listen to port.
My knowledge with AWS and its services is very limited (I was not the one to setup AWS ec2 and ACM) so if the solution is within aws, please teach me as simple as possible.
I was able to work the code after almost 2 weeks. With this setup, you no longer need to add the private key and certificate in .env file.
broadcasting.php
'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'),
'useTLS' => true,
#### ADD HERE
'encrypted' => false,
'host' => '127.0.0.1',
'port' => env('LARAVEL_WEBSOCKETS_PORT'),
'scheme' => 'http',
### ADD HERE
],
],
bootstrap.js
const Echo = new Echo({
broadcaster: "pusher",
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
wsHost: window.location.hostname,
// This is important
wsPort: 80,
wssPort: 443,
forceTLS: true,
encrypted: true,
enabledTransports: ["ws", "wss"],
// This is important
// optional if you are using jwt
auth: {
headers: {
Authorization:
"Bearer " + access_token,
Accept: "application/json",
},
},
// optional if you are using jwt
});
// Socket will try to reconnect indefinetly with this. If not added, echo will only try to reconnect twice as default
Echo.connector.pusher.connection.strategy.transports.ws.transport.manager.livesLeft =
Infinity;
Echo.connector.pusher.connection.strategy.transports.wss.transport.manager.livesLeft =
Infinity;
// Socket will try to reconnect indefinetly with this. If not added, echo will only try to reconnect twice as default
Use this Apache Proxy Pass if you are using the Custom WebSocket Handlers
ProxyRequests off
ProxyVia on
RewriteEngine On
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:6001/$1 [P,L]
ProxyPass /ws/chat http://127.0.0.1:6001/ws/chat
ProxyPassReverse /ws/chat http://127.0.0.1:6001/ws/chat