diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | examples/Makefile | 1 | ||||
| -rw-r--r-- | examples/async/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | examples/async/Makefile | 29 | ||||
| -rw-r--r-- | examples/async/README.md | 14 | ||||
| -rw-r--r-- | examples/async/main.c | 232 |
6 files changed, 281 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3370acb..b64430c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.13) +add_subdirectory(async) add_subdirectory(form) add_subdirectory(headers) add_subdirectory(hello) diff --git a/examples/Makefile b/examples/Makefile index b06f0a5..688895a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,6 +1,7 @@ .POSIX: DIRS = \ + async \ form \ headers \ hello \ diff --git a/examples/async/CMakeLists.txt b/examples/async/CMakeLists.txt new file mode 100644 index 0000000..4afd5ba --- /dev/null +++ b/examples/async/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.13) +project(async C) +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} PRIVATE web) diff --git a/examples/async/Makefile b/examples/async/Makefile new file mode 100644 index 0000000..d0a42c2 --- /dev/null +++ b/examples/async/Makefile @@ -0,0 +1,29 @@ +.POSIX: + +PROJECT = async +DEPS = \ + main.o +LIBWEB = ../../libweb.a +DYNSTR = ../../dynstr/libdynstr.a +CFLAGS = -I ../../include -I ../../dynstr/include +LIBWEB_FLAGS = -L ../../ -l web +DYNSTR_FLAGS = -L ../../dynstr -l dynstr + +all: $(PROJECT) + +clean: + rm -f $(DEPS) + +distclean: clean + rm -f $(PROJECT) + +FORCE: + +$(PROJECT): $(DEPS) $(LIBWEB) $(DYNSTR) + $(CC) $(LDFLAGS) $(DEPS) $(LIBWEB_FLAGS) $(DYNSTR_FLAGS) -o $@ + +$(LIBWEB): FORCE + +cd ../../ && $(MAKE) + +$(DYNSTR): FORCE + +cd ../../dynstr && $(MAKE) diff --git a/examples/async/README.md b/examples/async/README.md new file mode 100644 index 0000000..ece15e7 --- /dev/null +++ b/examples/async/README.md @@ -0,0 +1,14 @@ +# Asynchronous HTTP response example + +This example shows how to generate HTTP responses asynchronously. + +## How to build + +If using `make(1)`, just run `make` from this directory. + +If using CMake, examples are built by default when configuring the project +from [the top-level `CMakeLists.txt`](../../CMakeLists.txt). + +## How to run + +Run the executable without any command line arguments. diff --git a/examples/async/main.c b/examples/async/main.c new file mode 100644 index 0000000..dbf69a8 --- /dev/null +++ b/examples/async/main.c @@ -0,0 +1,232 @@ +/* As of FreeBSD 13.2, sigaction(2) still conforms to IEEE Std + * 1003.1-1990 (POSIX.1), which did not define SA_RESTART. + * FreeBSD supports it as an extension, but then _POSIX_C_SOURCE must + * not be defined. */ +#ifndef __FreeBSD__ +#define _POSIX_C_SOURCE 200809L +#endif + +#include <dynstr.h> +#include <libweb/handler.h> +#include <libweb/html.h> +#include <libweb/http.h> +#include <errno.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int send_response(const unsigned cnt, struct http_response *const r) +{ + int ret = -1; + + struct html_node *html = html_node_alloc("html"), *body, *p; + struct dynstr d, text; + + dynstr_init(&d); + dynstr_init(&text); + + if (!html) + { + fprintf(stderr, "%s: html_node_alloc failed\n", __func__); + goto end; + } + else if (!(body = html_node_add_child(html, "body"))) + { + fprintf(stderr, "%s: html_node_add_child body failed\n", __func__); + goto end; + } + else if (!(p = html_node_add_child(body, "p"))) + { + fprintf(stderr, "%s: html_node_add_child p failed\n", __func__); + goto end; + } + else if (dynstr_append(&text, "It took %u iterations to generate " + "this response!", cnt)) + { + fprintf(stderr, "%s: dynstr_append failed\n", __func__); + goto end; + } + else if (html_node_set_value(p, text.str)) + { + fprintf(stderr, "%s: html_node_set_value p failed\n", __func__); + goto end; + } + else if (html_serialize(html, &d)) + { + fprintf(stderr, "%s: html_serialize failed\n", __func__); + goto end; + } + + *r = (const struct http_response) + { + .status = HTTP_STATUS_OK, + .buf.rw = d.str, + .n = d.len, + .free = free + }; + + if (http_response_add_header(r, "Content-Type", "text/html")) + { + fprintf(stderr, "%s: http_response_add_header failed\n", __func__); + goto end; + } + + ret = 0; + +end: + dynstr_free(&text); + html_node_free(html); + + if (ret) + dynstr_free(&d); + + return ret; +} + +static int step(const struct http_payload *const pl, + struct http_response *const r, void *const user, void *step_args) +{ + unsigned *const cnt = step_args; + const unsigned max = 10; + + fprintf(stderr, "%s: step %u\n", __func__, *cnt); + + if (++(*cnt) >= max) + return send_response(*cnt, r); + + return 0; +} + +static int hello(const struct http_payload *const pl, + struct http_response *const r, void *const user) +{ + unsigned *const cnt = malloc(sizeof *cnt); + + if (!cnt) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + *r = (const struct http_response) + { + .step.payload = step, + .step_args = cnt + }; + + *cnt = 0; + return 0; +} + +static int on_length(const unsigned long long len, + const struct http_cookie *const c, struct http_response *const r, + void *const user) +{ + *r = (const struct http_response) + { + .status = HTTP_STATUS_FORBIDDEN + }; + + return 1; +} + +struct handler *handler; + +static void handle_signal(const int signum) +{ + switch (signum) + { + case SIGINT: + /* Fall through. */ + case SIGTERM: + handler_notify_close(handler); + break; + + default: + break; + } +} + +static int init_signals(void) +{ + struct sigaction sa = + { + .sa_handler = handle_signal, + .sa_flags = SA_RESTART + }; + + sigemptyset(&sa.sa_mask); + + static const struct signal + { + int signal; + const char *name; + } signals[] = + { + {.signal = SIGINT, .name = "SIGINT"}, + {.signal = SIGTERM, .name = "SIGTERM"}, + {.signal = SIGPIPE, .name = "SIGPIPE"} + }; + + for (size_t i = 0; i < sizeof signals / sizeof *signals; i++) + { + const struct signal *const s = &signals[i]; + + if (sigaction(s->signal, &sa, NULL)) + { + fprintf(stderr, "%s: sigaction(2) %s: %s\n", + __func__, s->name, strerror(errno)); + return -1; + } + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + const struct handler_cfg cfg = + { + .length = on_length + }; + + static const char *const urls[] = {"/", "/index.html"}; + + if (!(handler = handler_alloc(&cfg))) + { + fprintf(stderr, "%s: handler_alloc failed\n", __func__); + goto end; + } + + for (size_t i = 0; i < sizeof urls / sizeof *urls; i++) + if (handler_add(handler, urls[i], HTTP_OP_GET, hello, NULL)) + { + fprintf(stderr, "%s: handler_add failed\n", __func__); + goto end; + } + + unsigned short outport; + + if (handler_listen(handler, 0, &outport)) + { + fprintf(stderr, "%s: handler_listen failed\n", __func__); + goto end; + } + + printf("Listening on port %hu\n", outport); + + if (handler_loop(handler)) + { + fprintf(stderr, "%s: handler_loop failed\n", __func__); + goto end; + } + + ret = EXIT_SUCCESS; + +end: + handler_free(handler); + return ret; +} |
