http_server.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "daemon/common.h"
  3. #include "streaming/common.h"
  4. #include "http_server.h"
  5. #include "h2o.h"
  6. #include "h2o/http1.h"
  7. #include "streaming.h"
  8. #include "h2o_utils.h"
  9. static h2o_globalconf_t config;
  10. static h2o_context_t ctx;
  11. static h2o_accept_ctx_t accept_ctx;
  12. #define CONTENT_JSON_UTF8 H2O_STRLIT("application/json; charset=utf-8")
  13. #define CONTENT_TEXT_UTF8 H2O_STRLIT("text/plain; charset=utf-8")
  14. #define NBUF_INITIAL_SIZE_RESP (4096)
  15. #define API_V1_PREFIX "/api/v1/"
  16. #define API_V2_PREFIX "/api/v2/"
  17. #define HOST_SELECT_PREFIX "/host/"
  18. #define HTTPD_CONFIG_SECTION "httpd"
  19. #define HTTPD_ENABLED_DEFAULT false
  20. static void on_accept(h2o_socket_t *listener, const char *err)
  21. {
  22. h2o_socket_t *sock;
  23. if (err != NULL) {
  24. return;
  25. }
  26. if ((sock = h2o_evloop_socket_accept(listener)) == NULL)
  27. return;
  28. h2o_accept(&accept_ctx, sock);
  29. }
  30. static int create_listener(const char *ip, int port)
  31. {
  32. struct sockaddr_in addr;
  33. int fd, reuseaddr_flag = 1;
  34. h2o_socket_t *sock;
  35. memset(&addr, 0, sizeof(addr));
  36. addr.sin_family = AF_INET;
  37. addr.sin_addr.s_addr = inet_addr(ip);
  38. addr.sin_port = htons(port);
  39. if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ||
  40. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 ||
  41. bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 || listen(fd, SOMAXCONN) != 0) {
  42. if (fd != -1)
  43. close(fd);
  44. return -1;
  45. }
  46. sock = h2o_evloop_socket_create(ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ);
  47. h2o_socket_read_start(sock, on_accept);
  48. return 0;
  49. }
  50. static int ssl_init()
  51. {
  52. if (!config_get_boolean(HTTPD_CONFIG_SECTION, "ssl", false))
  53. return 0;
  54. char default_fn[FILENAME_MAX + 1];
  55. snprintfz(default_fn, FILENAME_MAX, "%s/ssl/key.pem", netdata_configured_user_config_dir);
  56. const char *key_fn = config_get(HTTPD_CONFIG_SECTION, "ssl key", default_fn);
  57. snprintfz(default_fn, FILENAME_MAX, "%s/ssl/cert.pem", netdata_configured_user_config_dir);
  58. const char *cert_fn = config_get(HTTPD_CONFIG_SECTION, "ssl certificate", default_fn);
  59. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
  60. accept_ctx.ssl_ctx = SSL_CTX_new(SSLv23_server_method());
  61. #else
  62. accept_ctx.ssl_ctx = SSL_CTX_new(TLS_server_method());
  63. #endif
  64. if (!accept_ctx.ssl_ctx) {
  65. netdata_log_error("Could not allocate a new SSL_CTX");
  66. return -1;
  67. }
  68. SSL_CTX_set_options(accept_ctx.ssl_ctx, SSL_OP_NO_SSLv2);
  69. /* load certificate and private key */
  70. if (SSL_CTX_use_PrivateKey_file(accept_ctx.ssl_ctx, key_fn, SSL_FILETYPE_PEM) != 1) {
  71. netdata_log_error("Could not load server key from \"%s\"", key_fn);
  72. return -1;
  73. }
  74. if (SSL_CTX_use_certificate_file(accept_ctx.ssl_ctx, cert_fn, SSL_FILETYPE_PEM) != 1) {
  75. netdata_log_error("Could not load certificate from \"%s\"", cert_fn);
  76. return -1;
  77. }
  78. h2o_ssl_register_alpn_protocols(accept_ctx.ssl_ctx, h2o_http2_alpn_protocols);
  79. netdata_log_info("SSL support enabled");
  80. return 0;
  81. }
  82. // I did not find a way to do wildcard paths to make common handler for urls like:
  83. // /api/v1/info
  84. // /host/child/api/v1/info
  85. // /host/uuid/api/v1/info
  86. // ideally we could do something like "/*/api/v1/info" subscription
  87. // so we do it "manually" here with uberhandler
  88. static inline int _netdata_uberhandler(h2o_req_t *req, RRDHOST **host)
  89. {
  90. if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
  91. return -1;
  92. static h2o_generator_t generator = { NULL, NULL };
  93. h2o_iovec_t norm_path = req->path_normalized;
  94. if (norm_path.len > strlen(HOST_SELECT_PREFIX) && !memcmp(norm_path.base, HOST_SELECT_PREFIX, strlen(HOST_SELECT_PREFIX))) {
  95. h2o_iovec_t host_id; // host_id can be either and UUID or a hostname of the child
  96. norm_path.base += strlen(HOST_SELECT_PREFIX);
  97. norm_path.len -= strlen(HOST_SELECT_PREFIX);
  98. host_id = norm_path;
  99. size_t end_loc = h2o_strstr(host_id.base, host_id.len, "/", 1);
  100. if (end_loc != SIZE_MAX) {
  101. host_id.len = end_loc;
  102. norm_path.base += end_loc;
  103. norm_path.len -= end_loc;
  104. }
  105. char *c_host_id = iovec_to_cstr(&host_id);
  106. *host = rrdhost_find_by_hostname(c_host_id);
  107. if (!*host)
  108. *host = rrdhost_find_by_guid(c_host_id);
  109. if (!*host)
  110. *host = find_host_by_node_id(c_host_id);
  111. if (!*host) {
  112. req->res.status = HTTP_RESP_BAD_REQUEST;
  113. req->res.reason = "Wrong host id";
  114. h2o_send_inline(req, H2O_STRLIT("Host id provided was not found!\n"));
  115. freez(c_host_id);
  116. return 0;
  117. }
  118. freez(c_host_id);
  119. // we have to rewrite URL here in case this is not an api call
  120. // so that the subsequent file upload handler can send the correct
  121. // files to the client
  122. // if this is not an API call we will abort this handler later
  123. // and let the internal serve file handler of h2o care for things
  124. if (end_loc == SIZE_MAX) {
  125. req->path.len = 1;
  126. req->path_normalized.len = 1;
  127. } else {
  128. size_t offset = norm_path.base - req->path_normalized.base;
  129. req->path.len -= offset;
  130. req->path.base += offset;
  131. req->query_at -= offset;
  132. req->path_normalized.len -= offset;
  133. req->path_normalized.base += offset;
  134. }
  135. }
  136. // workaround for a dashboard bug which causes sometimes urls like
  137. // "//api/v1/info" to be caled instead of "/api/v1/info"
  138. if (norm_path.len > 2 &&
  139. norm_path.base[0] == '/' &&
  140. norm_path.base[1] == '/' ) {
  141. norm_path.base++;
  142. norm_path.len--;
  143. }
  144. unsigned int api_version = 2;
  145. size_t api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V2_PREFIX));
  146. if (api_loc == SIZE_MAX) {
  147. api_version = 1;
  148. api_loc = h2o_strstr(norm_path.base, norm_path.len, H2O_STRLIT(API_V1_PREFIX));
  149. if (api_loc == SIZE_MAX)
  150. return 1;
  151. }
  152. // API_V1_PREFIX and API_V2_PREFIX are the same length
  153. // but I did this just in case someone changes the length of the prefix in future
  154. // so he will not be shot in the leg here
  155. // until then compiler will optimize this out
  156. size_t api_len = api_version == 1 ? strlen(API_V1_PREFIX) : strlen(API_V2_PREFIX);
  157. h2o_iovec_t api_command = norm_path;
  158. api_command.base += api_loc + api_len;
  159. api_command.len -= api_loc + api_len;
  160. if (!api_command.len)
  161. return 1;
  162. // this (emulating struct web_client) is a hack and will be removed
  163. // in future PRs but needs bigger changes in old http_api_v1
  164. // we need to make the web_client_api_request_v1 to be web server
  165. // agnostic and remove the old webservers dependency creep into the
  166. // individual response generators and thus remove the need to "emulate"
  167. // the old webserver calling this function here and in ACLK
  168. struct web_client w;
  169. memset(&w, 0, sizeof(w));
  170. w.response.data = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
  171. w.response.header = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
  172. w.url_query_string_decoded = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
  173. w.url_as_received = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
  174. w.acl = HTTP_ACL_DASHBOARD;
  175. char *path_c_str = iovec_to_cstr(&api_command);
  176. char *path_unescaped = url_unescape(path_c_str);
  177. buffer_strcat(w.url_as_received, iovec_to_cstr(&norm_path));
  178. freez(path_c_str);
  179. IF_HAS_URL_PARAMS(req) {
  180. h2o_iovec_t query_params = URL_PARAMS_IOVEC_INIT_WITH_QUESTIONMARK(req);
  181. char *query_c_str = iovec_to_cstr(&query_params);
  182. char *query_unescaped = url_unescape(query_c_str);
  183. freez(query_c_str);
  184. buffer_strcat(w.url_query_string_decoded, query_unescaped);
  185. freez(query_unescaped);
  186. }
  187. //inline int web_client_api_request_v2(RRDHOST *host, struct web_client *w, char *url_path_endpoint) {
  188. if (api_version == 2)
  189. web_client_api_request_v2(*host, &w, path_unescaped);
  190. else
  191. web_client_api_request_v1(*host, &w, path_unescaped);
  192. freez(path_unescaped);
  193. // we move msg body to req->pool managed memory as it has to
  194. // live until whole response has been encrypted and sent
  195. // when req is finished memory will be freed with the pool
  196. h2o_iovec_t body;
  197. {
  198. BUFFER *wb = w.response.data;
  199. body.base = wb->buffer;
  200. body.len = wb->len;
  201. void *managed = h2o_mem_alloc_shared(&req->pool, body.len, NULL);
  202. memcpy(managed, body.base, body.len);
  203. body.base = managed;
  204. }
  205. req->res.status = HTTP_RESP_OK;
  206. req->res.reason = "OK";
  207. if (w.response.data->content_type == CT_APPLICATION_JSON)
  208. h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_JSON_UTF8);
  209. else
  210. h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
  211. h2o_start_response(req, &generator);
  212. h2o_send(req, &body, 1, H2O_SEND_STATE_FINAL);
  213. buffer_free(w.response.data);
  214. buffer_free(w.response.header);
  215. buffer_free(w.url_query_string_decoded);
  216. buffer_free(w.url_as_received);
  217. return 0;
  218. }
  219. static int netdata_uberhandler(h2o_handler_t *self, h2o_req_t *req)
  220. {
  221. UNUSED(self);
  222. RRDHOST *host = localhost;
  223. int ret = _netdata_uberhandler(req, &host);
  224. if (!ret) {
  225. char host_uuid_str[UUID_STR_LEN];
  226. if (host != NULL)
  227. uuid_unparse_lower(host->host_uuid, host_uuid_str);
  228. nd_log(NDLS_ACCESS, NDLP_DEBUG, "HTTPD OK method: " PRINTF_H2O_IOVEC_FMT
  229. ", path: " PRINTF_H2O_IOVEC_FMT
  230. ", as host: %s"
  231. ", response: %d",
  232. PRINTF_H2O_IOVEC(&req->method),
  233. PRINTF_H2O_IOVEC(&req->input.path),
  234. host == NULL ? "unknown" : (localhost ? "localhost" : host_uuid_str),
  235. req->res.status);
  236. } else {
  237. nd_log(NDLS_ACCESS, NDLP_DEBUG, "HTTPD %d"
  238. " method: " PRINTF_H2O_IOVEC_FMT
  239. ", path: " PRINTF_H2O_IOVEC_FMT
  240. ", forwarding to file handler as path: " PRINTF_H2O_IOVEC_FMT,
  241. ret,
  242. PRINTF_H2O_IOVEC(&req->method),
  243. PRINTF_H2O_IOVEC(&req->input.path),
  244. PRINTF_H2O_IOVEC(&req->path));
  245. }
  246. return ret;
  247. }
  248. static int hdl_netdata_conf(h2o_handler_t *self, h2o_req_t *req)
  249. {
  250. UNUSED(self);
  251. if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")))
  252. return -1;
  253. BUFFER *buf = buffer_create(NBUF_INITIAL_SIZE_RESP, NULL);
  254. config_generate(buf, 0);
  255. void *managed = h2o_mem_alloc_shared(&req->pool, buf->len, NULL);
  256. memcpy(managed, buf->buffer, buf->len);
  257. req->res.status = HTTP_RESP_OK;
  258. req->res.reason = "OK";
  259. h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, NULL, CONTENT_TEXT_UTF8);
  260. h2o_send_inline(req, managed, buf->len);
  261. buffer_free(buf);
  262. return 0;
  263. }
  264. static int hdl_stream(h2o_handler_t *self, h2o_req_t *req)
  265. {
  266. UNUSED(self);
  267. netdata_log_info("Streaming request trough h2o received");
  268. h2o_stream_conn_t *conn = mallocz(sizeof(*conn));
  269. h2o_stream_conn_t_init(conn);
  270. if (is_streaming_handshake(req)) {
  271. h2o_stream_conn_t_destroy(conn);
  272. freez(conn);
  273. return 1;
  274. }
  275. /* build response */
  276. req->res.status = HTTP_RESP_SWITCH_PROTO;
  277. req->res.reason = "Switching Protocols";
  278. h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT(NETDATA_STREAM_PROTO_NAME));
  279. // TODO we should consider adding some nonce header here
  280. // h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("whatever reply"), 0, NULL, accept_key,
  281. // strlen(accept_key));
  282. h2o_http1_upgrade(req, NULL, 0, stream_on_complete, conn);
  283. return 0;
  284. }
  285. #define POLL_INTERVAL 100
  286. void *h2o_main(void *ptr) {
  287. struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
  288. h2o_pathconf_t *pathconf;
  289. h2o_hostconf_t *hostconf;
  290. netdata_thread_disable_cancelability();
  291. const char *bind_addr = config_get(HTTPD_CONFIG_SECTION, "bind to", "127.0.0.1");
  292. int bind_port = config_get_number(HTTPD_CONFIG_SECTION, "port", 19998);
  293. h2o_config_init(&config);
  294. hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), bind_port);
  295. pathconf = h2o_config_register_path(hostconf, "/netdata.conf", 0);
  296. h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
  297. handler->on_req = hdl_netdata_conf;
  298. pathconf = h2o_config_register_path(hostconf, NETDATA_STREAM_URL, 0);
  299. handler = h2o_create_handler(pathconf, sizeof(*handler));
  300. handler->on_req = hdl_stream;
  301. pathconf = h2o_config_register_path(hostconf, "/", 0);
  302. handler = h2o_create_handler(pathconf, sizeof(*handler));
  303. handler->on_req = netdata_uberhandler;
  304. h2o_file_register(pathconf, netdata_configured_web_dir, NULL, NULL, H2O_FILE_FLAG_SEND_COMPRESSED);
  305. h2o_context_init(&ctx, h2o_evloop_create(), &config);
  306. if(ssl_init()) {
  307. error_report("SSL was requested but could not be properly initialized. Aborting.");
  308. return NULL;
  309. }
  310. accept_ctx.ctx = &ctx;
  311. accept_ctx.hosts = config.hosts;
  312. if (create_listener(bind_addr, bind_port) != 0) {
  313. netdata_log_error("failed to create listener %s:%d", bind_addr, bind_port);
  314. return NULL;
  315. }
  316. usec_t last_wpoll = now_monotonic_usec();
  317. while (service_running(SERVICE_HTTPD)) {
  318. int rc = h2o_evloop_run(ctx.loop, POLL_INTERVAL);
  319. if (rc < 0 && errno != EINTR) {
  320. netdata_log_error("h2o_evloop_run returned (%d) with errno other than EINTR. Aborting", rc);
  321. break;
  322. }
  323. usec_t now = now_monotonic_usec();
  324. if (now - last_wpoll > POLL_INTERVAL * USEC_PER_MS) {
  325. last_wpoll = now;
  326. h2o_stream_check_pending_write_reqs();
  327. }
  328. }
  329. static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
  330. return NULL;
  331. }
  332. int httpd_is_enabled() {
  333. return config_get_boolean(HTTPD_CONFIG_SECTION, "enabled", HTTPD_ENABLED_DEFAULT);
  334. }