mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-15 16:15:49 +08:00
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
"""maxconn: a service serves at most that many connections at once, and a
|
|
connection over the limit waits for one of them to finish."""
|
|
|
|
import os
|
|
import signal
|
|
import socket
|
|
import time
|
|
|
|
|
|
def run(t):
|
|
# The origin runs in a process of its own, so the limit only applies to
|
|
# the mapper in front of it.
|
|
origin = t.free_port()
|
|
t.start("maxconn_origin", f"""
|
|
auth iponly
|
|
allow *
|
|
http echo * /echo**
|
|
httpsrv -p{origin}
|
|
""", ports=[origin])
|
|
|
|
mapped = t.free_port()
|
|
server = t.start("maxconn", f"""
|
|
log
|
|
maxconn 2
|
|
auth iponly
|
|
allow *
|
|
tcppm {mapped} 127.0.0.1 {origin}
|
|
""", ports=[mapped])
|
|
|
|
def connect():
|
|
sock = socket.create_connection(("127.0.0.1", mapped), 5)
|
|
opened.append(sock)
|
|
return sock
|
|
|
|
def served(sock, timeout):
|
|
"""Ask over a kept alive connection; True once the whole reply is in,
|
|
so the connection is ready for the next request."""
|
|
sock.sendall(b"GET /echo HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")
|
|
sock.settimeout(timeout)
|
|
data = b""
|
|
try:
|
|
while b"\r\n\r\n" not in data:
|
|
chunk = sock.recv(4096)
|
|
if not chunk:
|
|
return False
|
|
data += chunk
|
|
head, body = data.split(b"\r\n\r\n", 1)
|
|
length = 0
|
|
for line in head.split(b"\r\n")[1:]:
|
|
name, _, value = line.partition(b":")
|
|
if name.strip().lower() == b"content-length":
|
|
length = int(value)
|
|
while len(body) < length:
|
|
chunk = sock.recv(4096)
|
|
if not chunk:
|
|
return False
|
|
body += chunk
|
|
return head.startswith(b"HTTP/1.1 200")
|
|
except OSError:
|
|
return False
|
|
|
|
opened = []
|
|
try:
|
|
# the probe that saw the port open holds a slot until its session
|
|
# ends, so wait for both slots to be free before taking them
|
|
deadline = time.time() + 5
|
|
while True:
|
|
first, second = connect(), connect()
|
|
both = served(first, 1) and served(second, 1)
|
|
if both or time.time() > deadline:
|
|
break
|
|
first.close()
|
|
second.close()
|
|
t.eq(True, both, "two connections are served under maxconn 2")
|
|
|
|
third = connect()
|
|
t.eq(False, served(third, 1.5),
|
|
"a third connection waits while both slots are held")
|
|
t.contains(t.wait_output(server, "too many connected clients"),
|
|
"too many connected clients",
|
|
"a service at its limit says so in the log")
|
|
|
|
first.close()
|
|
t.eq(True, served(third, 5),
|
|
"the waiting connection is served once a slot is freed")
|
|
t.eq(True, served(second, 5),
|
|
"a connection holding a slot keeps being served")
|
|
|
|
if os.name == "nt":
|
|
t.skip("reload while at the limit (no SIGUSR1 on Windows)")
|
|
else:
|
|
waiting = connect()
|
|
t.eq(False, served(waiting, 1),
|
|
"the limit holds again with two connections open")
|
|
server.proc.send_signal(signal.SIGUSR1)
|
|
# A connection made the instant the signal arrives can land on
|
|
# the listener being replaced and be reset; give the new service
|
|
# the moment it needs.
|
|
time.sleep(1.5)
|
|
fresh = connect()
|
|
t.eq(True, served(fresh, 5),
|
|
"a reload while the limit is reached starts a service with "
|
|
"its slots free")
|
|
t.eq(None, server.proc.poll(), "the process survives the reload")
|
|
finally:
|
|
for sock in opened:
|
|
sock.close()
|