mirror of
https://github.com/neondatabase/neon.git
synced 2025-12-23 22:29:58 +00:00
## Problem Ref: https://github.com/neondatabase/neon/issues/10632 We use dns named `*.localtest.me` in our test, and that domain is well-known and widely used for that, with all the records there resolve to the localhost, both IPv4 and IPv6: `127.0.0.1` and `::1` In some cases on our runners these addresses resolves only to `IPv6`, and so components fail to connect when runner doesn't have `IPv6` address. We suspect issue in systemd-resolved here (https://github.com/systemd/systemd/issues/17745) To workaround that and improve test stability, we introduced our own domain `*.local.neon.build` with IPv4 address `127.0.0.1` only See full details and troubleshoot log in referred issue. p.s. If you're FritzBox user, don't forget to add that domain `local.neon.build` to the `DNS Rebind Protection` section under `Home Network -> Network -> Network Settings`, otherwise FritzBox will block addresses, resolving to the local addresses. For other devices/vendors, please check corresponding documentation, if resolving `local.neon.build` will produce empty answer for you. ## Summary of changes Replace all the occurrences of `localtest.me` with `local.neon.build`
155 lines
4.7 KiB
Python
Executable File
155 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# This program helps to test the WebSocket tunneling in proxy. It listens for a TCP
|
|
# connection on a port, and when you connect to it, it opens a websocket connection,
|
|
# and forwards all the traffic to the websocket connection, wrapped in WebSocket binary
|
|
# frames.
|
|
#
|
|
# This is used in the test_proxy::test_websockets test, but it is handy for manual testing too.
|
|
#
|
|
# Usage for manual testing:
|
|
#
|
|
# ## Launch Posgres on port 3000:
|
|
# postgres -D data -p3000
|
|
#
|
|
# ## Launch proxy with WSS enabled:
|
|
# openssl req -new -x509 -days 365 -nodes -text -out server.crt -keyout server.key -subj '/CN=*.local.neon.build'
|
|
# ./target/debug/proxy --wss 127.0.0.1:40433 --http 127.0.0.1:28080 --mgmt 127.0.0.1:9099 --proxy 127.0.0.1:4433 --tls-key server.key --tls-cert server.crt --auth-backend postgres
|
|
#
|
|
# ## Launch the tunnel:
|
|
#
|
|
# poetry run ./test_runner/websocket_tunnel.py --ws-port 40433 --ws-url "wss://ep-test.local.neon.build"
|
|
#
|
|
# ## Now you can connect with psql:
|
|
# psql "postgresql://heikki@localhost:40433/postgres"
|
|
#
|
|
|
|
import argparse
|
|
import asyncio
|
|
import logging
|
|
import ssl
|
|
from ssl import Purpose
|
|
|
|
import websockets
|
|
from fixtures.log_helper import log
|
|
|
|
|
|
# Enable verbose logging of all the traffic
|
|
def enable_verbose_logging():
|
|
logger = logging.getLogger("websockets")
|
|
logger.setLevel(logging.DEBUG)
|
|
logger.addHandler(logging.StreamHandler())
|
|
|
|
|
|
async def start_server(tcp_listen_host, tcp_listen_port, ws_url, ctx):
|
|
server = await asyncio.start_server(
|
|
lambda r, w: handle_client(r, w, ws_url, ctx), tcp_listen_host, tcp_listen_port
|
|
)
|
|
return server
|
|
|
|
|
|
async def handle_tcp_to_websocket(tcp_reader, ws):
|
|
try:
|
|
while not tcp_reader.at_eof():
|
|
data = await tcp_reader.read(1024)
|
|
|
|
await ws.send(data)
|
|
except websockets.exceptions.ConnectionClosedError as e:
|
|
log.debug(f"connection closed: {e}")
|
|
except websockets.exceptions.ConnectionClosedOK:
|
|
log.debug("connection closed")
|
|
except Exception as e:
|
|
log.error(e)
|
|
|
|
|
|
async def handle_websocket_to_tcp(ws, tcp_writer):
|
|
try:
|
|
async for message in ws:
|
|
tcp_writer.write(message)
|
|
await tcp_writer.drain()
|
|
except websockets.exceptions.ConnectionClosedError as e:
|
|
log.debug(f"connection closed: {e}")
|
|
except websockets.exceptions.ConnectionClosedOK:
|
|
log.debug("connection closed")
|
|
except Exception as e:
|
|
log.error(e)
|
|
|
|
|
|
async def handle_client(tcp_reader, tcp_writer, ws_url: str, ctx: ssl.SSLContext):
|
|
try:
|
|
log.info("Received TCP connection. Connecting to websockets proxy.")
|
|
|
|
async with websockets.connect(ws_url, ssl=ctx) as ws:
|
|
try:
|
|
log.info("Connected to websockets proxy")
|
|
|
|
async with asyncio.TaskGroup() as tg:
|
|
task1 = tg.create_task(handle_tcp_to_websocket(tcp_reader, ws))
|
|
task2 = tg.create_task(handle_websocket_to_tcp(ws, tcp_writer))
|
|
|
|
done, pending = await asyncio.wait(
|
|
[task1, task2], return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
tcp_writer.close()
|
|
await ws.close()
|
|
|
|
except* Exception as ex:
|
|
log.error(ex.exceptions)
|
|
except Exception as e:
|
|
log.error(e)
|
|
|
|
|
|
async def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--tcp-listen-addr",
|
|
default="localhost",
|
|
help="TCP addr to listen on",
|
|
)
|
|
parser.add_argument(
|
|
"--tcp-listen-port",
|
|
default="40444",
|
|
help="TCP port to listen on",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--ws-url",
|
|
default="wss://localhost/",
|
|
help="websocket URL to connect to. This determines the Host header sent to the server",
|
|
)
|
|
parser.add_argument(
|
|
"--ws-host",
|
|
default="127.0.0.1",
|
|
help="websockets host to connect to",
|
|
)
|
|
parser.add_argument(
|
|
"--ws-port",
|
|
type=int,
|
|
default=443,
|
|
help="websockets port to connect to",
|
|
)
|
|
parser.add_argument(
|
|
"--verbose",
|
|
action="store_true",
|
|
help="enable verbose logging",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
enable_verbose_logging()
|
|
|
|
ctx = ssl.create_default_context(Purpose.SERVER_AUTH)
|
|
ctx.check_hostname = False
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
server = await start_server(args.tcp_listen_addr, args.tcp_listen_port, args.ws_url, ctx)
|
|
print(
|
|
f"Listening for connections at {args.tcp_listen_addr}:{args.tcp_listen_port}, forwarding them to {args.ws_host}:{args.ws_port}"
|
|
)
|
|
async with server:
|
|
await server.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|