Beginner - Websocket requests not reaching any handlers

1. The problem I’m having:

I’m trying to use websockets, and my server is not receiving the requests. Both of the servers standard http handlers are working, and the frontend is correctly loading the pages. However, when I look at the debug output I don’t even see a request being made to the websocket handler. Based on the other posts in this forum, I’m not certain that the problem is with Caddy, so I’ll post the javascript code which is sending the websocket request, and show how the go backend is trying to handle that.

1.5 Relevant code:

This is the React component to test the websocket behavior, reachable at www.gamewithyourfriends.dev/testserver.

import { useEffect, useRef } from 'react';

function TestServer() {
  const socketUrl = 'www.gamewithyourfriends.dev/lm';
  const socket = useRef(null);
  // Create the socket connection
  useEffect(() => {
    console.log('testing socket hook');
    console.log(`wss://${socketUrl}/connectsocket`);
    // This should send a request to the endpoint /lm/connectsocket, but isn't.
    socket.current = new WebSocket(`wss://${socketUrl}/connectsocket`);

    socket.current.onopen = () => {
      console.log('lobby socket opened');
    };

    socket.current.onclose = () => console.log('lobby socket closed');
  }, []);

  return (
    <div>Success???</div>
  );
}

export default TestServer;

That should be sending a request to the handler shown here. The /lm/connectsocket handler is not being reached at all (theres a log statement at the top of it), whereas the /lm/lobbies handler works fine.

r := mux.NewRouter()
// cors, other handlers.
r.HandleFunc("/lm/lobbies", GetLobbiesHandler)
r.HandleFunc("/lm/connectsocket", AddClientHandler)
// other handlers, listenandserve

2. Error messages and/or full log output:

The output of trying to curl the domain:

curl https://www.gamewithyourfriends.dev/testserver
curl: (28) Failed to connect to www.gamewithyourfriends.dev port 443 after 75074 ms: Operation timed out

And the debug output from loading the page Vite + React

2023/06/28 18:33:41.865	DEBUG	http.handlers.rewrite	rewrote request	{"request": {"remote_ip": "192.168.1.254", "remote_port": "443", "proto": "HTTP/2.0", "method": "GET", "host": "www.gamewithyourfriends.dev", "uri": "/testserver", "headers": {"Pragma": ["no-cache"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\""], "Sec-Ch-Ua-Mobile": ["?0"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"], "Sec-Fetch-Site": ["same-origin"], "Sec-Ch-Ua-Platform": ["\"macOS\""], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"], "Sec-Fetch-User": ["?1"], "Sec-Fetch-Dest": ["document"], "Accept-Language": ["en-US,en;q=0.9"], "Cache-Control": ["no-cache"], "Upgrade-Insecure-Requests": ["1"], "Sec-Fetch-Mode": ["navigate"], "Accept-Encoding": ["gzip, deflate, br"]}, "tls": {"resumed": true, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "www.gamewithyourfriends.dev"}}, "method": "GET", "uri": "/index.html"}
2023/06/28 18:33:41.865	DEBUG	http.handlers.file_server	sanitized path join	{"site_root": "frontend/dist", "request_path": "/index.html", "result": "frontend/dist/index.html"}
2023/06/28 18:33:41.866	DEBUG	http.handlers.file_server	opening file	{"filename": "frontend/dist/index.html"}
2023/06/28 18:33:41.916	DEBUG	http.handlers.file_server	sanitized path join	{"site_root": "frontend/dist", "request_path": "/assets/index-98857a14.css", "result": "frontend/dist/assets/index-98857a14.css"}
2023/06/28 18:33:41.916	DEBUG	http.handlers.file_server	sanitized path join	{"site_root": "frontend/dist", "request_path": "/assets/index-387ebb63.js", "result": "frontend/dist/assets/index-387ebb63.js"}
2023/06/28 18:33:41.916	DEBUG	http.handlers.file_server	opening file	{"filename": "frontend/dist/assets/index-98857a14.css"}
2023/06/28 18:33:41.916	DEBUG	http.handlers.file_server	opening file	{"filename": "frontend/dist/assets/index-387ebb63.js"}
2023/06/28 18:33:41.996	DEBUG	http.handlers.reverse_proxy	selected upstream	{"dial": ":8000", "total_upstreams": 1}
2023/06/28 18:33:41.999	DEBUG	http.handlers.reverse_proxy	upstream roundtrip	{"upstream": ":8000", "duration": 0.002255375, "request": {"remote_ip": "192.168.1.254", "remote_port": "443", "proto": "HTTP/2.0", "method": "GET", "host": "www.gamewithyourfriends.dev", "uri": "/lm/sessionid", "headers": {"Accept-Encoding": ["gzip, deflate, br"], "Accept-Language": ["en-US,en;q=0.9"], "Cache-Control": ["no-cache"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\""], "Authorization": [], "X-Forwarded-Proto": ["https"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"], "Sec-Fetch-Dest": ["empty"], "Accept": ["application/json, text/plain, */*"], "Sec-Fetch-Mode": ["cors"], "Pragma": ["no-cache"], "Cookie": [], "Sec-Ch-Ua-Platform": ["\"macOS\""], "Sec-Fetch-Site": ["same-origin"], "Referer": ["https://www.gamewithyourfriends.dev/testserver"], "X-Forwarded-Host": ["www.gamewithyourfriends.dev"], "Sec-Ch-Ua-Mobile": ["?0"], "X-Forwarded-For": ["192.168.1.254"]}, "tls": {"resumed": true, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "www.gamewithyourfriends.dev"}}, "headers": {"Vary": ["Origin"], "Date": ["Wed, 28 Jun 2023 18:33:41 GMT"], "Content-Length": ["4"], "Content-Type": ["text/plain; charset=utf-8"]}, "status": 200}
2023/06/28 18:33:42.008	DEBUG	http.handlers.file_server	sanitized path join	{"site_root": "frontend/dist", "request_path": "/vite.svg", "result": "frontend/dist/vite.svg"}
2023/06/28 18:33:42.008	DEBUG	http.handlers.file_server	opening file	{"filename": "frontend/dist/vite.svg"}

3. Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

4. How I installed and ran Caddy:

brew install caddy

a. System environment:

MacOS, apple chip

b. Command:

caddy run

caddy run

c. Service/unit/compose file:

d. My complete Caddy config:

{
	debug
}

www.gamewithyourfriends.dev {
	handle /game* {
		reverse_proxy :8080
	}

	handle /lm* {
		reverse_proxy :8000
	}

	handle {
		root * frontend/dist
		try_files {path} index.html
		file_server
	}
}

5. Links to relevant resources:

n/a

6. Miscellaneous:

I think the problem might have to do with the output of curl shown in section 2.
‘Failed to connect to www.gamewithyourfriends.dev’ makes me think that the request path got changed somehow and isn’t reaching the handler at /lm/connectsocket, but i’m not sure why it would be. I can post other pieces of code from the react client or go backend if it helps.
Thank you for reading, this is my first time trying to set up a server, so I may have made a silly mistake somewhere along the way.

You use the path /connectsocket but you don’t have a handle for that path.

I suggest writing your handle matcher like this, for flexibility:

	@backend `path('/game*', '/lm*') || header({'Connection': '*Upgrade*', 'Upgrade': 'websocket'})`
	handle @backend {
		reverse_proxy :8000
	}

You can add/remove more arguments to path() as needed, and the header matcher will make sure any websocket request will make it to your Node backend.

When expanded the request is actually this one, sorry for the confusion -

socket.current = new WebSocket(`wss://$www.gamewithyourfriends.dev/lm/connectsocket`);

I would think that the handle for the path /lm* in the caddyfile should match that request (/lm/connectsocket) and forward it to port 8000.

Ah, yeah that should work fine (but I still suggest using a single matcher + handle for your proxy rather than two handle blocks, it’s slightly simpler).

Are you trying to connect to your domain from your local machine? Try from your phone over cellular networks (not wifi), does it work there?

If it works on your phone, then you’re running into NAT hairpinning issues with your router, i.e. your router doesn’t know how to route packets that have your WAN IP on it back into your network.

The typical solution is to run a DNS server in your LAN to resolve your domain to your LAN IP instead of your WAN IP, or you can override it in your /etc/hosts on any machines in your LAN if that’s an option for you.

1 Like

Thank you!! This is definitely the issue, as it works well over a different network. It’ll take me a while to understand the solution as I don’t know much about networks but this should be enough to get me on track.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.