mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-17 08:55:49 +08:00
Count the clients of each listener
A listener counts its clients, whichever of its services they came to, old or new: those connected now, raised when a client is spawned, and those done with, raised as one leaves its service. As the listener outlives a reload, so do the counts. The services page of the admin interface shows them for each service as listenerclients and listenerprocessed; services sharing a socket show the same counts. The takeover case checks that the count goes on through three reloads and that no client is left counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d83a48fe18
commit
113c9eaebe
@ -530,6 +530,14 @@ static void * ef_server_auth(struct node * node){
|
|||||||
return "uknown";
|
return "uknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void * ef_server_listenerclients(struct node * node){
|
||||||
|
return listenerclients((struct srvparam *)node->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * ef_server_listenerprocessed(struct node * node){
|
||||||
|
return listenerprocessed((struct srvparam *)node->value);
|
||||||
|
}
|
||||||
|
|
||||||
static void * ef_server_childcount(struct node * node){
|
static void * ef_server_childcount(struct node * node){
|
||||||
return &((struct srvparam *)node->value) -> childcount;
|
return &((struct srvparam *)node->value) -> childcount;
|
||||||
}
|
}
|
||||||
@ -886,6 +894,8 @@ static struct property prop_server[] = {
|
|||||||
{"nonprintable", ef_server_nonprintable, TYPE_STRING, "non printable characters"},
|
{"nonprintable", ef_server_nonprintable, TYPE_STRING, "non printable characters"},
|
||||||
{"replacement", ef_server_replacement, TYPE_CHAR, "replacement character"},
|
{"replacement", ef_server_replacement, TYPE_CHAR, "replacement character"},
|
||||||
{"childcount", ef_server_childcount, TYPE_INTEGER, "number of servers connected"},
|
{"childcount", ef_server_childcount, TYPE_INTEGER, "number of servers connected"},
|
||||||
|
{"listenerclients", ef_server_listenerclients, TYPE_UNSIGNED64, "clients connected to the listening socket, of its services old and new"},
|
||||||
|
{"listenerprocessed", ef_server_listenerprocessed, TYPE_UNSIGNED64, "clients of the listening socket done with"},
|
||||||
{"maxchild", ef_server_maxchild, TYPE_INTEGER, "max concurrent connections"},
|
{"maxchild", ef_server_maxchild, TYPE_INTEGER, "max concurrent connections"},
|
||||||
{"backlog", ef_server_backlog, TYPE_INTEGER, "listen backlog"},
|
{"backlog", ef_server_backlog, TYPE_INTEGER, "listen backlog"},
|
||||||
{"needuser", ef_server_needuser, TYPE_INTEGER, "require user authentication"},
|
{"needuser", ef_server_needuser, TYPE_INTEGER, "require user authentication"},
|
||||||
|
|||||||
@ -404,6 +404,8 @@ int reattach(struct clientparam *param, int auth);
|
|||||||
/* reattach() where configurations are reloaded, NULL elsewhere */
|
/* reattach() where configurations are reloaded, NULL elsewhere */
|
||||||
extern int (*reattachfunc)(struct clientparam *param, int auth);
|
extern int (*reattachfunc)(struct clientparam *param, int auth);
|
||||||
int clientauth(struct clientparam *param);
|
int clientauth(struct clientparam *param);
|
||||||
|
uint64_t *listenerclients(struct srvparam *srv);
|
||||||
|
uint64_t *listenerprocessed(struct srvparam *srv);
|
||||||
struct passwords * copypwl (struct passwords *pwl);
|
struct passwords * copypwl (struct passwords *pwl);
|
||||||
void freepwl(struct passwords *pw);
|
void freepwl(struct passwords *pw);
|
||||||
int copyfilter(struct filter *, struct srvparam *srv);
|
int copyfilter(struct filter *, struct srvparam *srv);
|
||||||
|
|||||||
@ -903,7 +903,37 @@ struct listener {
|
|||||||
/* held while the first of its services opens the socket, binding
|
/* held while the first of its services opens the socket, binding
|
||||||
which may take long: the others then use what it opened */
|
which may take long: the others then use what it opened */
|
||||||
_3proxy_mutex_t open_mutex;
|
_3proxy_mutex_t open_mutex;
|
||||||
|
/* its clients, whichever of its services, old or new, they came to:
|
||||||
|
those connected now, and those done with */
|
||||||
|
uint64_t clients, processed;
|
||||||
|
_3proxy_mutex_t count_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* A client of srv comes (1) or goes (-1). */
|
||||||
|
static void listenercount(struct srvparam *srv, int delta){
|
||||||
|
struct listener *l = srv->listener;
|
||||||
|
|
||||||
|
if(!l) return;
|
||||||
|
_3proxy_mutex_lock(&l->count_mutex);
|
||||||
|
if(delta > 0) l->clients++;
|
||||||
|
else {
|
||||||
|
l->clients--;
|
||||||
|
l->processed++;
|
||||||
|
}
|
||||||
|
_3proxy_mutex_unlock(&l->count_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t nocount = 0;
|
||||||
|
|
||||||
|
/* The counters of a running service's listener, for the admin pages; the
|
||||||
|
listener lasts as long as the service. */
|
||||||
|
uint64_t *listenerclients(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->clients : &nocount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t *listenerprocessed(struct srvparam *srv){
|
||||||
|
return srv->listener? &srv->listener->processed : &nocount;
|
||||||
|
}
|
||||||
static struct listener *listeners = NULL;
|
static struct listener *listeners = NULL;
|
||||||
_3proxy_mutex_t listener_mutex;
|
_3proxy_mutex_t listener_mutex;
|
||||||
|
|
||||||
@ -1350,6 +1380,9 @@ static int srvspawn(struct srvstart *s, SOCKET client_sock){
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
srv->childcount++;
|
srv->childcount++;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
listenercount(srv, 1);
|
||||||
|
#endif
|
||||||
if(!reused){
|
if(!reused){
|
||||||
newparam->workerid = (uint64_t)thread;
|
newparam->workerid = (uint64_t)thread;
|
||||||
_3proxy_mutex_lock(&tpool.mutex);
|
_3proxy_mutex_lock(&tpool.mutex);
|
||||||
@ -1719,7 +1752,10 @@ void mainclaim(void *service){
|
|||||||
free(newl);
|
free(newl);
|
||||||
newl = NULL;
|
newl = NULL;
|
||||||
}
|
}
|
||||||
else _3proxy_mutex_init(&newl->open_mutex);
|
else {
|
||||||
|
_3proxy_mutex_init(&newl->open_mutex);
|
||||||
|
_3proxy_mutex_init(&newl->count_mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_3proxy_mutex_lock(&listener_mutex);
|
_3proxy_mutex_lock(&listener_mutex);
|
||||||
for(l = listeners; l; l = l->next){
|
for(l = listeners; l; l = l->next){
|
||||||
@ -1745,6 +1781,7 @@ void mainclaim(void *service){
|
|||||||
_3proxy_mutex_unlock(&listener_mutex);
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
if(newl){
|
if(newl){
|
||||||
_3proxy_mutex_destroy(&newl->open_mutex);
|
_3proxy_mutex_destroy(&newl->open_mutex);
|
||||||
|
_3proxy_mutex_destroy(&newl->count_mutex);
|
||||||
free(newl->cbdest);
|
free(newl->cbdest);
|
||||||
free(newl->netns);
|
free(newl->netns);
|
||||||
free(newl->device);
|
free(newl->device);
|
||||||
@ -1793,6 +1830,7 @@ static void listenerrelease(struct srvparam *srv){
|
|||||||
_3proxy_mutex_unlock(&listener_mutex);
|
_3proxy_mutex_unlock(&listener_mutex);
|
||||||
if(!last) return;
|
if(!last) return;
|
||||||
_3proxy_mutex_destroy(&l->open_mutex);
|
_3proxy_mutex_destroy(&l->open_mutex);
|
||||||
|
_3proxy_mutex_destroy(&l->count_mutex);
|
||||||
if(l->netns) free(l->netns);
|
if(l->netns) free(l->netns);
|
||||||
if(l->device) free(l->device);
|
if(l->device) free(l->device);
|
||||||
if(l->cbdest) free(l->cbdest);
|
if(l->cbdest) free(l->cbdest);
|
||||||
@ -2134,6 +2172,9 @@ void releaseparam(struct clientparam * param) {
|
|||||||
param->next->prev = param->prev;
|
param->next->prev = param->prev;
|
||||||
}
|
}
|
||||||
(param->srv->childcount)--;
|
(param->srv->childcount)--;
|
||||||
|
#ifndef STDMAIN
|
||||||
|
listenercount(param->srv, -1);
|
||||||
|
#endif
|
||||||
/* under the mutex too: srvfree() may destroy the semaphore as
|
/* under the mutex too: srvfree() may destroy the semaphore as
|
||||||
soon as the mutex shows the last child gone */
|
soon as the mutex shows the last child gone */
|
||||||
_3proxy_sem_unlock(param->srv->childslots);
|
_3proxy_sem_unlock(param->srv->childslots);
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
the same address and port takes over the old service's socket, so clients
|
the same address and port takes over the old service's socket, so clients
|
||||||
connecting all the while are never refused nor reset."""
|
connecting all the while are never refused nor reset."""
|
||||||
|
|
||||||
|
import re
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@ -67,6 +68,24 @@ def run(t):
|
|||||||
t.eq(0, done["failed"], "no request is refused or reset by a reload "
|
t.eq(0, done["failed"], "no request is refused or reset by a reload "
|
||||||
f"(first errors: {done['errors']})")
|
f"(first errors: {done['errors']})")
|
||||||
|
|
||||||
|
# the listener counts its clients, whichever service took them: through
|
||||||
|
# the reloads the proxy's socket is one, its count goes on
|
||||||
|
def listener_counts(service):
|
||||||
|
"""The listener counters of the running service of that type: its
|
||||||
|
item has nested ones, acl and clients, before its counters."""
|
||||||
|
page = t.http(f"http://127.0.0.1:{adm}/S").text
|
||||||
|
m = re.search(r"type of the service/client</description><value><!\[CDATA\[" + service +
|
||||||
|
r"\]\].*?<name>listenerclients</name>.*?CDATA\[(\d+)\]"
|
||||||
|
r".*?<name>listenerprocessed</name>.*?CDATA\[(\d+)\]", page, re.S)
|
||||||
|
return (int(m.group(1)), int(m.group(2))) if m else (None, None)
|
||||||
|
|
||||||
|
clients, processed = listener_counts("PROXY")
|
||||||
|
t.eq(0, clients, "no client is connected to the proxy's listener after the run")
|
||||||
|
total = done["ok"] + done["failed"]
|
||||||
|
t.ok(f"its count of clients done with goes on through the reloads ({processed} of {total})") \
|
||||||
|
if processed is not None and processed >= total else \
|
||||||
|
t.fail("its count of clients done with goes on through the reloads", f"at least {total}", processed)
|
||||||
|
|
||||||
# --- UDP: the socket is handed over too, and a session of the old
|
# --- UDP: the socket is handed over too, and a session of the old
|
||||||
# service goes on through it, its datagrams forwarded by the new one ---
|
# service goes on through it, its datagrams forwarded by the new one ---
|
||||||
echo = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
echo = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user