feat: multiple listen

Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
main^2
Jianhui Zhao 2 years ago
parent 175f7c8f55
commit 9166d3ab80
  1. 32
      example/multi_process_server.c
  2. 34
      example/multi_process_server_reuseport.c
  3. 34
      example/simple_server.c
  4. 2
      gen_cert.sh
  5. 5
      src/connection.c
  6. 2
      src/connection.h
  7. 241
      src/uhttpd.c
  8. 14
      src/uhttpd.h
  9. 17
      src/uhttpd_internal.h

@ -59,9 +59,8 @@ static void usage(const char *prog)
fprintf(stderr, "Usage: %s [option]\n"
" -h docroot # Document root, default is .\n"
" -i index_page # Index page, default is index.html\n"
" -a addr # Default addr is localhost\n"
" -p port # Default port is 8080\n"
" -s # SSl on\n"
" -a addr # address to listen\n"
" -s addr # address to listen with ssl\n"
" -P # plugin path\n"
" -w # worker process number, default is equal to available CPUs\n"
" -v # verbose\n", prog);
@ -75,16 +74,17 @@ int main(int argc, char **argv)
struct uh_server *srv = NULL;
const char *plugin_path = NULL;
bool verbose = false;
bool ssl = false;
const char *docroot = ".";
const char *index_page = "index.html";
const char *addr = "localhost";
pid_t workers[MAX_WORKER] = {};
int nworker = get_nprocs();
int port = 8080;
int opt, i;
while ((opt = getopt(argc, argv, "h:i:a:p:sfP:w:v")) != -1) {
srv = uh_server_new(loop);
if (!srv)
return -1;
while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) {
switch (opt) {
case 'h':
docroot = optarg;
@ -93,13 +93,12 @@ int main(int argc, char **argv)
index_page = optarg;
break;
case 'a':
addr = optarg;
break;
case 'p':
port = atoi(optarg);
if (srv->listen(srv, optarg, false) < 1)
goto err;
break;
case 's':
ssl = true;
case 's':
if (srv->listen(srv, optarg, true) < 1)
goto err;
break;
case 'P':
plugin_path = optarg;
@ -124,13 +123,8 @@ int main(int argc, char **argv)
signal(SIGPIPE, SIG_IGN);
srv = uh_server_new(loop, addr, port);
if (!srv)
return -1;
#if UHTTPD_SSL_SUPPORT
if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
goto err;
srv->ssl_init(srv, "cert.pem", "key.pem");
#endif
srv->set_docroot(srv, docroot);

@ -58,28 +58,37 @@ static void usage(const char *prog)
fprintf(stderr, "Usage: %s [option]\n"
" -h docroot # Document root, default is .\n"
" -i index_page # Index page, default is index.html\n"
" -a addr # Default addr is localhost\n"
" -p port # Default port is 8080\n"
" -s # SSl on\n"
" -a addr # address to listen\n"
" -s addr # address to listen with ssl\n"
" -P # plugin path\n"
" -w # worker process number, default is equal to available CPUs\n"
" -v # verbose\n", prog);
exit(1);
}
static void start_server(const char *addr, int port, const char *docroot, const char *index_page, const char *plugin, bool ssl)
static void start_server(const char *addr, const char *addrs, const char *docroot, const char *index_page, const char *plugin, bool ssl)
{
struct ev_loop *loop = ev_loop_new(0);
struct uh_server *srv = NULL;
signal(SIGPIPE, SIG_IGN);
srv = uh_server_new(loop, addr, port);
srv = uh_server_new(loop);
if (!srv)
return;
if (addr) {
if (srv->listen(srv, addrs, false) < 0)
return;
} else if (addrs) {
if (srv->listen(srv, addrs, true) < 0)
return;
} else {
return;
}
#if UHTTPD_SSL_SUPPORT
if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
if (ssl && srv->ssl_init(srv, "cert.pem", "key.pem") < 0)
return;
#endif
@ -105,13 +114,13 @@ int main(int argc, char **argv)
bool ssl = false;
const char *docroot = ".";
const char *index_page = "index.html";
const char *addr = "localhost";
const char *addr = NULL;
const char *addrs = NULL;
pid_t workers[MAX_WORKER] = {};
int nworker = get_nprocs();
int port = 8080;
int opt, i;
while ((opt = getopt(argc, argv, "h:i:a:p:sP:w:v")) != -1) {
while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) {
switch (opt) {
case 'h':
docroot = optarg;
@ -122,11 +131,8 @@ int main(int argc, char **argv)
case 'a':
addr = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 's':
ssl = true;
addrs = optarg;
break;
case 'P':
plugin_path = optarg;
@ -164,7 +170,7 @@ int main(int argc, char **argv)
if (pid == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL);
start_server(addr, port, docroot, index_page, plugin_path, ssl);
start_server(addr, addrs, docroot, index_page, plugin_path, ssl);
return 0;
}

@ -48,9 +48,8 @@ static void usage(const char *prog)
fprintf(stderr, "Usage: %s [option]\n"
" -h docroot # Document root, default is .\n"
" -i index_page # Index page, default is index.html\n"
" -a addr # Default addr is localhost\n"
" -p port # Default port is 8080\n"
" -s # SSl on\n"
" -a addr # address to listen\n"
" -s addr # address to listen with ssl\n"
" -P # plugin path\n"
" -v # verbose\n", prog);
exit(1);
@ -63,14 +62,15 @@ int main(int argc, char **argv)
struct uh_server *srv = NULL;
const char *plugin_path = NULL;
bool verbose = false;
bool ssl = false;
const char *docroot = ".";
const char *index_page = "index.html";
const char *addr = "localhost";
int port = 8080;
int opt;
while ((opt = getopt(argc, argv, "h:i:a:p:sP:v")) != -1) {
srv = uh_server_new(loop);
if (!srv)
return -1;
while ((opt = getopt(argc, argv, "h:i:a:s:P:v")) != -1) {
switch (opt) {
case 'h':
docroot = optarg;
@ -79,13 +79,12 @@ int main(int argc, char **argv)
index_page = optarg;
break;
case 'a':
addr = optarg;
break;
case 'p':
port = atoi(optarg);
if (srv->listen(srv, optarg, false) < 1)
goto err;
break;
case 's':
ssl = true;
case 's':
if (srv->listen(srv, optarg, true) < 1)
goto err;
break;
case 'P':
plugin_path = optarg;
@ -93,7 +92,7 @@ int main(int argc, char **argv)
case 'v':
verbose = true;
break;
default: /* '?' */
default:
usage(argv[0]);
}
}
@ -105,13 +104,8 @@ int main(int argc, char **argv)
signal(SIGPIPE, SIG_IGN);
srv = uh_server_new(loop, addr, port);
if (!srv)
return -1;
#if UHTTPD_SSL_SUPPORT
if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
goto err;
srv->ssl_init(srv, "cert.pem", "key.pem");
#endif
srv->set_docroot(srv, docroot);

@ -1,3 +1,3 @@
#!/bin/sh
openssl req -x509 -newkey rsa:2048 -nodes -keyout server-key.pem -out server-cert.pem -subj "/C=CZ/O=Acme Inc./OU=ACME/CN=ACME-DEV-123"
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -subj "/C=CZ/O=Acme Inc./OU=ACME/CN=ACME-DEV-123"

@ -794,8 +794,9 @@ static void conn_init_cb(struct uh_connection *conn)
conn->close = conn_close;
}
struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv, int sock, struct sockaddr *addr)
struct uh_connection_internal *uh_new_connection(struct uh_listener *l, int sock, struct sockaddr *addr)
{
struct uh_server_internal *srv = l->srv;
struct uh_connection_internal *conn;
conn = calloc(1, sizeof(struct uh_connection_internal));
@ -822,7 +823,7 @@ struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv,
ev_timer_start(srv->loop, &conn->timer);
#if UHTTPD_SSL_SUPPORT
if (srv->ssl_ctx)
if (l->ssl)
conn->ssl = uh_ssl_new(srv->ssl_ctx, sock);
#endif

@ -97,7 +97,7 @@ struct uh_connection_internal {
void (*handler)(struct uh_connection *conn, int event);
};
struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv, int sock, struct sockaddr *addr);
struct uh_connection_internal *uh_new_connection(struct uh_listener *l, int sock, struct sockaddr *addr);
void conn_free(struct uh_connection_internal *conn);

@ -42,15 +42,11 @@ static void uh_server_free(struct uh_server *srv)
struct uh_server_internal *srvi = (struct uh_server_internal *)srv;
struct uh_connection_internal *conn = srvi->conns;
struct uh_path_handler *h = srvi->handlers;
struct uh_listener *l = srvi->listeners;
#ifdef HAVE_DLOPEN
struct uh_plugin *p = srvi->plugins;
#endif
ev_io_stop(srvi->loop, &srvi->ior);
if (srvi->sock > 0)
close(srvi->sock);
if (srvi->docroot)
free(srvi->docroot);
@ -69,6 +65,18 @@ static void uh_server_free(struct uh_server *srv)
free(temp);
}
while (l) {
struct uh_listener *temp = l;
ev_io_stop(srvi->loop, &l->ior);
if (l->sock > 0)
close(l->sock);
l = l->next;
free(temp);
}
#ifdef HAVE_DLOPEN
while (p) {
struct uh_plugin *temp = p;
@ -85,7 +93,8 @@ static void uh_server_free(struct uh_server *srv)
static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
{
struct uh_server_internal *srv = container_of(w, struct uh_server_internal, ior);
struct uh_listener *l = container_of(w, struct uh_listener, ior);
struct uh_server_internal *srv = l->srv;
struct uh_connection_internal *conn;
union {
struct sockaddr sa;
@ -97,7 +106,7 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
int port;
int sock;
sock = accept4(srv->sock, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
sock = accept4(l->sock, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (sock < 0) {
if (errno != EAGAIN)
uh_log_err("accept: %s\n", strerror(errno));
@ -109,7 +118,21 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
uh_log_debug("New Connection from: %s %d\n", addr_str, port);
}
conn = uh_new_connection(srv, sock, &addr.sa);
if (l->ssl) {
#if UHTTPD_SSL_SUPPORT
if (!srv->ssl_ctx) {
uh_log_err("SSL not initialized\n");
close(sock);
return;
}
#else
close(sock);
uh_log_err("SSL not enabled when build\n");
return;
#endif
}
conn = uh_new_connection(l, sock, &addr.sa);
if (!conn)
return;
@ -123,7 +146,7 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
srv->conns = conn;
}
struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port)
struct uh_server *uh_server_new(struct ev_loop *loop)
{
struct uh_server *srv;
@ -133,10 +156,7 @@ struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port
return NULL;
}
if (uh_server_init(srv, loop, host, port) < 0) {
free(srv);
return NULL;
}
uh_server_init(srv, loop);
return srv;
}
@ -279,93 +299,155 @@ static struct ev_loop *uh_get_loop(struct uh_server *srv)
return srvi->loop;
}
int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port)
static int parse_address(const char *addr, char **host, char **port)
{
static char buf[256];
char *s;
int l;
strcpy(buf, addr);
*host = NULL;
*port = buf;
s = strrchr(buf, ':');
if (!s)
return -1;
*host = buf;
*port = s + 1;
*s = 0;
if (*host && **host == '[') {
l = strlen(*host);
if (l >= 2) {
(*host)[l - 1] = 0;
(*host)++;
}
}
if ((*host)[0] == '\0')
*host = "0";
return 0;
}
static int uh_server_listen(struct uh_server *srv, const char *addr, bool ssl)
{
struct uh_server_internal *srvi = (struct uh_server_internal *)srv;
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr;
struct uh_listener *l;
char *host, *port;
struct addrinfo *addrs = NULL, *p = NULL;
static struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE,
};
char addr_str[INET6_ADDRSTRLEN];
socklen_t addrlen;
int sock = -1;
int bound = 0;
int on = 1;
int status;
int sock;
if (!host || *host == '\0') {
addr.sin.sin_family = AF_INET;
addr.sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (parse_address(addr, &host, &port) < 0) {
uh_log_err("invalid address\n");
return -1;
}
if (inet_pton(AF_INET, host, &addr.sin.sin_addr) == 1) {
addr.sa.sa_family = AF_INET;
} else if (inet_pton(AF_INET6, host, &addr.sin6.sin6_addr) == 1) {
addr.sa.sa_family = AF_INET6;
} else {
static struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE
};
struct addrinfo *ais;
int status;
status = getaddrinfo(host, NULL, &hints, &ais);
if (status != 0) {
uh_log_err("getaddrinfo(): %s\n", gai_strerror(status));
return -1;
status = getaddrinfo(host, port, &hints, &addrs);
if (status != 0) {
uh_log_err("getaddrinfo(): %s\n", gai_strerror(status));
return -1;
}
/* try to bind a new socket to each found address */
for (p = addrs; p; p = p->ai_next) {
sock = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, p->ai_protocol);
if (sock < 0) {
uh_log_err("socket: %s\n", strerror(errno));
continue;
}
memcpy(&addr, ais->ai_addr, ais->ai_addrlen);
freeaddrinfo(ais);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) < 0) {
uh_log_err("setsockopt: %s\n", strerror(errno));
goto err;
}
if (addr.sa.sa_family == AF_INET) {
addr.sin.sin_port = ntohs(port);
addrlen = sizeof(addr.sin);
inet_ntop(AF_INET, &addr.sin.sin_addr, addr_str, sizeof(addr_str));
} else {
addr.sin6.sin6_port = ntohs(port);
addrlen = sizeof(addr.sin6);
inet_ntop(AF_INET6, &addr.sin6.sin6_addr, addr_str, sizeof(addr_str));
}
/* required to get parallel v4 + v6 working */
if (p->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int)) < 0) {
uh_log_err("setsockopt: %s\n", strerror(errno));
goto err;
}
sock = socket(addr.sa.sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock < 0) {
uh_log_err("socket: %s\n", strerror(errno));
return -1;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) < 0) {
uh_log_err("setsockopt: %s\n", strerror(errno));
goto err;
}
if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) {
uh_log_err("bind: %s\n", strerror(errno));
goto err;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
if (listen(sock, SOMAXCONN) < 0) {
uh_log_err("bind: %s\n", strerror(errno));
goto err;
}
if (bind(sock, &addr.sa, addrlen) < 0) {
uh_log_err("bind: %s\n", strerror(errno));
goto err;
}
l = calloc(1, sizeof(struct uh_listener));
if (!l) {
uh_log_err("calloc: %s\n", strerror(errno));
goto err;
}
if (listen(sock, SOMAXCONN) < 0) {
uh_log_err("bind: %s\n", strerror(errno));
goto err;
}
l->sock = sock;
l->ssl = ssl;
l->srv = srvi;
if (uh_log_get_threshold() == LOG_DEBUG) {
saddr2str(&addr.sa, addr_str, sizeof(addr_str), &port);
uh_log_debug("Listen on: %s %d\n", addr_str, port);
ev_io_init(&l->ior, uh_accept_cb, sock, EV_READ);
ev_io_start(srvi->loop, &l->ior);
if (!srvi->listeners) {
srvi->listeners = l;
} else {
l->next = srvi->listeners;
srvi->listeners = l;
}
if (p->ai_family == AF_INET) {
struct sockaddr_in *ina = (struct sockaddr_in *)p->ai_addr;
inet_ntop(p->ai_family, &ina->sin_addr, addr_str, sizeof(addr_str));
uh_log_debug("Listen on: %s:%d with ssl %s\n", addr_str, ntohs(ina->sin_port), ssl ? "on" : "off");
} else {
struct sockaddr_in6 *in6a = (struct sockaddr_in6 *)p->ai_addr;
inet_ntop(p->ai_family, &in6a->sin6_addr, addr_str, sizeof(addr_str));
uh_log_debug("Listen on: [%s]:%d with ssl %s\n", addr_str, ntohs(in6a->sin6_port), ssl ? "on" : "off");
}
bound++;
continue;
err:
if (sock > -1)
close(sock);
}
freeaddrinfo(addrs);
return bound;
}
void uh_server_init(struct uh_server *srv, struct ev_loop *loop)
{
struct uh_server_internal *srvi = (struct uh_server_internal *)srv;
memset(srvi, 0, sizeof(struct uh_server_internal));
srvi->loop = loop ? loop : EV_DEFAULT;
srvi->sock = sock;
srv->get_loop = uh_get_loop;
srv->free = uh_server_free;
srv->listen = uh_server_listen;
#if UHTTPD_SSL_SUPPORT
srv->ssl_init = uh_server_ssl_init;
#endif
@ -378,13 +460,4 @@ int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host
srv->set_docroot = uh_set_docroot;
srv->set_index_page = uh_set_index_page;
ev_io_init(&srvi->ior, uh_accept_cb, sock, EV_READ);
ev_io_start(srvi->loop, &srvi->ior);
return 0;
err:
close(sock);
return -1;
}

@ -89,6 +89,14 @@ typedef void (*uh_path_handler_prototype)(struct uh_connection *conn, int event)
struct uh_server {
struct ev_loop *(*get_loop)(struct uh_server *srv);
void (*free)(struct uh_server *srv);
/*
** listen an address, multiple call allowed
** returns the number of successful listen
**
** :80 0:80 0.0.0.0:80 [::]:80
** localhost:80 [::1]:80
*/
int (*listen)(struct uh_server *srv, const char *addr, bool ssl);
#if UHTTPD_SSL_SUPPORT
int (*ssl_init)(struct uh_server *srv, const char *cert, const char *key);
#endif
@ -120,11 +128,9 @@ struct uh_path_handler {
/*
* uh_server_new - creat an uh_server struct and init it
* @loop: If NULL will use EV_DEFAULT
* @host: If NULL will listen on "0.0.0.0"
* @port: port to listen on
*/
struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port);
struct uh_server *uh_server_new(struct ev_loop *loop);
int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port);
void uh_server_init(struct uh_server *srv, struct ev_loop *loop);
#endif

@ -29,15 +29,23 @@
#include "uhttpd.h"
struct uh_server_internal;
struct uh_connection_internal;
struct uh_listener {
int sock;
bool ssl;
struct ev_io ior;
struct uh_server_internal *srv;
struct uh_listener *next;
};
struct uh_server_internal {
struct uh_server com;
int sock;
char *docroot;
char *index_page;
struct ev_loop *loop;
struct ev_io ior;
struct uh_listener *listeners;
struct uh_connection_internal *conns;
void (*conn_closed_cb)(struct uh_connection *conn);
void (*default_handler)(struct uh_connection *conn, int event);
@ -48,9 +56,4 @@ struct uh_server_internal {
struct uh_path_handler *handlers;
};
struct worker {
struct ev_child w;
int i;
};
#endif
Loading…
Cancel
Save