http-server.c

Basic HTTP server and router in C
git clone git://git.finwo.net/lib/http-server.c
Log | Files | Refs | README

commit 97ec78d1e268f68e9971444afa7735994a32ede7
parent f7b0ebc55151031b850e113eeddcbe9e68de3c26
Author: Yersa Nordman <yersa@finwo.nl>
Date:   Thu, 26 Oct 2023 22:10:05 +0200

Add method of shutting down the http server while keeping re-listen functionality

Diffstat:
Mexample.c | 22++++++++++++++++------
Mpackage.ini | 3+--
Msrc/http-server.c | 44+++++++++++++++++++++++++++++++++++++++++---
Msrc/http-server.h | 1+
4 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/example.c b/example.c @@ -1,3 +1,4 @@ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -14,19 +15,24 @@ void onServing(char *addr, uint16_t port, void *udata) { printf("Serving at %s:%d\n", addr, port); } -int ticksHad = 0; +const int countDownOrg = 60; +int countDown = countDownOrg; void onTick(void *udata) { - printf("Tick %d\n", ticksHad); - if (++ticksHad >= 10) { - printf("10 seconds have passed\n"); - ticksHad = 0; + struct http_server_opts *opts = udata; + + // Handle auto-shutdown + printf("Shutdown in %d second(s)\n", --countDown); + if (countDown <= 0) { + opts->shutdown = true; + return; } - struct http_server_opts *opts = udata; + // Handle port re-assign if (opts->port != targetPort) { opts->port = targetPort; fnet_close(opts->listen_connection); } + } void route_get_hello(struct http_server_reqdata *reqdata) { @@ -35,6 +41,7 @@ void route_get_hello(struct http_server_reqdata *reqdata) { reqdata->reqres->response->body->data = strdup("Hello World\n"); reqdata->reqres->response->body->len = strlen(reqdata->reqres->response->body->data); http_server_response_send(reqdata, true); + countDown = countDownOrg; return; } @@ -76,4 +83,7 @@ int main() { http_server_route("POST", "/port" , route_post_port); http_server_main(&opts); + fnet_shutdown(); + + printf("Server has shut down\n"); } diff --git a/package.ini b/package.ini @@ -1,11 +1,10 @@ [dependencies] finwo/fnet=https://github.com/finwo/c-fnet/archive/refs/tags/edge.tar.gz finwo/http-parser=https://github.com/finwo/http-parser/archive/refs/tags/edge.tar.gz - +pierreguillot/thread=https://raw.githubusercontent.com/finwo/dep-repository/main/pierreguillot/thread/package.ini [export] config.mk=config.mk include/finwo/http-server.h=src/http-server.h - [package] deps=lib name=finwo/http-parser diff --git a/src/http-server.c b/src/http-server.c @@ -1,12 +1,33 @@ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "finwo/http-parser.h" #include "finwo/fnet.h" +#include "pierreguillot/thread.h" #include "http-server.h" +#ifndef UNUSED(x) +#define UNUSED(x) (void)x +#endif + +static void sleep_ms(long ms) { +#if defined(__APPLE__) + usleep(ms * 1000); +#elif defined(_WIN32) + Sleep(ms); +#else + time_t sec = (int)(ms / 1000); + const long t = ms -(sec * 1000); + struct timespec req; + req.tv_sec = sec; + req.tv_nsec = t * 1000000L; + while(-1 == nanosleep(&req, &req)); +#endif +} + struct fnet_udata { struct http_server_opts *opts; struct fnet_options_t *fnet_opts; @@ -124,12 +145,20 @@ void _hs_onListenClose(struct fnet_ev *ev) { ludata->opts->listen_connection = fnet_listen(ludata->opts->addr, ludata->opts->port, ludata->fnet_opts); } +void _thread_network(void *arg) { + UNUSED(arg); + FNET_RETURNCODE ret = fnet_main(); +} + void http_server_main(struct http_server_opts *opts) { if (!opts) exit(1); + opts->shutdown = false; + // Prepare http context struct fnet_udata *ludata = calloc(1, sizeof(struct fnet_udata)); ludata->opts = opts; + // Prepare network options struct fnet_options_t fnet_opts = { .proto = FNET_PROTO_TCP, .flags = 0, @@ -141,13 +170,22 @@ void http_server_main(struct http_server_opts *opts) { .udata = ludata, }; - ludata->fnet_opts = &fnet_opts; + // Track network options in http context + ludata->fnet_opts = &fnet_opts; + // Signal that we want our port ludata->opts->listen_connection = fnet_listen(ludata->opts->addr, ludata->opts->port, ludata->fnet_opts); if (!(ludata->opts->listen_connection)) { exit(1); } - // This is a forever function - fnet_main(); + // Launch network management thread + // May or may not keep running (either is fine) + thd_thread thread; + thd_thread_detach(&thread, _thread_network, NULL); + + // This is a forever function, controlled by network thread + while(!opts->shutdown) { + sleep_ms(100); + } } diff --git a/src/http-server.h b/src/http-server.h @@ -16,6 +16,7 @@ struct http_server_opts { char *addr; uint16_t port; void *udata; + bool shutdown; struct fnet_t *listen_connection; };