@cryptotaxi247 / netdata-1 / commits / 66c47355b

initial minimal h2o webserver integration (#14585)

Introduces h2o based web server as an alternative

Timotej S committed May 10, 2023 at 13:37 UTC 66c47355b4c0a33f7cef0615fe7331bbce463361
21 files changed +707 -28
.github/codeql/c-cpp-config.yml new
+2
@@ -0,0 +1,2 @@
1 +paths-ignore:
2 + - httpd/h2o
.github/workflows/codeql.yml
+1
@@ -84,6 +84,7 @@ jobs:
84 uses: github/codeql-action/init@v2
85 with:
86 languages: cpp
87 + config-file: ./.github/codeql/c-cpp-config.yml
88 - name: Prepare environment
89 run: ./packaging/installer/install-required-packages.sh --dont-wait --non-interactive netdata
90 - name: Build netdata
.gitmodules
+4
@@ -9,3 +9,7 @@
9 url = https://github.com/davisking/dlib.git
10 shallow = true
11 ignore = dirty
12 +[submodule "httpd/h2o"]
13 + path = httpd/h2o
14 + url = https://github.com/h2o/h2o.git
15 + ignore = untracked
CMakeLists.txt
+1
@@ -513,6 +513,7 @@ set(LIBNETDATA_FILES
513 libnetdata/worker_utilization/worker_utilization.h
514 libnetdata/parser/parser.h
515 libnetdata/parser/parser.c
516 + libnetdata/http/http_defs.h
517 )
518
519 IF(ENABLE_PLUGIN_EBPF)
Makefile.am
+126 -1
@@ -83,6 +83,7 @@ dist_noinst_DATA = \
83 packaging/protobuf.version \
84 packaging/version \
85 database/engine/journalfile_v2.ksy.in \
86 + httpd/h2o \
87 $(NULL)
88
89 # until integrated within build
@@ -197,6 +198,7 @@ LIBNETDATA_FILES = \
198 libnetdata/string/utf8.h \
199 libnetdata/worker_utilization/worker_utilization.c \
200 libnetdata/worker_utilization/worker_utilization.h \
201 + libnetdata/http/http_defs.h \
202 $(NULL)
203
204 if ENABLE_PLUGIN_EBPF
@@ -749,7 +751,8 @@ libmqttwebsockets_a_SOURCES = \
751 mqtt_websockets/c-rbuf/src/ringbuffer_internal.h \
752 mqtt_websockets/c_rhash/src/c_rhash.c \
753 mqtt_websockets/c_rhash/include/c_rhash.h \
752 - mqtt_websockets/c_rhash/src/c_rhash_internal.h
754 + mqtt_websockets/c_rhash/src/c_rhash_internal.h \
755 + $(NULL)
756
757 libmqttwebsockets_a_CFLAGS = $(CFLAGS) -DMQTT_WSS_CUSTOM_ALLOC -DRBUF_CUSTOM_MALLOC -DMQTT_WSS_CPUSTATS -I$(srcdir)/aclk/helpers -I$(srcdir)/mqtt_websockets/c_rhash/include
758
@@ -939,6 +942,123 @@ DAEMON_FILES = \
942 daemon/unit_test.h \
943 $(NULL)
944
945 +HTTPD_FILES = \
946 + httpd/http_server.c \
947 + httpd/http_server.h \
948 + httpd/h2o_utils.c \
949 + httpd/h2o_utils.h \
950 + $(NULL)
951 +
952 +libh2o_dir = httpd/h2o
953 +
954 +libh2o_a_SOURCES = \
955 + $(libh2o_dir)/deps/cloexec/cloexec.c \
956 + $(libh2o_dir)/deps/libgkc/gkc.c \
957 + $(libh2o_dir)/deps/libyrmcds/close.c \
958 + $(libh2o_dir)/deps/libyrmcds/connect.c \
959 + $(libh2o_dir)/deps/libyrmcds/recv.c \
960 + $(libh2o_dir)/deps/libyrmcds/send.c \
961 + $(libh2o_dir)/deps/libyrmcds/send_text.c \
962 + $(libh2o_dir)/deps/libyrmcds/socket.c \
963 + $(libh2o_dir)/deps/libyrmcds/strerror.c \
964 + $(libh2o_dir)/deps/libyrmcds/text_mode.c \
965 + $(libh2o_dir)/deps/picohttpparser/picohttpparser.c \
966 + $(libh2o_dir)/lib/common/cache.c \
967 + $(libh2o_dir)/lib/common/file.c \
968 + $(libh2o_dir)/lib/common/filecache.c \
969 + $(libh2o_dir)/lib/common/hostinfo.c \
970 + $(libh2o_dir)/lib/common/http1client.c \
971 + $(libh2o_dir)/lib/common/memcached.c \
972 + $(libh2o_dir)/lib/common/memory.c \
973 + $(libh2o_dir)/lib/common/multithread.c \
974 + $(libh2o_dir)/lib/common/serverutil.c \
975 + $(libh2o_dir)/lib/common/socket.c \
976 + $(libh2o_dir)/lib/common/socketpool.c \
977 + $(libh2o_dir)/lib/common/string.c \
978 + $(libh2o_dir)/lib/common/time.c \
979 + $(libh2o_dir)/lib/common/timeout.c \
980 + $(libh2o_dir)/lib/common/url.c \
981 + $(libh2o_dir)/lib/core/config.c \
982 + $(libh2o_dir)/lib/core/configurator.c \
983 + $(libh2o_dir)/lib/core/context.c \
984 + $(libh2o_dir)/lib/core/headers.c \
985 + $(libh2o_dir)/lib/core/logconf.c \
986 + $(libh2o_dir)/lib/core/proxy.c \
987 + $(libh2o_dir)/lib/core/request.c \
988 + $(libh2o_dir)/lib/core/token.c \
989 + $(libh2o_dir)/lib/core/util.c \
990 + $(libh2o_dir)/lib/handler/access_log.c \
991 + $(libh2o_dir)/lib/handler/chunked.c \
992 + $(libh2o_dir)/lib/handler/compress.c \
993 + $(libh2o_dir)/lib/handler/compress/gzip.c \
994 + $(libh2o_dir)/lib/handler/errordoc.c \
995 + $(libh2o_dir)/lib/handler/expires.c \
996 + $(libh2o_dir)/lib/handler/fastcgi.c \
997 + $(libh2o_dir)/lib/handler/file.c \
998 + $(libh2o_dir)/lib/handler/headers.c \
999 + $(libh2o_dir)/lib/handler/mimemap.c \
1000 + $(libh2o_dir)/lib/handler/proxy.c \
1001 + $(libh2o_dir)/lib/handler/redirect.c \
1002 + $(libh2o_dir)/lib/handler/reproxy.c \
1003 + $(libh2o_dir)/lib/handler/throttle_resp.c \
1004 + $(libh2o_dir)/lib/handler/status.c \
1005 + $(libh2o_dir)/lib/handler/headers_util.c \
1006 + $(libh2o_dir)/lib/handler/status/events.c \
1007 + $(libh2o_dir)/lib/handler/status/requests.c \
1008 + $(libh2o_dir)/lib/handler/http2_debug_state.c \
1009 + $(libh2o_dir)/lib/handler/status/durations.c \
1010 + $(libh2o_dir)/lib/handler/configurator/access_log.c \
1011 + $(libh2o_dir)/lib/handler/configurator/compress.c \
1012 + $(libh2o_dir)/lib/handler/configurator/errordoc.c \
1013 + $(libh2o_dir)/lib/handler/configurator/expires.c \
1014 + $(libh2o_dir)/lib/handler/configurator/fastcgi.c \
1015 + $(libh2o_dir)/lib/handler/configurator/file.c \
1016 + $(libh2o_dir)/lib/handler/configurator/headers.c \
1017 + $(libh2o_dir)/lib/handler/configurator/proxy.c \
1018 + $(libh2o_dir)/lib/handler/configurator/redirect.c \
1019 + $(libh2o_dir)/lib/handler/configurator/reproxy.c \
1020 + $(libh2o_dir)/lib/handler/configurator/throttle_resp.c \
1021 + $(libh2o_dir)/lib/handler/configurator/status.c \
1022 + $(libh2o_dir)/lib/handler/configurator/http2_debug_state.c \
1023 + $(libh2o_dir)/lib/handler/configurator/headers_util.c \
1024 + $(libh2o_dir)/lib/http1.c \
1025 + $(libh2o_dir)/lib/tunnel.c \
1026 + $(libh2o_dir)/lib/http2/cache_digests.c \
1027 + $(libh2o_dir)/lib/http2/casper.c \
1028 + $(libh2o_dir)/lib/http2/connection.c \
1029 + $(libh2o_dir)/lib/http2/frame.c \
1030 + $(libh2o_dir)/lib/http2/hpack.c \
1031 + $(libh2o_dir)/lib/http2/scheduler.c \
1032 + $(libh2o_dir)/lib/http2/stream.c \
1033 + $(libh2o_dir)/lib/http2/http2_debug_state.c \
1034 + $(NULL)
1035 +
1036 +libh2o_a_INCLUDES = \
1037 + -I$(srcdir)/$(libh2o_dir)/include \
1038 + -I$(srcdir)/$(libh2o_dir)/deps/cloexec \
1039 + -I$(srcdir)/$(libh2o_dir)/deps/brotli/enc \
1040 + -I$(srcdir)/$(libh2o_dir)/deps/golombset \
1041 + -I$(srcdir)/$(libh2o_dir)/deps/libgkc \
1042 + -I$(srcdir)/$(libh2o_dir)/deps/libyrmcds \
1043 + -I$(srcdir)/$(libh2o_dir)/deps/klib \
1044 + -I$(srcdir)/$(libh2o_dir)/deps/neverbleed \
1045 + -I$(srcdir)/$(libh2o_dir)/deps/picohttpparser \
1046 + -I$(srcdir)/$(libh2o_dir)/deps/picotest \
1047 + -I$(srcdir)/$(libh2o_dir)/deps/yaml/include \
1048 + -I$(srcdir)/$(libh2o_dir)/deps/yoml \
1049 + $(NULL)
1050 +
1051 +if ENABLE_HTTPD
1052 +noinst_LIBRARIES += libh2o.a
1053 +
1054 +# until h2o updates support for OpenSSL 3.0 we silence the warnings
1055 +libh2o_a_CFLAGS = $(CFLAGS) -Wno-deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers -DH2O_USE_LIBUV=0 $(libh2o_a_INCLUDES)
1056 +
1057 +if LINUX
1058 + libh2o_a_CFLAGS += -D_GNU_SOURCE
1059 +endif
1060 +endif #ENABLE_HTTPD
1061 +
1062 NETDATA_FILES = \
1063 collectors/all.h \
1064 $(DAEMON_FILES) \
@@ -1007,6 +1127,11 @@ if ENABLE_ACLK
1127 NETDATA_COMMON_LIBS += libmqttwebsockets.a
1128 endif
1129
1130 +if ENABLE_HTTPD
1131 + NETDATA_FILES += $(HTTPD_FILES)
1132 + NETDATA_COMMON_LIBS += libh2o.a
1133 +endif
1134 +
1135 if LINK_STATIC_JSONC
1136 NETDATA_COMMON_LIBS += $(abs_top_srcdir)/externaldeps/jsonc/libjson-c.a
1137 endif
configure.ac
+39 -1
@@ -213,6 +213,12 @@ AC_ARG_ENABLE(
213 [aclk_ssl_debug="yes"],
214 [aclk_ssl_debug="no"]
215 )
216 +AC_ARG_ENABLE(
217 + [httpd],
218 + [AS_HELP_STRING([--disable-httpd], [Disable webserver (h2o based) @<:@default autodetect@:>@])],
219 + ,
220 + [enable_httpd="detect"]
221 +)
222
223 # -----------------------------------------------------------------------------
224 # Enforce building with C99, bail early if we can't.
@@ -799,6 +805,38 @@ fi
805 AC_MSG_RESULT([${with_libcap}])
806 AM_CONDITIONAL([ENABLE_CAPABILITY], [test "${with_libcap}" = "yes"])
807
808 +# -----------------------------------------------------------------------------
809 +# HTTPD and h2o related
810 +
811 +can_build_httpd="no"
812 +if test "${enable_httpd}" != "no"; then
813 + can_build_httpd="yes"
814 + AC_MSG_CHECKING([can build HTTPD])
815 + if test -z "${UV_LIBS}"; then
816 + can_build_httpd="no"
817 + fi
818 + if test -n "${SSL_LIBS}"; then
819 + OPTIONAL_SSL_CFLAGS="${SSL_CFLAGS}"
820 + OPTIONAL_SSL_LIBS="${SSL_LIBS}"
821 + else
822 + can_build_httpd="no"
823 + fi
824 + if test "${with_zlib}" != "yes"; then
825 + can_build_httpd="no"
826 + fi
827 + AC_MSG_RESULT([${can_build_httpd}])
828 +
829 + if test "${can_build_httpd}" = "no" -a "${enable_httpd}" = "yes"; then
830 + AC_MSG_ERROR([HTTPD was requested but it cannot be built])
831 + fi
832 +
833 + if test "${can_build_httpd}" = "yes"; then
834 + AC_DEFINE([ENABLE_HTTPD], [1], [HTTPD (h2o based web server)])
835 + HTTPD_CFLAGS="-I\$(abs_top_srcdir)/httpd/h2o/include -I\$(abs_top_srcdir)/httpd/h2o/deps/picotls/include -I\$(abs_top_srcdir)/httpd/h2o/deps/quicly/include -DH2O_USE_LIBUV=0"
836 + fi
837 +fi
838 +AM_CONDITIONAL([ENABLE_HTTPD], [test "${can_build_httpd}" = "yes"])
839 +
840 # -----------------------------------------------------------------------------
841 # ACLK
842
@@ -1689,7 +1727,7 @@ CFLAGS="${originalCFLAGS} ${OPTIONAL_LTO_CFLAGS} ${OPTIONAL_PROTOBUF_CFLAGS} ${O
1727 ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS} ${OPTIONAL_CUPS_CFLAGS} ${OPTIONAL_XENSTAT_FLAGS} \
1728 ${OPTIONAL_KINESIS_CFLAGS} ${OPTIONAL_PUBSUB_CFLAGS} ${OPTIONAL_PROMETHEUS_REMOTE_WRITE_CFLAGS} \
1729 ${OPTIONAL_MONGOC_CFLAGS} ${LWS_CFLAGS} ${OPTIONAL_JSONC_STATIC_CFLAGS} ${OPTIONAL_YAML_STATIC_CFLAGS} ${OPTIONAL_BPF_CFLAGS} ${JUDY_CFLAGS} \
1692 - ${OPTIONAL_ACLK_CFLAGS} ${OPTIONAL_ML_CFLAGS} ${OPTIONAL_OS_DEP_CFLAGS}"
1730 + ${OPTIONAL_ACLK_CFLAGS} ${OPTIONAL_ML_CFLAGS} ${OPTIONAL_OS_DEP_CFLAGS} ${HTTPD_CFLAGS}"
1731
1732 CXXFLAGS="${CFLAGS} ${OPTIONAL_KINESIS_CXXFLAGS} ${CPP_STD_FLAG}"
1733
daemon/buildinfo.c
+9 -1
@@ -20,6 +20,12 @@
20 #endif
21 #endif
22
23 +#ifdef ENABLE_HTTPD
24 +#define FEAT_HTTPD 1
25 +#else
26 +#define FEAT_HTTPD 0
27 +#endif
28 +
29 #ifdef ENABLE_DBENGINE
30 #define FEAT_DBENGINE 1
31 #else
@@ -275,6 +281,7 @@ void print_build_info(void) {
281 printf(" TLS Host Verification: %s\n", FEAT_YES_NO(FEAT_TLS_HOST_VERIFY));
282 printf(" Machine Learning: %s\n", FEAT_YES_NO(FEAT_ML));
283 printf(" Stream Compression: %s\n", FEAT_YES_NO(FEAT_STREAM_COMPRESSION));
284 + printf(" HTTPD (h2o): %s\n", FEAT_YES_NO(FEAT_HTTPD));
285
286 printf("Libraries:\n");
287 printf(" protobuf: %s%s\n", FEAT_YES_NO(FEAT_PROTOBUF), FEAT_PROTOBUF_BUNDLED);
@@ -328,7 +335,8 @@ void print_build_info_json(void) {
335
336 printf(" \"tls-host-verify\": %s,\n", FEAT_JSON_BOOL(FEAT_TLS_HOST_VERIFY));
337 printf(" \"machine-learning\": %s\n", FEAT_JSON_BOOL(FEAT_ML));
331 - printf(" \"stream-compression\": %s\n", FEAT_JSON_BOOL(FEAT_STREAM_COMPRESSION));
338 + printf(" \"stream-compression\": %s\n", FEAT_JSON_BOOL(FEAT_STREAM_COMPRESSION));
339 + printf(" \"httpd-h2o\": %s\n", FEAT_JSON_BOOL(FEAT_HTTPD));
340 printf(" },\n");
341
342 printf(" \"libs\": {\n");
daemon/common.h
+5
@@ -41,6 +41,11 @@
41 // the netdata webserver(s)
42 #include "web/server/web_server.h"
43
44 +// the new h2o based netdata webserver
45 +#ifdef ENABLE_HTTPD
46 +#include "httpd/http_server.h"
47 +#endif
48 +
49 // streaming metrics between netdata servers
50 #include "streaming/rrdpush.h"
51
daemon/main.c
+11
@@ -170,6 +170,8 @@ static void service_to_buffer(BUFFER *wb, SERVICE_TYPE service) {
170 buffer_strcat(wb, "ANALYTICS ");
171 if(service & SERVICE_EXPORTERS)
172 buffer_strcat(wb, "EXPORTERS ");
173 + if(service & SERVICE_HTTPD)
174 + buffer_strcat(wb, "HTTPD ");
175 }
176
177 static bool service_wait_exit(SERVICE_TYPE service, usec_t timeout_ut) {
@@ -365,6 +367,7 @@ void netdata_cleanup_and_exit(int ret) {
367 | SERVICE_EXPORTERS
368 | SERVICE_HEALTH
369 | SERVICE_WEB_SERVER
370 + | SERVICE_HTTPD
371 , 3 * USEC_PER_SEC);
372
373 delta_shutdown_time("stop collectors and streaming threads");
@@ -1979,6 +1982,14 @@ int main(int argc, char **argv) {
1982
1983 if(web_server_mode != WEB_SERVER_MODE_NONE)
1984 api_listen_sockets_setup();
1985 +
1986 +#ifdef ENABLE_HTTPD
1987 + delta_startup_time("initialize httpd server");
1988 + for (int i = 0; static_threads[i].name; i++) {
1989 + if (static_threads[i].start_routine == httpd_main)
1990 + static_threads[i].enabled = httpd_is_enabled();
1991 + }
1992 +#endif
1993 }
1994
1995 delta_startup_time("set resource limits");
daemon/main.h
+2 -1
@@ -41,7 +41,8 @@ typedef enum {
41 SERVICE_CONTEXT = (1 << 10),
42 SERVICE_ANALYTICS = (1 << 11),
43 SERVICE_EXPORTERS = (1 << 12),
44 - SERVICE_ACLKSYNC = (1 << 13)
44 + SERVICE_ACLKSYNC = (1 << 13),
45 + SERVICE_HTTPD = (1 << 14)
46 } SERVICE_TYPE;
47
48 typedef enum {
daemon/static_threads.c
+12
@@ -142,6 +142,18 @@ const struct netdata_static_thread static_threads_common[] = {
142 .start_routine = socket_listen_main_static_threaded
143 },
144
145 +#ifdef ENABLE_HTTPD
146 + {
147 + .name = "httpd",
148 + .config_section = NULL,
149 + .config_name = NULL,
150 + .enabled = 0,
151 + .thread = NULL,
152 + .init_routine = NULL,
153 + .start_routine = httpd_main
154 + },
155 +#endif
156 +
157 #ifdef ENABLE_ACLK
158 {
159 .name = "ACLK_MAIN",
httpd/h2o new
+1
@@ -0,0 +1 @@
1 +Subproject commit 7359e98d78d018a35f5da7523feac69f64eddb4b
httpd/h2o_utils.c new
+60
@@ -0,0 +1,60 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "h2o_utils.h"
4 +
5 +#include "h2o/string_.h"
6 +
7 +#include "libnetdata/libnetdata.h"
8 +
9 +char *iovec_to_cstr(h2o_iovec_t *str)
10 +{
11 + char *c_str = mallocz(str->len + 1);
12 + memcpy(c_str, str->base, str->len);
13 + c_str[str->len] = 0;
14 + return c_str;
15 +}
16 +
17 +#define KEY_VAL_BUFFER_GROWTH_STEP 5
18 +h2o_iovec_pair_vector_t *parse_URL_params(h2o_mem_pool_t *pool, h2o_iovec_t params_string)
19 +{
20 + h2o_iovec_pair_vector_t *params_vec = h2o_mem_alloc_shared(pool, sizeof(h2o_iovec_pair_vector_t), NULL);
21 + memset(params_vec, 0, sizeof(h2o_iovec_pair_vector_t));
22 +
23 + h2o_iovec_pair_t param;
24 + while ((param.name.base = (char*)h2o_next_token(&params_string, '&', &param.name.len, &param.value)) != NULL) {
25 + if (params_vec->capacity == params_vec->size)
26 + h2o_vector_reserve(pool, params_vec, params_vec->capacity + KEY_VAL_BUFFER_GROWTH_STEP);
27 +
28 + params_vec->entries[params_vec->size++] = param;
29 + }
30 +
31 + return params_vec;
32 +}
33 +
34 +h2o_iovec_pair_t *get_URL_param_by_name(h2o_iovec_pair_vector_t *params_vec, const void *needle, size_t needle_len)
35 +{
36 + for (size_t i = 0; i < params_vec->size; i++) {
37 + h2o_iovec_pair_t *ret = &params_vec->entries[i];
38 + if (h2o_memis(ret->name.base, ret->name.len, needle, needle_len))
39 + return ret;
40 + }
41 + return NULL;
42 +}
43 +
44 +char *url_unescape(const char *url)
45 +{
46 + char *result = mallocz(strlen(url) + 1);
47 +
48 + int i, j;
49 + for (i = 0, j = 0; url[i] != 0; i++, j++) {
50 + if (url[i] == '%' && isxdigit(url[i+1]) && isxdigit(url[i+2])) {
51 + char hex[3] = { url[i+1], url[i+2], 0 };
52 + result[j] = strtol(hex, NULL, 16);
53 + i += 2;
54 + } else
55 + result[j] = url[i];
56 + }
57 + result[j] = 0;
58 +
59 + return result;
60 +}
httpd/h2o_utils.h new
+38
@@ -0,0 +1,38 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_H2O_UTILS_H
4 +#define NETDATA_H2O_UTILS_H
5 +
6 +#include "h2o/memory.h"
7 +
8 +#define __HAS_URL_PARAMS(reqptr) ((reqptr)->query_at != SIZE_MAX && ((reqptr)->path.len - (reqptr)->query_at > 1))
9 +#define IF_HAS_URL_PARAMS(reqptr) if __HAS_URL_PARAMS(reqptr)
10 +#define UNLESS_HAS_URL_PARAMS(reqptr) if (!__HAS_URL_PARAMS(reqptr))
11 +#define URL_PARAMS_IOVEC_INIT(reqptr) { .base = &(reqptr)->path.base[(reqptr)->query_at + 1], \
12 + .len = (reqptr)->path.len - (reqptr)->query_at - 1 }
13 +#define URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(reqptr) { .base = &(reqptr)->path.base[(reqptr)->query_at], \
14 + .len = (reqptr)->path.len - (reqptr)->query_at }
15 +
16 +#define PRINTF_H2O_IOVEC_FMT "%.*s"
17 +#define PRINTF_H2O_IOVEC(iovec) ((int)(iovec)->len), ((iovec)->base)
18 +
19 +char *iovec_to_cstr(h2o_iovec_t *str);
20 +
21 +typedef struct h2o_iovec_pair {
22 + h2o_iovec_t name;
23 + h2o_iovec_t value;
24 +} h2o_iovec_pair_t;
25 +
26 +typedef H2O_VECTOR(h2o_iovec_pair_t) h2o_iovec_pair_vector_t;
27 +
28 +// Takes the part of url behind ? (the url encoded parameters)
29 +// and parse it to vector of name/value pairs without copying the actual strings
30 +h2o_iovec_pair_vector_t *parse_URL_params(h2o_mem_pool_t *pool, h2o_iovec_t params_string);
31 +
32 +// Searches for parameter by name (provided in needle)
33 +// returns pointer to it or NULL
34 +h2o_iovec_pair_t *get_URL_param_by_name(h2o_iovec_pair_vector_t *params_vec, const void *needle, size_t needle_len);
35 +
36 +char *url_unescape(const char *url);
37 +
38 +#endif /* NETDATA_H2O_UTILS_H */
httpd/http_server.c new
+339
@@ -0,0 +1,339 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "daemon/common.h"
4 +#include "http_server.h"
5 +#include "h2o.h"
6 +
7 +#include "h2o_utils.h"
8 +
9 +static h2o_globalconf_t config;
10 +static h2o_context_t ctx;
11 +static h2o_accept_ctx_t accept_ctx;
12 +
13 +#define CONTENT_JSON_UTF8 H2O_STRLIT("application/json; charset=utf-8")
14 +#define CONTENT_TEXT_UTF8 H2O_STRLIT("text/plain; charset=utf-8")
15 +#define NBUF_INITIAL_SIZE_RESP (4096)
16 +#define API_V1_PREFIX "/api/v1/"
17 +#define HOST_SELECT_PREFIX "/host/"
18 +
19 +#define HTTPD_CONFIG_SECTION "httpd"
20 +#define HTTPD_ENABLED_DEFAULT false
21 +
22 +static void on_accept(h2o_socket_t *listener, const char *err)
23 +{
24 + h2o_socket_t *sock;
25 +
26 + if (err != NULL) {
27 + return;
28 + }
29 +
30 + if ((sock = h2o_evloop_socket_accept(listener)) == NULL)
31 + return;
32 + h2o_accept(&accept_ctx, sock);
33 +}
34 +
35 +static int create_listener(const char *ip, int port)
36 +{
37 + struct sockaddr_in addr;
38 + int fd, reuseaddr_flag = 1;
39 + h2o_socket_t *sock;
40 +
41 + memset(&addr, 0, sizeof(addr));
42 + addr.sin_family = AF_INET;
43 + addr.sin_addr.s_addr = inet_addr(ip);
44 + addr.sin_port = htons(port);
45 +
46 + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ||
47 + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 ||
48 + bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 || listen(fd, SOMAXCONN) != 0) {
49 + return -1;
50 + }
51 +
52 + sock = h2o_evloop_socket_create(ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ);
53 + h2o_socket_read_start(sock, on_accept);
54 +
55 + return 0;
56 +}
57 +
58 +static int ssl_init()
59 +{
60 + if (!config_get_boolean(HTTPD_CONFIG_SECTION, "ssl", false))
61 + return 0;
62 +
63 + char default_fn[FILENAME_MAX + 1];
64 +
65 + snprintfz(default_fn, FILENAME_MAX, "%s/ssl/key.pem", netdata_configured_user_config_dir);
66 + const char *key_fn = config_get(HTTPD_CONFIG_SECTION, "ssl key", default_fn);
67 +
68 + snprintfz(default_fn, FILENAME_MAX, "%s/ssl/cert.pem", netdata_configured_user_config_dir);
69 + const char *cert_fn = config_get(HTTPD_CONFIG_SECTION, "ssl certificate", default_fn);
70 +
71 +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
72 + accept_ctx.ssl_ctx = SSL_CTX_new(SSLv23_server_method());
73 +#else
74 + accept_ctx.ssl_ctx = SSL_CTX_new(TLS_server_method());
75 +#endif
76 +
77 + SSL_CTX_set_options(accept_ctx.ssl_ctx, SSL_OP_NO_SSLv2);
78 +
79 + /* load certificate and private key */
80 + if (SSL_CTX_use_PrivateKey_file(accept_ctx.ssl_ctx, key_fn, SSL_FILETYPE_PEM) != 1) {
81 + error("Could not load server key from \"%s\"", key_fn);
82 + return -1;
83 + }
84 + if (SSL_CTX_use_certificate_file(accept_ctx.ssl_ctx, cert_fn, SSL_FILETYPE_PEM) != 1) {
85 + error("Could not load certificate from \"%s\"", cert_fn);
86 + return -1;
87 + }
88 +
89 + h2o_ssl_register_alpn_protocols(accept_ctx.ssl_ctx, h2o_http2_alpn_protocols);
90 +
91 + info("SSL support enabled");
92 +
93 + return 0;
94 +}
95 +
96 +// I did not find a way to do wildcard paths to make common handler for urls like:
97 +// /api/v1/info
98 +// /host/child/api/v1/info
99 +// /host/uuid/api/v1/info
100 +// ideally we could do something like "/*/api/v1/info" subscription
101 +// so we do it "manually" here with uberhandler
102 +static inline int _netdata_uberhandler(h2o_req_t *req, RRDHOST **host)
103 +{
104 + if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
105 + return -1;
106 +
107 + static h2o_generator_t generator = { NULL, NULL };
108 +
109 + h2o_iovec_t norm_path = req->path_normalized;
110 +
111 + if (norm_path.len > strlen(HOST_SELECT_PREFIX) && !memcmp(norm_path.base, HOST_SELECT_PREFIX, strlen(HOST_SELECT_PREFIX))) {
112 + h2o_iovec_t host_id; // host_id can be either and UUID or a hostname of the child
113 +
114 + norm_path.base += strlen(HOST_SELECT_PREFIX);
115 + norm_path.len -= strlen(HOST_SELECT_PREFIX);
116 +
117 + host_id = norm_path;
118 +
119 + size_t end_loc = h2o_strstr(host_id.base, host_id.len, "/", 1);
120 + if (end_loc != SIZE_MAX) {
121 + host_id.len = end_loc;
122 + norm_path.base += end_loc;
123 + norm_path.len -= end_loc;
124 + }
125 +
126 + char *c_host_id = iovec_to_cstr(&host_id);
127 + *host = rrdhost_find_by_hostname(c_host_id);
128 + if (!*host)
129 + *host = rrdhost_find_by_guid(c_host_id);
130 + if (!*host) {
131 + req->res.status = HTTP_RESP_BAD_REQUEST;
132 + req->res.reason = "Wrong host id";
133 + h2o_send_inline(req, H2O_STRLIT("Host id provided was not found!\n"));
134 + freez(c_host_id);
135 + return 0;
136 + }
137 + freez(c_host_id);
138 +
139 + // we have to rewrite URL here in case this is not an api call
140 + // so that the subsequent file upload handler can send the correct
141 + // files to the client
142 + // if this is not an API call we will abort this handler later
143 + // and let the internal serve file handler of h2o care for things
144 +
145 + if (end_loc == SIZE_MAX) {
146 + req->path.len = 1;
147 + req->path_normalized.len = 1;
148 + } else {
149 + size_t offset = norm_path.base - req->path_normalized.base;
150 + req->path.len -= offset;
151 + req->path.base += offset;
152 + req->query_at -= offset;
153 + req->path_normalized.len -= offset;
154 + req->path_normalized.base += offset;
155 + }
156 + }
157 +
158 + // workaround for a dashboard bug which causes sometimes urls like
159 + // "//api/v1/info" to be caled instead of "/api/v1/info"
160 + if (norm_path.len > 2 &&
161 + norm_path.base[0] == '/' &&
162 + norm_path.base[1] == '/' ) {
163 + norm_path.base++;
164 + norm_path.len--;
165 + }
166 +
167 + size_t api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V1_PREFIX));
168 + if (api_loc == SIZE_MAX)
169 + return 1;
170 +
171 + h2o_iovec_t api_command = norm_path;
172 + api_command.base += api_loc + strlen(API_V1_PREFIX);
173 + api_command.len -= api_loc + strlen(API_V1_PREFIX);
174 +
175 + if (!api_command.len)
176 + return 1;
177 +
178 + // this (emulating struct web_client) is a hack and will be removed
179 + // in future PRs but needs bigger changes in old http_api_v1
180 + // we need to make the web_client_api_request_v1 to be web server
181 + // agnostic and remove the old webservers dependency creep into the
182 + // individual response generators and thus remove the need to "emulate"
183 + // the old webserver calling this function here and in ACLK
184 + struct web_client w;
185 + w.response.data = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
186 + w.response.header = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
187 + w.url_query_string_decoded = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
188 + w.acl = WEB_CLIENT_ACL_DASHBOARD;
189 +
190 + char *path_c_str = iovec_to_cstr(&api_command);
191 + char *path_unescaped = url_unescape(path_c_str);
192 + freez(path_c_str);
193 +
194 + IF_HAS_URL_PARAMS(req) {
195 + h2o_iovec_t query_params = URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(req);
196 + char *query_c_str = iovec_to_cstr(&query_params);
197 + char *query_unescaped = url_unescape(query_c_str);
198 + freez(query_c_str);
199 + buffer_strcat(w.url_query_string_decoded, query_unescaped);
200 + freez(query_unescaped);
201 + }
202 +
203 + web_client_api_request_v1(*host, &w, path_unescaped);
204 + freez(path_unescaped);
205 +
206 + h2o_iovec_t body = buffer_to_h2o_iovec(w.response.data);
207 +
208 + // we move msg body to req->pool managed memory as it has to
209 + // live until whole response has been encrypted and sent
210 + // when req is finished memory will be freed with the pool
211 + void *managed = h2o_mem_alloc_shared(&req->pool, body.len, NULL);
212 + memcpy(managed, body.base, body.len);
213 + body.base = managed;
214 +
215 + req->res.status = HTTP_RESP_OK;
216 + req->res.reason = "OK";
217 + if (w.response.data->content_type == CT_APPLICATION_JSON)
218 + h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_JSON_UTF8);
219 + else
220 + h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
221 + h2o_start_response(req, &generator);
222 + h2o_send(req, &body, 1, H2O_SEND_STATE_FINAL);
223 +
224 + buffer_free(w.response.data);
225 + buffer_free(w.response.header);
226 + buffer_free(w.url_query_string_decoded);
227 +
228 + return 0;
229 +}
230 +
231 +static int netdata_uberhandler(h2o_handler_t *self, h2o_req_t *req)
232 +{
233 + UNUSED(self);
234 + RRDHOST *host = localhost;
235 +
236 + int ret = _netdata_uberhandler(req, &host);
237 +
238 + char host_uuid_str[UUID_STR_LEN];
239 + uuid_unparse_lower(host->host_uuid, host_uuid_str);
240 +
241 + if (!ret) {
242 + log_access("HTTPD OK method: " PRINTF_H2O_IOVEC_FMT
243 + ", path: " PRINTF_H2O_IOVEC_FMT
244 + ", as host: %s"
245 + ", response: %d",
246 + PRINTF_H2O_IOVEC(&req->method),
247 + PRINTF_H2O_IOVEC(&req->input.path),
248 + host == localhost ? "localhost" : host_uuid_str,
249 + req->res.status);
250 + } else {
251 + log_access("HTTPD %d"
252 + " method: " PRINTF_H2O_IOVEC_FMT
253 + ", path: " PRINTF_H2O_IOVEC_FMT
254 + ", forwarding to file handler as path: " PRINTF_H2O_IOVEC_FMT,
255 + ret,
256 + PRINTF_H2O_IOVEC(&req->method),
257 + PRINTF_H2O_IOVEC(&req->input.path),
258 + PRINTF_H2O_IOVEC(&req->path));
259 + }
260 +
261 + return ret;
262 +}
263 +
264 +static int hdl_netdata_conf(h2o_handler_t *self, h2o_req_t *req)
265 +{
266 + UNUSED(self);
267 + if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
268 + return -1;
269 +
270 + BUFFER *buf = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
271 + config_generate(buf, 0);
272 +
273 + void *managed = h2o_mem_alloc_shared(&req->pool, buf->len, NULL);
274 + memcpy(managed, buf->buffer, buf->len);
275 +
276 + req->res.status = HTTP_RESP_OK;
277 + req->res.reason = "OK";
278 + h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
279 + h2o_send_inline(req, managed, buf->len);
280 + buffer_free(buf);
281 +
282 + return 0;
283 +}
284 +
285 +#define POLL_INTERVAL 100
286 +
287 +void *httpd_main(void *ptr) {
288 + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
289 +
290 + h2o_pathconf_t *pathconf;
291 + h2o_hostconf_t *hostconf;
292 +
293 + netdata_thread_disable_cancelability();
294 +
295 + const char *bind_addr = config_get(HTTPD_CONFIG_SECTION, "bind to", "127.0.0.1");
296 + int bind_port = config_get_number(HTTPD_CONFIG_SECTION, "port", 19998);
297 +
298 + h2o_config_init(&config);
299 + hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), bind_port);
300 +
301 + pathconf = h2o_config_register_path(hostconf, "/netdata.conf", 0);
302 + h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
303 + handler->on_req = hdl_netdata_conf;
304 +
305 + pathconf = h2o_config_register_path(hostconf, "/", 0);
306 + handler = h2o_create_handler(pathconf, sizeof(*handler));
307 + handler->on_req = netdata_uberhandler;
308 + h2o_file_register(pathconf, netdata_configured_web_dir, NULL, NULL, H2O_FILE_FLAG_SEND_COMPRESSED);
309 +
310 + h2o_context_init(&ctx, h2o_evloop_create(), &config);
311 +
312 + if(ssl_init()) {
313 + error_report("SSL was requested but could not be properly initialized. Aborting.");
314 + return NULL;
315 + }
316 +
317 + accept_ctx.ctx = &ctx;
318 + accept_ctx.hosts = config.hosts;
319 +
320 + if (create_listener(bind_addr, bind_port) != 0) {
321 + error("failed to create listener %s:%d", bind_addr, bind_port);
322 + return NULL;
323 + }
324 +
325 + while (service_running(SERVICE_HTTPD)) {
326 + int rc = h2o_evloop_run(ctx.loop, POLL_INTERVAL);
327 + if (rc < 0 && errno != EINTR) {
328 + error("h2o_evloop_run returned (%d) with errno other than EINTR. Aborting", rc);
329 + break;
330 + }
331 + }
332 +
333 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
334 + return NULL;
335 +}
336 +
337 +int httpd_is_enabled() {
338 + return config_get_boolean(HTTPD_CONFIG_SECTION, "enabled", HTTPD_ENABLED_DEFAULT);
339 +}
httpd/http_server.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef HTTP_SERVER_H
4 +#define HTTP_SERVER_H
5 +
6 +void *httpd_main(void * ptr);
7 +
8 +int httpd_is_enabled();
9 +
10 +#endif /* HTTP_SERVER_H */
libnetdata/buffer/buffer.c
+8
@@ -503,3 +503,11 @@ int buffer_unittest(void) {
503 return errors;
504 }
505
506 +#ifdef ENABLE_HTTPD
507 +h2o_iovec_t buffer_to_h2o_iovec(BUFFER *wb) {
508 + h2o_iovec_t ret;
509 + ret.base = wb->buffer;
510 + ret.len = wb->len;
511 + return ret;
512 +}
513 +#endif
libnetdata/buffer/buffer.h
+8
@@ -6,6 +6,10 @@
6 #include "../string/utf8.h"
7 #include "../libnetdata.h"
8
9 +#ifdef ENABLE_HTTPD
10 +#include "h2o/memory.h"
11 +#endif
12 +
13 #define WEB_DATA_LENGTH_INCREASE_STEP 1024
14
15 #define BUFFER_JSON_MAX_DEPTH 32 // max is 255
@@ -129,6 +133,10 @@ void buffer_char_replace(BUFFER *wb, char from, char to);
133
134 void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit);
135
136 +#ifdef ENABLE_HTTPD
137 +h2o_iovec_t buffer_to_h2o_iovec(BUFFER *wb);
138 +#endif
139 +
140 static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) {
141 if(unlikely(buffer->len + needed_free_size >= buffer->size))
142 buffer_increase(buffer, needed_free_size + 1);
libnetdata/http/http_defs.h new
+30
@@ -0,0 +1,30 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_HTTP_DEFS_H
4 +#define NETDATA_HTTP_DEFS_H
5 +
6 +// HTTP_CODES 2XX Success
7 +#define HTTP_RESP_OK 200
8 +
9 +// HTTP_CODES 3XX Redirections
10 +#define HTTP_RESP_MOVED_PERM 301
11 +#define HTTP_RESP_REDIR_TEMP 307
12 +#define HTTP_RESP_REDIR_PERM 308
13 +
14 +// HTTP_CODES 4XX Client Errors
15 +#define HTTP_RESP_BAD_REQUEST 400
16 +#define HTTP_RESP_UNAUTHORIZED 401
17 +#define HTTP_RESP_FORBIDDEN 403
18 +#define HTTP_RESP_NOT_FOUND 404
19 +#define HTTP_RESP_CONFLICT 409
20 +#define HTTP_RESP_PRECOND_FAIL 412
21 +#define HTTP_RESP_CONTENT_TOO_LONG 413
22 +
23 +// HTTP_CODES 5XX Server Errors
24 +#define HTTP_RESP_INTERNAL_SERVER_ERROR 500
25 +#define HTTP_RESP_BACKEND_FETCH_FAILED 503 // 503 is right
26 +#define HTTP_RESP_SERVICE_UNAVAILABLE 503 // 503 is right
27 +#define HTTP_RESP_GATEWAY_TIMEOUT 504
28 +#define HTTP_RESP_BACKEND_RESPONSE_INVALID 591
29 +
30 +#endif /* NETDATA_HTTP_DEFS_H */
libnetdata/libnetdata.h
+1
@@ -667,6 +667,7 @@ extern char *netdata_configured_host_prefix;
667 #include "worker_utilization/worker_utilization.h"
668 #include "parser/parser.h"
669 #include "yaml.h"
670 +#include "http/http_defs.h"
671
672 // BEWARE: this exists in alarm-notify.sh
673 #define DEFAULT_CLOUD_BASE_URL "https://app.netdata.cloud"
web/server/web_client.h
-24
@@ -9,30 +9,6 @@
9 extern int web_enable_gzip, web_gzip_level, web_gzip_strategy;
10 #endif /* NETDATA_WITH_ZLIB */
11
12 -// HTTP_CODES 2XX Success
13 -#define HTTP_RESP_OK 200
14 -
15 -// HTTP_CODES 3XX Redirections
16 -#define HTTP_RESP_MOVED_PERM 301
17 -#define HTTP_RESP_REDIR_TEMP 307
18 -#define HTTP_RESP_REDIR_PERM 308
19 -
20 -// HTTP_CODES 4XX Client Errors
21 -#define HTTP_RESP_BAD_REQUEST 400
22 -#define HTTP_RESP_UNAUTHORIZED 401
23 -#define HTTP_RESP_FORBIDDEN 403
24 -#define HTTP_RESP_NOT_FOUND 404
25 -#define HTTP_RESP_CONFLICT 409
26 -#define HTTP_RESP_PRECOND_FAIL 412
27 -#define HTTP_RESP_CONTENT_TOO_LONG 413
28 -
29 -// HTTP_CODES 5XX Server Errors
30 -#define HTTP_RESP_INTERNAL_SERVER_ERROR 500
31 -#define HTTP_RESP_BACKEND_FETCH_FAILED 503
32 -#define HTTP_RESP_SERVICE_UNAVAILABLE 503
33 -#define HTTP_RESP_GATEWAY_TIMEOUT 504
34 -#define HTTP_RESP_BACKEND_RESPONSE_INVALID 591
35 -
12 #define HTTP_REQ_MAX_HEADER_FETCH_TRIES 100
13
14 extern int respect_web_browser_do_not_track_policy;