Re: httpd: multiple addresses for one server

2015-01-03 Thread Reyk Floeter
On Thu, Jan 01, 2015 at 11:54:46PM -0500, Geoff Steckel wrote:
 Is there any way todo the equivalent of:
 
 server an.example.com
 listen on 192.168.2.99
 listen on 2001.fefe.1.1::99
 
 ??
 It appears that the code in parse.y explicitly forbids this
 and the data structures for a server don't *seem*
 to have more than one slot for an address.
 
 Is there another way to achieve this effect?
 From one comment in the checkins, it looks like
 
 server an.example.com
 listen on 192.168.2.99
 .
 server an.example.com
 listen on 2001.fefe.1.1::99
 
 would work.
 
 Duplicating the entire server description is
 difficult to maintain.
 
 Is someone planning to work in this area soon?
 
 thanks
 Geoff Steckel
 

I used include directives to avoid duplications (see previous reply)
but the following diff allows to add aliases and multiple listen
statements.

Reyk

Index: config.c
===
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 config.c
--- config.c21 Dec 2014 00:54:49 -  1.26
+++ config.c3 Jan 2015 13:33:25 -
@@ -174,7 +174,9 @@ config_setserver(struct httpd *env, stru
if ((what  CONFIG_SERVERS) == 0 || id == privsep_process)
continue;
 
-   DPRINTF(%s: sending server \%s[%u]\ to %s fd %d, __func__,
+   DPRINTF(%s: sending %s \%s[%u]\ to %s fd %d, __func__,
+   (srv-srv_conf.flags  SRVFLAG_LOCATION) ?
+   location : server,
srv-srv_conf.name, srv-srv_conf.id,
ps-ps_title[id], srv-srv_s);
 
Index: httpd.conf.5
===
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.40
diff -u -p -u -p -r1.40 httpd.conf.5
--- httpd.conf.528 Dec 2014 13:53:23 -  1.40
+++ httpd.conf.53 Jan 2015 13:33:25 -
@@ -135,6 +135,10 @@ must have a
 .Ar name
 and include one or more lines of the following syntax:
 .Bl -tag -width Ds
+.It Ic alias Ar name
+Specify an additional alias
+.Ar name
+for this server.
 .It Ic connection Ar option
 Set the specified options and limits for HTTP connections.
 Valid options are:
@@ -180,6 +184,7 @@ and defaults to
 .Pa /run/slowcgi.sock .
 .It Ic listen on Ar address Oo Ic tls Oc Ic port Ar number
 Set the listen address and port.
+This statement can be specified multiple times.
 .It Ic location Ar path Brq ...
 Specify server configuration rules for a specific location.
 The
@@ -391,6 +396,13 @@ If the same address is repeated multiple
 statement,
 the server will be matched based on the requested host name.
 .Bd -literal -offset indent
+server www.example.com {
+   alias example.com
+   listen on * port 80
+   listen on * tls port 443
+   root /htdocs/www.example.com
+}
+
 server www.a.example.com {
listen on 203.0.113.1 port 80
root /htdocs/www.a.example.com
Index: parse.y
===
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.46
diff -u -p -u -p -r1.46 parse.y
--- parse.y 21 Dec 2014 00:54:49 -  1.46
+++ parse.y 3 Jan 2015 13:33:26 -
@@ -106,6 +106,8 @@ int  host_if(const char *, struct addre
 int host(const char *, struct addresslist *,
int, struct portrange *, const char *, int);
 voidhost_free(struct addresslist *);
+struct server  *server_inherit(struct server *, const char *,
+   struct server_config *);
 int getservice(char *);
 int is_if_in_group(const char *, const char *);
 
@@ -125,10 +127,10 @@ typedef struct {
 
 %}
 
-%token ACCESS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
+%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
 %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION
 %token LOG LOGDIR MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT
-%token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES 
+%token SACK SERVER SOCKET STYLE SYSLOG TCP TIMEOUT TLS TYPES
 %token ERROR INCLUDE
 %token v.string  STRING
 %token  v.number NUMBER
@@ -247,8 +249,14 @@ server : SERVER STRING {
srv_conf = srv-srv_conf;
 
SPLAY_INIT(srv-srv_clients);
+   TAILQ_INIT(srv-srv_hosts);
+
+   TAILQ_INSERT_TAIL(srv-srv_hosts, srv_conf, entry);
} '{' optnl serveropts_l '}'{
-   struct server   *s = NULL;
+   struct server   *s = NULL, *sn;
+   struct server_config*a, *b;
+
+   srv_conf = srv-srv_conf;
 
TAILQ_FOREACH(s, conf-sc_servers, srv_entry) {
if 

Re: httpd: multiple addresses for one server

2015-01-03 Thread Reyk Floeter
On Sat, Jan 03, 2015 at 04:40:19PM +0100, Christopher Zimmermann wrote:
 Hi,
 
 
 On Sat, 3 Jan 2015 14:42:18 +0100 Reyk Floeter r...@openbsd.org wrote:
 
  On Thu, Jan 01, 2015 at 11:54:46PM -0500, Geoff Steckel wrote:
   Is there any way todo the equivalent of:
   
   server an.example.com
   listen on 192.168.2.99
   listen on 2001.fefe.1.1::99
  
  I used include directives to avoid duplications (see previous reply)
  but the following diff allows to add aliases and multiple listen
  statements.
  
  Reyk
 
 As I understand your diff you will duplicate the entire struct
 server_config for each combination of hostname (alias), listen address
 and location section.

Correct.

 Isn't this overkill?

No, it is just a few K per server block that is allocated once on
startup.  How many aliases do you have?

My tests show that memory usage is not the problem but that there's
indeed a problem with the pre-opened file descriptors for servers;
something that is not necessary with aliases. A simple fix will follow.

 To me it seems like server_config is serving too much purposes here. A
 clean design should split up the struct server_config into one struct
 for connection settings, which will contain a TAILQ of hostnames, a
 TAILQ of listen addresses and a TAILQ of structs with location settings:
 

A clean design should ...

One could argue that a clean design wouldn't try to add too many
layers of indirection that add code complexity and kill some of the
flexibility that we have with the current implementation.  I might
split up struct server_config at some point, when it is really needed,
but it is not the right time.

 TAILQ_HEAD(hostnames, char*);
 
 struct server_config {
   charlocation[NAME_MAX];
   charroot[MAXPATHLEN];
   [...]
 }
 TAILQ_HEAD(server_configs, server_config);
 
 struct serverhost {
   struct sockaddr_storages*srv_ss;
   char*tls_cert;
   int  tcpbufsize;
   [... other connection settings ...]
   struct hostnames*srv_names;
   struct server_configs   *srv_confs;
 }
 
 
 For now you could simply loop over a TAILQ of hostnames in
 server_privinit() and add an inner LOOP in server_response() searching
 for the hostname/aliases.
 
 

Which makes the implementation more complicated, especially the reload
and privsep code, adds additional loops to save a few bytes of memory.

Don't get me wrong - I do care about memory usage - but this is not
per connection - it is persistent during runtime.

Reyk



Re: httpd: multiple addresses for one server

2015-01-03 Thread Christopher Zimmermann
Hi,


On Sat, 3 Jan 2015 14:42:18 +0100 Reyk Floeter r...@openbsd.org wrote:

 On Thu, Jan 01, 2015 at 11:54:46PM -0500, Geoff Steckel wrote:
  Is there any way todo the equivalent of:
  
  server an.example.com
  listen on 192.168.2.99
  listen on 2001.fefe.1.1::99
 
 I used include directives to avoid duplications (see previous reply)
 but the following diff allows to add aliases and multiple listen
 statements.
 
 Reyk

As I understand your diff you will duplicate the entire struct
server_config for each combination of hostname (alias), listen address
and location section. Isn't this overkill?
To me it seems like server_config is serving too much purposes here. A
clean design should split up the struct server_config into one struct
for connection settings, which will contain a TAILQ of hostnames, a
TAILQ of listen addresses and a TAILQ of structs with location settings:

TAILQ_HEAD(hostnames, char*);

struct server_config {
charlocation[NAME_MAX];
charroot[MAXPATHLEN];
[...]
}
TAILQ_HEAD(server_configs, server_config);

struct serverhost {
struct sockaddr_storages*srv_ss;
char*tls_cert;
int  tcpbufsize;
[... other connection settings ...]
struct hostnames*srv_names;
struct server_configs   *srv_confs;
}


For now you could simply loop over a TAILQ of hostnames in
server_privinit() and add an inner LOOP in server_response() searching
for the hostname/aliases.


Christopher



-- 
http://gmerlin.de
OpenPGP: http://gmerlin.de/christopher.pub
F190 D013 8F01 AA53 E080  3F3C F17F B0A1 D44E 4FEE



Re: httpd: multiple addresses for one server

2015-01-03 Thread Reyk Floeter
On Sat, Jan 03, 2015 at 05:19:16PM +0100, Reyk Floeter wrote:
 My tests show that memory usage is not the problem but that there's
 indeed a problem with the pre-opened file descriptors for servers;
 something that is not necessary with aliases. A simple fix will follow.
 

Here is a fix for the previous: in the parent, only open a socket once
per unique listen statement.  This allows to specify many aliases and
listen statements.

Reyk

Index: config.c
===
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.27
diff -u -p -u -p -r1.27 config.c
--- config.c3 Jan 2015 15:49:18 -   1.27
+++ config.c3 Jan 2015 19:32:00 -
@@ -200,7 +200,9 @@ config_setserver(struct httpd *env, stru
n = -1;
proc_range(ps, id, n, m);
for (n = 0; n  m; n++) {
-   if ((fd = dup(srv-srv_s)) == -1)
+   if (srv-srv_s == -1)
+   fd = -1;
+   else if ((fd = dup(srv-srv_s)) == -1)
return (-1);
proc_composev_imsg(ps, id, n,
IMSG_CFG_SERVER, fd, iov, c);
@@ -211,9 +213,6 @@ config_setserver(struct httpd *env, stru
}
}
 
-   close(srv-srv_s);
-   srv-srv_s = -1;
-
return (0);
 }
 
@@ -356,8 +355,12 @@ config_getserver(struct httpd *env, stru
if ((srv = server_byaddr((struct sockaddr *)
srv_conf.ss, srv_conf.port)) != NULL) {
/* Add host to existing listening server */
-   if (imsg-fd != -1)
-   close(imsg-fd);
+   if (imsg-fd != -1) {
+   if (srv-srv_s == -1)
+   srv-srv_s = imsg-fd;
+   else
+   close(imsg-fd);
+   }
return (config_getserver_config(env, srv, imsg));
}
 
Index: parse.y
===
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.48
diff -u -p -u -p -r1.48 parse.y
--- parse.y 3 Jan 2015 16:20:31 -   1.48
+++ parse.y 3 Jan 2015 19:32:01 -
@@ -226,6 +226,7 @@ server  : SERVER STRING {
strlcpy(s-srv_conf.errorlog, HTTPD_ERROR_LOG,
sizeof(s-srv_conf.errorlog));
s-srv_conf.id = ++last_server_id;
+   s-srv_s = -1;
s-srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
s-srv_conf.maxrequests = SERVER_MAXREQUESTS;
s-srv_conf.maxrequestbody = SERVER_MAXREQUESTBODY;
@@ -493,6 +494,7 @@ serveroptsl : LISTEN ON STRING opttls po
/* A location entry uses the parent id */
s-srv_conf.id = srv-srv_conf.id;
s-srv_conf.flags = SRVFLAG_LOCATION;
+   s-srv_s = -1;
memcpy(s-srv_conf.ss, srv-srv_conf.ss,
sizeof(s-srv_conf.ss));
s-srv_conf.port = srv-srv_conf.port;
@@ -1742,6 +1744,8 @@ server_inherit(struct server *src, const
fatal(out of memory);
 
dst-srv_conf.id = ++last_server_id;
+   dst-srv_s = -1;
+
if (last_server_id == INT_MAX) {
yyerror(too many servers defined);
serverconfig_free(dst-srv_conf);
@@ -1806,6 +1810,7 @@ server_inherit(struct server *src, const
sizeof(dstl-srv_conf.ss));
dstl-srv_conf.port = addr-port;
dstl-srv_conf.prefixlen = addr-prefixlen;
+   dstl-srv_s = -1;
 
DPRINTF(adding location \%s\ for \%s[%u]\,
dstl-srv_conf.location,
Index: server.c
===
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.49
diff -u -p -u -p -r1.49 server.c
--- server.c21 Dec 2014 00:54:49 -  1.49
+++ server.c3 Jan 2015 19:32:01 -
@@ -101,11 +101,27 @@ server_shutdown(void)
 int
 server_privinit(struct server *srv)
 {
+   struct server   *s;
+
if (srv-srv_conf.flags  SRVFLAG_LOCATION)
return (0);
 
log_debug(%s: adding server %s, __func__, srv-srv_conf.name);
 
+   /*
+* There's no need to open a new socket if a server with the
+* same address already exists.
+*/
+   TAILQ_FOREACH(s, env-sc_servers, srv_entry) {
+   if (s != srv  s-srv_s != -1 
+   s-srv_conf.port == srv-srv_conf.port 
+   sockaddr_cmp((struct sockaddr *)s-srv_conf.ss,
+   (struct sockaddr *)srv-srv_conf.ss,
+