client state machine: function pointers instead of enum+switch

This commit is contained in:
Omar Polo 2021-02-01 20:00:33 +00:00
parent 1e7591a922
commit 112802ea31
2 changed files with 26 additions and 68 deletions

16
gmid.h
View File

@ -124,22 +124,16 @@ struct parser {
const char *err;
};
enum {
S_HANDSHAKE,
S_OPEN,
S_INITIALIZING,
S_SENDING_FILE,
S_SENDING_DIR,
S_SENDING_CGI,
S_CLOSING,
};
struct client;
typedef void (*statefn)(struct pollfd*, struct client*);
struct client {
struct tls *ctx;
char req[GEMINI_URL_LEN];
struct iri iri;
char domain[DOMAIN_NAME_LEN];
int state, next;
statefn state, next;
int code;
const char *meta;
int fd, waiting_on_child;
@ -209,6 +203,7 @@ void mark_nonblock(int);
void handle_handshake(struct pollfd*, struct client*);
void handle_open_conn(struct pollfd*, struct client*);
void start_reply(struct pollfd*, struct client*, int, const char*);
void handle_start_reply(struct pollfd*, struct client*);
void start_cgi(const char*, const char*, struct pollfd*, struct client*);
void send_file(struct pollfd*, struct client*);
void open_dir(struct pollfd*, struct client*);
@ -220,7 +215,6 @@ void cgi_poll_on_client(struct pollfd*, struct client*);
void handle_cgi(struct pollfd*, struct client*);
void close_conn(struct pollfd*, struct client*);
void do_accept(int, struct tls*, struct pollfd*, struct client*);
void handle(struct pollfd*, struct client*);
void loop(struct tls*, int, int);
/* ex.c */

View File

@ -195,7 +195,7 @@ load_file(struct pollfd *fds, struct client *c)
return;
}
c->i = c->buf;
c->next = S_SENDING_FILE;
c->next = send_file;
start_reply(fds, c, SUCCESS, mime(c->host, c->iri.path));
}
@ -294,7 +294,7 @@ handle_handshake(struct pollfd *fds, struct client *c)
/* h->domain != NULL ? h->domain : "(null)"); */
if (h->domain != NULL) {
c->state = S_OPEN;
c->state = handle_open_conn;
c->host = h;
handle_open_conn(fds, c);
return;
@ -353,20 +353,25 @@ handle_open_conn(struct pollfd *fds, struct client *c)
void
start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
{
c->code = code;
c->meta = meta;
c->state = handle_start_reply;
c->state(pfd, c);
}
void
handle_start_reply(struct pollfd *pfd, struct client *c)
{
char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */
const char *lang;
size_t len;
c->code = code;
c->meta = meta;
c->state = S_INITIALIZING;
lang = vhost_lang(c->host, c->iri.path);
snprintf(buf, sizeof(buf), "%d ", code);
strlcat(buf, meta, sizeof(buf));
if (!strcmp(meta, "text/gemini") && lang != NULL) {
snprintf(buf, sizeof(buf), "%d ", c->code);
strlcat(buf, c->meta, sizeof(buf));
if (!strcmp(c->meta, "text/gemini") && lang != NULL) {
strlcat(buf, "; lang=", sizeof(buf));
strlcat(buf, lang, sizeof(buf));
}
@ -396,7 +401,7 @@ start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
/* advance the state machine */
c->state = c->next;
handle(pfd, c);
c->state(pfd, c);
}
void
@ -439,7 +444,7 @@ start_cgi(const char *spath, const char *relpath,
start_reply(fds, c, TEMP_FAILURE, "internal server error");
return;
}
c->state = S_SENDING_CGI;
c->state = handle_cgi;
cgi_poll_on_child(fds, c);
c->code = -1;
/* handle_cgi(fds, c); */
@ -455,9 +460,6 @@ send_file(struct pollfd *fds, struct client *c)
{
ssize_t ret, len;
/* ensure the correct state */
c->state = S_SENDING_FILE;
len = (c->buf + c->len) - c->i;
while (len > 0) {
@ -542,7 +544,7 @@ open_dir(struct pollfd *fds, struct client *c)
}
c->fd = dirfd;
c->next = S_SENDING_DIR;
c->next = send_directory_listing;
if ((c->dir = fdopendir(c->fd)) == NULL) {
LOGE(c, "can't fdopendir(%d) (vhost:%s) %s: %s",
@ -762,7 +764,7 @@ end:
void
close_conn(struct pollfd *pfd, struct client *c)
{
c->state = S_CLOSING;
c->state = close_conn;
switch (tls_close(c->ctx)) {
case TLS_WANT_POLLIN:
@ -816,8 +818,8 @@ do_accept(int sock, struct tls *ctx, struct pollfd *fds, struct client *clients)
fds[i].fd = fd;
fds[i].events = POLLIN;
clients[i].state = S_HANDSHAKE;
clients[i].next = S_SENDING_FILE;
clients[i].state = handle_handshake;
clients[i].next = send_file;
clients[i].fd = -1;
clients[i].waiting_on_child = 0;
clients[i].buf = MAP_FAILED;
@ -832,44 +834,6 @@ do_accept(int sock, struct tls *ctx, struct pollfd *fds, struct client *clients)
close(fd);
}
void
handle(struct pollfd *fds, struct client *client)
{
switch (client->state) {
case S_HANDSHAKE:
handle_handshake(fds, client);
break;
case S_OPEN:
handle_open_conn(fds, client);
break;
case S_INITIALIZING:
start_reply(fds, client, client->code, client->meta);
break;
case S_SENDING_FILE:
send_file(fds, client);
break;
case S_SENDING_DIR:
send_directory_listing(fds, client);
break;
case S_SENDING_CGI:
handle_cgi(fds, client);
break;
case S_CLOSING:
close_conn(fds, client);
break;
default:
/* unreachable */
abort();
}
}
void
loop(struct tls *ctx, int sock4, int sock6)
{
@ -920,7 +884,7 @@ loop(struct tls *ctx, int sock4, int sock6)
else if (fds[i].fd == sock6)
do_accept(sock6, ctx, fds, clients);
else
handle(&fds[i], &clients[i]);
clients[i].state(&fds[i], &clients[i]);
}
}
}