aboutsummaryrefslogtreecommitdiff
path: root/serve.c
diff options
context:
space:
mode:
Diffstat (limited to 'serve.c')
-rw-r--r--serve.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/serve.c b/serve.c
new file mode 100644
index 0000000..dd61da1
--- /dev/null
+++ b/serve.c
@@ -0,0 +1,203 @@
+/**
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.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>"},
+ {&not_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)
+{
+ int i;
+ struct MHD_Daemon *daemon;
+ 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;
+
+ 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;
+}