I have followed tutorials to configure laravel with websockets using docker, except that my php version is PHP 8.1.9, and laravel 8.
My nginx container is a reverse proxy to web_dev container, and does SSL termination. So further communication internally is over http.
nginx.conf
server {
listen 80;
server_name mydomain.co.uk *.mydomain.co.uk;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name mydomain.co.uk *.mydomain.co.uk;
include common.conf;
include ssl.conf;
location / {
include common_location.conf;
proxy_pass http://web_dev;
}
}
server {
listen 6001 ssl;
server_name mydomain.co.uk *.mydomain.co.uk;
include common.conf;
include ssl.conf;
location /ws {
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_set_header Sec-WebSocket-Key 'SGVsbG8sIHdvcmxkIQAAAA==';
proxy_set_header Sec-WebSocket-Version '13';
include common_location.conf;
proxy_pass http://web_dev:6001/;
}
}
Then I have a curl command:
curl -k \
--no-buffer \
--header "Connection: upgrade" \
--header "Upgrade: websocket" \
-v \
https://mydomain.co.uk:6001/ws/app/mydomainkey
This is the output:
Connected to mydomain.co.uk (127.0.0.1) port 6001 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
....
* TLSv1.2 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET /ws/app/mydomainkey HTTP/1.1
> Host: mydomain.co.uk:6001
> User-Agent: curl/7.81.0
> Accept: */*
> Connection: upgrade
> Upgrade: websocket
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
< Server: nginx/1.21.5
< Date: Sun, 04 Sep 2022 12:00:33 GMT
< Connection: upgrade
< Upgrade: websocket
< Sec-WebSocket-Accept: 5Tz1rM6Lpj3X4PQub3+HhJRM11s=
< X-Powered-By: Ratchet/0.4.4
< Strict-Transport-Security: max-age=31536000; includeSubDomains
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
�r{"event":"pusher:connection_established","data":"{\"socket_id\":\"155833011.137698690\",\"activity_timeout\":30}"}
This I think shows that the port 6001 and the SSL are configured correctly and that the websocket connection was established.
When I go to the url for websockets dashboard and click connect, I get
WebSocket connection to 'wss://mydomain.co.uk:6001/ws/app/bookhaircut?protocol=7&client=js&version=4.3.1&flash=false' failed:
I also tried fetch("wss://mydomain.co.uk:6001/ws/app/mydomainkey?protocol=7&client=js&version=4.3.1&flash=false");
which gives a different error:
On the web_dev container, I'm running supervisor which runs php artisan websockets:serve
. I can verify that my web_dev container can connect to its websockets running service, because I ran in php artisan tinker
:
`event (new \App\Events\NewTrade('test'))`
=> []
Then I checked supervisor logs I get many entries:
So in conclusion:
Any ideas?
I tried to modify the dashboard to pull a different version https://cdnjs.cloudflare.com/ajax/libs/pusher/6.0.3/pusher.js And that didnt make any difference.
Could nginx support wss instead of http? I have only seen configs with http. Configs with wss: throw an error.
So I'm out of ideas.
Here's my config files:
websockets.php
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'path' => "/ws",
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
],
],
/* ssl is left empty.*/
related problems:
How I found out:
I first tried to set SSL cert for the internal communication despite nginx doing termination (I had to try it) - but then the curl command broke, I couldn't connect to my websockets unless I removed my SSL certificates (was getting bad gateway).
So I ended up with these:
'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' => false,
'host' => "127.0.0.1",
'port' => 6001,
'useTLS' => false,
'scheme' => 'http',
],
],
And in config/websockets.php
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'path' => "/ws", //<-- important for nginx path detection
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
],
],
'ssl' => [
'local_cert' => null,
'local_pk' => null,
'passphrase' => null,
],
Then I noticed one thing about logging. When I got the websocket error in the browser, I was also receiving a new logging entry for my supervisor/websocket.log.
I tried to remove the 'path' => "/ws"
, and I received the same exact browser error but no log entry.
That gave me the fist hint when something is better than nothing. And I needed to stick to any settings that would not break this logging feature.
Then I found that when I remove these lines from nginx.conf
, it started all working, shockingly!!
proxy_set_header Sec-WebSocket-Key 'SGVsbG8sIHdvcmxkIQAAAA==';
proxy_set_header Sec-WebSocket-Version '13';
Originally I added these so I could simplify the curl command. But I didn't know that this would brake the browser's websockets. So the curl for testing now looks like this:
curl -k \
--no-buffer \
--header "Connection: upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQAAAA==" \ //<-- this is random you can use it
--header "Sec-WebSocket-Version: 13" \
-v \
https://mydomain.co.uk:6001/ws/app/mydomainkey