mirror of
https://github.com/simonw/datasette.git
synced 2026-06-13 04:27:00 +02:00
Closes #873. When `datasette -p 0 --root` was used, the printed auth-token URL contained the literal placeholder port 0 instead of the OS-assigned port that uvicorn would later bind to. Same applied to `--open`. Fix: when `port == 0` and we need to print/open a URL before the server starts (because of --root or --open), pre-bind a TCP socket on (host, 0), read the assigned port via getsockname(), and hand the bound socket to uvicorn via Server.run(sockets=[...]). uvicorn.run()'s own `fd=` parameter assumes AF_UNIX so we use the Config/Server API in this branch only; the existing uvicorn.run() path is unchanged. Adds a regression test that launches `datasette --memory -p 0 --root`, parses the printed URL, asserts the port is non-zero, and confirms a server is actually listening on that port.
90 lines
2.9 KiB
Python
90 lines
2.9 KiB
Python
import httpx
|
|
import pytest
|
|
import re
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
|
|
|
|
@pytest.mark.serial
|
|
def test_serve_localhost_http(ds_localhost_http_server):
|
|
response = httpx.get("http://localhost:8041/_memory.json")
|
|
assert {
|
|
"database": "_memory",
|
|
"path": "/_memory",
|
|
"tables": [],
|
|
}.items() <= response.json().items()
|
|
|
|
|
|
@pytest.mark.serial
|
|
@pytest.mark.skipif(
|
|
not hasattr(socket, "AF_UNIX"), reason="Requires socket.AF_UNIX support"
|
|
)
|
|
def test_serve_unix_domain_socket(ds_unix_domain_socket_server):
|
|
_, uds = ds_unix_domain_socket_server
|
|
transport = httpx.HTTPTransport(uds=uds)
|
|
client = httpx.Client(transport=transport)
|
|
response = client.get("http://localhost/_memory.json")
|
|
assert {
|
|
"database": "_memory",
|
|
"path": "/_memory",
|
|
"tables": [],
|
|
}.items() <= response.json().items()
|
|
|
|
|
|
@pytest.mark.serial
|
|
def test_serve_root_url_uses_actual_port_when_port_is_zero():
|
|
# Regression test for https://github.com/simonw/datasette/issues/873
|
|
# `datasette -p 0 --root` printed http://127.0.0.1:0/... instead of
|
|
# the OS-assigned port.
|
|
proc = subprocess.Popen(
|
|
[sys.executable, "-m", "datasette", "--memory", "-p", "0", "--root"],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
cwd=tempfile.gettempdir(),
|
|
text=True,
|
|
)
|
|
try:
|
|
# Read lines until we see the auth-token URL or time out
|
|
url_line = None
|
|
deadline = time.time() + 10.0
|
|
while time.time() < deadline:
|
|
line = proc.stdout.readline()
|
|
if not line:
|
|
if proc.poll() is not None:
|
|
break
|
|
continue
|
|
if "/-/auth-token?token=" in line:
|
|
url_line = line.strip()
|
|
break
|
|
assert url_line, "Did not see auth-token URL in datasette output"
|
|
match = re.match(r"http://127\.0\.0\.1:(\d+)/-/auth-token\?token=", url_line)
|
|
assert match, f"Unexpected auth-token URL format: {url_line!r}"
|
|
printed_port = int(match.group(1))
|
|
assert printed_port != 0, (
|
|
"datasette -p 0 --root should print the OS-assigned port, "
|
|
"not the placeholder 0"
|
|
)
|
|
# Confirm a server is actually listening on that printed port
|
|
deadline2 = time.time() + 5.0
|
|
last_err = None
|
|
while time.time() < deadline2:
|
|
try:
|
|
response = httpx.get(f"http://127.0.0.1:{printed_port}/_memory.json")
|
|
assert response.status_code == 200
|
|
break
|
|
except httpx.ConnectError as exc:
|
|
last_err = exc
|
|
time.sleep(0.1)
|
|
else:
|
|
raise AssertionError(
|
|
f"Could not connect to printed port {printed_port}: {last_err}"
|
|
)
|
|
finally:
|
|
proc.terminate()
|
|
try:
|
|
proc.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
proc.kill()
|