1
0
mirror of https://github.com/graygnuorg/pound.git synced 2026-10-28 11:35:07 +03:00

Identify listeners and services by their names (labels) as well as indices

* src/config.c (parse_listen_http)
(parse_listen_https): Handle optional listener name.  Make sure it
is unique.
(parse_service): Make sure service name is unique.
* src/pound.h (LISTENER): New field: name.
* src/poundctl.tmpl: Show listener name, if available.
* svc.c (get_nth_listener, get_nth_service)
(get_nth_backend,ctl_getnum): Remove.
(IDENT): New data type.
(ctl_getident,locate_listener,locate_service)
(locate_backend): New functions.
(service_serialize): Represent NULL service name as null.
(listener_serialize): Add listener name.
(ctl_service,ctl_listener): Identify objects by their ids or numbers.

* NEWS: Document changes.
* doc/pound.8: Likewise.
* doc/poundctl.8: Likewise.
This commit is contained in:
Sergey Poznyakoff
2023-03-03 09:26:47 +02:00
parent c7cad5c14e
commit 8f0228551b
7 changed files with 277 additions and 103 deletions

29
NEWS
View File

@@ -1,4 +1,4 @@
Pound -- history of user-visible changes. 2023-02-20
Pound -- history of user-visible changes. 2023-03-03
See the end of file for copying conditions.
Pound is a continuation of the software originally developed by
@@ -113,6 +113,33 @@ Request accessor can be used in all strings where the use of
backreferences is allowed: i.e. arguments to Redirect, ACME,
Error directives, and to all request modification directives described
above.
* Listener labels
Listeners can be assigned symbolic labels. The syntax is:
ListenHTTP "name"
or
ListenHTTPS "name"
The "name" must be unique among all listeners defined in the
configuration.
This symbolic name can be used to identify listener in poundctl
requests (see below).
* Service labels
Service labels must be unique among all services within the
listener (or in the configuration file, for global ones).
* Use of listener and service labels in poundctl
Listeners and services can be identified both by their numbers and
labels. For example:
poundctl list /main/static/1
Version 4.5, 2023-02-12

View File

@@ -14,7 +14,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with pound. If not, see <http://www.gnu.org/licenses/>.
.TH POUND 8 "February 19, 2023" "pound" "System Manager's Manual"
.TH POUND 8 "March 3, 2023" "pound" "System Manager's Manual"
.SH NAME
pound \- HTTP/HTTPS reverse-proxy and load-balancer
.SH SYNOPSIS
@@ -144,6 +144,13 @@ Normally, the connection between
and
.I backends
is via plain HTTP. It is, however, possible to use HTTPS as well.
.PP
A working
.B pound
configuration may define multiple listeners and services. They can be
identified either by 0-based ordinal number within the configuration
(or, for services, within the listener) or by their symbolic name.
Backends are identified by their ordinal number within service.
.SH WORKER MODEL
Each incoming request is processed by a specific \fIworker\fR, i.e. a
thread in the running program. The number of running workers is
@@ -549,12 +556,18 @@ the PID file will remain on disk after termination of the program.
.SH "HTTP Listener"
An HTTP listener defines an address and port that
.B pound
will listen on for HTTP requests. All configuration directives enclosed
between
will listen on for HTTP requests. The listener declaration begins with
the keyword
.B ListenHTTP
on a separate line. The keyword may be followed by a quoted string
supplying listener name. This name is a unique label that identifies
the listener.
.PP
All configuration directives enclosed between
.B ListenHTTP
and
.B End
are specific to a single HTTP listener. At the very least you must specify
are specific to a single HTTP listener. At the very least you must specify
and address and a port for each listener. The following directives are
available:
.TP
@@ -673,10 +686,12 @@ value.
.TP
\fBService\fR [ "name" ]
This defines a private service (see below for service definition
syntax). This service will be used only by this listener. The service
may be optionally named, with the name showing in the
syntax). This service will be used only by this listener. Optional
\fIname\fR supplies the label, that can be used in
.BR poundctl (8)
listings.
requests to identify the service. This label must be unique among all
services within the enclosing listener, or, for global services,
within the configuration.
.TP
\fBACME\fR "directory"
Serve ACME challenge requests from the given
@@ -777,8 +792,14 @@ Same as \fBDeleteHeader\fR.
.SH "HTTPS Listener"
An HTTPS listener defines an address and port that
.B pound
will listen on for HTTPS requests. All configuration directives enclosed
between
will listen on for HTTPS requests. The listener declaration begins with
the keyword
.B ListenHTTPS
on a separate line. The keyword may be followed by a quoted string
supplying listener name. This name is a unique label that identifies
the listener.
.PP
All configuration directives enclosed between
.B ListenHTTPS
and
.B End
@@ -1124,7 +1145,7 @@ statement. Numbering of subexpressions starts at 1 (\fB$0\fR refers
to the entire matching string). Numbering of matches starts
at 0.
.IP
For example, given the following statements
For example, given the following statements
.IP
.EX
Host -re "www\\.(.+)"

View File

@@ -14,7 +14,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with pound. If not, see <http://www.gnu.org/licenses/>.
.TH POUNDCTL 8 "January 13, 2023" "poundctl" "System Manager's Manual"
.TH POUNDCTL 8 "March 3, 2023" "poundctl" "System Manager's Manual"
.SH NAME
poundctl \- control the pound daemon
.SH SYNOPSIS
@@ -62,8 +62,22 @@ All commands take the \fB/\fIL\fB/\fIS\fB/\fIB\fR argument, which
specifies the
.B pound
object to apply the command to. Here, \fIL\fR, \fIS\fR, and \fIB\fR
stand for the numbers of \fIlistener\fR, \fIservice\fR and
\fIbackend\fR, correspondingly. Depending on the command, either
stand for the identifiers of \fIlistener\fR, \fIservice\fR and
\fIbackend\fR, correspondingly. For listeners and services, both
numeric identifiers or symbolic names are allowed. Numeric
identifiers refer to the ordinal number of the listener in the
configuration file, or service within the enclosing listener
(or in the configuration file, if \fIL\fR is \fB\-\fR, see below).
Symbolic names refer to the names assigned with the corresponding
.BR ListenHTTP ,
.BR ListenHTTPS ,
or
.B Service
statement in the configuration file. The identifier \fIB\fR is
always numeric and refers to the ordinal number of the backend in
the service.
.PP
Depending on the command, either
\fIB\fR or both /\fIS\fR/\fIB\fR/ may be omitted. For example,
the following command will disable backend 2 in service 1 of listener 0:
.PP

View File

@@ -45,7 +45,7 @@ typedef unsigned TOKENMASK;
#define T_MASK_ISSET(m,t) ((m) & T_BIT(t))
#define T_ANY 0 /* any token, inlcuding newline */
/* Unquoted character sequence */
#define T_UNQ (T_BIT (T_IDENT) | T_BIT (T_NUMBER) | T_BIT(T_LITERAL))
#define T_UNQ (T_BIT (T_IDENT) | T_BIT (T_NUMBER) | T_BIT (T_LITERAL))
/* Locations in the source file */
struct locus_point
@@ -2650,6 +2650,18 @@ static PARSER_TABLE service_parsetab[] = {
{ NULL }
};
static int
find_service_ident (SERVICE_HEAD *svc_head, char const *name)
{
SERVICE *svc;
SLIST_FOREACH (svc, svc_head, next)
{
if (svc->name && strcmp (svc->name, name) == 0)
return 1;
}
return 0;
}
static int
parse_service (void *call_data, void *section_data)
{
@@ -2672,7 +2684,14 @@ parse_service (void *call_data, void *section_data)
return PARSER_FAIL;
if (tok->type == T_STRING)
svc->name = xstrdup (tok->str);
{
if (find_service_ident (head, tok->str))
{
conf_error ("%s", "service name is not unique");
return PARSER_FAIL;
}
svc->name = xstrdup (tok->str);
}
else
putback_tkn (tok);
@@ -3055,6 +3074,18 @@ listener_alloc (POUND_DEFAULTS *dfl)
return lst;
}
static int
find_listener_ident (LISTENER_HEAD *list_head, char const *name)
{
LISTENER *lstn;
SLIST_FOREACH (lstn, list_head, next)
{
if (lstn->name && strcmp (lstn->name, name) == 0)
return 1;
}
return 0;
}
static int
parse_listen_http (void *call_data, void *section_data)
{
@@ -3062,10 +3093,25 @@ parse_listen_http (void *call_data, void *section_data)
LISTENER_HEAD *list_head = call_data;
POUND_DEFAULTS *dfl = section_data;
struct locus_range range;
struct token *tok;
if ((lst = listener_alloc (dfl)) == NULL)
return PARSER_FAIL;
if ((tok = gettkn_any ()) == NULL)
return PARSER_FAIL;
else if (tok->type == T_STRING)
{
if (find_listener_ident (list_head, tok->str))
{
conf_error ("%s", "listener name is not unique");
return PARSER_FAIL;
}
lst->name = xstrdup (tok->str);
}
else
putback_tkn (tok);
if (parser_loop (http_parsetab, lst, section_data, &range))
return PARSER_FAIL;
@@ -3585,10 +3631,25 @@ parse_listen_https (void *call_data, void *section_data)
struct locus_range range;
POUND_CTX *pc;
struct stringbuf sb;
struct token *tok;
if ((lst = listener_alloc (dfl)) == NULL)
return PARSER_FAIL;
if ((tok = gettkn_any ()) == NULL)
return PARSER_FAIL;
else if (tok->type == T_STRING)
{
if (find_listener_ident (list_head, tok->str))
{
conf_error ("%s", "listener name is not unique");
return PARSER_FAIL;
}
lst->name = xstrdup (tok->str);
}
else
putback_tkn (tok);
lst->ssl_op_enable = SSL_OP_ALL;
#ifdef SSL_OP_NO_COMPRESSION
lst->ssl_op_enable |= SSL_OP_NO_COMPRESSION;

View File

@@ -613,6 +613,7 @@ typedef SLIST_HEAD (,_pound_ctx) POUND_CTX_HEAD;
/* Listener definition */
typedef struct _listener
{
char *name; /* symbolic name */
struct addrinfo addr; /* IPv4/6 address */
int sock; /* listening socket */
POUND_CTX_HEAD ctx_head; /* CTX for SSL connections */

View File

@@ -54,12 +54,12 @@ Listeners:
{{range $lno,$lstn = .listeners -}}
{{printf "%3d" $lno}}. {{block "default.print_listener" $lstn -}}
Listener {{.protocol}}://{{.address}} {{if .enabled}}enabled{{else}}disabled{{end}}
Listener {{if .name}}"{{.name}}" {{end}}{{.protocol}}://{{.address}} {{if .enabled}}enabled{{else}}disabled{{end}}
{{- /* For each listener, show all its services */ -}}
{{block "default.print_services" .services}}
{{- range $sno,$svc = .}}
{{printf "%3d" $sno}}. {{block "default.print_service" $svc -}}
Service "{{.name}}" {{if .enabled}}active{{else}}disabled{{end}} ({{.tot_pri}})
Service {{if .name}}"{{.name}}" {{end}}{{if .enabled}}active{{else}}disabled{{end}} ({{.tot_pri}})
{{- /* List each backend in service */ -}}
{{- range $bno,$backend = .backends}}
{{printf "%3d" $bno}}. {{block "default.print_backend" $backend -}}
@@ -125,7 +125,7 @@ Global services:
{{- /* Iterate over all listeners */ -}}
{{- range $lno,$lstn = .listeners }}
{{block "xml.print_listener" $lstn -}}
<listener index="{{ $lno }}" protocol="{{ .protocol }}" address="{{ .address }}" status="{{if .enabled}}active{{else}}DISABLED{{end}}>
<listener name="{{ .name }}" index="{{ $lno }}" protocol="{{ .protocol }}" address="{{ .address }}" status="{{if .enabled}}active{{else}}DISABLED{{end}}>
{{- block "xml.print_services" .services}}
{{- range $sno,$svc = . }}
{{block "xml.print_service" $svc -}}

218
src/svc.c
View File

@@ -1503,57 +1503,6 @@ get_param (char const *url, char const *param, size_t *ret_len)
return p;
}
/*
* return listener by its number
*/
static LISTENER *
get_nth_listener (int n)
{
LISTENER *lstn;
SLIST_FOREACH (lstn, &listeners, next)
{
if (n == 0)
break;
n--;
}
return lstn;
}
/*
* return service by its number
*/
static SERVICE *
get_nth_service (LISTENER *lstn, int n)
{
SERVICE *svc;
SERVICE_HEAD *head = lstn ? &lstn->services : &services;
SLIST_FOREACH (svc, head, next)
{
if (n == 0)
break;
n--;
}
return svc;
}
/*
* select a back-end by number
*/
static BACKEND *
get_nth_backend (SERVICE *svc, int n)
{
BACKEND *be;
SLIST_FOREACH (be, &svc->backends, next)
{
if (n == 0)
break;
n--;
}
return be;
}
static int
session_backend_index (SESSION *sess)
{
@@ -1821,7 +1770,7 @@ service_serialize (SERVICE *svc)
pthread_mutex_lock (&svc->mut);
typename = sess_type_to_str (svc->sess_type);
if (json_object_set (obj, "name", json_new_string (svc->name ? svc->name : ""))
if ((svc->name && json_object_set (obj, "name", json_new_string (svc->name)))
|| json_object_set (obj, "enabled", json_new_bool (!svc->disabled))
|| json_object_set (obj, "tot_pri", json_new_integer (svc->tot_pri))
|| json_object_set (obj, "abs_pri", json_new_integer (svc->abs_pri))
@@ -1850,7 +1799,9 @@ listener_serialize (LISTENER *lstn)
{
int err = 0;
err = json_object_set (obj, "address", addrinfo_serialize (&lstn->addr));
if (lstn->name)
err = json_object_set (obj, "name", json_new_string (lstn->name));
err |= json_object_set (obj, "address", addrinfo_serialize (&lstn->addr));
is_https = !SLIST_EMPTY (&lstn->ctx_head);
err |= json_object_set (obj, "protocol",
@@ -2050,26 +2001,139 @@ control_list_all (BIO *c, char const *url)
return rc;
}
static int
ctl_getnum (char const **url)
/*
* Type of object identifier.
*/
enum
{
IDTYPE_ERR = -1,
IDTYPE_NUM, /* Numeric identifier (index). */
IDTYPE_STR /* String identifier (name). */
};
/*
* Object identifier.
*/
typedef struct
{
int type; /* Identifier type. */
union
{
int n; /* Index value (IDTYPE_NUM). */
struct /* String value (IDTYPE_STR). */
{
int len; /* Name length. */
char const *name; /* Pointer to the name. */
} s;
};
} IDENT;
/*
* Get single identifier from the URL (must point to a /, on entry).
* On success, advance URL to the point past the end of the identifier
* (normally next / or end of URL).
* On error, return ident with its type set to IDTYPE_ERR.
*/
static IDENT
ctl_getident (char const **url)
{
IDENT retval = { IDTYPE_ERR };
long n;
char *end;
if (**url == '/')
++*url;
else
return -1;
errno = 0;
n = strtol (*url, &end, 10);
if (errno || n > INT_MAX)
return -1;
if (*end != 0 && *end != '/' && *end != '?')
return -1;
*url = end;
return n;
{
++*url;
errno = 0;
n = strtol (*url, &end, 10);
if (*end != 0 && *end != '/' && *end != '?')
{
end = (char*) *url + strcspn (*url, "/?");
retval.s.name = *url;
retval.s.len = end - *url;
retval.type = IDTYPE_STR;
*url = end;
}
else if (errno == 0 && n <= INT_MAX)
{
retval.type = IDTYPE_NUM;
retval.n = n;
*url = end;
}
}
return retval;
}
/*
* Locate listener identified by ID.
*/
static LISTENER *
locate_listener (IDENT id)
{
LISTENER *lstn = NULL;
if (id.type != IDTYPE_ERR)
{
long n = 0;
SLIST_FOREACH (lstn, &listeners, next)
{
if ((id.type == IDTYPE_NUM && n == id.n) ||
(lstn->name && strlen (lstn->name) == id.s.len &&
memcmp (lstn->name, id.s.name, id.s.len) == 0))
break;
n++;
}
}
return lstn;
}
/*
* Locate service identified by ID. If LSTN is null, look it up in the
* global service list.
*/
static SERVICE *
locate_service (LISTENER *lstn, IDENT id)
{
SERVICE *svc = 0;
if (id.type != IDTYPE_ERR)
{
int n = 0;
SERVICE_HEAD *head = lstn ? &lstn->services : &services;
SLIST_FOREACH (svc, head, next)
{
if ((id.type == IDTYPE_NUM && n == id.n) ||
(svc->name && strlen (svc->name) == id.s.len &&
memcmp (svc->name, id.s.name, id.s.len) == 0))
break;
n++;
}
}
return svc;
}
/*
* Locate backend identified by ID. Only IDTYPE_NUM is supported.
*/
static BACKEND *
locate_backend (SERVICE *svc, IDENT id)
{
BACKEND *be = NULL;
if (id.type == IDTYPE_NUM)
{
long n = 0;
SLIST_FOREACH (be, &svc->backends, next)
{
if (n == id.n)
break;
n++;
}
}
return be;
}
enum object_type
{
OBJ_LISTENER,
@@ -2093,12 +2157,9 @@ typedef int (*OBJHANDLER) (BIO *, OBJECT *, char const *, void *);
static int
ctl_backend (OBJHANDLER func, void *data, BIO *c, char const *url, SERVICE *svc)
{
int n;
OBJECT obj = { .type = OBJ_BACKEND };
if ((n = ctl_getnum (&url)) < 0)
return HTTP_STATUS_NOT_FOUND;
if ((obj.be = get_nth_backend (svc, n)) == NULL)
if ((obj.be = locate_backend (svc, ctl_getident (&url))) == NULL)
return HTTP_STATUS_NOT_FOUND;
return func (c, &obj, url, data);
}
@@ -2106,13 +2167,9 @@ ctl_backend (OBJHANDLER func, void *data, BIO *c, char const *url, SERVICE *svc)
static int
ctl_service (OBJHANDLER func, void *data, BIO *c, char const *url, LISTENER *lstn)
{
int n;
OBJECT obj = { .type = OBJ_SERVICE };
if ((n = ctl_getnum (&url)) < 0)
return HTTP_STATUS_NOT_FOUND;
if ((obj.svc = get_nth_service (lstn, n)) == NULL)
if ((obj.svc = locate_service (lstn, ctl_getident (&url))) == NULL)
return HTTP_STATUS_NOT_FOUND;
if (*url && *url != '?')
@@ -2123,7 +2180,6 @@ ctl_service (OBJHANDLER func, void *data, BIO *c, char const *url, LISTENER *lst
static int
ctl_listener (OBJHANDLER func, void *data, BIO *c, char const *url)
{
int n;
OBJECT obj = { .type = OBJ_LISTENER };
size_t len = strcspn (url, "?");
@@ -2137,14 +2193,8 @@ ctl_listener (OBJHANDLER func, void *data, BIO *c, char const *url)
obj.lstn = NULL;
url += 2;
}
else
{
if ((n = ctl_getnum (&url)) < 0)
return HTTP_STATUS_NOT_FOUND;
if ((obj.lstn = get_nth_listener (n)) == NULL)
return HTTP_STATUS_NOT_FOUND;
}
else if ((obj.lstn = locate_listener (ctl_getident (&url))) == NULL)
return HTTP_STATUS_NOT_FOUND;
if (*url && *url != '?')
return ctl_service (func, data, c, url, obj.lstn);