/** * part of Hydrilla * Serving data queries over HTTP using libmicrohttpd. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. * * This file is based on Christian Grothoff's public comain code from * `doc/examples/logging.c' in libmicrohttpd source tree. */ #define _POSIX_C_SOURCE 199506L /* sigwait() */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/types.h> #include <sys/select.h> #include <sys/socket.h> #include <microhttpd.h> #include "scriptbase.h" #define PORT 10111 #define ARRLEN(array) (sizeof(array) / sizeof(*array)) static struct MHD_Response *default_response, *error_response, *not_found_response; static struct { struct MHD_Response **response; const char *const text; } static_responses[] = { {&default_response, "<html><body>Nothing to see here</body></html>"}, {&error_response, "<html><body>Error occured</body></html>"}, {¬_found_response, "<html><body>Not found</body></html>"} }; static int add_CORS_header(struct MHD_Response *response) { return -1 * (MHD_add_response_header(response, "Access-Control-Allow-Origin", "*") == MHD_NO); } typedef char *(*request_handler_t)(const char *queried, struct scriptbase *base); static struct { const char *path; request_handler_t request_handler; } resources[] = { {"/script", get_script_json}, {"/bag", get_bag_json}, {"/pattern", get_pattern_json}, {"/query", get_page_query_json} }; struct request_handling { struct scriptbase *base; request_handler_t handler; char *json_response; int error_number; }; static int handle_query_argument(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { struct request_handling *handling = cls; if (strcmp(key, "n")) { fprintf(stderr, "Unknown argument: \"%s\" = \"%s\"\n", key, value); return MHD_YES; } errno = 0; handling->json_response = handling->handler(value, handling->base); handling->error_number = errno; return MHD_NO; } static int answer_with(struct MHD_Connection *connection, request_handler_t handler, struct scriptbase *base) { struct request_handling handling = {base, handler, NULL, EINVAL}; struct MHD_Response *response; int retval; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, handle_query_argument, &handling); if (!handling.json_response) { if (handling.error_number) goto send_error; goto send_not_found; } response = MHD_create_response_from_buffer (strlen(handling.json_response), handling.json_response, MHD_RESPMEM_MUST_FREE); if (!response || add_CORS_header(response)) goto send_error; retval = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); return retval; send_not_found: return MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, not_found_response); send_error: free(handling.json_response); return MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, error_response); } static int answer(void *data, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { static int aptr; int i; struct scriptbase *base = data; printf("New %s request for %s using version %s\n", method, url, version); if (strcmp(method, "GET")) return MHD_NO; if (&aptr != *ptr) { /* do never respond on first call */ *ptr = &aptr; return MHD_YES; } *ptr = NULL; /* reset when done */ for (i = 0; i < ARRLEN(resources); i++) { if (strcmp(resources[i].path, url)) continue; return answer_with(connection, resources[i].request_handler, base); } return MHD_queue_response(connection, MHD_HTTP_OK, default_response); } int getchar(void); int serve_scriptbase(struct scriptbase *base, bool wait_for_sigterm) { int i; struct MHD_Daemon *daemon; sigset_t sigterm_set; int signal_number; /* only written, not read */ int retval = -1; if (init_url_lookup_regex()) return -1; for (i = 0; i < ARRLEN(static_responses); i++) { *static_responses[i].response = MHD_create_response_from_buffer (strlen(static_responses[i].text), (char*) static_responses[i].text, MHD_RESPMEM_PERSISTENT); if (!*static_responses[i].response || add_CORS_header(*static_responses[i].response)) goto free_resources; } daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL, &answer, (void*) base, MHD_OPTION_END); if (!daemon) goto free_resources; if (wait_for_sigterm) { sigemptyset(&sigterm_set); sigaddset(&sigterm_set, SIGTERM); sigwait(&sigterm_set, &signal_number); } else { getchar(); } MHD_stop_daemon(daemon); retval = 0; free_resources: for (i = 0; i < ARRLEN(static_responses); i++) { if (*static_responses[i].response) MHD_destroy_response(*static_responses[i].response); *static_responses[i].response = NULL; } destroy_url_lookup_regex(); return retval; }