From: Stephan Bosch Date: Thu, 14 Nov 2019 23:32:00 +0000 (+0100) Subject: stats: Add HTTP server support. X-Git-Tag: 2.3.11.2~314 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=06f9f4dd8dcaecf558ffab8ecc3f4fd34576a0b3;p=thirdparty%2Fdovecot%2Fcore.git stats: Add HTTP server support. --- diff --git a/src/stats/Makefile.am b/src/stats/Makefile.am index ae0c1ea678..10aa86c594 100644 --- a/src/stats/Makefile.am +++ b/src/stats/Makefile.am @@ -31,6 +31,7 @@ stats_SOURCES = \ libstats_local_la_SOURCES = \ client-reader.c \ client-writer.c \ + client-http.c \ event-exporter-fmt.c \ event-exporter-fmt-json.c \ event-exporter-fmt-none.c \ @@ -46,6 +47,7 @@ noinst_HEADERS = \ stats-common.h \ client-reader.h \ client-writer.h \ + client-http.h\ event-exporter.h \ stats-event-category.h \ stats-metrics.h \ diff --git a/src/stats/client-http.c b/src/stats/client-http.c new file mode 100644 index 0000000000..6ffcb933b8 --- /dev/null +++ b/src/stats/client-http.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "stats-common.h" +#include "str.h" +#include "array.h" +#include "strescape.h" +#include "connection.h" +#include "ostream.h" +#include "master-service.h" +#include "http-server.h" +#include "http-url.h" +#include "stats-metrics.h" +#include "client-http.h" + +struct stats_http_client; + +struct stats_http_client { + struct http_server_connection *http_conn; +}; + +struct stats_http_resource { + pool_t pool; + const char *title; + struct http_server_resource *resource; + + stats_http_resource_callback_t *callback; + void *context; +}; + +static struct http_server *stats_http_server; +static ARRAY(struct stats_http_resource *) stats_http_resources; + +/* + * Request + */ + +static void +stats_http_server_handle_request(void *context ATTR_UNUSED, + struct http_server_request *http_sreq) +{ + http_server_request_fail(http_sreq, 404, "Path Not Found"); +} + +/* + * Connection + */ + +static void +stats_http_server_connection_destroy(void *context, const char *reason); + +static const struct http_server_callbacks stats_http_callbacks = { + .connection_destroy = stats_http_server_connection_destroy, + .handle_request = stats_http_server_handle_request +}; + +void client_http_create(struct master_service_connection *conn) +{ + struct stats_http_client *client; + + client = i_new(struct stats_http_client, 1); + + client->http_conn = http_server_connection_create( + stats_http_server, conn->fd, conn->fd, conn->ssl, + &stats_http_callbacks, client); +} + +static void stats_http_client_destroy(struct stats_http_client *client) +{ + i_free(client); + + master_service_client_connection_destroyed(master_service); +} + +static void +stats_http_server_connection_destroy(void *context, + const char *reason ATTR_UNUSED) +{ + struct stats_http_client *client = context; + + if (client->http_conn == NULL) { + /* Already destroying client directly */ + return; + } + + /* HTTP connection is destroyed already now */ + client->http_conn = NULL; + + /* Destroy the connection itself */ + stats_http_client_destroy(client); +} + +/* + * Resources + */ + +/* Registry */ + +static void +stats_http_resource_callback(struct stats_http_resource *res, + struct http_server_request *req, + const char *sub_path) +{ + res->callback(res->context, req, sub_path); +} + +#undef stats_http_resource_add +void stats_http_resource_add(const char *path, const char *title, + stats_http_resource_callback_t *callback, + void *context) +{ + struct stats_http_resource *res; + pool_t pool; + + pool = pool_alloconly_create("stats http resource", 2048); + res = p_new(pool, struct stats_http_resource, 1); + res->pool = pool; + res->title = p_strdup(pool, title); + res->callback = callback; + res->context = context; + + res->resource = http_server_resource_create( + stats_http_server, pool, stats_http_resource_callback, res); + http_server_resource_add_location(res->resource, path); + + pool_unref(&pool); + array_append(&stats_http_resources, &res, 1); +} + +/* Root */ + +static void +stats_http_resource_root_make_response(struct http_server_response *resp, + const struct http_request *hreq) +{ + struct stats_http_resource *const *res_p; + struct http_url url; + string_t *msg; + + http_url_init_authority_from(&url, hreq->target.url); + + msg = t_str_new(1024); + + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "Dovecot Stats\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + + str_append(msg, "

Dovecot Stats:

\n"); + str_append(msg, "

\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + str_append(msg, "\n"); + + http_server_response_set_payload_data( + resp, str_data(msg), str_len(msg)); +} + +static void +stats_http_resource_root_request(void *context ATTR_UNUSED, + struct http_server_request *req, + const char *sub_path) +{ + const struct http_request *hreq = http_server_request_get(req); + struct http_server_response *resp; + + if (strcmp(hreq->method, "OPTIONS") == 0) { + resp = http_server_response_create(req, 200, "OK"); + http_server_response_submit(resp); + return; + } + if (strcmp(hreq->method, "GET") != 0) { + http_server_request_fail(req, 405, "Method Not Allowed"); + return; + } + if (*sub_path != '\0') { + http_server_request_fail(req, 404, "Not Found"); + return; + } + + resp = http_server_response_create(req, 200, "OK"); + http_server_response_add_header(resp, "Content-Type", + "text/html; charset=utf-8"); + + stats_http_resource_root_make_response(resp, hreq); + + http_server_response_submit(resp); +} + +/* + * Server + */ + +void client_http_init(void) +{ + struct http_server_settings http_set = { + .rawlog_dir = stats_settings->stats_http_rawlog_dir, + }; + + i_array_init(&stats_http_resources, 8); + + stats_http_server = http_server_init(&http_set); + stats_http_resource_add("/", NULL, + stats_http_resource_root_request, NULL); +} + +void client_http_deinit(void) +{ + http_server_deinit(&stats_http_server); + array_free(&stats_http_resources); +} diff --git a/src/stats/client-http.h b/src/stats/client-http.h new file mode 100644 index 0000000000..ecbe51528a --- /dev/null +++ b/src/stats/client-http.h @@ -0,0 +1,28 @@ +#ifndef CLIENT_HTTP_H +#define CLIENT_HTTP_H + +struct master_service_connection; +struct http_server_request; + +typedef void +(stats_http_resource_callback_t)(void *context, + struct http_server_request *req, + const char *sub_path); + +void client_http_create(struct master_service_connection *conn); + +void stats_http_resource_add(const char *path, const char *title, + stats_http_resource_callback_t *callback, + void *context); +#define stats_http_resource_add(path, title, callback, context) \ + stats_http_resource_add(path, title, \ + (stats_http_resource_callback_t *)callback, \ + (TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)( \ + typeof(context), struct http_server_request *req, \ + const char *sub_path)))) + +void client_http_init(void); +void client_http_deinit(void); + +#endif diff --git a/src/stats/main.c b/src/stats/main.c index c3eeca6b59..f9204568fa 100644 --- a/src/stats/main.c +++ b/src/stats/main.c @@ -10,6 +10,7 @@ #include "stats-metrics.h" #include "client-writer.h" #include "client-reader.h" +#include "client-http.h" const struct stats_settings *stats_settings; struct stats_metrics *stats_metrics; @@ -36,7 +37,9 @@ static bool client_is_writer(const char *path) static void client_connected(struct master_service_connection *conn) { - if (client_is_writer(conn->name)) + if (strcmp(conn->name, "http") == 0) + client_http_create(conn); + else if (client_is_writer(conn->name)) client_writer_create(conn->fd); else client_reader_create(conn->fd); @@ -64,12 +67,14 @@ static void main_init(void) stats_event_categories_init(); client_readers_init(); client_writers_init(); + client_http_init(); } static void main_deinit(void) { client_readers_deinit(); client_writers_deinit(); + client_http_deinit(); stats_event_categories_deinit(); stats_metrics_deinit(&stats_metrics); } diff --git a/src/stats/stats-settings.c b/src/stats/stats-settings.c index 674a11f35a..ed42078720 100644 --- a/src/stats/stats-settings.c +++ b/src/stats/stats-settings.c @@ -141,18 +141,25 @@ const struct setting_parser_info stats_metric_setting_parser_info = { * top-level settings */ +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct stats_settings, name), NULL } #undef DEFLIST_UNIQUE #define DEFLIST_UNIQUE(field, name, defines) \ { SET_DEFLIST_UNIQUE, name, \ offsetof(struct stats_settings, field), defines } static const struct setting_define stats_setting_defines[] = { + DEF(SET_STR, stats_http_rawlog_dir), + DEFLIST_UNIQUE(metrics, "metric", &stats_metric_setting_parser_info), DEFLIST_UNIQUE(exporters, "event_exporter", &stats_exporter_setting_parser_info), SETTING_DEFINE_LIST_END }; const struct stats_settings stats_default_settings = { + .stats_http_rawlog_dir = "", + .metrics = ARRAY_INIT, .exporters = ARRAY_INIT, }; diff --git a/src/stats/stats-settings.h b/src/stats/stats-settings.h index 821056c4e8..7346d360ea 100644 --- a/src/stats/stats-settings.h +++ b/src/stats/stats-settings.h @@ -113,6 +113,8 @@ struct stats_metric_settings { }; struct stats_settings { + const char *stats_http_rawlog_dir; + ARRAY(struct stats_exporter_settings *) exporters; ARRAY(struct stats_metric_settings *) metrics; };