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(¶ms_string, '&', ¶m.name.len, ¶m.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 = ¶ms_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;