diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:18 +0000 |
commit | 5da14042f70711ea5cf66e034699730335462f66 (patch) | |
tree | 0f6354ccac934ed87a2d555f45be4c831cf92f4a /web/server/h2o/http_server.c | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz netdata-5da14042f70711ea5cf66e034699730335462f66.zip |
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/server/h2o/http_server.c')
-rw-r--r-- | web/server/h2o/http_server.c | 410 |
1 files changed, 0 insertions, 410 deletions
diff --git a/web/server/h2o/http_server.c b/web/server/h2o/http_server.c deleted file mode 100644 index 08ad120b1..000000000 --- a/web/server/h2o/http_server.c +++ /dev/null @@ -1,410 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "streaming/common.h" -#include "http_server.h" - -#include "h2o.h" -#include "h2o/http1.h" - -#include "streaming.h" -#include "h2o_utils.h" - -static h2o_globalconf_t config; -static h2o_context_t ctx; -static h2o_accept_ctx_t accept_ctx; - -#define CONTENT_JSON_UTF8 H2O_STRLIT("application/json; charset=utf-8") -#define CONTENT_TEXT_UTF8 H2O_STRLIT("text/plain; charset=utf-8") -#define NBUF_INITIAL_SIZE_RESP (4096) -#define API_V1_PREFIX "/api/v1/" -#define API_V2_PREFIX "/api/v2/" -#define HOST_SELECT_PREFIX "/host/" - -#define HTTPD_CONFIG_SECTION "httpd" -#define HTTPD_ENABLED_DEFAULT false - -static void on_accept(h2o_socket_t *listener, const char *err) -{ - h2o_socket_t *sock; - - if (err != NULL) { - return; - } - - if ((sock = h2o_evloop_socket_accept(listener)) == NULL) - return; - h2o_accept(&accept_ctx, sock); -} - -static int create_listener(const char *ip, int port) -{ - struct sockaddr_in addr; - int fd, reuseaddr_flag = 1; - h2o_socket_t *sock; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip); - addr.sin_port = htons(port); - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 || - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 || - bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 || listen(fd, SOMAXCONN) != 0) { - if (fd != -1) - close(fd); - return -1; - } - - sock = h2o_evloop_socket_create(ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ); - h2o_socket_read_start(sock, on_accept); - - return 0; -} - -static int ssl_init() -{ - if (!config_get_boolean(HTTPD_CONFIG_SECTION, "ssl", false)) - return 0; - - char default_fn[FILENAME_MAX + 1]; - - snprintfz(default_fn, FILENAME_MAX, "%s/ssl/key.pem", netdata_configured_user_config_dir); - const char *key_fn = config_get(HTTPD_CONFIG_SECTION, "ssl key", default_fn); - - snprintfz(default_fn, FILENAME_MAX, "%s/ssl/cert.pem", netdata_configured_user_config_dir); - const char *cert_fn = config_get(HTTPD_CONFIG_SECTION, "ssl certificate", default_fn); - -#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110 - accept_ctx.ssl_ctx = SSL_CTX_new(SSLv23_server_method()); -#else - accept_ctx.ssl_ctx = SSL_CTX_new(TLS_server_method()); -#endif - if (!accept_ctx.ssl_ctx) { - netdata_log_error("Could not allocate a new SSL_CTX"); - return -1; - } - - SSL_CTX_set_options(accept_ctx.ssl_ctx, SSL_OP_NO_SSLv2); - - /* load certificate and private key */ - if (SSL_CTX_use_PrivateKey_file(accept_ctx.ssl_ctx, key_fn, SSL_FILETYPE_PEM) != 1) { - netdata_log_error("Could not load server key from \"%s\"", key_fn); - return -1; - } - if (SSL_CTX_use_certificate_file(accept_ctx.ssl_ctx, cert_fn, SSL_FILETYPE_PEM) != 1) { - netdata_log_error("Could not load certificate from \"%s\"", cert_fn); - return -1; - } - - h2o_ssl_register_alpn_protocols(accept_ctx.ssl_ctx, h2o_http2_alpn_protocols); - - netdata_log_info("SSL support enabled"); - - return 0; -} - -// I did not find a way to do wildcard paths to make common handler for urls like: -// /api/v1/info -// /host/child/api/v1/info -// /host/uuid/api/v1/info -// ideally we could do something like "/*/api/v1/info" subscription -// so we do it "manually" here with uberhandler -static inline int _netdata_uberhandler(h2o_req_t *req, RRDHOST **host) -{ - if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) - return -1; - - static h2o_generator_t generator = { NULL, NULL }; - - h2o_iovec_t norm_path = req->path_normalized; - - if (norm_path.len > strlen(HOST_SELECT_PREFIX) && !memcmp(norm_path.base, HOST_SELECT_PREFIX, strlen(HOST_SELECT_PREFIX))) { - h2o_iovec_t host_id; // host_id can be either and UUID or a hostname of the child - - norm_path.base += strlen(HOST_SELECT_PREFIX); - norm_path.len -= strlen(HOST_SELECT_PREFIX); - - host_id = norm_path; - - size_t end_loc = h2o_strstr(host_id.base, host_id.len, "/", 1); - if (end_loc != SIZE_MAX) { - host_id.len = end_loc; - norm_path.base += end_loc; - norm_path.len -= end_loc; - } - - char *c_host_id = iovec_to_cstr(&host_id); - *host = rrdhost_find_by_hostname(c_host_id); - if (!*host) - *host = rrdhost_find_by_guid(c_host_id); - if (!*host) - *host = find_host_by_node_id(c_host_id); - if (!*host) { - req->res.status = HTTP_RESP_BAD_REQUEST; - req->res.reason = "Wrong host id"; - h2o_send_inline(req, H2O_STRLIT("Host id provided was not found!\n")); - freez(c_host_id); - return 0; - } - freez(c_host_id); - - // we have to rewrite URL here in case this is not an api call - // so that the subsequent file upload handler can send the correct - // files to the client - // if this is not an API call we will abort this handler later - // and let the internal serve file handler of h2o care for things - - if (end_loc == SIZE_MAX) { - req->path.len = 1; - req->path_normalized.len = 1; - } else { - size_t offset = norm_path.base - req->path_normalized.base; - req->path.len -= offset; - req->path.base += offset; - req->query_at -= offset; - req->path_normalized.len -= offset; - req->path_normalized.base += offset; - } - } - - // workaround for a dashboard bug which causes sometimes urls like - // "//api/v1/info" to be caled instead of "/api/v1/info" - if (norm_path.len > 2 && - norm_path.base[0] == '/' && - norm_path.base[1] == '/' ) { - norm_path.base++; - norm_path.len--; - } - - unsigned int api_version = 2; - size_t api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V2_PREFIX)); - if (api_loc == SIZE_MAX) { - api_version = 1; - api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V1_PREFIX)); - if (api_loc == SIZE_MAX) - return 1; - } - - // API_V1_PREFIX and API_V2_PREFIX are the same length - // but I did this just in case someone changes the length of the prefix in future - // so he will not be shot in the leg here - // until then compiler will optimize this out - size_t api_len = api_version == 1 ? strlen(API_V1_PREFIX) : strlen(API_V2_PREFIX); - - h2o_iovec_t api_command = norm_path; - api_command.base += api_loc + api_len; - api_command.len -= api_loc + api_len; - - if (!api_command.len) - return 1; - - // this (emulating struct web_client) is a hack and will be removed - // in future PRs but needs bigger changes in old http_api_v1 - // we need to make the web_client_api_request_v1 to be web server - // agnostic and remove the old webservers dependency creep into the - // individual response generators and thus remove the need to "emulate" - // the old webserver calling this function here and in ACLK - struct web_client w; - memset(&w, 0, sizeof(w)); - w.response.data = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL); - w.response.header = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL); - w.url_query_string_decoded = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL); - w.url_as_received = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL); - w.acl = WEB_CLIENT_ACL_DASHBOARD; - - char *path_c_str = iovec_to_cstr(&api_command); - char *path_unescaped = url_unescape(path_c_str); - buffer_strcat(w.url_as_received, iovec_to_cstr(&norm_path)); - freez(path_c_str); - - IF_HAS_URL_PARAMS(req) { - h2o_iovec_t query_params = URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(req); - char *query_c_str = iovec_to_cstr(&query_params); - char *query_unescaped = url_unescape(query_c_str); - freez(query_c_str); - buffer_strcat(w.url_query_string_decoded, query_unescaped); - freez(query_unescaped); - } - -//inline int web_client_api_request_v2(RRDHOST *host, struct web_client *w, char *url_path_endpoint) { - if (api_version == 2) - web_client_api_request_v2(*host, &w, path_unescaped); - else - web_client_api_request_v1(*host, &w, path_unescaped); - freez(path_unescaped); - - h2o_iovec_t body = buffer_to_h2o_iovec(w.response.data); - - // we move msg body to req->pool managed memory as it has to - // live until whole response has been encrypted and sent - // when req is finished memory will be freed with the pool - void *managed = h2o_mem_alloc_shared(&req->pool, body.len, NULL); - memcpy(managed, body.base, body.len); - body.base = managed; - - req->res.status = HTTP_RESP_OK; - req->res.reason = "OK"; - if (w.response.data->content_type == CT_APPLICATION_JSON) - h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_JSON_UTF8); - else - h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8); - h2o_start_response(req, &generator); - h2o_send(req, &body, 1, H2O_SEND_STATE_FINAL); - - buffer_free(w.response.data); - buffer_free(w.response.header); - buffer_free(w.url_query_string_decoded); - buffer_free(w.url_as_received); - - return 0; -} - -static int netdata_uberhandler(h2o_handler_t *self, h2o_req_t *req) -{ - UNUSED(self); - RRDHOST *host = localhost; - - int ret = _netdata_uberhandler(req, &host); - - if (!ret) { - char host_uuid_str[UUID_STR_LEN]; - - if (host != NULL) - uuid_unparse_lower(host->host_uuid, host_uuid_str); - - nd_log(NDLS_ACCESS, NDLP_DEBUG, "HTTPD OK method: " PRINTF_H2O_IOVEC_FMT - ", path: " PRINTF_H2O_IOVEC_FMT - ", as host: %s" - ", response: %d", - PRINTF_H2O_IOVEC(&req->method), - PRINTF_H2O_IOVEC(&req->input.path), - host == NULL ? "unknown" : (localhost ? "localhost" : host_uuid_str), - req->res.status); - } else { - nd_log(NDLS_ACCESS, NDLP_DEBUG, "HTTPD %d" - " method: " PRINTF_H2O_IOVEC_FMT - ", path: " PRINTF_H2O_IOVEC_FMT - ", forwarding to file handler as path: " PRINTF_H2O_IOVEC_FMT, - ret, - PRINTF_H2O_IOVEC(&req->method), - PRINTF_H2O_IOVEC(&req->input.path), - PRINTF_H2O_IOVEC(&req->path)); - } - - return ret; -} - -static int hdl_netdata_conf(h2o_handler_t *self, h2o_req_t *req) -{ - UNUSED(self); - if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) - return -1; - - BUFFER *buf = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL); - config_generate(buf, 0); - - void *managed = h2o_mem_alloc_shared(&req->pool, buf->len, NULL); - memcpy(managed, buf->buffer, buf->len); - - req->res.status = HTTP_RESP_OK; - req->res.reason = "OK"; - h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8); - h2o_send_inline(req, managed, buf->len); - buffer_free(buf); - - return 0; -} - -static int hdl_stream(h2o_handler_t *self, h2o_req_t *req) -{ - UNUSED(self); - netdata_log_info("Streaming request trough h2o received"); - h2o_stream_conn_t *conn = mallocz(sizeof(*conn)); - h2o_stream_conn_t_init(conn); - - if (is_streaming_handshake(req)) { - h2o_stream_conn_t_destroy(conn); - freez(conn); - return 1; - } - - /* build response */ - req->res.status = HTTP_RESP_SWITCH_PROTO; - req->res.reason = "Switching Protocols"; - h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT(NETDATA_STREAM_PROTO_NAME)); - -// TODO we should consider adding some nonce header here -// h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("whatever reply"), 0, NULL, accept_key, -// strlen(accept_key)); - - h2o_http1_upgrade(req, NULL, 0, stream_on_complete, conn); - - return 0; -} - -#define POLL_INTERVAL 100 - -void *h2o_main(void *ptr) { - struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; - - h2o_pathconf_t *pathconf; - h2o_hostconf_t *hostconf; - - netdata_thread_disable_cancelability(); - - const char *bind_addr = config_get(HTTPD_CONFIG_SECTION, "bind to", "127.0.0.1"); - int bind_port = config_get_number(HTTPD_CONFIG_SECTION, "port", 19998); - - h2o_config_init(&config); - hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), bind_port); - - pathconf = h2o_config_register_path(hostconf, "/netdata.conf", 0); - h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler)); - handler->on_req = hdl_netdata_conf; - - pathconf = h2o_config_register_path(hostconf, NETDATA_STREAM_URL, 0); - handler = h2o_create_handler(pathconf, sizeof(*handler)); - handler->on_req = hdl_stream; - - pathconf = h2o_config_register_path(hostconf, "/", 0); - handler = h2o_create_handler(pathconf, sizeof(*handler)); - handler->on_req = netdata_uberhandler; - h2o_file_register(pathconf, netdata_configured_web_dir, NULL, NULL, H2O_FILE_FLAG_SEND_COMPRESSED); - - h2o_context_init(&ctx, h2o_evloop_create(), &config); - - if(ssl_init()) { - error_report("SSL was requested but could not be properly initialized. Aborting."); - return NULL; - } - - accept_ctx.ctx = &ctx; - accept_ctx.hosts = config.hosts; - - if (create_listener(bind_addr, bind_port) != 0) { - netdata_log_error("failed to create listener %s:%d", bind_addr, bind_port); - return NULL; - } - - usec_t last_wpoll = now_monotonic_usec(); - while (service_running(SERVICE_HTTPD)) { - int rc = h2o_evloop_run(ctx.loop, POLL_INTERVAL); - if (rc < 0 && errno != EINTR) { - netdata_log_error("h2o_evloop_run returned (%d) with errno other than EINTR. Aborting", rc); - break; - } - usec_t now = now_monotonic_usec(); - if (now - last_wpoll > POLL_INTERVAL * USEC_PER_MS) { - last_wpoll = now; - - h2o_stream_check_pending_write_reqs(); - } - } - - static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; - return NULL; -} - -int httpd_is_enabled() { - return config_get_boolean(HTTPD_CONFIG_SECTION, "enabled", HTTPD_ENABLED_DEFAULT); -} |