/* 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 #include #include #include #include #include #include #include #include #include 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 *args) { unsigned *const cnt = 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, .args = cnt }; *cnt = 0; return 0; } static int on_length(const enum http_op op, const char *const res, 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; }