summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/mk_server
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/monkey/mk_server')
-rw-r--r--fluent-bit/lib/monkey/mk_server/CMakeLists.txt57
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_cache.c81
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_clock.c171
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_config.c636
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_fifo.c463
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_header.c451
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_http.c1638
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_http2.c384
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_http_parser.c744
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_http_thread.c290
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_kernel.c165
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_lib.c796
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_mimetype.c227
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_net.c284
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_plugin.c804
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_scheduler.c866
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_server.c679
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_socket.c402
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_stream.c338
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_user.c175
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_utils.c589
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_vhost.c821
-rw-r--r--fluent-bit/lib/monkey/mk_server/monkey.c241
23 files changed, 11302 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/mk_server/CMakeLists.txt b/fluent-bit/lib/monkey/mk_server/CMakeLists.txt
new file mode 100644
index 000000000..457525e62
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/CMakeLists.txt
@@ -0,0 +1,57 @@
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+set(src
+ monkey.c
+ mk_lib.c
+ mk_fifo.c
+ mk_mimetype.c
+ mk_vhost.c
+ mk_header.c
+ mk_config.c
+ mk_user.c
+ mk_utils.c
+ mk_stream.c
+ mk_scheduler.c
+ mk_http.c
+ mk_http_parser.c
+ mk_http_thread.c
+ mk_socket.c
+ mk_net.c
+ mk_clock.c
+ mk_cache.c
+ mk_server.c
+ mk_kernel.c
+ mk_plugin.c
+ )
+
+if(MK_HTTP2)
+ set(src
+ ${src}
+ "mk_http2.c"
+ )
+endif()
+
+# Always build a static library, thats our core :)
+add_library(monkey-core-static STATIC ${src})
+set_target_properties(monkey-core-static PROPERTIES OUTPUT_NAME monkey)
+target_link_libraries(monkey-core-static mk_core ${CMAKE_THREAD_LIBS_INIT} ${STATIC_PLUGINS_LIBS} ${CMAKE_DL_LIBS} rbtree co)
+
+message(STATUS "LINKING ${STATIC_PLUGINS_LIBS}")
+
+if(NOT DEFINED MK_HAVE_REGEX)
+ target_link_libraries(monkey-core-static regex)
+endif()
+
+# Linux Kqueue emulation
+if(MK_HAVE_LINUX_KQUEUE)
+ target_link_libraries(monkey-core-static kqueue)
+endif()
+
+# FreeBSD backtrace
+if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ target_link_libraries(monkey-core-static execinfo)
+endif()
+
+if (CMAKE_SYSTEM_NAME MATCHES "SunOS")
+ target_link_libraries(monkey-core-static socket nsl)
+endif()
diff --git a/fluent-bit/lib/monkey/mk_server/mk_cache.c b/fluent-bit/lib/monkey/mk_server/mk_cache.c
new file mode 100644
index 000000000..c678afa82
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_cache.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <monkey/mk_core.h>
+#include <monkey/mk_cache.h>
+#include <monkey/mk_cache_tls.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_tls.h>
+
+/* This function is called when a thread is created */
+void mk_cache_worker_init()
+{
+ char *cache_error;
+ mk_ptr_t *p_tmp;
+
+ /* Cache header request -> last modified */
+ p_tmp = mk_mem_alloc_z(sizeof(mk_ptr_t));
+ p_tmp->data = mk_mem_alloc_z(32);
+ p_tmp->len = -1;
+ MK_TLS_SET(mk_tls_cache_header_lm, p_tmp);
+
+ /* Cache header request -> content length */
+ p_tmp = mk_mem_alloc_z(sizeof(mk_ptr_t));
+ p_tmp->data = mk_mem_alloc_z(MK_UTILS_INT2MKP_BUFFER_LEN);
+ p_tmp->len = -1;
+ MK_TLS_SET(mk_tls_cache_header_cl, p_tmp);
+
+ /* Cache gmtime buffer */
+ MK_TLS_SET(mk_tls_cache_gmtime, mk_mem_alloc(sizeof(struct tm)));
+
+ /* Cache the most used text representations of utime2gmt */
+ MK_TLS_SET(mk_tls_cache_gmtext,
+ mk_mem_alloc_z(sizeof(struct mk_gmt_cache) * MK_GMT_CACHES));
+
+ /* Cache buffer for strerror_r(2) */
+ cache_error = mk_mem_alloc(MK_UTILS_ERROR_SIZE);
+ pthread_setspecific(mk_utils_error_key, (void *) cache_error);
+}
+
+void mk_cache_worker_exit()
+{
+ char *cache_error;
+
+ /* Cache header request -> last modified */
+ mk_ptr_free(MK_TLS_GET(mk_tls_cache_header_lm));
+ mk_mem_free(MK_TLS_GET(mk_tls_cache_header_lm));
+
+ /* Cache header request -> content length */
+ mk_ptr_free(MK_TLS_GET(mk_tls_cache_header_cl));
+ mk_mem_free(MK_TLS_GET(mk_tls_cache_header_cl));
+
+ /* Cache gmtime buffer */
+ mk_mem_free(MK_TLS_GET(mk_tls_cache_gmtime));
+
+ /* Cache the most used text representations of utime2gmt */
+ mk_mem_free(MK_TLS_GET(mk_tls_cache_gmtext));
+
+ /* Cache buffer for strerror_r(2) */
+ cache_error = pthread_getspecific(mk_utils_error_key);
+ mk_mem_free(cache_error);
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_clock.c b/fluent-bit/lib/monkey/mk_server/mk_clock.c
new file mode 100644
index 000000000..3a45512f2
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_clock.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <mk_core/mk_pthread.h>
+#include <mk_core/mk_unistd.h>
+
+#include <monkey/mk_core.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_tls.h>
+
+#ifdef _WIN32
+static struct tm* localtime_r(const time_t* timep, struct tm* result)
+{
+ localtime_s(result, timep);
+
+ return result;
+}
+
+static struct tm* gmtime_r(const time_t* timep, struct tm* result)
+{
+ gmtime_s(result, timep);
+
+ return result;
+}
+#endif
+
+
+/*
+ * The mk_ptr_ts have two buffers for avoid in half-way access from
+ * another thread while a buffer is being modified. The function below returns
+ * one of two buffers to work with.
+ */
+static inline char *_next_buffer(mk_ptr_t *pointer, char **buffers)
+{
+ if (pointer->data == buffers[0]) {
+ return buffers[1];
+ }
+ else {
+ return buffers[0];
+ }
+}
+
+static void mk_clock_log_set_time(time_t utime, struct mk_server *server)
+{
+ char *time_string;
+ struct tm result;
+
+ time_string = _next_buffer(&server->clock_context->log_current_time, server->clock_context->log_time_buffers);
+ server->clock_context->log_current_utime = utime;
+
+ strftime(time_string, LOG_TIME_BUFFER_SIZE, "[%d/%b/%G %T %z]",
+ localtime_r(&utime, &result));
+
+ server->clock_context->log_current_time.data = time_string;
+}
+
+static void mk_clock_headers_preset(time_t utime, struct mk_server *server)
+{
+ int len1;
+ int len2;
+ struct tm *gmt_tm;
+ struct tm result;
+ char *buffer;
+
+ buffer = _next_buffer(&server->clock_context->headers_preset,
+ server->clock_context->header_time_buffers);
+
+ gmt_tm = gmtime_r(&utime, &result);
+
+ len1 = snprintf(buffer,
+ HEADER_TIME_BUFFER_SIZE,
+ "%s",
+ server->server_signature_header);
+
+ len2 = strftime(buffer + len1,
+ HEADER_PRESET_SIZE - len1,
+ MK_CLOCK_GMT_DATEFORMAT,
+ gmt_tm);
+
+ server->clock_context->headers_preset.data = buffer;
+ server->clock_context->headers_preset.len = len1 + len2;
+}
+
+void *mk_clock_worker_init(void *data)
+{
+ time_t cur_time;
+ struct mk_server *server = data;
+
+ mk_utils_worker_rename("monkey: clock");
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ server->clock_context->mk_clock_tid = pthread_self();
+
+ while (1) {
+ cur_time = time(NULL);
+
+ if(cur_time != ((time_t)-1)) {
+ mk_clock_log_set_time(cur_time, server);
+ mk_clock_headers_preset(cur_time, server);
+ }
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+void mk_clock_exit(struct mk_server *server)
+{
+ pthread_cancel(server->clock_context->mk_clock_tid);
+ pthread_join(server->clock_context->mk_clock_tid, NULL);
+
+ mk_mem_free(server->clock_context->header_time_buffers[0]);
+ mk_mem_free(server->clock_context->header_time_buffers[1]);
+ mk_mem_free(server->clock_context->log_time_buffers[0]);
+ mk_mem_free(server->clock_context->log_time_buffers[1]);
+
+ mk_mem_free(server->clock_context);
+}
+
+/* This function must be called before any threads are created */
+void mk_clock_sequential_init(struct mk_server *server)
+{
+ server->clock_context = mk_mem_alloc_z(sizeof(struct mk_clock_context));
+
+ if (server->clock_context == NULL) {
+ return;
+ }
+
+ /* Time when monkey was started */
+ server->clock_context->monkey_init_time = time(NULL);
+
+ server->clock_context->log_current_time.len = LOG_TIME_BUFFER_SIZE - 2;
+ server->clock_context->headers_preset.len = HEADER_PRESET_SIZE - 1;
+
+ server->clock_context->header_time_buffers[0] = mk_mem_alloc_z(HEADER_PRESET_SIZE);
+ server->clock_context->header_time_buffers[1] = mk_mem_alloc_z(HEADER_PRESET_SIZE);
+
+ server->clock_context->log_time_buffers[0] = mk_mem_alloc_z(LOG_TIME_BUFFER_SIZE);
+ server->clock_context->log_time_buffers[1] = mk_mem_alloc_z(LOG_TIME_BUFFER_SIZE);
+
+ /* Set the time once */
+ time_t cur_time = time(NULL);
+
+ if (cur_time != ((time_t)-1)) {
+ mk_clock_log_set_time(cur_time, server);
+ mk_clock_headers_preset(cur_time, server);
+ }
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_config.c b/fluent-bit/lib/monkey/mk_server/mk_config.c
new file mode 100644
index 000000000..f8dc9c00f
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_config.c
@@ -0,0 +1,636 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_kernel.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_info.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_info.h>
+
+#include <ctype.h>
+#include <limits.h>
+#include <mk_core/mk_dirent.h>
+#include <sys/stat.h>
+
+struct mk_server_config *mk_config;
+
+static int mk_config_key_have(struct mk_list *list, const char *value)
+{
+ struct mk_list *head;
+ struct mk_string_line *entry;
+
+ mk_list_foreach(head, list) {
+ entry = mk_list_entry(head, struct mk_string_line, _head);
+ if (strcasecmp(entry->val, value) == 0) {
+ return MK_TRUE;
+ }
+ }
+ return MK_FALSE;
+}
+
+void mk_config_listeners_free(struct mk_server *server)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_config_listener *l;
+
+ mk_list_foreach_safe(head, tmp, &server->listeners) {
+ l = mk_list_entry(head, struct mk_config_listener, _head);
+ mk_list_del(&l->_head);
+ mk_mem_free(l->address);
+ mk_mem_free(l->port);
+ mk_mem_free(l);
+ }
+}
+
+void mk_config_free_all(struct mk_server *server)
+{
+ mk_vhost_free_all(server);
+ mk_mimetype_free_all(server);
+
+ if (server->config) {
+ mk_rconf_free(server->config);
+ }
+
+ if (server->path_conf_root) {
+ mk_mem_free(server->path_conf_root);
+ }
+
+ if (server->path_conf_pidfile) {
+ mk_mem_free(server->path_conf_pidfile);
+ }
+
+ if (server->conf_user_pub) {
+ mk_mem_free(server->conf_user_pub);
+ }
+
+ /* free config->index_files */
+ if (server->index_files) {
+ mk_string_split_free(server->index_files);
+ }
+
+ if (server->user) {
+ mk_mem_free(server->user);
+ }
+
+ if (server->transport_layer) {
+ mk_mem_free(server->transport_layer);
+ }
+
+ mk_config_listeners_free(server);
+
+ mk_ptr_free(&server->server_software);
+ mk_mem_free(server);
+}
+
+/* Print a specific error */
+static void mk_config_print_error_msg(char *variable, char *path)
+{
+ mk_err("[config] %s at %s has an invalid value",
+ variable, path);
+ mk_mem_free(path);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Check if at least one of the Listen interfaces are being used by another
+ * process.
+ */
+int mk_config_listen_check_busy(struct mk_server *server)
+{
+ int fd;
+ struct mk_list *head;
+ struct mk_plugin *p;
+ struct mk_config_listener *listen;
+
+ p = mk_plugin_cap(MK_CAP_SOCK_PLAIN, server);
+ if (!p) {
+ mk_warn("Listen check: consider build monkey with basic socket handling!");
+ return MK_FALSE;
+ }
+
+ mk_list_foreach(head, &server->listeners) {
+ listen = mk_list_entry(head, struct mk_config_listener, _head);
+
+ fd = mk_socket_connect(listen->address, atol(listen->port), MK_FALSE);
+ if (fd != -1) {
+ close(fd);
+ return MK_TRUE;
+ }
+ }
+
+ return MK_FALSE;
+}
+
+int mk_config_listen_parse(char *value, struct mk_server *server)
+{
+ int ret = -1;
+ int flags = 0;
+ long port_num;
+ char *address = NULL;
+ char *port = NULL;
+ char *divider;
+ struct mk_list *list = NULL;
+ struct mk_string_line *listener;
+
+ list = mk_string_split_line(value);
+ if (!list) {
+ goto error;
+ }
+
+ if (mk_list_is_empty(list) == 0) {
+ goto error;
+ }
+
+ /* Parse the listener interface */
+ listener = mk_list_entry_first(list, struct mk_string_line, _head);
+ if (listener->val[0] == '[') {
+ /* IPv6 address */
+ divider = strchr(listener->val, ']');
+ if (divider == NULL) {
+ mk_err("[config] Expected closing ']' in IPv6 address.");
+ goto error;
+ }
+ if (divider[1] != ':' || divider[2] == '\0') {
+ mk_err("[config] Expected ':port' after IPv6 address.");
+ goto error;
+ }
+
+ address = mk_string_copy_substr(listener->val + 1, 0,
+ divider - listener->val - 1);
+ port = mk_string_dup(divider + 2);
+ }
+ else if (strchr(listener->val, ':') != NULL) {
+ /* IPv4 address */
+ divider = strrchr(listener->val, ':');
+ if (divider == NULL || divider[1] == '\0') {
+ mk_err("[config] Expected ':port' after IPv4 address.");
+ goto error;
+ }
+
+ address = mk_string_copy_substr(listener->val, 0,
+ divider - listener->val);
+ port = mk_string_dup(divider + 1);
+ }
+ else {
+ /* Port only */
+ address = NULL;
+ port = mk_string_dup(listener->val);
+ }
+
+ errno = 0;
+ port_num = strtol(port, NULL, 10);
+ if (errno != 0 || port_num == LONG_MAX || port_num == LONG_MIN) {
+ mk_warn("Using defaults, could not understand \"Listen %s\"",
+ listener->val);
+ port = NULL;
+ }
+
+ /* Check extra properties of the listener */
+ flags = MK_CAP_HTTP;
+ if (mk_config_key_have(list, "!http")) {
+ flags |= ~MK_CAP_HTTP;
+ }
+
+#ifdef MK_HAVE_HTTP2
+ if (mk_config_key_have(list, "h2")) {
+ flags |= (MK_CAP_HTTP2 | MK_CAP_SOCK_TLS);
+ }
+
+ if (mk_config_key_have(list, "h2c")) {
+ flags |= MK_CAP_HTTP2;
+ }
+#endif
+
+ if (mk_config_key_have(list, "tls")) {
+ flags |= MK_CAP_SOCK_TLS;
+ }
+
+ /* register the new listener */
+ mk_config_listener_add(address, port, flags, server);
+ mk_string_split_free(list);
+ list = NULL;
+ ret = 0;
+
+error:
+ if (address) {
+ mk_mem_free(address);
+ }
+ if (port) {
+ mk_mem_free(port);
+ }
+ if (list) {
+ mk_string_split_free(list);
+ }
+
+ return ret;
+}
+
+static int mk_config_listen_read(struct mk_rconf_section *section,
+ struct mk_server *server)
+{
+ int ret;
+ struct mk_list *cur;
+ struct mk_rconf_entry *entry;
+
+ mk_list_foreach(cur, &section->entries) {
+ entry = mk_list_entry(cur, struct mk_rconf_entry, _head);
+ if (strcasecmp(entry->key, "Listen")) {
+ continue;
+ }
+
+ ret = mk_config_listen_parse(entry->val, server);
+ if (ret != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Read configuration files */
+static int mk_config_read_files(char *path_conf, char *file_conf,
+ struct mk_server *server)
+{
+ unsigned long len;
+ char *tmp = NULL;
+ struct stat checkdir;
+ struct mk_rconf *cnf;
+ struct mk_rconf_section *section;
+
+ if (!path_conf) {
+ return -1;
+ }
+
+ if (!file_conf) {
+ file_conf = "monkey.conf";
+ }
+
+ server->path_conf_root = mk_string_dup(path_conf);
+
+ if (stat(server->path_conf_root, &checkdir) == -1) {
+ mk_err("ERROR: Cannot find/open '%s'", server->path_conf_root);
+ return -1;
+ }
+
+ mk_string_build(&tmp, &len, "%s/%s", path_conf, file_conf);
+ cnf = mk_rconf_open(tmp);
+ if (!cnf) {
+ mk_mem_free(tmp);
+ mk_err("Cannot read '%s'", server->conf_main);
+ return -1;
+ }
+ section = mk_rconf_section_get(cnf, "SERVER");
+ if (!section) {
+ mk_err("ERROR: No 'SERVER' section defined");
+ return -1;
+ }
+
+ /* Map source configuration */
+ server->config = cnf;
+
+ /* Listen */
+ if (!server->port_override) {
+ /* Process each Listen entry */
+ if (mk_config_listen_read(section, server)) {
+ mk_err("[config] Failed to read listen sections.");
+ }
+ if (mk_list_is_empty(&server->listeners) == 0) {
+ mk_warn("[config] No valid Listen entries found, set default");
+ mk_config_listener_add(NULL, NULL, MK_CAP_HTTP, server);
+ }
+ }
+ else {
+ mk_config_listener_add(NULL, server->port_override,
+ MK_CAP_HTTP, server);
+ }
+
+ /* Number of thread workers */
+ if (server->workers == -1) {
+ server->workers = (size_t) mk_rconf_section_get_key(section,
+ "Workers",
+ MK_RCONF_NUM);
+ }
+
+ if (server->workers < 1) {
+ server->workers = mk_utils_get_system_core_count();
+
+ if (server->workers < 1) {
+ mk_config_print_error_msg("Workers", tmp);
+ }
+ }
+
+ /* Timeout */
+ server->timeout = (size_t) mk_rconf_section_get_key(section,
+ "Timeout", MK_RCONF_NUM);
+ if (server->timeout < 1) {
+ mk_config_print_error_msg("Timeout", tmp);
+ }
+
+ /* KeepAlive */
+ server->keep_alive = (size_t) mk_rconf_section_get_key(section,
+ "KeepAlive",
+ MK_RCONF_BOOL);
+ if (server->keep_alive == MK_ERROR) {
+ mk_config_print_error_msg("KeepAlive", tmp);
+ }
+
+ /* MaxKeepAliveRequest */
+ server->max_keep_alive_request = (size_t)
+ mk_rconf_section_get_key(section,
+ "MaxKeepAliveRequest",
+ MK_RCONF_NUM);
+
+ if (server->max_keep_alive_request == 0) {
+ mk_config_print_error_msg("MaxKeepAliveRequest", tmp);
+ }
+
+ /* KeepAliveTimeout */
+ server->keep_alive_timeout = (size_t) mk_rconf_section_get_key(section,
+ "KeepAliveTimeout",
+ MK_RCONF_NUM);
+ if (server->keep_alive_timeout == 0) {
+ mk_config_print_error_msg("KeepAliveTimeout", tmp);
+ }
+
+ /* Pid File */
+ if (!server->path_conf_pidfile) {
+ server->path_conf_pidfile = mk_rconf_section_get_key(section,
+ "PidFile",
+ MK_RCONF_STR);
+ }
+
+ /* Home user's directory /~ */
+ server->conf_user_pub = mk_rconf_section_get_key(section,
+ "UserDir",
+ MK_RCONF_STR);
+
+ /* Index files */
+ server->index_files = mk_rconf_section_get_key(section,
+ "Indexfile", MK_RCONF_LIST);
+
+ /* HideVersion Variable */
+ server->hideversion = (size_t) mk_rconf_section_get_key(section,
+ "HideVersion",
+ MK_RCONF_BOOL);
+ if (server->hideversion == MK_ERROR) {
+ mk_config_print_error_msg("HideVersion", tmp);
+ }
+
+ /* User Variable */
+ server->user = mk_rconf_section_get_key(section, "User", MK_RCONF_STR);
+
+ /* Resume */
+ server->resume = (size_t) mk_rconf_section_get_key(section,
+ "Resume", MK_RCONF_BOOL);
+ if (server->resume == MK_ERROR) {
+ mk_config_print_error_msg("Resume", tmp);
+ }
+
+ /* Max Request Size */
+ server->max_request_size = (size_t) mk_rconf_section_get_key(section,
+ "MaxRequestSize",
+ MK_RCONF_NUM);
+ if (server->max_request_size <= 0) {
+ mk_config_print_error_msg("MaxRequestSize", tmp);
+ }
+ else {
+ server->max_request_size *= 1024;
+ }
+
+ /* Symbolic Links */
+ server->symlink = (size_t) mk_rconf_section_get_key(section,
+ "SymLink", MK_RCONF_BOOL);
+ if (server->symlink == MK_ERROR) {
+ mk_config_print_error_msg("SymLink", tmp);
+ }
+
+ /* Transport Layer plugin */
+ if (!server->transport_layer) {
+ server->transport_layer = mk_rconf_section_get_key(section,
+ "TransportLayer",
+ MK_RCONF_STR);
+ }
+
+ /* Default Mimetype */
+ mk_mem_free(tmp);
+ tmp = mk_rconf_section_get_key(section, "DefaultMimeType", MK_RCONF_STR);
+ if (tmp) {
+ mk_string_build(&server->mimetype_default_str, &len, "%s\r\n", tmp);
+ }
+
+ /* File Descriptor Table (FDT) */
+ server->fdt = (size_t) mk_rconf_section_get_key(section,
+ "FDT",
+ MK_RCONF_BOOL);
+
+ /* FIXME: Overcapacity not ready */
+ server->fd_limit = (size_t) mk_rconf_section_get_key(section,
+ "FDLimit",
+ MK_RCONF_NUM);
+ /* Get each worker clients capacity based on FDs system limits */
+ server->server_capacity = mk_server_capacity(server);
+
+
+ if (!server->one_shot) {
+ mk_vhost_init(path_conf, server);
+ }
+ else {
+ mk_vhost_set_single(server->one_shot, server);
+ }
+
+ mk_mem_free(tmp);
+ return 0;
+}
+
+void mk_config_signature(struct mk_server *server)
+{
+ unsigned long len;
+
+ /* Server Signature */
+ if (server->hideversion == MK_FALSE) {
+ snprintf(server->server_signature,
+ sizeof(server->server_signature) - 1,
+ "Monkey/%s", MK_VERSION_STR);
+ }
+ else {
+ snprintf(server->server_signature,
+ sizeof(server->server_signature) - 1,
+ "Monkey");
+ }
+ len = snprintf(server->server_signature_header,
+ sizeof(server->server_signature_header) - 1,
+ "Server: %s\r\n", server->server_signature);
+ server->server_signature_header_len = len;
+}
+
+/* read main configuration from monkey.conf */
+void mk_config_start_configure(struct mk_server *server)
+{
+ int ret;
+ unsigned long len;
+
+ ret = mk_config_read_files(server->path_conf_root,
+ server->conf_main, server);
+ if (ret != 0) {
+ return;
+ }
+
+ /* Load mimes */
+ mk_mimetype_read_config(server);
+
+ mk_ptr_reset(&server->server_software);
+
+ /* Basic server information */
+ if (server->hideversion == MK_FALSE) {
+ mk_string_build(&server->server_software.data,
+ &len, "Monkey/%s (%s)", MK_VERSION_STR, MK_BUILD_OS);
+ server->server_software.len = len;
+ }
+ else {
+ mk_string_build(&server->server_software.data, &len, "Monkey Server");
+ server->server_software.len = len;
+ }
+}
+
+/* Register a new listener into the main configuration */
+struct mk_config_listener *mk_config_listener_add(char *address,
+ char *port, int flags,
+ struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_config_listener *check;
+ struct mk_config_listener *listen = NULL;
+
+ listen = mk_mem_alloc(sizeof(struct mk_config_listener));
+ if (!listen) {
+ mk_err("[listen_add] malloc() failed");
+ return NULL;
+ }
+
+ if (!address) {
+ listen->address = mk_string_dup(MK_DEFAULT_LISTEN_ADDR);
+ }
+ else {
+ listen->address = mk_string_dup(address);
+ }
+
+ /* Set the port */
+ if (!port) {
+ mk_err("[listen_add] TCP port not defined");
+ exit(EXIT_FAILURE);
+ }
+
+ listen->port = mk_string_dup(port);
+ listen->flags = flags;
+
+ /* Before to add a new listener, lets make sure it's not a duplicated */
+ mk_list_foreach(head, &server->listeners) {
+ check = mk_list_entry(head, struct mk_config_listener, _head);
+ if (strcmp(listen->address, check->address) == 0 &&
+ strcmp(listen->port, check->port) == 0) {
+ mk_warn("Listener: duplicated %s:%s, skip.",
+ listen->address, listen->port);
+
+ /* free resources */
+ mk_mem_free(listen->address);
+ mk_mem_free(listen->port);
+ mk_mem_free(listen);
+ return NULL;
+ }
+ }
+
+ mk_list_add(&listen->_head, &server->listeners);
+ return listen;
+}
+
+void mk_config_set_init_values(struct mk_server *server)
+{
+ /* Init values */
+ server->is_seteuid = MK_FALSE;
+ server->timeout = 15;
+ server->hideversion = MK_FALSE;
+ server->keep_alive = MK_TRUE;
+ server->keep_alive_timeout = 15;
+ server->max_keep_alive_request = 50;
+ server->resume = MK_TRUE;
+ server->standard_port = 80;
+ server->symlink = MK_FALSE;
+ server->nhosts = 0;
+ mk_list_init(&server->hosts);
+ server->user = NULL;
+ server->open_flags = O_RDONLY; /* The only place this is effectively used (other than the sanity check)
+ * is mk_http.c where it's used to test for file existence and the fd is apparently leaked */
+ server->index_files = NULL;
+ server->conf_user_pub = NULL;
+ server->workers = 1;
+
+ /* TCP REUSEPORT: available on Linux >= 3.9 */
+ if (server->scheduler_mode == -1) {
+ if (server->kernel_features & MK_KERNEL_SO_REUSEPORT) {
+ server->scheduler_mode = MK_SCHEDULER_REUSEPORT;
+ }
+ else {
+ server->scheduler_mode = MK_SCHEDULER_FAIR_BALANCING;
+ }
+ }
+
+ /* Max request buffer size allowed
+ * right now, every chunk size is 4KB (4096 bytes),
+ * so we are setting a maximum request size to 32 KB */
+ server->max_request_size = MK_REQUEST_CHUNK * 8;
+
+ /* Internals */
+ server->safe_event_write = MK_FALSE;
+
+ /* Init plugin list */
+ mk_list_init(&server->plugins);
+
+ /* Init listeners */
+ mk_list_init(&server->listeners);
+}
+
+void mk_config_sanity_check(struct mk_server *server)
+{
+ /* Check O_NOATIME for current user, flag will just be used
+ * if running user is allowed to.
+ */
+ int fd;
+ int flags;
+
+ if (!server->path_conf_root) {
+ return;
+ }
+
+ flags = server->open_flags;
+ flags |= O_NOATIME;
+ fd = open(server->path_conf_root, flags);
+
+ if (fd > -1) {
+ server->open_flags = flags;
+ close(fd);
+ }
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_fifo.c b/fluent-bit/lib/monkey/mk_server/mk_fifo.c
new file mode 100644
index 000000000..fd148db7b
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_fifo.c
@@ -0,0 +1,463 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_fifo.h>
+#include <monkey/mk_scheduler.h>
+
+#ifdef _WIN32
+#include <event.h>
+#endif
+
+static struct mk_fifo_worker *mk_fifo_worker_create(struct mk_fifo *ctx,
+ void *data)
+{
+ int id;
+ int ret;
+ struct mk_fifo_worker *fw;
+
+ /* Get an ID */
+ id = mk_list_size(&ctx->workers);
+
+ fw = mk_mem_alloc_z(sizeof(struct mk_fifo_worker));
+ if (!fw) {
+ perror("malloc");
+ return NULL;
+ }
+ MK_EVENT_NEW(&fw->event);
+
+ fw->worker_id = id;
+ fw->data = data;
+ fw->fifo = ctx;
+
+ fw->buf_data = mk_mem_alloc(MK_FIFO_BUF_SIZE);
+ if (!fw->buf_data) {
+ perror("malloc");
+ mk_mem_free(fw);
+ return NULL;
+ }
+ fw->buf_len = 0;
+ fw->buf_size = MK_FIFO_BUF_SIZE;
+
+#ifdef _WIN32
+ ret = evutil_socketpair(AF_INET, SOCK_STREAM, 0, fw->channel);
+ if (ret == -1) {
+ perror("socketpair");
+ mk_mem_free(fw);
+ return NULL;
+ }
+#else
+ ret = pipe(fw->channel);
+ if (ret == -1) {
+ perror("pipe");
+ mk_mem_free(fw);
+ return NULL;
+ }
+#endif
+
+ mk_list_add(&fw->_head, &ctx->workers);
+ return fw;
+}
+
+/*
+ * Function used as a callback triggered by mk_worker_callback() or
+ * through a mk_sched_worker_cb_add(). It purpose is to prepare the
+ * channels on the final worker thread so it can consume pushed
+ * messages.
+ */
+void mk_fifo_worker_setup(void *data)
+{
+ struct mk_fifo_worker *mw = NULL;
+ struct mk_fifo *ctx = data;
+
+ pthread_mutex_lock(&ctx->mutex_init);
+
+ mw = mk_fifo_worker_create(ctx, data);
+ if (!mw) {
+ mk_err("[msg] error configuring msg-worker context ");
+ pthread_mutex_unlock(&ctx->mutex_init);
+ return;
+ }
+
+ /* Make the current worker context available */
+ pthread_setspecific(*ctx->key, mw);
+ pthread_mutex_unlock(&ctx->mutex_init);
+}
+
+struct mk_fifo *mk_fifo_create(pthread_key_t *key, void *data)
+{
+ struct mk_fifo *ctx;
+
+ ctx = mk_mem_alloc(sizeof(struct mk_fifo));
+ if (!ctx) {
+ perror("malloc");
+ return NULL;
+ }
+ ctx->data = data;
+
+ /* Lists */
+ mk_list_init(&ctx->queues);
+ mk_list_init(&ctx->workers);
+
+
+ /* Pthread specifics */
+
+ /* We need to isolate this because there is a key that's shared between monkey
+ * instances by design.
+ */
+ if (key != NULL) {
+ ctx->key = key;
+ pthread_key_create(ctx->key, NULL);
+ }
+
+ pthread_mutex_init(&ctx->mutex_init, NULL);
+
+ return ctx;
+}
+
+int mk_fifo_queue_create(struct mk_fifo *ctx, char *name,
+ void (*cb)(struct mk_fifo_queue *, void *,
+ size_t, void *),
+ void *data)
+
+{
+ int id = -1;
+ int len;
+ struct mk_list *head;
+ struct mk_fifo_queue *q;
+
+ /* Get ID for the new queue */
+ if (mk_list_is_empty(&ctx->queues) == 0) {
+ id = 0;
+ }
+ else {
+ q = mk_list_entry_last(&ctx->queues, struct mk_fifo_queue, _head);
+ id = q->id + 1;
+ }
+
+ /* queue name might need to be truncated if is too long */
+ len = strlen(name);
+ if (len > (int) sizeof(q->name) - 1) {
+ len = sizeof(q->name) - 1;
+ }
+
+ /* Validate that name is not a duplicated */
+ mk_list_foreach(head, &ctx->queues) {
+ q = mk_list_entry(head, struct mk_fifo_queue, _head);
+ if (strlen(q->name) != (unsigned int) len) {
+ continue;
+ }
+
+ if (strncmp(q->name, name, len) == 0) {
+ return -1;
+ }
+ }
+
+ /* Allocate and register queue */
+ q = mk_mem_alloc(sizeof(struct mk_fifo_queue));
+ if (!q) {
+ perror("malloc");
+ return -1;
+ }
+ q->id = id;
+ q->cb_message = cb;
+ q->data = data;
+
+ strncpy(q->name, name, len);
+ q->name[len] = '\0';
+ mk_list_add(&q->_head, &ctx->queues);
+
+ return id;
+}
+
+struct mk_fifo_queue *mk_fifo_queue_get(struct mk_fifo *ctx, int id)
+{
+ struct mk_list *head;
+ struct mk_fifo_queue *q = NULL;
+
+ mk_list_foreach(head, &ctx->queues) {
+ q = mk_list_entry(head, struct mk_fifo_queue, _head);
+ if (q->id == id) {
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+int mk_fifo_queue_destroy(struct mk_fifo *ctx, struct mk_fifo_queue *q)
+{
+ (void) ctx;
+
+ mk_list_del(&q->_head);
+ mk_mem_free(q);
+ return 0;
+}
+
+int mk_fifo_queue_id_destroy(struct mk_fifo *ctx, int id)
+{
+ struct mk_fifo_queue *q;
+
+ q = mk_fifo_queue_get(ctx, id);
+ if (!q) {
+ return -1;
+ }
+
+ mk_fifo_queue_destroy(ctx, q);
+ return 0;
+}
+
+static int mk_fifo_queue_destroy_all(struct mk_fifo *ctx)
+{
+ int c = 0;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_fifo_queue *q;
+
+ mk_list_foreach_safe(head, tmp, &ctx->queues) {
+ q = mk_list_entry(head, struct mk_fifo_queue, _head);
+ mk_fifo_queue_destroy(ctx, q);
+ c++;
+ }
+
+ return c;
+}
+
+static int mk_fifo_worker_destroy_all(struct mk_fifo *ctx)
+{
+ int c = 0;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_fifo_worker *fw;
+
+ mk_list_foreach_safe(head, tmp, &ctx->workers) {
+ fw = mk_list_entry(head, struct mk_fifo_worker, _head);
+
+#ifdef _WIN32
+ evutil_closesocket(fw->channel[0]);
+ evutil_closesocket(fw->channel[1]);
+#else
+ close(fw->channel[0]);
+ close(fw->channel[1]);
+#endif
+ mk_list_del(&fw->_head);
+ mk_mem_free(fw->buf_data);
+ mk_mem_free(fw);
+ c++;
+ }
+
+ return c;
+}
+
+static int msg_write(int fd, void *buf, size_t count)
+{
+ ssize_t bytes;
+ size_t total = 0;
+
+ do {
+#ifdef _WIN32
+ bytes = send(fd, (uint8_t *)buf + total, count - total, 0);
+#else
+ bytes = write(fd, (uint8_t *)buf + total, count - total);
+#endif
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ /*
+ * This could happen, since this function goal is not to
+ * return until all data have been read, just sleep a little
+ * bit (0.05 seconds)
+ */
+
+#ifdef _WIN32
+ Sleep(5);
+#else
+ usleep(50000);
+#endif
+ continue;
+ }
+ }
+ else if (bytes == 0) {
+ /* Broken pipe ? */
+ perror("write");
+ return -1;
+ }
+ total += bytes;
+
+ } while (total < count);
+
+ return total;
+}
+
+/*
+ * Push a message into a queue: this function runs from the parent thread
+ * so it needs to write the message to every thread pipe channel.
+ */
+int mk_fifo_send(struct mk_fifo *ctx, int id, void *data, size_t size)
+{
+ int ret;
+ struct mk_list *head;
+ struct mk_fifo_msg msg;
+ struct mk_fifo_queue *q;
+ struct mk_fifo_worker *fw;
+
+ /* Validate queue ID */
+ q = mk_fifo_queue_get(ctx, id);
+ if (!q) {
+ return -1;
+ }
+
+ pthread_mutex_lock(&ctx->mutex_init);
+
+ mk_list_foreach(head, &ctx->workers) {
+ fw = mk_list_entry(head, struct mk_fifo_worker, _head);
+
+ msg.length = size;
+ msg.flags = 0;
+ msg.queue_id = (uint16_t) id;
+
+ ret = msg_write(fw->channel[1], &msg, sizeof(struct mk_fifo_msg));
+ if (ret == -1) {
+ pthread_mutex_unlock(&ctx->mutex_init);
+ perror("write");
+ fprintf(stderr, "[msg] error writing message header\n");
+ return -1;
+ }
+
+ ret = msg_write(fw->channel[1], data, size);
+ if (ret == -1) {
+ pthread_mutex_unlock(&ctx->mutex_init);
+ perror("write");
+ fprintf(stderr, "[msg] error writing message body\n");
+ return -1;
+ }
+ }
+
+ pthread_mutex_unlock(&ctx->mutex_init);
+
+ return 0;
+}
+
+static inline void consume_bytes(char *buf, int bytes, int length)
+{
+ memmove(buf, buf + bytes, length - bytes);
+}
+
+static inline int fifo_drop_msg(struct mk_fifo_worker *fw)
+{
+ size_t drop_bytes;
+ struct mk_fifo_msg *msg;
+
+ msg = (struct mk_fifo_msg *) fw->buf_data;
+ drop_bytes = (sizeof(struct mk_fifo_msg) + msg->length);
+ consume_bytes(fw->buf_data, drop_bytes, fw->buf_len);
+ fw->buf_len -= drop_bytes;
+
+ return 0;
+}
+
+static inline int fifo_is_msg_ready(struct mk_fifo_worker *fw)
+{
+ struct mk_fifo_msg *msg;
+
+ msg = (struct mk_fifo_msg *) fw->buf_data;
+ if (fw->buf_len >= (msg->length + sizeof(struct mk_fifo_msg))) {
+ return MK_TRUE;
+ }
+
+ return MK_FALSE;
+}
+
+int mk_fifo_worker_read(void *event)
+{
+ int available;
+ char *tmp;
+ size_t size;
+ ssize_t bytes;
+ struct mk_fifo_msg *fm;
+ struct mk_fifo_worker *fw;
+ struct mk_fifo_queue *fq;
+
+ fw = (struct mk_fifo_worker *) event;
+
+ /* Check available space */
+ available = fw->buf_size - fw->buf_len;
+ if (available <= 1) {
+ size = fw->buf_size + (MK_FIFO_BUF_SIZE / 2);
+ tmp = mk_mem_realloc(fw->buf_data, size);
+ if (!tmp) {
+ perror("realloc");
+ return -1;
+ }
+ fw->buf_data = tmp;
+ fw->buf_size = size;
+ available = fw->buf_size - fw->buf_len;
+ }
+
+ /* Read data from pipe */
+#ifdef _WIN32
+ bytes = recv(fw->channel[0], fw->buf_data + fw->buf_len, available, 0);
+#else
+ bytes = read(fw->channel[0], fw->buf_data + fw->buf_len, available);
+#endif
+
+ if (bytes == 0) {
+ return -1;
+ }
+ else if (bytes == -1){
+ perror("read");
+ return -1;
+ }
+
+ fw->buf_len += bytes;
+
+ /* Find messages and trigger callbacks */
+ while (fw->buf_len > 0) {
+ if (fifo_is_msg_ready(fw) == MK_TRUE) {
+ /* we got a complete message */
+ fm = (struct mk_fifo_msg *) fw->buf_data;
+ fq = mk_fifo_queue_get(fw->fifo, fm->queue_id);
+ if (!fq) {
+ /* Invalid queue */
+ fprintf(stderr, "[fifo worker read] invalid queue id %i\n",
+ fm->queue_id);
+ fifo_drop_msg(fw);
+ continue;
+ }
+
+ /* Trigger callback if any */
+ if (fq->cb_message) {
+ fq->cb_message(fq, fm->data, fm->length, fq->data);
+ }
+ fifo_drop_msg(fw);
+ }
+ else {
+ /* msg not ready */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int mk_fifo_destroy(struct mk_fifo *ctx)
+{
+ mk_fifo_queue_destroy_all(ctx);
+ mk_fifo_worker_destroy_all(ctx);
+ mk_mem_free(ctx);
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_header.c b/fluent-bit/lib/monkey/mk_server/mk_header.c
new file mode 100644
index 000000000..cd8f77bde
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_header.c
@@ -0,0 +1,451 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_header.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_http_status.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_socket.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_cache.h>
+#include <monkey/mk_http.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_tls.h>
+
+#define MK_HEADER_SHORT_DATE "Date: "
+#define MK_HEADER_SHORT_LOCATION "Location: "
+#define MK_HEADER_SHORT_CT "Content-Type: "
+#define MK_HEADER_ACCEPT_RANGES "Accept-Ranges: bytes" MK_CRLF
+#define MK_HEADER_ALLOWED_METHODS "Allow: "
+#define MK_HEADER_CONN_KA "Connection: Keep-Alive" MK_CRLF
+#define MK_HEADER_CONN_CLOSE "Connection: Close" MK_CRLF
+#define MK_HEADER_CONN_UPGRADE "Connection: Upgrade" MK_CRLF
+#define MK_HEADER_CONTENT_LENGTH "Content-Length: "
+#define MK_HEADER_CONTENT_ENCODING "Content-Encoding: "
+#define MK_HEADER_TE_CHUNKED "Transfer-Encoding: chunked" MK_CRLF
+#define MK_HEADER_LAST_MODIFIED "Last-Modified: "
+#define MK_HEADER_UPGRADE_H2C "Upgrade: h2c" MK_CRLF
+
+const mk_ptr_t mk_header_short_date = mk_ptr_init(MK_HEADER_SHORT_DATE);
+const mk_ptr_t mk_header_short_location = mk_ptr_init(MK_HEADER_SHORT_LOCATION);
+const mk_ptr_t mk_header_short_ct = mk_ptr_init(MK_HEADER_SHORT_CT);
+const mk_ptr_t mk_header_allow = mk_ptr_init(MK_HEADER_ALLOWED_METHODS);
+
+const mk_ptr_t mk_header_conn_ka = mk_ptr_init(MK_HEADER_CONN_KA);
+const mk_ptr_t mk_header_conn_close = mk_ptr_init(MK_HEADER_CONN_CLOSE);
+const mk_ptr_t mk_header_conn_upgrade = mk_ptr_init(MK_HEADER_CONN_UPGRADE);
+const mk_ptr_t mk_header_content_length = mk_ptr_init(MK_HEADER_CONTENT_LENGTH);
+const mk_ptr_t mk_header_content_encoding = mk_ptr_init(MK_HEADER_CONTENT_ENCODING);
+const mk_ptr_t mk_header_accept_ranges = mk_ptr_init(MK_HEADER_ACCEPT_RANGES);
+const mk_ptr_t mk_header_te_chunked = mk_ptr_init(MK_HEADER_TE_CHUNKED);
+const mk_ptr_t mk_header_last_modified = mk_ptr_init(MK_HEADER_LAST_MODIFIED);
+const mk_ptr_t mk_header_upgrade_h2c = mk_ptr_init(MK_HEADER_UPGRADE_H2C);
+
+#define status_entry(num, str) {num, sizeof(str) - 1, str}
+
+static const struct header_status_response status_response[] = {
+
+ /*
+ * The most used first:
+ *
+ * - HTTP/1.1 200 OK
+ * - HTTP/1.1 404 Not Found
+ */
+ status_entry(MK_HTTP_OK, MK_RH_HTTP_OK),
+ status_entry(MK_CLIENT_NOT_FOUND, MK_RH_CLIENT_NOT_FOUND),
+
+ /* Informational */
+ status_entry(MK_INFO_CONTINUE, MK_RH_INFO_CONTINUE),
+ status_entry(MK_INFO_SWITCH_PROTOCOL, MK_RH_INFO_SWITCH_PROTOCOL),
+
+ /* Successful */
+ status_entry(MK_HTTP_CREATED, MK_RH_HTTP_CREATED),
+ status_entry(MK_HTTP_ACCEPTED, MK_RH_HTTP_ACCEPTED),
+ status_entry(MK_HTTP_NON_AUTH_INFO, MK_RH_HTTP_NON_AUTH_INFO),
+ status_entry(MK_HTTP_NOCONTENT, MK_RH_HTTP_NOCONTENT),
+ status_entry(MK_HTTP_RESET, MK_RH_HTTP_RESET),
+ status_entry(MK_HTTP_PARTIAL, MK_RH_HTTP_PARTIAL),
+
+ /* Redirections */
+ status_entry(MK_REDIR_MULTIPLE, MK_RH_REDIR_MULTIPLE),
+ status_entry(MK_REDIR_MOVED, MK_RH_REDIR_MOVED),
+ status_entry(MK_REDIR_MOVED_T, MK_RH_REDIR_MOVED_T),
+ status_entry(MK_REDIR_SEE_OTHER, MK_RH_REDIR_SEE_OTHER),
+ status_entry(MK_NOT_MODIFIED, MK_RH_NOT_MODIFIED),
+ status_entry(MK_REDIR_USE_PROXY, MK_RH_REDIR_USE_PROXY),
+
+ /* Client side errors */
+ status_entry(MK_CLIENT_BAD_REQUEST, MK_RH_CLIENT_BAD_REQUEST),
+ status_entry(MK_CLIENT_UNAUTH, MK_RH_CLIENT_UNAUTH),
+ status_entry(MK_CLIENT_PAYMENT_REQ, MK_RH_CLIENT_PAYMENT_REQ),
+ status_entry(MK_CLIENT_FORBIDDEN, MK_RH_CLIENT_FORBIDDEN),
+ status_entry(MK_CLIENT_METHOD_NOT_ALLOWED, MK_RH_CLIENT_METHOD_NOT_ALLOWED),
+ status_entry(MK_CLIENT_NOT_ACCEPTABLE, MK_RH_CLIENT_NOT_ACCEPTABLE),
+ status_entry(MK_CLIENT_PROXY_AUTH, MK_RH_CLIENT_PROXY_AUTH),
+ status_entry(MK_CLIENT_REQUEST_TIMEOUT, MK_RH_CLIENT_REQUEST_TIMEOUT),
+ status_entry(MK_CLIENT_CONFLICT, MK_RH_CLIENT_CONFLICT),
+ status_entry(MK_CLIENT_GONE, MK_RH_CLIENT_GONE),
+ status_entry(MK_CLIENT_LENGTH_REQUIRED, MK_RH_CLIENT_LENGTH_REQUIRED),
+ status_entry(MK_CLIENT_PRECOND_FAILED, MK_RH_CLIENT_PRECOND_FAILED),
+ status_entry(MK_CLIENT_REQUEST_ENTITY_TOO_LARGE,
+ MK_RH_CLIENT_REQUEST_ENTITY_TOO_LARGE),
+ status_entry(MK_CLIENT_REQUEST_URI_TOO_LONG,
+ MK_RH_CLIENT_REQUEST_URI_TOO_LONG),
+ status_entry(MK_CLIENT_UNSUPPORTED_MEDIA, MK_RH_CLIENT_UNSUPPORTED_MEDIA),
+ status_entry(MK_CLIENT_REQUESTED_RANGE_NOT_SATISF,
+ MK_RH_CLIENT_REQUESTED_RANGE_NOT_SATISF),
+
+ /* Server side errors */
+ status_entry(MK_SERVER_INTERNAL_ERROR, MK_RH_SERVER_INTERNAL_ERROR),
+ status_entry(MK_SERVER_NOT_IMPLEMENTED, MK_RH_SERVER_NOT_IMPLEMENTED),
+ status_entry(MK_SERVER_BAD_GATEWAY, MK_RH_SERVER_BAD_GATEWAY),
+ status_entry(MK_SERVER_SERVICE_UNAV, MK_RH_SERVER_SERVICE_UNAV),
+ status_entry(MK_SERVER_GATEWAY_TIMEOUT, MK_RH_SERVER_GATEWAY_TIMEOUT),
+ status_entry(MK_SERVER_HTTP_VERSION_UNSUP, MK_RH_SERVER_HTTP_VERSION_UNSUP)
+};
+
+static const int status_response_len =
+ (sizeof(status_response)/(sizeof(status_response[0])));
+
+static void mk_header_cb_finished(struct mk_stream_input *in)
+{
+ struct mk_iov *iov = in->buffer;
+
+ mk_iov_free_marked(iov);
+
+#if defined(__APPLE__)
+ /*
+ * Disable TCP_CORK right away, according to:
+ *
+ * ---
+ * commit 81e8b869d70f9da93ddfbfb17ec7f12ce3c28fc6
+ * Author: Sonny Karlsson <ksonny@lotrax.org>
+ * Date: Sat Oct 18 12:11:49 2014 +0200
+ *
+ * http: Remove cork before first call to sendfile().
+ *
+ * This removes a large delay on Mac OS X when headers and file content
+ * does not fill a single frame.
+ * Deactivating TCP_NOPUSH does not cause pending frames to be sent until
+ * the next write operation.
+ * ---
+ */
+
+ mk_server_cork_flag(in->stream->channel->fd, TCP_CORK_OFF);
+#endif
+}
+
+static void cb_stream_iov_extended_free(struct mk_stream_input *in)
+{
+ struct mk_iov *iov;
+
+ iov = in->buffer;
+ mk_iov_free(iov);
+}
+
+/* Send response headers */
+int mk_header_prepare(struct mk_http_session *cs, struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int i = 0;
+ unsigned long len = 0;
+ char *buffer = 0;
+ mk_ptr_t response;
+ struct response_headers *sh;
+ struct mk_iov *iov;
+
+ sh = &sr->headers;
+ iov = &sh->headers_iov;
+
+ /* HTTP Status Code */
+ if (sh->status == MK_CUSTOM_STATUS) {
+ response.data = sh->custom_status.data;
+ response.len = sh->custom_status.len;
+ }
+ else {
+ for (i = 0; i < status_response_len; i++) {
+ if (status_response[i].status == sh->status) {
+ response.data = status_response[i].response;
+ response.len = status_response[i].length;
+ break;
+ }
+ }
+ }
+
+ /* Invalid status set */
+ mk_bug(i == status_response_len);
+
+ mk_iov_add(iov, response.data, response.len, MK_FALSE);
+
+ /*
+ * Preset headers (mk_clock.c):
+ *
+ * - Server
+ * - Date
+ */
+ mk_iov_add(iov,
+ server->clock_context->headers_preset.data,
+ server->clock_context->headers_preset.len,
+ MK_FALSE);
+
+ /* Last-Modified */
+ if (sh->last_modified > 0) {
+ mk_ptr_t *lm = MK_TLS_GET(mk_tls_cache_header_lm);
+ lm->len = mk_utils_utime2gmt(&lm->data, sh->last_modified);
+
+ mk_iov_add(iov,
+ mk_header_last_modified.data,
+ mk_header_last_modified.len,
+ MK_FALSE);
+ mk_iov_add(iov,
+ lm->data,
+ lm->len,
+ MK_FALSE);
+ }
+
+ /* Connection */
+ if (sh->connection == 0) {
+ if (cs->close_now == MK_FALSE) {
+ if (sr->connection.len > 0) {
+ if (sr->protocol != MK_HTTP_PROTOCOL_11) {
+ mk_iov_add(iov,
+ mk_header_conn_ka.data,
+ mk_header_conn_ka.len,
+ MK_FALSE);
+ }
+ }
+ }
+ else {
+ mk_iov_add(iov,
+ mk_header_conn_close.data,
+ mk_header_conn_close.len,
+ MK_FALSE);
+ }
+ }
+ else if (sh->connection == MK_HEADER_CONN_UPGRADED) {
+ mk_iov_add(iov,
+ mk_header_conn_upgrade.data,
+ mk_header_conn_upgrade.len,
+ MK_FALSE);
+ }
+
+ /* Location */
+ if (sh->location != NULL) {
+ mk_iov_add(iov,
+ mk_header_short_location.data,
+ mk_header_short_location.len,
+ MK_FALSE);
+
+ mk_iov_add(iov,
+ sh->location,
+ strlen(sh->location),
+ MK_TRUE);
+ }
+
+ /* allowed methods */
+ if (sh->allow_methods.len > 0) {
+ mk_iov_add(iov,
+ mk_header_allow.data,
+ mk_header_allow.len,
+ MK_FALSE);
+ mk_iov_add(iov,
+ sh->allow_methods.data,
+ sh->allow_methods.len,
+ MK_FALSE);
+ }
+
+ /* Content type */
+ if (sh->content_type.len > 0) {
+ mk_iov_add(iov,
+ sh->content_type.data,
+ sh->content_type.len,
+ MK_FALSE);
+ }
+
+ /*
+ * Transfer Encoding: the transfer encoding header is just sent when
+ * the response has some content defined by the HTTP status response
+ */
+ switch (sh->transfer_encoding) {
+ case MK_HEADER_TE_TYPE_CHUNKED:
+ mk_iov_add(iov,
+ mk_header_te_chunked.data,
+ mk_header_te_chunked.len,
+ MK_FALSE);
+ break;
+ }
+
+ /* E-Tag */
+ if (sh->etag_len > 0) {
+ mk_iov_add(iov, sh->etag_buf, sh->etag_len, MK_FALSE);
+ }
+
+ /* Content-Encoding */
+ if (sh->content_encoding.len > 0) {
+ mk_iov_add(iov, mk_header_content_encoding.data,
+ mk_header_content_encoding.len,
+ MK_FALSE);
+ mk_iov_add(iov, sh->content_encoding.data,
+ sh->content_encoding.len,
+ MK_FALSE);
+ }
+
+ /* Content-Length */
+ if (sh->content_length >= 0 && sh->transfer_encoding != 0) {
+ /* Map content length to MK_POINTER */
+ mk_ptr_t *cl = MK_TLS_GET(mk_tls_cache_header_cl);
+ mk_string_itop(sh->content_length, cl);
+
+ /* Set headers */
+ mk_iov_add(iov,
+ mk_header_content_length.data,
+ mk_header_content_length.len,
+ MK_FALSE);
+ mk_iov_add(iov,
+ cl->data,
+ cl->len,
+ MK_FALSE);
+ }
+
+ if ((sh->content_length != 0 && (sh->ranges[0] >= 0 || sh->ranges[1] >= 0)) &&
+ server->resume == MK_TRUE) {
+ buffer = 0;
+
+ /* yyy- */
+ if (sh->ranges[0] >= 0 && sh->ranges[1] == -1) {
+ mk_string_build(&buffer,
+ &len,
+ "%s bytes %d-%ld/%ld\r\n",
+ RH_CONTENT_RANGE,
+ sh->ranges[0],
+ (sh->real_length - 1), sh->real_length);
+ mk_iov_add(iov, buffer, len, MK_TRUE);
+ }
+
+ /* yyy-xxx */
+ if (sh->ranges[0] >= 0 && sh->ranges[1] >= 0) {
+ mk_string_build(&buffer,
+ &len,
+ "%s bytes %d-%d/%ld\r\n",
+ RH_CONTENT_RANGE,
+ sh->ranges[0], sh->ranges[1], sh->real_length);
+
+ mk_iov_add(iov, buffer, len, MK_TRUE);
+ }
+
+ /* -xxx */
+ if (sh->ranges[0] == -1 && sh->ranges[1] > 0) {
+ mk_string_build(&buffer,
+ &len,
+ "%s bytes %ld-%ld/%ld\r\n",
+ RH_CONTENT_RANGE,
+ (sh->real_length - sh->ranges[1]),
+ (sh->real_length - 1), sh->real_length);
+ mk_iov_add(iov, buffer, len, MK_TRUE);
+ }
+ }
+
+ if (sh->upgrade == MK_HEADER_UPGRADED_H2C) {
+ mk_iov_add(iov, mk_header_upgrade_h2c.data, mk_header_upgrade_h2c.len,
+ MK_FALSE);
+ }
+
+
+ if (sh->cgi == SH_NOCGI || sh->breakline == MK_HEADER_BREAKLINE) {
+ if (!sr->headers._extra_rows) {
+ mk_iov_add(iov, mk_iov_crlf.data, mk_iov_crlf.len,
+ MK_FALSE);
+ }
+ else {
+ mk_iov_add(sr->headers._extra_rows, mk_iov_crlf.data,
+ mk_iov_crlf.len, MK_FALSE);
+ }
+ }
+
+ /*
+ * Configure the Stream to dispatch the headers
+ */
+
+ /* Set the IOV input stream */
+ sr->in_headers.buffer = iov;
+ sr->in_headers.bytes_total = iov->total_len;
+ sr->in_headers.cb_finished = mk_header_cb_finished;
+
+ if (sr->headers._extra_rows) {
+ /* Our main sr->stream contains the main headers (header_iov)
+ * and 'may' have already some linked data. If we have some
+ * extra headers rows we need to link this IOV right after
+ * the main header_iov.
+ */
+ struct mk_stream_input *in = &sr->in_headers_extra;
+ in->type = MK_STREAM_IOV;
+ in->dynamic = MK_FALSE;
+ in->cb_consumed = NULL;
+ in->cb_finished = cb_stream_iov_extended_free;
+ in->stream = &sr->stream;
+ in->buffer = sr->headers._extra_rows;
+ in->bytes_total = sr->headers._extra_rows->total_len;
+
+ mk_list_add_after(&sr->in_headers_extra._head,
+ &sr->in_headers._head,
+ &sr->stream.inputs);
+ }
+
+ sh->sent = MK_TRUE;
+
+ return 0;
+}
+
+void mk_header_set_http_status(struct mk_http_request *sr, int status)
+{
+ mk_bug(!sr);
+ sr->headers.status = status;
+
+ MK_TRACE("Set HTTP status = %i", status);
+}
+
+void mk_header_response_reset(struct response_headers *header)
+{
+ struct mk_iov *iov;
+
+ header->status = -1;
+ header->sent = MK_FALSE;
+ header->ranges[0] = -1;
+ header->ranges[1] = -1;
+ header->content_length = -1;
+ header->connection = 0;
+ header->transfer_encoding = -1;
+ header->last_modified = -1;
+ header->upgrade = -1;
+ header->cgi = SH_NOCGI;
+ mk_ptr_reset(&header->content_type);
+ mk_ptr_reset(&header->content_encoding);
+ header->location = NULL;
+ header->_extra_rows = NULL;
+ header->allow_methods.len = 0;
+
+ /* Initialize headers IOV */
+ iov = &header->headers_iov;
+ iov->io = (struct iovec *) &header->__iov_io;
+ iov->buf_to_free = (void *) &header->__iov_buf;
+ mk_iov_init(&header->headers_iov, MK_HEADER_IOV, 0);
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_http.c b/fluent-bit/lib/monkey/mk_server/mk_http.c
new file mode 100644
index 000000000..1e2d219de
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_http.c
@@ -0,0 +1,1638 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+//#include <regex.h>
+#include <re.h>
+
+#include <monkey/monkey.h>
+#include <monkey/mk_user.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_http.h>
+#include <monkey/mk_http_status.h>
+#include <monkey/mk_http_thread.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_socket.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_header.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_plugin_stage.h>
+
+const mk_ptr_t mk_http_method_get_p = mk_ptr_init(MK_METHOD_GET_STR);
+const mk_ptr_t mk_http_method_post_p = mk_ptr_init(MK_METHOD_POST_STR);
+const mk_ptr_t mk_http_method_head_p = mk_ptr_init(MK_METHOD_HEAD_STR);
+const mk_ptr_t mk_http_method_put_p = mk_ptr_init(MK_METHOD_PUT_STR);
+const mk_ptr_t mk_http_method_delete_p = mk_ptr_init(MK_METHOD_DELETE_STR);
+const mk_ptr_t mk_http_method_options_p = mk_ptr_init(MK_METHOD_OPTIONS_STR);
+const mk_ptr_t mk_http_method_null_p = { NULL, 0 };
+
+const mk_ptr_t mk_http_protocol_09_p = mk_ptr_init(MK_HTTP_PROTOCOL_09_STR);
+const mk_ptr_t mk_http_protocol_10_p = mk_ptr_init(MK_HTTP_PROTOCOL_10_STR);
+const mk_ptr_t mk_http_protocol_11_p = mk_ptr_init(MK_HTTP_PROTOCOL_11_STR);
+const mk_ptr_t mk_http_protocol_null_p = { NULL, 0 };
+
+/* Create a memory allocation in order to handle the request data */
+void mk_http_request_init(struct mk_http_session *session,
+ struct mk_http_request *request,
+ struct mk_server *server)
+{
+ struct mk_list *host_list = &server->hosts;
+
+ request->port = 0;
+ request->status = MK_TRUE;
+ request->uri.data = NULL;
+ request->method = MK_METHOD_UNKNOWN;
+ request->protocol = MK_HTTP_PROTOCOL_UNKNOWN;
+ request->connection.len = -1;
+ request->file_fd = -1;
+ request->file_info.size = -1;
+ request->vhost_fdt_id = 0;
+ request->vhost_fdt_hash = 0;
+ request->vhost_fdt_enabled = MK_FALSE;
+ request->host.data = NULL;
+ request->stage30_blocked = MK_FALSE;
+ request->session = session;
+ request->host_conf = mk_list_entry_first(host_list, struct mk_vhost, _head);
+ request->uri_processed.data = NULL;
+ request->real_path.data = NULL;
+ request->handler_data = NULL;
+
+ request->in_file.fd = -1;
+
+ /* Response Headers */
+ mk_header_response_reset(&request->headers);
+
+ /* Reset callbacks for headers stream */
+ mk_stream_set(&request->stream,
+ session->channel,
+ NULL,
+ NULL, NULL, NULL);
+}
+
+static inline int mk_http_point_header(mk_ptr_t *h,
+ struct mk_http_parser *parser, int key)
+{
+ struct mk_http_header *header;
+
+ header = &parser->headers[key];
+ if (header->type == key) {
+ h->data = header->val.data;
+ h->len = header->val.len;
+ return 0;
+ }
+ else {
+ h->data = NULL;
+ h->len = -1;
+ }
+
+ return -1;
+}
+
+static int mk_http_request_prepare(struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int ret;
+ int status = 0;
+ char *temp;
+ struct mk_list *hosts = &server->hosts;
+ struct mk_list *alias;
+ struct mk_http_header *header;
+
+ /*
+ * Process URI, if it contains ASCII encoded strings like '%20',
+ * it will return a new memory buffer with the decoded string, otherwise
+ * it returns NULL
+ */
+ temp = mk_utils_url_decode(sr->uri);
+
+ if (temp) {
+ sr->uri_processed.data = temp;
+ sr->uri_processed.len = strlen(temp);
+ }
+ else {
+ sr->uri_processed.data = sr->uri.data;
+ sr->uri_processed.len = sr->uri.len;
+ }
+
+ /* Always assign the default vhost' */
+ sr->host_conf = mk_list_entry_first(hosts, struct mk_vhost, _head);
+ sr->user_home = MK_FALSE;
+
+ /* Valid request URI? */
+ if (sr->uri_processed.data[0] != '/') {
+ mk_http_error(MK_CLIENT_BAD_REQUEST, cs, sr, server);
+ return MK_EXIT_OK;
+ }
+
+ /* Check if we have a Host header: Hostname ; port */
+ mk_http_point_header(&sr->host, &cs->parser, MK_HEADER_HOST);
+
+ /* Header: Connection */
+ mk_http_point_header(&sr->connection, &cs->parser, MK_HEADER_CONNECTION);
+
+ /* Header: Range */
+ mk_http_point_header(&sr->range, &cs->parser, MK_HEADER_RANGE);
+
+ /* Header: If-Modified-Since */
+ mk_http_point_header(&sr->if_modified_since,
+ &cs->parser,
+ MK_HEADER_IF_MODIFIED_SINCE);
+
+ /* HTTP/1.1 needs Host header */
+ if (!sr->host.data && sr->protocol == MK_HTTP_PROTOCOL_11) {
+ mk_http_error(MK_CLIENT_BAD_REQUEST, cs, sr, server);
+ return MK_EXIT_OK;
+ }
+
+ /* Should we close the session after this request ? */
+ mk_http_keepalive_check(cs, sr, server);
+
+ /* Content Length */
+ header = &cs->parser.headers[MK_HEADER_CONTENT_LENGTH];
+ if (header->type == MK_HEADER_CONTENT_LENGTH) {
+ sr->_content_length.data = header->val.data;
+ sr->_content_length.len = header->val.len;
+ }
+ else {
+ sr->_content_length.data = NULL;
+ }
+
+ /* Assign the first node alias */
+ alias = &sr->host_conf->server_names;
+ sr->host_alias = mk_list_entry_first(alias,
+ struct mk_vhost_alias, _head);
+
+ if (sr->host.data) {
+ /* Set the given port */
+ if (cs->parser.header_host_port > 0) {
+ sr->port = cs->parser.header_host_port;
+ }
+
+ /* Match the virtual host */
+ mk_vhost_get(sr->host, &sr->host_conf, &sr->host_alias, server);
+
+ /* Check if this virtual host have some redirection */
+ if (sr->host_conf->header_redirect.data) {
+ mk_header_set_http_status(sr, MK_REDIR_MOVED);
+ sr->headers.location = mk_string_dup(sr->host_conf->header_redirect.data);
+ sr->headers.content_length = 0;
+ sr->headers.location = NULL;
+ mk_header_prepare(cs, sr, server);
+ return 0;
+ }
+ }
+
+ /* Is requesting an user home directory ? */
+ if (server->conf_user_pub &&
+ sr->uri_processed.len > 2 &&
+ sr->uri_processed.data[1] == MK_USER_HOME) {
+
+ if (mk_user_init(cs, sr, server) != 0) {
+ mk_http_error(MK_CLIENT_NOT_FOUND, cs, sr, server);
+ return MK_EXIT_ABORT;
+ }
+ }
+
+ /* Plugins Stage 20 */
+ ret = mk_plugin_stage_run_20(cs, sr, server);
+ if (ret == MK_PLUGIN_RET_CLOSE_CONX) {
+ MK_TRACE("STAGE 20 requested close conexion");
+ return MK_EXIT_ABORT;
+ }
+
+ /* Normal HTTP process */
+ status = mk_http_init(cs, sr, server);
+
+ MK_TRACE("[FD %i] HTTP Init returning %i", cs->socket, status);
+ return status;
+}
+
+/*
+ * This function allow the core to invoke the closing connection process
+ * when some connection was not proceesed due to a premature close or similar
+ * exception, it also take care of invoke the STAGE_40 and STAGE_50 plugins events
+ */
+static void mk_request_premature_close(int http_status, struct mk_http_session *cs,
+ struct mk_server *server)
+{
+ struct mk_http_request *sr;
+ struct mk_list *sr_list = &cs->request_list;
+ struct mk_list *host_list = &server->hosts;
+
+ /*
+ * If the connection is too premature, we need to allocate a temporal session_request
+ * to do not break the plugins stages
+ */
+ if (mk_list_is_empty(sr_list) == 0) {
+ sr = &cs->sr_fixed;
+ memset(sr, 0, sizeof(struct mk_http_request));
+ mk_http_request_init(cs, sr, server);
+ mk_list_add(&sr->_head, &cs->request_list);
+ }
+ else {
+ sr = mk_list_entry_first(sr_list, struct mk_http_request, _head);
+ }
+
+ /* Raise error */
+ if (http_status > 0) {
+ if (!sr->host_conf) {
+ sr->host_conf = mk_list_entry_first(host_list,
+ struct mk_vhost, _head);
+ }
+ mk_http_error(http_status, cs, sr, server);
+
+ /* STAGE_40, request has ended */
+ mk_plugin_stage_run_40(cs, sr, server);
+ }
+
+ /* STAGE_50, connection closed and remove the http_session */
+ mk_plugin_stage_run_50(cs->socket, server);
+ mk_http_session_remove(cs, server);
+}
+
+int mk_http_handler_read(struct mk_sched_conn *conn, struct mk_http_session *cs,
+ struct mk_server *server)
+{
+ int bytes;
+ int max_read;
+ int available = 0;
+ int new_size;
+ int total_bytes = 0;
+ char *tmp = 0;
+
+#ifdef MK_HAVE_TRACE
+ int socket = conn->event.fd;
+#endif
+
+ MK_TRACE("MAX REQUEST SIZE: %i", server->max_request_size);
+
+ try_pending:
+
+ available = cs->body_size - cs->body_length;
+ if (available <= 0) {
+ /* Reallocate buffer size if pending data does not have space */
+ new_size = cs->body_size + conn->net->buffer_size;
+ if (new_size > server->max_request_size) {
+ MK_TRACE("Requested size is > mk_config->max_request_size");
+ mk_request_premature_close(MK_CLIENT_REQUEST_ENTITY_TOO_LARGE, cs,
+ server);
+ return -1;
+ }
+
+ /*
+ * Check if the body field still points to the initial body_fixed, if so,
+ * allow the new space required in body, otherwise perform a realloc over
+ * body.
+ */
+ if (cs->body == cs->body_fixed) {
+ cs->body = mk_mem_alloc(new_size + 1);
+ cs->body_size = new_size;
+ memcpy(cs->body, cs->body_fixed, cs->body_length);
+ MK_TRACE("[FD %i] New size: %i, length: %i",
+ socket, new_size, cs->body_length);
+ }
+ else {
+ MK_TRACE("[FD %i] Realloc from %i to %i",
+ socket, cs->body_size, new_size);
+ tmp = mk_mem_realloc(cs->body, new_size + 1);
+ if (tmp) {
+ cs->body = tmp;
+ cs->body_size = new_size;
+ }
+ else {
+ mk_request_premature_close(MK_SERVER_INTERNAL_ERROR, cs,
+ server);
+ return -1;
+ }
+ }
+ }
+
+ /* Read content */
+ max_read = (cs->body_size - cs->body_length);
+ bytes = mk_sched_conn_read(conn, cs->body + cs->body_length, max_read);
+ MK_TRACE("[FD %i] read %i", socket, bytes);
+
+ if (bytes == 0) {
+ MK_TRACE("[FD %i] broken pipe?", socket);
+ errno = 0;
+ return -1;
+ }
+ else if (bytes == -1) {
+ return -1;
+ }
+
+ if (bytes > max_read) {
+ MK_TRACE("[FD %i] Buffer still have data: %i",
+ socket, bytes - max_read);
+ cs->body_length += max_read;
+ cs->body[cs->body_length] = '\0';
+ total_bytes += max_read;
+
+ goto try_pending;
+ }
+ else {
+ cs->body_length += bytes;
+ cs->body[cs->body_length] = '\0';
+
+ total_bytes += bytes;
+ }
+
+ MK_TRACE("[FD %i] Retry total bytes: %i", socket, total_bytes);
+ return total_bytes;
+}
+
+/* Build error page */
+static int mk_http_error_page(char *title, mk_ptr_t *message, char *signature,
+ char **out_buf, unsigned long *out_size)
+{
+ char *temp;
+
+ *out_buf = NULL;
+
+ if (message) {
+ temp = mk_ptr_to_buf(*message);
+ }
+ else {
+ temp = mk_string_dup("");
+ }
+
+ mk_string_build(out_buf, out_size,
+ MK_REQUEST_DEFAULT_PAGE, title, temp, signature);
+ mk_mem_free(temp);
+ return 0;
+}
+
+static int mk_http_range_set(struct mk_http_request *sr, size_t file_size,
+ struct mk_server *server)
+{
+ struct response_headers *sh = &sr->headers;
+ struct mk_stream_input *in;
+
+ in = &sr->in_file;
+ in->bytes_total = file_size;
+ in->bytes_offset = 0;
+
+ if (server->resume == MK_TRUE && sr->range.data) {
+ /* yyy- */
+ if (sh->ranges[0] >= 0 && sh->ranges[1] == -1) {
+ in->bytes_offset = sh->ranges[0];
+ in->bytes_total = file_size - in->bytes_offset;
+ }
+
+ /* yyy-xxx */
+ if (sh->ranges[0] >= 0 && sh->ranges[1] >= 0) {
+ in->bytes_offset = sh->ranges[0];
+ in->bytes_total = labs(sh->ranges[1] - sh->ranges[0]) + 1;
+ }
+
+ /* -xxx */
+ if (sh->ranges[0] == -1 && sh->ranges[1] > 0) {
+ in->bytes_total = sh->ranges[1];
+ in->bytes_offset = file_size - sh->ranges[1];
+ }
+
+ if ((size_t) in->bytes_offset >= file_size ||
+ in->bytes_total > file_size) {
+ return -1;
+ }
+
+ lseek(in->fd, in->bytes_offset, SEEK_SET);
+ }
+ return 0;
+}
+
+static int mk_http_range_parse(struct mk_http_request *sr)
+{
+ int eq_pos, sep_pos, len;
+ char *buffer = 0;
+ struct response_headers *sh;
+
+ if (!sr->range.data)
+ return -1;
+
+ if ((eq_pos = mk_string_char_search(sr->range.data, '=', sr->range.len)) < 0)
+ return -1;
+
+ if (strncasecmp(sr->range.data, "Bytes", eq_pos) != 0)
+ return -1;
+
+ if ((sep_pos = mk_string_char_search(sr->range.data, '-', sr->range.len)) < 0)
+ return -1;
+
+ len = sr->range.len;
+ sh = &sr->headers;
+
+ /* =-xxx */
+ if (eq_pos + 1 == sep_pos) {
+ sh->ranges[0] = -1;
+ sh->ranges[1] = (unsigned long) atol(sr->range.data + sep_pos + 1);
+
+ if (sh->ranges[1] <= 0) {
+ return -1;
+ }
+
+ sh->content_length = sh->ranges[1];
+ return 0;
+ }
+
+ /* =yyy-xxx */
+ if ((eq_pos + 1 != sep_pos) && (len > sep_pos + 1)) {
+ buffer = mk_string_copy_substr(sr->range.data, eq_pos + 1, sep_pos);
+ sh->ranges[0] = (unsigned long) atol(buffer);
+ mk_mem_free(buffer);
+
+ buffer = mk_string_copy_substr(sr->range.data, sep_pos + 1, len);
+ sh->ranges[1] = (unsigned long) atol(buffer);
+ mk_mem_free(buffer);
+
+ if (sh->ranges[1] < 0 || (sh->ranges[0] > sh->ranges[1])) {
+ return -1;
+ }
+
+ sh->content_length = abs(sh->ranges[1] - sh->ranges[0]) + 1;
+ return 0;
+ }
+ /* =yyy- */
+ if ((eq_pos + 1 != sep_pos) && (len == sep_pos + 1)) {
+ buffer = mk_string_copy_substr(sr->range.data, eq_pos + 1, len);
+ sr->headers.ranges[0] = (unsigned long) atol(buffer);
+ mk_mem_free(buffer);
+
+ sh->content_length = (sh->content_length - sh->ranges[0]);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int mk_http_directory_redirect_check(struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int port_redirect = 0;
+ char *host;
+ char *location = 0;
+ char *real_location = 0;
+ char *protocol = "http";
+ unsigned long len;
+
+ /*
+ * We have to check if there is a slash at the end of
+ * this string. If it doesn't exist, we send a redirection header.
+ */
+ if (sr->uri_processed.data[sr->uri_processed.len - 1] == '/') {
+ return 0;
+ }
+
+ host = mk_ptr_to_buf(sr->host);
+
+ /*
+ * Add ending slash to the location string
+ */
+ location = mk_mem_alloc(sr->uri_processed.len + 2);
+ memcpy(location, sr->uri_processed.data, sr->uri_processed.len);
+ location[sr->uri_processed.len] = '/';
+ location[sr->uri_processed.len + 1] = '\0';
+
+ /* FIXME: should we done something similar for SSL = 443 */
+ if (sr->host.data && sr->port > 0) {
+ if (sr->port != server->standard_port) {
+ port_redirect = sr->port;
+ }
+ }
+
+ if (MK_SCHED_CONN_PROP(cs->conn) & MK_CAP_SOCK_TLS) {
+ protocol = "https";
+ }
+
+ if (port_redirect > 0) {
+ mk_string_build(&real_location, &len, "%s://%s:%i%s\r\n",
+ protocol, host, port_redirect, location);
+ }
+ else {
+ mk_string_build(&real_location, &len, "%s://%s%s\r\n",
+ protocol, host, location);
+ }
+
+ MK_TRACE("Redirecting to '%s'", real_location);
+ mk_mem_free(host);
+
+ mk_header_set_http_status(sr, MK_REDIR_MOVED);
+ sr->headers.content_length = 0;
+
+ mk_ptr_reset(&sr->headers.content_type);
+ sr->headers.location = real_location;
+ sr->headers.cgi = SH_NOCGI;
+ sr->headers.pconnections_left =
+ (server->max_keep_alive_request - cs->counter_connections);
+
+ mk_header_prepare(cs, sr, server);
+
+ /* we do not free() real_location as it's freed by iov */
+ mk_mem_free(location);
+ sr->headers.location = NULL;
+ return -1;
+}
+
+/* Look for some index.xxx in pathfile */
+static inline char *mk_http_index_lookup(mk_ptr_t *path_base,
+ char *buf, size_t buf_size,
+ size_t *out, size_t *bytes,
+ struct mk_server *server)
+{
+ off_t off = 0;
+ size_t len;
+ struct mk_string_line *entry;
+ struct mk_list *head;
+
+ if (!server->index_files) {
+ return NULL;
+ }
+
+ off = path_base->len;
+ memcpy(buf, path_base->data, off);
+
+ mk_list_foreach(head, server->index_files) {
+ entry = mk_list_entry(head, struct mk_string_line, _head);
+
+ len = off + entry->len + 1;
+ if (len >= buf_size) {
+ continue;
+ }
+
+ memcpy(buf + off, entry->val, entry->len);
+ buf[off + entry->len] = '\0';
+
+ if (access(buf, F_OK) == 0) {
+ MK_TRACE("Index lookup OK '%s'", buf);
+ *out = off + entry->len;
+ *bytes = path_base->len - 1;
+ return buf;
+ }
+ }
+
+ return NULL;
+}
+
+/* Turn CORK_OFF once headers are sent */
+#if defined (__linux__)
+static inline void mk_http_cb_file_on_consume(struct mk_stream_input *in,
+ long bytes)
+{
+ int ret;
+ (void) bytes;
+
+ /*
+ * This callback is invoked just once as we want to turn off
+ * the TCP Cork. We do this just overriding the callback for
+ * the file stream.
+ */
+ ret = mk_server_cork_flag(in->stream->channel->fd, TCP_CORK_OFF);
+ if (ret == -1) {
+ mk_warn("Could not set TCP_CORK/TCP_NOPUSH off");
+ }
+ MK_TRACE("[FD %i] Disable TCP_CORK/TCP_NOPUSH",
+ in->stream->channel->fd);
+ in->cb_consumed = NULL;
+}
+#endif
+
+int mk_http_init(struct mk_http_session *cs, struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int ret;
+ int ret_file;
+ struct mk_mimetype *mime;
+ struct mk_list *head;
+ struct mk_list *handlers;
+ struct mk_plugin *plugin;
+ struct mk_vhost_handler *h_handler;
+ struct mk_http_thread *mth = NULL;
+ size_t index_length;
+ size_t index_bytes;
+ char *index_path = NULL;
+
+ MK_TRACE("[FD %i] HTTP Protocol Init, session %p", cs->socket, sr);
+
+ /* Request to root path of the virtualhost in question */
+ if (sr->uri_processed.len == 1 && sr->uri_processed.data[0] == '/') {
+ sr->real_path.data = sr->host_conf->documentroot.data;
+ sr->real_path.len = sr->host_conf->documentroot.len;
+ }
+
+ /* Compose real path */
+ if (sr->user_home == MK_FALSE) {
+ int len;
+
+ len = sr->host_conf->documentroot.len + sr->uri_processed.len;
+ if (len < MK_PATH_BASE) {
+ memcpy(sr->real_path_static,
+ sr->host_conf->documentroot.data,
+ sr->host_conf->documentroot.len);
+ memcpy(sr->real_path_static + sr->host_conf->documentroot.len,
+ sr->uri_processed.data,
+ sr->uri_processed.len);
+ sr->real_path_static[len] = '\0';
+ sr->real_path.data = sr->real_path_static;
+ sr->real_path.len = len;
+ }
+ else {
+ ret = mk_buffer_cat(&sr->real_path,
+ sr->host_conf->documentroot.data,
+ sr->host_conf->documentroot.len,
+ sr->uri_processed.data,
+ sr->uri_processed.len);
+
+ if (ret < 0) {
+ MK_TRACE("Error composing real path");
+ return MK_EXIT_ERROR;
+ }
+ }
+ }
+
+ /* Check if this is related to a protocol upgrade */
+#ifdef MK_HAVE_HTTP2
+ if (cs->parser.header_connection & MK_HTTP_PARSER_CONN_UPGRADE) {
+ /* HTTP/2.0 upgrade ? */
+ if (cs->parser.header_connection & MK_HTTP_PARSER_CONN_HTTP2_SE) {
+ MK_TRACE("Connection Upgrade request: HTTP/2.0");
+ /*
+ * This is a HTTP/2.0 upgrade, we need to validate that we
+ * have at least the 'Upgrade' and 'HTTP2-Settings' headers.
+ */
+ struct mk_http_header *p;
+ p = &cs->parser.headers[MK_HEADER_HTTP2_SETTINGS];
+ if (cs->parser.header_upgrade == MK_HTTP_PARSER_UPGRADE_H2C &&
+ p->key.data) {
+ /*
+ * Switch protocols and invoke the callback upgrade to prepare
+ * the new protocol internals.
+ */
+ mk_sched_switch_protocol(cs->conn, MK_CAP_HTTP2);
+ return cs->conn->protocol->cb_upgrade(cs, sr, server);
+ }
+ else {
+ MK_TRACE("Invalid client upgrade request, skip it");
+ }
+ }
+ }
+#endif
+
+ /* Check backward directory request */
+ if (memmem(sr->uri_processed.data, sr->uri_processed.len,
+ MK_HTTP_DIRECTORY_BACKWARD,
+ sizeof(MK_HTTP_DIRECTORY_BACKWARD) - 1)) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+
+ if (sr->_content_length.data &&
+ (sr->method != MK_METHOD_POST &&
+ sr->method != MK_METHOD_PUT)) {
+ sr->_content_length.data = NULL;
+ sr->_content_length.len = 0;
+ }
+
+ ret_file = mk_file_get_info(sr->real_path.data, &sr->file_info, MK_FILE_READ);
+
+ /* Manually set the headers input streams */
+ sr->in_headers.type = MK_STREAM_IOV;
+ sr->in_headers.dynamic = MK_FALSE;
+ sr->in_headers.cb_consumed = NULL;
+ sr->in_headers.cb_finished = NULL;
+ sr->in_headers.stream = &sr->stream;
+ mk_list_add(&sr->in_headers._head, &sr->stream.inputs);
+
+ /* Plugin Stage 30: look for handlers for this request */
+ if (sr->stage30_blocked == MK_FALSE) {
+ sr->uri_processed.data[sr->uri_processed.len] = '\0';
+ handlers = &sr->host_conf->handlers;
+ mk_list_foreach(head, handlers) {
+ h_handler = mk_list_entry(head, struct mk_vhost_handler, _head);
+
+ if (re_matchp(h_handler->match,
+ sr->uri_processed.data, NULL) == -1) {
+ continue;
+ }
+
+ if (h_handler->cb) {
+ /* Create coroutine/thread context */
+ sr->headers.content_length = 0;
+ mth = mk_http_thread_create(MK_HTTP_THREAD_LIB,
+ h_handler,
+ cs, sr,
+ 0, NULL);
+ if (!mth) {
+ return -1;
+ }
+
+ mk_http_thread_start(mth);
+ return MK_EXIT_OK;
+ }
+ else {
+ if (!h_handler->handler) {
+ return mk_http_error(MK_SERVER_INTERNAL_ERROR, cs, sr,
+ server);
+ }
+ plugin = h_handler->handler;
+ sr->stage30_handler = h_handler->handler;
+ ret = plugin->stage->stage30(plugin, cs, sr,
+ h_handler->n_params,
+ &h_handler->params);
+ mk_header_prepare(cs, sr, server);
+ }
+
+ MK_TRACE("[FD %i] STAGE_30 returned %i", cs->socket, ret);
+ switch (ret) {
+ case MK_PLUGIN_RET_CONTINUE:
+ /* FIXME: PLUGINS DISABLED
+ if ((plugin->flags & MK_PLUGIN_THREAD) &&
+ plugin->stage->stage30_thread) {
+ mth = mk_http_thread_new(MK_HTTP_THREAD_PLUGIN,
+ plugin, cs, sr,
+ h_handler->n_params,
+ &h_handler->params);
+ printf("[http thread] %p\n", mth);
+ mk_http_thread_resume(mth->parent);
+ }
+ */
+ return MK_PLUGIN_RET_CONTINUE;
+ case MK_PLUGIN_RET_CLOSE_CONX:
+ if (sr->headers.status > 0) {
+ return mk_http_error(sr->headers.status, cs, sr, server);
+ }
+ else {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+ case MK_PLUGIN_RET_END:
+ return MK_EXIT_OK;
+ }
+ }
+ }
+
+ /* If there is no handler and the resource don't exists, raise a 404 */
+ if (ret_file == -1) {
+ return mk_http_error(MK_CLIENT_NOT_FOUND, cs, sr, server);
+ }
+
+ /* is it a valid directory ? */
+ if (sr->file_info.is_directory == MK_TRUE) {
+ /* Send redirect header if end slash is not found */
+ if (mk_http_directory_redirect_check(cs, sr, server) == -1) {
+ MK_TRACE("Directory Redirect");
+
+ /* Redirect has been sent */
+ return -1;
+ }
+
+ /* looking for an index file */
+ char tmppath[MK_MAX_PATH];
+ index_path = mk_http_index_lookup(&sr->real_path,
+ tmppath, MK_MAX_PATH,
+ &index_length, &index_bytes,
+ server);
+ if (index_path) {
+ if (sr->real_path.data != sr->real_path_static) {
+ mk_ptr_free(&sr->real_path);
+ sr->real_path.data = mk_string_dup(index_path);
+ }
+ /* If it's static and it still fits */
+ else if (index_length < MK_PATH_BASE) {
+ memcpy(sr->real_path_static, index_path, index_length);
+ sr->real_path_static[index_length] = '\0';
+ }
+ /* It was static, but didn't fit */
+ else {
+ sr->real_path.data = mk_string_dup(index_path);
+ }
+ sr->real_path.len = index_length;
+
+ ret = mk_file_get_info(sr->real_path.data,
+ &sr->file_info, MK_FILE_READ);
+ if (ret != 0) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+
+ }
+ }
+
+#ifndef _WIN32
+ /* Check symbolic link file */
+ if (sr->file_info.is_link == MK_TRUE) {
+ if (server->symlink == MK_FALSE) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+ else {
+ int n;
+ char linked_file[MK_MAX_PATH];
+ n = readlink(sr->real_path.data, linked_file, MK_MAX_PATH);
+ if (n < 0) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+ }
+ }
+#endif
+
+ /* Plugin Stage 30: look for handlers for this request */
+ if (sr->stage30_blocked == MK_FALSE) {
+ char *uri;
+
+ if (!index_path) {
+ sr->uri_processed.data[sr->uri_processed.len] = '\0';
+ uri = sr->uri_processed.data;
+ }
+ else {
+ uri = sr->real_path.data + index_bytes;
+ }
+
+ handlers = &sr->host_conf->handlers;
+ mk_list_foreach(head, handlers) {
+ h_handler = mk_list_entry(head, struct mk_vhost_handler, _head);
+ if (re_matchp(h_handler->match, uri, NULL) == -1) {
+ continue;
+ }
+
+ plugin = h_handler->handler;
+ sr->stage30_handler = h_handler->handler;
+ ret = plugin->stage->stage30(plugin, cs, sr,
+ h_handler->n_params,
+ &h_handler->params);
+
+ MK_TRACE("[FD %i] STAGE_30 returned %i", cs->socket, ret);
+ switch (ret) {
+ case MK_PLUGIN_RET_CONTINUE:
+ return MK_PLUGIN_RET_CONTINUE;
+ case MK_PLUGIN_RET_CLOSE_CONX:
+ if (sr->headers.status > 0) {
+ return mk_http_error(sr->headers.status, cs, sr, server);
+ }
+ else {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+ case MK_PLUGIN_RET_END:
+ return MK_EXIT_OK;
+ }
+ }
+ }
+
+ /*
+ * Monkey listens for PUT and DELETE methods in addition to GET, POST and
+ * HEAD, but it does not care about them, so if any plugin did not worked
+ * on it, Monkey will return error 501 (501 Not Implemented).
+ */
+ if (sr->method == MK_METHOD_PUT || sr->method == MK_METHOD_DELETE) {
+ return mk_http_error(MK_CLIENT_METHOD_NOT_ALLOWED, cs, sr, server);
+ }
+ else if (sr->method == MK_METHOD_UNKNOWN) {
+ return mk_http_error(MK_SERVER_NOT_IMPLEMENTED, cs, sr, server);
+ }
+
+ /* counter connections */
+ sr->headers.pconnections_left = (int)
+ (server->max_keep_alive_request - cs->counter_connections);
+
+ /* Set default value */
+ mk_header_set_http_status(sr, MK_HTTP_OK);
+ sr->headers.location = NULL;
+ sr->headers.content_length = 0;
+
+ /*
+ * For OPTIONS method, we let the plugin handle it and
+ * return without any content.
+ */
+ if (sr->method == MK_METHOD_OPTIONS) {
+ /* FIXME: OPTIONS NOT WORKING */
+ //sr->headers.allow_methods.data = MK_METHOD_AVAILABLE;
+ //sr->headers.allow_methods.len = strlen(MK_METHOD_AVAILABLE);
+
+ mk_ptr_reset(&sr->headers.content_type);
+ mk_header_prepare(cs, sr, server);
+ return MK_EXIT_OK;
+ }
+ else {
+ mk_ptr_reset(&sr->headers.allow_methods);
+ }
+
+ /* read permissions and check file */
+ if (sr->file_info.read_access == MK_FALSE) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+
+ /* Matching MimeType */
+ mime = mk_mimetype_find(server, &sr->real_path);
+ if (!mime) {
+ mime = server->mimetype_default;
+ }
+
+ if (sr->file_info.is_directory == MK_TRUE) {
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+
+ /* get file size */
+ if (sr->file_info.size == 0) {
+ return mk_http_error(MK_CLIENT_NOT_FOUND, cs, sr, server);
+ }
+
+ /* Configure some headers */
+ sr->headers.last_modified = sr->file_info.last_modification;
+ sr->headers.etag_len = snprintf(sr->headers.etag_buf,
+ MK_HEADER_ETAG_SIZE,
+ "ETag: \"%x-%zx\"\r\n",
+ (unsigned int) sr->file_info.last_modification,
+ sr->file_info.size);
+
+ if (sr->if_modified_since.data && sr->method == MK_METHOD_GET) {
+ time_t date_client; /* Date sent by client */
+ time_t date_file_server; /* Date server file */
+
+ date_client = mk_utils_gmt2utime(sr->if_modified_since.data);
+ date_file_server = sr->file_info.last_modification;
+
+ if (date_file_server <= date_client &&
+ date_client > 0) {
+ mk_header_set_http_status(sr, MK_NOT_MODIFIED);
+ mk_header_prepare(cs, sr, server);
+ return MK_EXIT_OK;
+ }
+ }
+
+ /* Object size for log and response headers */
+ sr->headers.content_length = sr->file_info.size;
+ sr->headers.real_length = sr->file_info.size;
+
+ /* Open file */
+ if (mk_likely(sr->file_info.size > 0)) {
+ sr->file_fd = mk_vhost_open(sr, server);
+ if (sr->file_fd == -1) {
+ MK_TRACE("open() failed");
+ return mk_http_error(MK_CLIENT_FORBIDDEN, cs, sr, server);
+ }
+ sr->in_file.fd = sr->file_fd;
+ sr->in_file.bytes_offset = 0;
+ sr->in_file.bytes_total = sr->file_info.size;
+ sr->in_file.stream = &sr->stream;
+ }
+
+ /* Process methods */
+ if (sr->method == MK_METHOD_GET || sr->method == MK_METHOD_HEAD) {
+ if (mime) {
+ sr->headers.content_type = mime->header_type;
+ }
+
+ /* HTTP Ranges */
+ if (sr->range.data != NULL && server->resume == MK_TRUE) {
+ if (mk_http_range_parse(sr) < 0) {
+ sr->headers.ranges[0] = -1;
+ sr->headers.ranges[1] = -1;
+ return mk_http_error(MK_CLIENT_BAD_REQUEST, cs, sr, server);
+ }
+ if (sr->headers.ranges[0] >= 0 || sr->headers.ranges[1] >= 0) {
+ mk_header_set_http_status(sr, MK_HTTP_PARTIAL);
+ }
+
+ /* Calc bytes to send & offset */
+ if (mk_http_range_set(sr, sr->file_info.size, server) != 0) {
+ sr->headers.content_length = -1;
+ sr->headers.ranges[0] = -1;
+ sr->headers.ranges[1] = -1;
+ return mk_http_error(MK_CLIENT_REQUESTED_RANGE_NOT_SATISF,
+ cs, sr, server);
+ }
+ }
+ }
+ else {
+ /* without content-type */
+ mk_ptr_reset(&sr->headers.content_type);
+ }
+
+ /* Send headers */
+ mk_header_prepare(cs, sr, server);
+ if (mk_unlikely(sr->headers.content_length == 0)) {
+ return 0;
+ }
+ /* Send file content */
+ if (sr->method == MK_METHOD_GET || sr->method == MK_METHOD_POST) {
+ /* Note: bytes and offsets are set after the Range check */
+ sr->in_file.type = MK_STREAM_FILE;
+ mk_stream_append(&sr->in_file, &sr->stream);
+ }
+
+ /*
+ * Enable TCP Cork for the remote socket. It will be disabled
+ * later by the file stream on the channel after send the first
+ * file bytes.
+ */
+#if defined(__linux__)
+ sr->in_file.cb_consumed = mk_http_cb_file_on_consume;
+#endif
+
+ /*
+ * Enable CORK/NO_PUSH
+ * -------------------
+ * If it was compiled for Linux, it will turn the Cork off after
+ * send the first round of bytes from the target static file.
+ *
+ * For OSX, it sets TCP_NOPUSH off after send all HTTP headers. Refer
+ * to mk_header.c for more details.
+ */
+ //mk_server_cork_flag(cs->socket, TCP_CORK_ON);
+
+ /* Start sending data to the channel */
+ return MK_EXIT_OK;
+}
+
+/*
+ * Check if a connection can stay open using
+ * the keepalive headers vars and Monkey configuration as criteria
+ */
+int mk_http_keepalive_check(struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ if (server->keep_alive == MK_FALSE) {
+ return -1;
+ }
+
+ /* Default Keepalive is off */
+ if (sr->protocol == MK_HTTP_PROTOCOL_10) {
+ cs->close_now = MK_TRUE;
+ }
+ else if (sr->protocol == MK_HTTP_PROTOCOL_11) {
+ cs->close_now = MK_FALSE;
+ }
+
+ if (sr->connection.data) {
+ if (cs->parser.header_connection == MK_HTTP_PARSER_CONN_KA &&
+ sr->protocol == MK_HTTP_PROTOCOL_11) {
+ cs->close_now = MK_FALSE;
+ }
+ else if (cs->parser.header_connection == MK_HTTP_PARSER_CONN_CLOSE) {
+ cs->close_now = MK_TRUE;
+ }
+ }
+
+ /* Client has reached keep-alive connections limit */
+ if (cs->counter_connections >= server->max_keep_alive_request) {
+ cs->close_now = MK_TRUE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void mk_http_request_ka_next(struct mk_http_session *cs)
+{
+ cs->body_length = 0;
+ cs->counter_connections++;
+
+ /* Update data for scheduler */
+ cs->init_time = cs->server->clock_context->log_current_utime;
+ cs->status = MK_REQUEST_STATUS_INCOMPLETE;
+
+ /* Initialize parser */
+ mk_http_parser_init(&cs->parser);
+}
+
+int mk_http_request_end(struct mk_http_session *cs, struct mk_server *server)
+{
+ int ret;
+ int status;
+ int len;
+ struct mk_http_request *sr = NULL;
+
+ if (server->max_keep_alive_request <= cs->counter_connections) {
+ cs->close_now = MK_TRUE;
+ goto shutdown;
+ }
+
+ /* Check if we have some enqueued pipeline requests */
+ ret = mk_http_parser_more(&cs->parser, cs->body_length);
+ if (ret == MK_TRUE) {
+ /* Our pipeline request limit is the same that our keepalive limit */
+ cs->counter_connections++;
+ len = (cs->body_length - cs->parser.i) -1;
+ memmove(cs->body,
+ cs->body + cs->parser.i + 1,
+ len);
+ cs->body_length = len;
+
+ /* Prepare for next one */
+ sr = mk_list_entry_first(&cs->request_list, struct mk_http_request, _head);
+ mk_http_request_free(sr, server);
+ mk_http_request_init(cs, sr, server);
+ mk_http_parser_init(&cs->parser);
+ status = mk_http_parser(sr, &cs->parser, cs->body, cs->body_length,
+ server);
+ if (status == MK_HTTP_PARSER_OK) {
+ ret = mk_http_request_prepare(cs, sr, server);
+ if (ret == MK_EXIT_ABORT) {
+ return -1;
+ }
+
+ /*
+ * Return 1 means, we still have more data to send in a different
+ * scheduler round.
+ */
+ return 1;
+ }
+ else if (status == MK_HTTP_PARSER_PENDING) {
+ return 0;
+ }
+ else if (status == MK_HTTP_PARSER_ERROR) {
+ cs->close_now = MK_TRUE;
+ }
+ }
+
+ shutdown:
+ /*
+ * We need to ask to http_keepalive if this
+ * connection can continue working or we must
+ * close it.
+ */
+ if (cs->close_now == MK_TRUE) {
+ MK_TRACE("[FD %i] No KeepAlive mode, remove", cs->conn->event.fd);
+ mk_http_session_remove(cs, server);
+ return -1;
+ }
+ else {
+ mk_http_request_free_list(cs, server);
+ mk_http_request_ka_next(cs);
+ mk_sched_conn_timeout_add(cs->conn, mk_sched_get_thread_conf());
+ return 0;
+ }
+
+ return -1;
+}
+
+void cb_stream_page_finished(struct mk_stream_input *in)
+{
+ mk_ptr_t *page = in->buffer;
+
+ mk_ptr_free(page);
+ mk_mem_free(page);
+}
+
+/* Enqueue an error response. This function always returns MK_EXIT_OK */
+int mk_http_error(int http_status, struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int ret, fd;
+ size_t count;
+ mk_ptr_t message;
+ mk_ptr_t page;
+ struct mk_vhost_error_page *entry;
+ struct mk_list *head;
+ struct file_info finfo;
+ struct mk_iov *iov;
+
+ /* This function requires monkey to be properly initialized which is not the case
+ * when it's just used to parse http requests in fluent-bit so we want it to ignore
+ * that case and let fluent-bit handle it.
+ */
+ if (server->workers == 0) {
+ return MK_EXIT_OK;
+ }
+
+ mk_header_set_http_status(sr, http_status);
+ mk_ptr_reset(&page);
+
+ /*
+ * We are nice sending error pages for clients who at least respect
+ * the especification
+ */
+ if (http_status != MK_CLIENT_LENGTH_REQUIRED &&
+ http_status != MK_CLIENT_BAD_REQUEST &&
+ http_status != MK_CLIENT_REQUEST_ENTITY_TOO_LARGE) {
+
+ /* Lookup a customized error page */
+ mk_list_foreach(head, &sr->host_conf->error_pages) {
+ entry = mk_list_entry(head, struct mk_vhost_error_page, _head);
+ if (entry->status != http_status) {
+ continue;
+ }
+
+ /* validate error file */
+ ret = mk_file_get_info(entry->real_path, &finfo, MK_FILE_READ);
+ if (ret == -1) {
+ break;
+ }
+
+ /* open file */
+ fd = open(entry->real_path, server->open_flags);
+ if (fd == -1) {
+ break;
+ }
+ /* This fd seems to be leaked, we need to verify this logic */
+
+ /* Outgoing headers */
+ sr->headers.content_length = finfo.size;
+ sr->headers.real_length = finfo.size;
+ mk_header_prepare(cs, sr, server);
+
+ /* Stream setup */
+ mk_stream_in_file(&sr->stream, &sr->in_file, sr->file_fd,
+ finfo.size, 0, NULL, NULL);
+ return MK_EXIT_OK;
+ }
+ }
+
+ mk_ptr_reset(&message);
+
+ switch (http_status) {
+ case MK_CLIENT_FORBIDDEN:
+ mk_http_error_page("Forbidden",
+ &sr->uri,
+ server->server_signature,
+ &page.data, &page.len);
+ break;
+ case MK_CLIENT_NOT_FOUND:
+ mk_string_build(&message.data, &message.len,
+ "The requested URL was not found on this server.");
+ mk_http_error_page("Not Found",
+ &message,
+ server->server_signature,
+ &page.data, &page.len);
+ mk_ptr_free(&message);
+ break;
+ case MK_CLIENT_REQUEST_ENTITY_TOO_LARGE:
+ mk_string_build(&message.data, &message.len,
+ "The request entity is too large.");
+ mk_http_error_page("Entity too large",
+ &message,
+ server->server_signature,
+ &page.data, &page.len);
+ mk_ptr_free(&message);
+ break;
+ case MK_CLIENT_METHOD_NOT_ALLOWED:
+ mk_http_error_page("Method Not Allowed",
+ &sr->uri,
+ server->server_signature,
+ &page.data, &page.len);
+ break;
+ case MK_SERVER_NOT_IMPLEMENTED:
+ mk_http_error_page("Method Not Implemented",
+ &sr->uri,
+ server->server_signature,
+ &page.data, &page.len);
+ break;
+ case MK_SERVER_INTERNAL_ERROR:
+ mk_http_error_page("Internal Server Error",
+ &sr->uri,
+ server->server_signature,
+ &page.data, &page.len);
+ break;
+ }
+
+ if (page.len > 0 && sr->method != MK_METHOD_HEAD && sr->method != MK_METHOD_UNKNOWN) {
+ sr->headers.content_length = page.len;
+ }
+ else {
+ sr->headers.content_length = 0;
+ }
+
+ sr->headers.location = NULL;
+ sr->headers.cgi = SH_NOCGI;
+ sr->headers.pconnections_left = 0;
+ sr->headers.last_modified = -1;
+
+ if (!page.data) {
+ mk_ptr_reset(&sr->headers.content_type);
+ }
+ else {
+ mk_ptr_set(&sr->headers.content_type, "Content-Type: text/html\r\n");
+ }
+
+ mk_header_prepare(cs, sr, server);
+ if (page.data) {
+ if (sr->method != MK_METHOD_HEAD) {
+ if (sr->headers._extra_rows) {
+ iov = sr->headers._extra_rows;
+ sr->in_headers_extra.bytes_total += page.len;
+ }
+ else {
+ iov = &sr->headers.headers_iov;
+ sr->in_headers.bytes_total += page.len;
+ }
+ mk_iov_add(iov, page.data, page.len, MK_TRUE);
+ }
+ else {
+ mk_mem_free(page.data);
+ }
+ }
+
+ mk_channel_write(cs->channel, &count);
+ mk_http_request_end(cs, server);
+
+ return MK_EXIT_OK;
+}
+
+/*
+ * From thread mk_sched_worker "list", remove the http_session
+ * struct information
+ */
+void mk_http_session_remove(struct mk_http_session *cs,
+ struct mk_server *server)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_plugin *handler;
+ struct mk_http_request *sr;
+
+ MK_TRACE("[FD %i] HTTP Session remove", cs->socket);
+ if (cs->_sched_init == MK_FALSE) {
+ return;
+ }
+
+ /* On session remove, make sure to cleanup any handler */
+ mk_list_foreach_safe(head, tmp, &cs->request_list) {
+ sr = mk_list_entry(head, struct mk_http_request, _head);
+ if (sr->stage30_handler) {
+ MK_TRACE("Hangup stage30 handler");
+ handler = sr->stage30_handler;
+ if (mk_unlikely(!handler->stage->stage30_hangup)) {
+ mk_warn("Plugin %s, do not implement stage30_hangup", handler->name);
+ continue;
+ }
+ handler->stage->stage30_hangup(handler, cs, sr);
+ }
+ }
+
+ if (cs->body != cs->body_fixed) {
+ mk_mem_free(cs->body);
+ }
+ mk_http_request_free_list(cs, server);
+ mk_list_del(&cs->request_list);
+
+ cs->_sched_init = MK_FALSE;
+}
+
+/* FIXME: nobody is using this */
+struct mk_http_session *mk_http_session_lookup(int socket)
+{
+ (void) socket;
+ return NULL;
+}
+
+
+/* Initialize a HTTP session (just created) */
+int mk_http_session_init(struct mk_http_session *cs, struct mk_sched_conn *conn,
+ struct mk_server *server)
+{
+ /* Alloc memory for node */
+ cs->_sched_init = MK_TRUE;
+ cs->pipelined = MK_FALSE;
+ cs->counter_connections = 0;
+ cs->close_now = MK_FALSE;
+ cs->socket = conn->event.fd;
+ cs->status = MK_REQUEST_STATUS_INCOMPLETE;
+ cs->server = server;
+
+ /* Map the channel, just for protocol-handler internal stuff */
+ cs->channel = &conn->channel;
+
+ /* Map the connection instance, required to handle exceptions */
+ cs->conn = conn;
+
+ /* creation time in unix time */
+ cs->init_time = conn->arrive_time;
+
+ /* alloc space for body content */
+ if (conn->net->buffer_size > MK_REQUEST_CHUNK) {
+ cs->body = mk_mem_alloc(conn->net->buffer_size);
+ cs->body_size = conn->net->buffer_size;
+ }
+ else {
+ /* Buffer size based in Chunk bytes */
+ cs->body = cs->body_fixed;
+ cs->body_size = MK_REQUEST_CHUNK;
+ }
+
+ /* Current data length */
+ cs->body_length = 0;
+
+ /* Init session request list */
+ mk_list_init(&cs->request_list);
+
+ /* Initialize the parser */
+ mk_http_parser_init(&cs->parser);
+
+ return 0;
+}
+
+
+void mk_http_request_free(struct mk_http_request *sr, struct mk_server *server)
+{
+ /* Let the vhost interface to handle the session close */
+ mk_vhost_close(sr, server);
+
+ if (sr->headers.location) {
+ mk_mem_free(sr->headers.location);
+ }
+
+ if (sr->uri_processed.data != sr->uri.data) {
+ mk_ptr_free(&sr->uri_processed);
+ }
+
+ if (sr->real_path.data != sr->real_path_static) {
+ mk_ptr_free(&sr->real_path);
+ }
+
+ if (sr->stream.channel) {
+ mk_stream_release(&sr->stream);
+ }
+}
+
+void mk_http_request_free_list(struct mk_http_session *cs,
+ struct mk_server *server)
+{
+ struct mk_list *head, *tmp;
+ struct mk_http_request *request;
+
+ /* sr = last node */
+ MK_TRACE("[FD %i] Free struct client_session", cs->socket);
+ mk_list_foreach_safe(head, tmp, &cs->request_list) {
+ request = mk_list_entry(head, struct mk_http_request, _head);
+ mk_list_del(&request->_head);
+
+ mk_http_request_free(request, server);
+ if (request != &cs->sr_fixed) {
+ mk_mem_free(request);
+ }
+ }
+}
+
+/*
+ * Lookup a known header or a non-known header. For unknown headers
+ * set the 'key' value wth a lowercase string
+ */
+struct mk_http_header *mk_http_header_get(int name, struct mk_http_request *req,
+ const char *key, unsigned int len)
+{
+ int i;
+ struct mk_http_parser *parser = &req->session->parser;
+ struct mk_http_header *header;
+
+ /* Known header */
+ if (name >= 0 && name < MK_HEADER_SIZEOF) {
+ return &parser->headers[name];
+ }
+
+ /* Check if want to retrieve a custom header */
+ if (name == MK_HEADER_OTHER) {
+ /* Iterate over the extra headers identified by the parser */
+ for (i = 0; i < parser->headers_extra_count; i++) {
+ header = &parser->headers_extra[i];
+ if (header->key.len != len) {
+ continue;
+ }
+
+ if (strncmp(header->key.data, key, len) == 0) {
+ return header;
+ }
+ }
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Main callbacks for the Scheduler
+ */
+int mk_http_sched_read(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker,
+ struct mk_server *server)
+{
+ int ret;
+ int status;
+ size_t count;
+ (void) worker;
+ struct mk_http_session *cs;
+ struct mk_http_request *sr;
+
+#ifdef MK_HAVE_TRACE
+ int socket = conn->event.fd;
+#endif
+
+ cs = mk_http_session_get(conn);
+ if (cs->_sched_init == MK_FALSE) {
+ /* Create session for the client */
+ MK_TRACE("[FD %i] Create HTTP session", socket);
+ ret = mk_http_session_init(cs, conn, server);
+ if (ret == -1) {
+ return -1;
+ }
+ }
+
+ /* Invoke the read handler, on this case we only support HTTP (for now :) */
+ ret = mk_http_handler_read(conn, cs, server);
+ if (ret > 0) {
+ if (mk_list_is_empty(&cs->request_list) == 0) {
+ /* Add the first entry */
+ sr = &cs->sr_fixed;
+ mk_list_add(&sr->_head, &cs->request_list);
+ mk_http_request_init(cs, sr, server);
+ }
+ else {
+ sr = mk_list_entry_first(&cs->request_list, struct mk_http_request, _head);
+ }
+ status = mk_http_parser(sr, &cs->parser, cs->body,
+ cs->body_length, server);
+ if (status == MK_HTTP_PARSER_OK) {
+ MK_TRACE("[FD %i] HTTP_PARSER_OK", socket);
+ if (mk_http_status_completed(cs, conn) == -1) {
+ mk_http_session_remove(cs, server);
+ return -1;
+ }
+ mk_sched_conn_timeout_del(conn);
+ ret = mk_http_request_prepare(cs, sr, server);
+ }
+ else if (status == MK_HTTP_PARSER_ERROR) {
+ /* The HTTP parser may enqueued some response error */
+ if (mk_channel_is_empty(cs->channel) != 0) {
+ mk_channel_write(cs->channel, &count);
+ }
+ mk_http_session_remove(cs, server);
+ MK_TRACE("[FD %i] HTTP_PARSER_ERROR", socket);
+ return -1;
+ }
+ else {
+ MK_TRACE("[FD %i] HTTP_PARSER_PENDING", socket);
+ }
+ }
+
+ return ret;
+}
+
+/* The scheduler got a connection close event from the remote client */
+int mk_http_sched_close(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ int type, struct mk_server *server)
+{
+ struct mk_http_session *session;
+ (void) sched;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("[FD %i] HTTP sched close (type=%i)", conn->event.fd, type);
+#else
+ (void) type;
+#endif
+
+ /* Release resources of the requests and session */
+ session = mk_http_session_get(conn);
+ mk_http_session_remove(session, server);
+ return 0;
+}
+
+int mk_http_sched_done(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker,
+ struct mk_server *server)
+{
+ (void) worker;
+ struct mk_http_session *session;
+ struct mk_http_request *sr;
+
+ session = mk_http_session_get(conn);
+ sr = mk_list_entry_first(&session->request_list,
+ struct mk_http_request, _head);
+ mk_plugin_stage_run_40(session, sr, server);
+
+ return mk_http_request_end(session, server);
+}
+
+struct mk_sched_handler mk_http_handler = {
+ .name = "http",
+ .cb_read = mk_http_sched_read,
+ .cb_close = mk_http_sched_close,
+ .cb_done = mk_http_sched_done,
+ .sched_extra_size = sizeof(struct mk_http_session),
+ .capabilities = MK_CAP_HTTP
+};
diff --git a/fluent-bit/lib/monkey/mk_server/mk_http2.c b/fluent-bit/lib/monkey/mk_server/mk_http2.c
new file mode 100644
index 000000000..7a4f1e82b
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_http2.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+
+#include <monkey/mk_http2.h>
+#include <monkey/mk_http2_settings.h>
+#include <monkey/mk_header.h>
+#include <monkey/mk_scheduler.h>
+
+/* HTTP/2 Connection Preface */
+#define MK_HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+static mk_ptr_t http2_preface = {
+ .data = MK_HTTP2_PREFACE,
+ .len = sizeof(MK_HTTP2_PREFACE) - 1
+};
+
+static inline void buffer_consume(struct mk_http2_session *h2s, int bytes)
+{
+ memmove(h2s->buffer,
+ h2s->buffer + bytes,
+ h2s->buffer_length - bytes);
+
+ MK_TRACE("[h2] consume buffer length from %i to %i",
+ h2s->buffer_length, h2s->buffer_length - bytes);
+ h2s->buffer_length -= bytes;
+}
+
+static struct mk_http2_session *mk_http2_session_create()
+{
+ struct mk_http2_session *h2s;
+
+ h2s = mk_mem_alloc(sizeof(struct mk_http2_session));
+ if (!h2s) {
+ return NULL;
+ }
+ h2s->buffer = NULL;
+ h2s->buffer_length = 0;
+ h2s->buffer_size = sizeof(h2s->buffer_fixed);
+ h2s->buffer = h2s->buffer_fixed;
+ h2s->settings = MK_HTTP2_SETTINGS_DEFAULT;
+
+ return h2s;
+}
+
+/* FIXME
+static int mk_http2_session_destroy(struct mk_http2_session *h2s)
+{
+ if (h2s->buffer != h2s->buffer_fixed) {
+ mk_mem_free(h2s->buffer);
+ }
+ mk_mem_free(h2s);
+ return 0;
+}
+
+static int mk_http2_frame_header(char *buf, uint32_t length, uint8_t type,
+ uint32_t flags, void *data)
+{
+ struct mk_http2_frame *f = (struct mk_http2_frame *) buf;
+
+ f->len_type = (length << 8 | type);
+ f->flags = flags;
+ f->payload = data;
+
+ return sizeof(struct mk_http2_frame);
+}
+
+*/
+
+/* Handle an upgraded session */
+static int mk_http2_upgrade(void *cs, void *sr, struct mk_server *server)
+{
+ struct mk_http_session *s = cs;
+ struct mk_http_request *r = sr;
+ struct mk_http2_session *h2s;
+
+ mk_header_set_http_status(r, MK_INFO_SWITCH_PROTOCOL);
+ r->headers.connection = MK_HEADER_CONN_UPGRADED;
+ r->headers.upgrade = MK_HEADER_UPGRADED_H2C;
+ mk_header_prepare(s, r, server);
+
+ h2s = mk_http2_session_create();
+ if (!h2s) {
+ return -1;
+ }
+
+ h2s->status = MK_HTTP2_UPGRADED;
+ s->conn->data = h2s;
+
+ return MK_HTTP_OK;
+}
+
+/* FIXME Decode a frame header, no more... no less
+static inline void mk_http2_frame_decode_header(uint8_t *buf,
+ struct mk_http2_frame *frame)
+{
+ struct mk_http2_session *h2s;
+ (void) h2s;
+
+ frame->len_type = mk_http2_bitdec_32u(buf);
+ frame->flags = buf[4];
+ frame->stream_id = mk_http2_bitdec_stream_id(buf + 5);
+ frame->payload = buf + 9;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("Frame Header");
+ printf(" length=%i, type=%i, stream_id=%i\n",
+ mk_http2_frame_len(frame),
+ mk_http2_frame_type(frame),
+ frame->stream_id);
+#endif
+}
+*/
+
+static inline int mk_http2_handle_settings(struct mk_sched_conn *conn,
+ struct mk_http2_frame *frame)
+{
+ int i;
+ int frame_len;
+ int settings;
+ int setting_size = 6; /* 16 bits identifier + 32 bits value = 6 bytes */
+ uint16_t setting_id;
+ uint32_t setting_value;
+ uint8_t *p;
+ struct mk_http2_session *h2s;
+
+ h2s = conn->data;
+ frame_len = mk_http2_frame_len(frame);
+ if (frame->flags == MK_HTTP2_SETTINGS_ACK) {
+ /*
+ * Nothing to do, the peer just received our SETTINGS and it's
+ * sending an acknowledge.
+ *
+ * note: validate that frame length is zero.
+ */
+ if (frame_len > 0) {
+ /*
+ * This must he handled as a connection error, we must reply
+ * with a FRAME_SIZE_ERROR. ref:
+ *
+ * https://httpwg.github.io/specs/rfc7540.html#SETTINGS
+ */
+
+ /* FIXME: send a GOAWAY error frame */
+ MK_TRACE("FRAME SIZE ERR: %i\n", frame_len);
+ return -1;
+
+ }
+ return 0;
+ }
+
+ /*
+ * Iterate our SETTINGS payload, it may contain many entries in the
+ * following format:
+ *
+ * +-------------------------------+
+ * | Identifier (16) |
+ * +-------------------------------+-------------------------------+
+ * | Value (32) |
+ * +---------------------------------------------------------------+
+ *
+ * 48 bits = 6 bytes
+ */
+ settings = (frame_len / setting_size);
+ for (i = 0; i < settings; i++ ) {
+ /* Seek payload per SETTINGS entry */
+ p = frame->payload + (setting_size * i);
+
+ setting_id = p[0] << 8 | p[1];
+ setting_value = p[2] << 24 | p[3] << 16 | p[4] << 8 | p[5];
+ MK_H2_TRACE(conn, "[Setting] ID=%" PRIu16 " VAL=%" PRIu32,
+ setting_id, setting_value);
+
+ switch (setting_id) {
+ case MK_HTTP2_SETTINGS_HEADER_TABLE_SIZE:
+ /* unhandled */
+ break;
+ case MK_HTTP2_SETTINGS_ENABLE_PUSH:
+ if (setting_value != 0 && setting_value != 1) {
+ /* FIXME: PROTOCOL_ERROR */
+ MK_H2_TRACE(conn, "Invalid SETTINGS_ENABLE_PUSH");
+ return -1;
+ }
+ h2s->settings.enable_push = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
+ if (setting_value < 64) {
+ h2s->settings.max_concurrent_streams = setting_value;
+ }
+ else {
+ h2s->settings.max_concurrent_streams = 64;
+ }
+ MK_H2_TRACE(conn, "SETTINGS MAX_CONCURRENT_STREAMS=%i",
+ setting_value);
+ break;
+ case MK_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
+ if (setting_value < 65535 || setting_value > 2147483647) {
+ /* FIXME: send FLOW_CONTROL_ERROR */
+ MK_H2_TRACE(conn, "Invalid INITIAL_WINDOW_SIZE");
+ return -1;
+ }
+ h2s->settings.initial_window_size = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_FRAME_SIZE:
+ if (setting_value < 16384 || setting_value > 2147483647) {
+ /* FIXME: send PROTOCOL_ERROR */
+ return -1;
+ }
+ h2s->settings.max_frame_size = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
+ /* Unhandled */
+ break;
+ default:
+ /*
+ * 5.5 Extending HTTP/2: ...Implementations MUST ignore unknown
+ * or unsupported values in all extensible protocol elements...
+ */
+ break;
+ }
+ }
+
+ /* FIXME // No errors, send the ACK
+ mk_http2_send_raw(conn, MK_HTTP2_SETTINGS_ACK_FRAME,
+ sizeof(MK_HTTP2_SETTINGS_ACK_FRAME) - 1);
+ */
+ return 0;
+}
+
+
+static inline int mk_http2_frame_run(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker)
+{
+ int ret;
+ struct mk_http2_frame frame;
+ struct mk_http2_session *h2s;
+ (void) worker;
+
+ h2s = conn->data;
+
+ /* Decode the frame header */
+ //FIXME mk_http2_frame_decode_header(h2s->buffer, &frame);
+
+ /* Do some validations */
+ if (h2s->buffer_length < (MK_HTTP2_HEADER_SIZE + (frame.len_type >> 8))) {
+ /* FIXME: need more data */
+ return 0;
+ }
+
+ /* Do some work based on the frame type */
+ if (mk_http2_frame_type(&frame) == MK_HTTP2_SETTINGS) {
+ ret = mk_http2_handle_settings(conn, &frame);
+ /* FIXME: send our MK_HTTP2_SETTINGS_ACK_FRAME */
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mk_http2_sched_read(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker,
+ struct mk_server *server)
+{
+ int bytes;
+ int new_size;
+ int available;
+ char *tmp;
+ struct mk_http2_session *h2s;
+ (void) worker;
+ (void) server;
+
+ h2s = conn->data;
+ available = h2s->buffer_size - h2s->buffer_length;
+ if (available == 0) {
+ new_size = h2s->buffer_size + MK_HTTP2_CHUNK;
+ if (h2s->buffer == h2s->buffer_fixed) {
+ h2s->buffer = mk_mem_alloc(new_size);
+ if (!h2s->buffer) {
+ /* FIXME: send internal server error ? */
+ return -1;
+ }
+ memcpy(h2s->buffer, h2s->buffer_fixed, h2s->buffer_length);
+ MK_TRACE("[FD %i] Buffer new size: %i, length: %i",
+ conn->event.fd, new_size, h2s->buffer_length);
+ }
+ else {
+ MK_TRACE("[FD %i] Buffer realloc from %i to %i",
+ conn->event.fd, h2s->buffer_size, new_size);
+ tmp = mk_mem_realloc(h2s->buffer, new_size);
+ if (tmp) {
+ h2s->buffer = tmp;
+ h2s->buffer_size = new_size;
+ }
+ else {
+ /* FIXME: send internal server error ? */
+ return -1;
+ }
+
+ }
+ }
+
+ /* Read the incoming data */
+ bytes = mk_sched_conn_read(conn,
+ h2s->buffer,
+ h2s->buffer_size - h2s->buffer_length);
+ if (bytes == 0) {
+ errno = 0;
+ return -1;
+ }
+ else if (bytes == -1) {
+ return -1;
+ }
+
+ h2s->buffer_length += bytes;
+
+ /* Upgraded connections from HTTP/1.x requires the preface */
+ if (h2s->status == MK_HTTP2_UPGRADED) {
+ if (h2s->buffer_length >= http2_preface.len) {
+ if (memcmp(h2s->buffer,
+ http2_preface.data, http2_preface.len) != 0) {
+ MK_H2_TRACE(conn, "Invalid HTTP/2 preface");
+ return 0;
+ }
+
+ MK_H2_TRACE(conn, "HTTP/2 preface OK");
+
+ buffer_consume(h2s, http2_preface.len);
+ h2s->status = MK_HTTP2_OK;
+
+ /* Send out our default settings
+ mk_stream_set(&h2s->stream_settings,
+ MK_STREAM_RAW,
+ &conn->channel,
+ MK_HTTP2_SETTINGS_DEFAULT_FRAME,
+ sizeof(MK_HTTP2_SETTINGS_DEFAULT_FRAME) - 1,
+ NULL,
+ NULL, NULL, NULL);
+ */
+ }
+ else {
+ /* We need more data */
+ return 0;
+ }
+ }
+
+ /* Check that we have a minimum header size */
+ if (h2s->buffer_length < MK_HTTP2_HEADER_SIZE) {
+ MK_TRACE("HEADER FRAME incomplete %i/%i bytes",
+ h2s->buffer_length, MK_HTTP2_HEADER_SIZE);
+ return 0;
+ }
+
+ /* We have at least one frame */
+ return mk_http2_frame_run(conn, worker);
+}
+
+
+struct mk_sched_handler mk_http2_handler = {
+ .name = "http2",
+ .cb_read = mk_http2_sched_read,
+ .cb_close = NULL,
+ .cb_done = NULL,
+ .cb_upgrade = mk_http2_upgrade,
+ .sched_extra_size = sizeof(struct mk_http2_session),
+ .capabilities = MK_CAP_HTTP2
+};
diff --git a/fluent-bit/lib/monkey/mk_server/mk_http_parser.c b/fluent-bit/lib/monkey/mk_server/mk_http_parser.c
new file mode 100644
index 000000000..4e7aa3161
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_http_parser.c
@@ -0,0 +1,744 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <monkey/mk_http.h>
+#include <monkey/mk_http_parser.h>
+#include <monkey/mk_http_status.h>
+
+#define mark_end() \
+ p->end = p->i; \
+ p->chars = -1;
+
+#define start_next() \
+ p->start = p->i + 1; \
+ continue
+
+#define field_len() (p->end - p->start)
+#define header_scope_eq(p, x) p->header_min = p->header_max = x
+
+struct row_entry {
+ int len;
+ const char name[32];
+};
+
+struct row_entry mk_methods_table[] = {
+ { 3, "GET" },
+ { 4, "POST" },
+ { 4, "HEAD" },
+ { 3, "PUT" },
+ { 6, "DELETE" },
+ { 7, "OPTIONS" }
+};
+
+struct row_entry mk_headers_table[] = {
+ { 6, "accept" },
+ { 14, "accept-charset" },
+ { 15, "accept-encoding" },
+ { 15, "accept-language" },
+ { 13, "authorization" },
+ { 13, "cache-control" },
+ { 6, "cookie" },
+ { 10, "connection" },
+ { 14, "content-length" },
+ { 13, "content-range" },
+ { 12, "content-type" },
+ { 4, "host" },
+ { 14, "http2-settings" },
+ { 17, "if-modified-since" },
+ { 13, "last-modified" },
+ { 19, "last-modified-since" },
+ { 5, "range" },
+ { 7, "referer" },
+ { 7, "upgrade" },
+ { 10, "user-agent" }
+};
+
+static inline void reverse_char_lookup(char *buf, char c, int len, struct mk_http_parser *p)
+{
+ int x = 0;
+ int y = 0;
+
+ x = p->i;
+ do {
+ if (buf[x - y] == c) {
+ p->i = x - y;
+ return;
+ }
+ y++;
+ } while (y < len);
+}
+
+static inline void char_lookup(char *buf, char c, int len, struct mk_http_parser *p)
+{
+ int x = 0;
+
+ x = p->i;
+ do {
+ if (buf[x] == c) {
+ p->i = x;
+ return;
+ }
+ x++;
+ } while (x < len);
+}
+
+static inline int str_searchr(char *buf, char c, int len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; i--) {
+ if (buf[i] == c) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static inline int method_lookup(struct mk_http_request *req,
+ struct mk_http_parser *p, char *buffer)
+{
+ int i = 0;
+ int len;
+
+ /* Method lenght */
+ len = field_len();
+
+ /* Point the buffer */
+ req->method = MK_METHOD_UNKNOWN;
+ req->method_p.data = buffer + p->start;
+ req->method_p.len = len;
+
+ if (p->method >= 0) {
+ if (strncmp(buffer + p->start + 1,
+ mk_methods_table[p->method].name + 1,
+ len - 1) == 0) {
+ req->method = p->method;
+ return req->method;
+ }
+ }
+
+ for (i = 0; i < MK_METHOD_SIZEOF; i++) {
+ if (len != mk_methods_table[i].len) {
+ continue;
+ }
+
+ if (strncmp(buffer + p->start, mk_methods_table[i].name, len) == 0) {
+ req->method = i;
+ return i;
+ }
+ }
+ return MK_METHOD_UNKNOWN;
+}
+
+static inline void request_set(mk_ptr_t *ptr, struct mk_http_parser *p, char *buffer)
+{
+ ptr->data = buffer + p->start;
+ ptr->len = field_len();
+}
+
+/*
+ * expected: a known & expected value in lowercase
+ * value : the expected string value in the header
+ * len : the value string length.
+ *
+ * If it matches it return zero. Otherwise -1.
+ */
+static inline int header_cmp(const char *expected, char *value, int len)
+{
+ int i = 0;
+
+ if (len >= 8) {
+ if (expected[0] != tolower(value[0])) return -1;
+ if (expected[1] != tolower(value[1])) return -1;
+ if (expected[2] != tolower(value[2])) return -1;
+ if (expected[3] != tolower(value[3])) return -1;
+ if (expected[4] != tolower(value[4])) return -1;
+ if (expected[5] != tolower(value[5])) return -1;
+ if (expected[6] != tolower(value[6])) return -1;
+ if (expected[7] != tolower(value[7])) return -1;
+ i = 8;
+ }
+
+ for (; i < len; i++) {
+ if (expected[i] != tolower(value[i])) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int header_lookup(struct mk_http_parser *p, char *buffer)
+{
+ int i;
+ int len;
+ int pos;
+ long val;
+ char *endptr;
+ char *tmp;
+
+ struct mk_http_header *header;
+ struct mk_http_header *header_extra;
+ struct row_entry *h;
+
+ len = (p->header_sep - p->header_key);
+ for (i = p->header_min; i <= p->header_max && i >= 0; i++) {
+ h = &mk_headers_table[i];
+ /* Check string length first */
+ if (h->len != len) {
+ continue;
+ }
+
+ if (header_cmp(h->name + 1, buffer + p->header_key + 1, len - 1) == 0) {
+ /* We got a header match, register the header index */
+ header = &p->headers[i];
+ header->type = i;
+ header->key.data = buffer + p->header_key;
+ header->key.len = len;
+ header->val.data = buffer + p->header_val;
+ header->val.len = p->end - p->header_val;
+ p->header_count++;
+ mk_list_add(&header->_head, &p->header_list);
+
+ if (i == MK_HEADER_HOST) {
+ /* Handle a possible port number in the Host header */
+ int sep = str_searchr(header->val.data, ':', header->val.len);
+ if (sep > 0) {
+ int plen;
+ short int port_size = 6;
+ char port[6]; /* Can't use port_size to declare a stack allocated array in vc++ */
+
+ plen = header->val.len - sep - 1;
+ if (plen <= 0 || plen >= port_size) {
+ return -MK_CLIENT_BAD_REQUEST;
+ }
+ memcpy(&port, header->val.data + sep + 1, plen);
+ port[plen] = '\0';
+
+ errno = 0;
+ val = strtol(port, &endptr, 10);
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0)) {
+ return -MK_CLIENT_BAD_REQUEST;
+ }
+
+ if (endptr == port || *endptr != '\0') {
+ return -MK_CLIENT_BAD_REQUEST;
+ }
+
+ p->header_host_port = val;
+
+ /* Re-set the Host header value without port */
+ header->val.len = sep;
+ }
+ }
+ else if (i == MK_HEADER_CONTENT_LENGTH) {
+ errno = 0;
+ val = strtol(header->val.data, &endptr, 10);
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0)) {
+ return -MK_CLIENT_REQUEST_ENTITY_TOO_LARGE;
+ }
+ if (endptr == header->val.data) {
+ return -1;
+ }
+ if (val < 0) {
+ return -1;
+ }
+
+ p->header_content_length = val;
+ }
+ else if (i == MK_HEADER_CONNECTION) {
+ /* Check Connection: Keep-Alive */
+ if (header->val.len == sizeof(MK_CONN_KEEP_ALIVE) - 1) {
+ if (header_cmp(MK_CONN_KEEP_ALIVE,
+ header->val.data,
+ header->val.len ) == 0) {
+ p->header_connection = MK_HTTP_PARSER_CONN_KA;
+ }
+ }
+ /* Check Connection: Close */
+ else if (header->val.len == sizeof(MK_CONN_CLOSE) -1) {
+ if (header_cmp(MK_CONN_CLOSE,
+ header->val.data, header->val.len) == 0) {
+ p->header_connection = MK_HTTP_PARSER_CONN_CLOSE;
+ }
+ }
+ else {
+ p->header_connection = MK_HTTP_PARSER_CONN_UNKNOWN;
+
+ /* Try to find some known values */
+
+ /* Connection: upgrade */
+ pos = mk_string_search_n(header->val.data,
+ "Upgrade",
+ MK_STR_INSENSITIVE,
+ header->val.len);
+ if (pos >= 0) {
+ p->header_connection = MK_HTTP_PARSER_CONN_UPGRADE;
+ }
+
+ /* Connection: HTTP2-Settings */
+ pos = mk_string_search_n(header->val.data,
+ "HTTP2-Settings",
+ MK_STR_INSENSITIVE,
+ header->val.len);
+ if (pos >= 0) {
+ p->header_connection |= MK_HTTP_PARSER_CONN_HTTP2_SE;
+ }
+ }
+ }
+ else if (i == MK_HEADER_UPGRADE) {
+ if (header_cmp(MK_UPGRADE_H2C,
+ header->val.data, header->val.len) == 0) {
+ p->header_upgrade = MK_HTTP_PARSER_UPGRADE_H2C;
+ }
+ }
+
+ return 0;
+ }
+ }
+
+ /*
+ * The header_lookup did not match any known header, so we register this
+ * entry into the headers_extra array.
+ */
+ if (p->headers_extra_count < MK_HEADER_EXTRA_SIZE) {
+ header_extra = &p->headers_extra[p->headers_extra_count];
+ header_extra->key.data = tmp = (buffer + p->header_key);
+ header_extra->key.len = len;
+
+ /* Transform the header key string to lowercase */
+ for (i = 0; i < len; i++) {
+ tmp[i] = tolower(tmp[i]);
+ }
+
+ header_extra->val.data = buffer + p->header_val;
+ header_extra->val.len = p->end - p->header_val;
+ p->headers_extra_count++;
+ p->header_count++;
+ mk_list_add(&header_extra->_head, &p->header_list);
+ return 0;
+ }
+
+ /*
+ * Header is unknown and we cannot store it on our extra headers
+ * list as it's already full. Request is too large.
+ */
+ return -MK_CLIENT_REQUEST_ENTITY_TOO_LARGE;
+}
+
+/*
+ * This function is invoked everytime the parser evaluate the request is
+ * OK. Here we perform some extra validations mostly based on some logic
+ * and protocol requirements according to the data received.
+ */
+static inline int mk_http_parser_ok(struct mk_http_request *req,
+ struct mk_http_parser *p,
+ struct mk_server *server)
+{
+ /* Validate HTTP Version */
+ if (req->protocol == MK_HTTP_PROTOCOL_UNKNOWN) {
+ mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP, req->session, req, server);
+ return MK_HTTP_PARSER_ERROR;
+ }
+
+ /* POST checks */
+ if (req->method == MK_METHOD_POST || req->method == MK_METHOD_PUT) {
+ /* validate Content-Length exists */
+ if (p->headers[MK_HEADER_CONTENT_LENGTH].type == 0) {
+ mk_http_error(MK_CLIENT_LENGTH_REQUIRED, req->session, req, server);
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+
+ return MK_HTTP_PARSER_OK;
+}
+
+/*
+ * Parse the protocol and point relevant fields, don't take logic decisions
+ * based on this, just parse to locate things.
+ */
+int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p,
+ char *buffer, int buf_len, struct mk_server *server)
+{
+ int s;
+ int tmp;
+ int ret;
+ int len;
+
+ /* lazy test
+ printf("p->i=%i buf_len=%i\n",
+ p->i, buf_len);
+
+ for (s = p->i; s < buf_len; s++) {
+ if (buffer[s] == '\r') {
+ printf("CR");
+ }
+ else if (buffer[s] == '\n') {
+ printf("LF");
+ }
+ else {
+ printf("%c", buffer[s]);
+ }
+ }
+ printf("\n");
+ */
+
+ len = buf_len;
+ for (; p->i < len; p->i++, p->chars++) {
+ /* FIRST LINE LEVEL: Method, URI & Protocol */
+ if (p->level == REQ_LEVEL_FIRST) {
+ switch (p->status) {
+ case MK_ST_REQ_METHOD: /* HTTP Method */
+ if (p->chars == -1) {
+ switch (buffer[p->i]) {
+ case 'G':
+ p->method = MK_METHOD_GET;
+ break;
+ case 'P':
+ p->method = MK_METHOD_POST;
+ break;
+ case 'H':
+ p->method = MK_METHOD_HEAD;
+ break;
+ case 'D':
+ p->method = MK_METHOD_DELETE;
+ break;
+ case 'O':
+ p->method = MK_METHOD_OPTIONS;
+ break;
+ }
+ continue;
+ }
+
+ if (buffer[p->i] == ' ') {
+ mark_end();
+ p->status = MK_ST_REQ_URI;
+ if (p->end < 2) {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ method_lookup(req, p, buffer);
+ start_next();
+ }
+ else {
+ if ((p->i - p->start) > 10) {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+ break;
+ case MK_ST_REQ_URI: /* URI */
+ if (buffer[p->i] == ' ') {
+ mark_end();
+ p->status = MK_ST_REQ_PROT_VERSION;
+ if (field_len() < 1) {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ request_set(&req->uri, p, buffer);
+ start_next();
+ }
+ else if (buffer[p->i] == '?') {
+ mark_end();
+ request_set(&req->uri, p, buffer);
+ p->status = MK_ST_REQ_QUERY_STRING;
+ start_next();
+ }
+ else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
+ mk_http_error(MK_CLIENT_BAD_REQUEST, req->session,
+ req, server);
+ return MK_HTTP_PARSER_ERROR;
+ }
+ break;
+ case MK_ST_REQ_QUERY_STRING: /* Query string */
+ char_lookup(buffer, '\n', len, p);
+
+ if (buffer[p->i] == '\n') {
+ reverse_char_lookup(buffer, ' ', p->i, p);
+ }
+
+ if (buffer[p->i] == ' ') {
+ mark_end();
+ request_set(&req->query_string, p, buffer);
+ p->status = MK_ST_REQ_PROT_VERSION;
+ start_next();
+ }
+ else if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
+ mk_http_error(MK_CLIENT_BAD_REQUEST, req->session,
+ req, server);
+ return MK_HTTP_PARSER_ERROR;
+ }
+ break;
+ case MK_ST_REQ_PROT_VERSION: /* Protocol Version */
+ /*
+ * Most of the time we already have the string version in our
+ * buffer, for that case try to match the version and avoid
+ * loop rounds.
+ */
+ if (p->start + 6 >= p->i) {
+ continue;
+ }
+
+ tmp = p->start;
+ if (buffer[tmp] == 'H' &&
+ buffer[tmp + 1] == 'T' &&
+ buffer[tmp + 2] == 'T' &&
+ buffer[tmp + 3] == 'P' &&
+ buffer[tmp + 4] == '/' &&
+ buffer[tmp + 5] == '1' &&
+ buffer[tmp + 6] == '.') {
+
+ request_set(&req->protocol_p, p, buffer);
+ req->protocol_p.len = 8;
+ mk_http_set_minor_version(buffer[tmp + 7]);
+ }
+ else {
+ mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP,
+ req->session, req, server);
+ return MK_HTTP_PARSER_ERROR;
+ }
+ p->status = MK_ST_FIRST_CONTINUE;
+ break;
+ case MK_ST_FIRST_CONTINUE:
+ if (buffer[p->i] == '\r') {
+ p->status = MK_ST_FIRST_FINALIZING;
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ break;
+ case MK_ST_FIRST_FINALIZING: /* New Line */
+ if (buffer[p->i] == '\n') {
+ p->level = REQ_LEVEL_CONTINUE;
+ start_next();
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ break;
+ case MK_ST_BLOCK_END:
+ if (buffer[p->i] == '\n') {
+ return mk_http_parser_ok(req, p, server);
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ break;
+ };
+ }
+ else if (p->level == REQ_LEVEL_CONTINUE) {
+ if (buffer[p->i] == '\r') {
+ p->level = REQ_LEVEL_FIRST;
+ p->status = MK_ST_BLOCK_END;
+ }
+ else {
+ p->level = REQ_LEVEL_HEADERS;
+ p->status = MK_ST_HEADER_KEY;
+ p->chars = 0;
+ }
+ }
+ /* HEADERS: all headers stuff */
+ if (p->level == REQ_LEVEL_HEADERS) {
+ /* Expect a Header key */
+ if (p->status == MK_ST_HEADER_KEY) {
+ if (buffer[p->i] == '\r') {
+ if (p->chars == 0) {
+ p->level = REQ_LEVEL_END;
+ start_next();
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+
+ if (p->chars == 0) {
+ /*
+ * We reach the start of a Header row, lets catch the most
+ * probable header.
+ *
+ * The goal of this 'first row character lookup', is to define a
+ * small range set of probable headers comparison once we catch
+ * a header end.
+ */
+ s = tolower(buffer[p->i]);
+ switch (s) {
+ case 'a':
+ p->header_min = MK_HEADER_ACCEPT;
+ p->header_max = MK_HEADER_AUTHORIZATION;
+ break;
+ case 'c':
+ p->header_min = MK_HEADER_CACHE_CONTROL;
+ p->header_max = MK_HEADER_CONTENT_TYPE;
+ break;
+ case 'h':
+ p->header_min = MK_HEADER_HOST;
+ p->header_max = MK_HEADER_HTTP2_SETTINGS;
+ break;
+ case 'i':
+ header_scope_eq(p, MK_HEADER_IF_MODIFIED_SINCE);
+ break;
+ case 'l':
+ p->header_min = MK_HEADER_LAST_MODIFIED;
+ p->header_max = MK_HEADER_LAST_MODIFIED_SINCE;
+ break;
+ case 'r':
+ p->header_min = MK_HEADER_RANGE;
+ p->header_max = MK_HEADER_REFERER;
+ break;
+ case 'u':
+ p->header_min = MK_HEADER_UPGRADE;
+ p->header_max = MK_HEADER_USER_AGENT;
+ break;
+ default:
+ p->header_key = -1;
+ p->header_sep = -1;
+ p->header_min = -1;
+ p->header_max = -1;
+ };
+ p->header_key = p->i;
+ continue;
+ }
+
+ /* Found key/value separator */
+ char_lookup(buffer, ':', len, p);
+ if (buffer[p->i] == ':') {
+ /* Set the key/value middle point */
+ p->header_sep = p->i;
+
+ /* validate length */
+ mark_end();
+ if (field_len() < 1) {
+ return MK_HTTP_PARSER_ERROR;
+ }
+
+ /* Wait for a value */
+ p->status = MK_ST_HEADER_VALUE;
+ start_next();
+ }
+ }
+ /* Parsing the header value */
+ else if (p->status == MK_ST_HEADER_VALUE) {
+ /* Trim left, set starts only when found something != ' ' */
+ if (buffer[p->i] == '\r' || buffer[p->i] == '\n') {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ else if (buffer[p->i] != ' ') {
+ p->status = MK_ST_HEADER_VAL_STARTS;
+ p->start = p->header_val = p->i;
+ }
+ continue;
+ }
+ /* New header row starts */
+ else if (p->status == MK_ST_HEADER_VAL_STARTS) {
+ /* Maybe there is no more headers and we reach the end ? */
+ if (buffer[p->i] == '\r') {
+ mark_end();
+ if (field_len() <= 0) {
+ return MK_HTTP_PARSER_ERROR;
+ }
+
+ /*
+ * A header row has ended, lets lookup the header and populate
+ * our headers table index.
+ */
+ ret = header_lookup(p, buffer);
+ if (ret != 0) {
+ if (ret < -1) {
+ mk_http_error(-ret, req->session, req, server);
+ }
+ return MK_HTTP_PARSER_ERROR;
+ }
+
+ /* Try to catch next LF */
+ if (p->i + 1 < len) {
+ if (buffer[p->i + 1] == '\n') {
+ p->i++;
+ p->status = MK_ST_HEADER_KEY;
+ p->chars = -1;
+ start_next();
+ }
+ }
+
+ p->status = MK_ST_HEADER_END;
+ start_next();
+ }
+ else if (buffer[p->i] == '\n' && buffer[p->i - 1] != '\r') {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+ else if (p->status == MK_ST_HEADER_END) {
+ if (buffer[p->i] == '\n') {
+ p->status = MK_ST_HEADER_KEY;
+ p->chars = -1;
+ start_next();
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+ }
+ else if (p->level == REQ_LEVEL_END) {
+ if (buffer[p->i] == '\n') {
+ if (p->header_content_length > 0) {
+ p->level = REQ_LEVEL_BODY;
+ p->chars = -1;
+ start_next();
+ }
+ else {
+ return mk_http_parser_ok(req, p, server);
+ }
+ }
+ else {
+ return MK_HTTP_PARSER_ERROR;
+ }
+ }
+ else if (p->level == REQ_LEVEL_BODY) {
+ /*
+ * Reaching this level can means two things:
+ *
+ * - A Pipeline Request
+ * - A Body content (POST/PUT methods)
+ */
+ if (p->header_content_length > 0) {
+
+ p->body_received = len - p->start;
+ if ((len - p->start) < p->header_content_length) {
+ return MK_HTTP_PARSER_PENDING;
+ }
+
+ /* Cut off */
+ p->i += p->body_received;
+ req->data.len = p->body_received;
+ req->data.data = (buffer + p->start);
+ }
+ return mk_http_parser_ok(req, p, server);
+ }
+ }
+
+ return MK_HTTP_PARSER_PENDING;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_http_thread.c b/fluent-bit/lib/monkey/mk_server/mk_http_thread.c
new file mode 100644
index 000000000..d3c606f3a
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_http_thread.c
@@ -0,0 +1,290 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2016 Monkey Software LLC <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_info.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_thread.h>
+#include <monkey/mk_net.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_http_thread.h>
+
+#include <stdlib.h>
+
+/*
+ * libco do not support parameters in the entrypoint function due to the
+ * complexity of implementation in terms of architecture and compiler, but
+ * it provide a workaround using a global structure as a middle entry-point
+ * that achieve the same stuff.
+ */
+struct mk_http_libco_params {
+ int type;
+ struct mk_vhost_handler *handler;
+ struct mk_http_session *session;
+ struct mk_http_request *request;
+ int n_params;
+ struct mk_list *params;
+ struct mk_thread *th;
+};
+
+pthread_once_t mk_http_thread_initialize_tls_once_flag = PTHREAD_ONCE_INIT;
+
+MK_TLS_DEFINE(struct mk_http_libco_params, mk_http_thread_libco_params);
+MK_TLS_DEFINE(struct mk_thread, mk_thread);
+
+/* This function could return NULL if the process runs out of memory, in that
+ * case failure is imminent.
+ */
+static inline struct mk_http_libco_params *thread_get_libco_params()
+{
+ struct mk_http_libco_params *libco_params;
+
+ libco_params = MK_TLS_GET(mk_http_thread_libco_params);
+
+ if (libco_params == NULL) {
+ libco_params = mk_mem_alloc_z(sizeof(struct mk_http_libco_params));
+
+ if (libco_params == NULL) {
+ mk_err("libco thread params could not be allocated.");
+ }
+
+ MK_TLS_SET(mk_http_thread_libco_params, libco_params);
+ }
+
+ return libco_params;
+}
+
+static void mk_http_thread_initialize_tls_once()
+{
+ MK_TLS_INIT(mk_http_thread_libco_params);
+ MK_TLS_INIT(mk_thread);
+}
+
+void mk_http_thread_initialize_tls()
+{
+ pthread_once(&mk_http_thread_initialize_tls_once_flag,
+ mk_http_thread_initialize_tls_once);
+}
+
+static inline void thread_cb_init_vars()
+{
+ struct mk_http_libco_params *libco_params;
+ struct mk_vhost_handler *handler;
+ struct mk_http_session *session;
+ struct mk_http_request *request;
+ int close;
+ int type;
+ struct mk_http_thread *mth;
+ struct mk_thread *th;
+
+ libco_params = thread_get_libco_params();
+
+ type = libco_params->type;
+ handler = libco_params->handler;
+ session = libco_params->session;
+ request = libco_params->request;
+ th = libco_params->th;
+
+ /*
+ * Until this point the th->callee already set the variables, so we
+ * wait until the core wanted to resume so we really trigger the
+ * output callback.
+ */
+ co_switch(th->caller);
+
+ if (type == MK_HTTP_THREAD_LIB) {
+ /* Invoke the handler callback */
+ handler->cb(request, handler->data);
+
+ /*
+ * Once the callback finished, we need to sanitize the connection
+ * so other further requests can be processed.
+ */
+ int ret;
+ struct mk_sched_worker *sched;
+ struct mk_channel *channel;
+
+ channel = request->session->channel;
+ sched = mk_sched_get_thread_conf();
+
+ MK_EVENT_NEW(channel->event);
+ ret = mk_event_add(sched->loop,
+ channel->fd,
+ MK_EVENT_CONNECTION,
+ MK_EVENT_READ, channel->event);
+ if (ret == -1) {
+ //return -1;
+ }
+
+ /* Save temporal session */
+ mth = request->thread;
+
+ /*
+ * Finalize request internally, if ret == -1 means we should
+ * ask to shutdown the connection.
+ */
+ ret = mk_http_request_end(session, session->server);
+ if (ret == -1) {
+ close = MK_TRUE;
+ }
+ else {
+ close = MK_FALSE;
+ }
+ mk_http_thread_purge(mth, close);
+
+ /* Return control to caller */
+ mk_thread_yield(th);
+ }
+ else if (type == MK_HTTP_THREAD_PLUGIN) {
+ /* FIXME: call plugin handler callback with params */
+ }
+}
+
+static inline void thread_params_set(struct mk_thread *th,
+ int type,
+ struct mk_vhost_handler *handler,
+ struct mk_http_session *session,
+ struct mk_http_request *request,
+ int n_params,
+ struct mk_list *params)
+{
+ struct mk_http_libco_params *libco_params;
+
+ libco_params = thread_get_libco_params();
+
+ /* Callback parameters in order */
+ libco_params->type = type;
+ libco_params->handler = handler;
+ libco_params->session = session;
+ libco_params->request = request;
+ libco_params->n_params = n_params;
+ libco_params->params = params;
+ libco_params->th = th;
+
+ co_switch(th->callee);
+}
+
+struct mk_http_thread *mk_http_thread_create(int type,
+ struct mk_vhost_handler *handler,
+ struct mk_http_session *session,
+ struct mk_http_request *request,
+ int n_params,
+ struct mk_list *params)
+{
+ size_t stack_size;
+ struct mk_thread *th = NULL;
+ struct mk_http_thread *mth;
+ struct mk_sched_worker *sched;
+
+ sched = mk_sched_get_thread_conf();
+ if (!sched) {
+ return NULL;
+ }
+
+ th = mk_thread_new(sizeof(struct mk_http_thread), NULL);
+ if (!th) {
+ return NULL;
+ }
+
+ mth = (struct mk_http_thread *) MK_THREAD_DATA(th);
+ if (!mth) {
+ return NULL;
+ }
+
+ mth->session = session;
+ mth->request = request;
+ mth->parent = th;
+ mth->close = MK_FALSE;
+ request->thread = mth;
+ mk_list_add(&mth->_head, &sched->threads);
+
+ th->caller = co_active();
+ th->callee = co_create(MK_THREAD_STACK_SIZE,
+ thread_cb_init_vars, &stack_size);
+
+#ifdef MK_HAVE_VALGRIND
+ th->valgrind_stack_id = VALGRIND_STACK_REGISTER(th->callee,
+ ((char *)th->callee) + stack_size);
+#endif
+
+ /* Workaround for makecontext() */
+ thread_params_set(th, type, handler, session, request, n_params, params);
+
+ return mth;
+}
+
+/*
+ * Move a http thread context from sched->thread to sched->threads_purge list.
+ * On this way the scheduler will release or reasign the resource later.
+ */
+int mk_http_thread_purge(struct mk_http_thread *mth, int close)
+{
+ struct mk_sched_worker *sched;
+
+ sched = mk_sched_get_thread_conf();
+ if (!sched) {
+ return -1;
+ }
+
+ mth->close = close;
+ mk_list_del(&mth->_head);
+ mk_list_add(&mth->_head, &sched->threads_purge);
+
+ return 0;
+}
+
+int mk_http_thread_destroy(struct mk_http_thread *mth)
+{
+ struct mk_thread *th;
+
+ /* Unlink from scheduler thread list */
+ mk_list_del(&mth->_head);
+
+ /* release original memory context */
+ th = mth->parent;
+ mth->session->channel->event->type = MK_EVENT_CONNECTION;
+ mk_thread_destroy(th);
+
+ return 0;
+}
+
+int mk_http_thread_event(struct mk_event *event)
+{
+ struct mk_sched_conn *conn = (struct mk_sched_conn *) event;
+
+ /*
+ struct mk_thread *th;
+ struct mk_http_thread *mth;
+
+ th = conn->channel.thread;
+ mth = (struct mk_http_thread *) MK_THREAD_DATA(th);
+ */
+
+ mk_thread_resume(conn->channel.thread);
+ return 0;
+}
+
+/*
+ * Start the co-routine: invoke coroutine callback and start processing
+ * data flush requests.
+ */
+int mk_http_thread_start(struct mk_http_thread *mth)
+{
+ mk_http_thread_resume(mth);
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_kernel.c b/fluent-bit/lib/monkey/mk_server/mk_kernel.c
new file mode 100644
index 000000000..cc69e96c5
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_kernel.c
@@ -0,0 +1,165 @@
+/*-*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_kernel.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_scheduler.h>
+
+#include <ctype.h>
+
+#ifndef _WIN32
+#include <sys/utsname.h>
+
+int mk_kernel_version()
+{
+ int a, b, c;
+ int len;
+ int pos;
+ char *p, *t;
+ char *tmp;
+ struct utsname uts;
+
+ if (uname(&uts) == -1) {
+ mk_libc_error("uname");
+ }
+ len = strlen(uts.release);
+
+ /* Fixme: this don't support Linux Kernel 10.x.x :P */
+ a = (*uts.release - '0');
+
+ /* Second number */
+ p = (uts.release) + 2;
+ pos = mk_string_char_search(p, '.', len - 2);
+ if (pos <= 0) {
+ /* Some Debian systems uses a different notation, e.g: 3.14-2-amd64 */
+ pos = mk_string_char_search(p, '-', len - 2);
+ if (pos <= 0) {
+ return -1;
+ }
+ }
+
+ tmp = mk_string_copy_substr(p, 0, pos);
+ if (!tmp) {
+ return -1;
+ }
+ b = atoi(tmp);
+ mk_mem_free(tmp);
+
+ /* Last number (it needs filtering) */
+ t = p = p + pos + 1;
+ do {
+ t++;
+ } while (isdigit(*t));
+
+ tmp = mk_string_copy_substr(p, 0, t - p);
+ if (!tmp) {
+ return -1;
+ }
+ c = atoi(tmp);
+ mk_mem_free(tmp);
+
+ MK_TRACE("Kernel detected: %i.%i.%i", a, b, c);
+ return MK_KERNEL_VERSION(a, b, c);
+}
+
+/* Detect specific Linux Kernel features that we may use */
+int mk_kernel_features(int version)
+{
+ int flags = 0;
+
+ /*
+ * TCP Auto Corking (disabled by #175)
+ * -----------------------------------
+ * I found that running some benchmarks on Linux 3.16 with
+ * tcp_autocorking enabled, it lead to lower performance, looks like
+ * a manual cork fits better for our needs.
+ *
+ * I think there is something wrong that we need to clarify, by now
+ * I've logged the following issue:
+ *
+ * https://github.com/monkey/monkey/issues/175
+ *
+ if (mk_kernel_runver >= MK_KERNEL_VERSION(3, 14, 0) &&
+ mk_socket_tcp_autocorking() == MK_TRUE) {
+ flags |= MK_KERNEL_TCP_AUTOCORKING;
+ }
+ */
+
+ /* SO_REUSEPORT */
+ if (version >= MK_KERNEL_VERSION(3, 9, 0)) {
+ flags |= MK_KERNEL_SO_REUSEPORT;
+ }
+
+ /* TCP_FASTOPEN */
+ if (version >= MK_KERNEL_VERSION(3, 7, 0)) {
+ flags |= MK_KERNEL_TCP_FASTOPEN;
+ }
+
+ return flags;
+}
+
+int mk_kernel_features_print(char *buffer, size_t size,
+ struct mk_server *server)
+{
+ int offset = 0;
+ int features = 0;
+
+ if (server->kernel_features & MK_KERNEL_TCP_FASTOPEN) {
+ offset += snprintf(buffer, size - offset, "%s", "TCP_FASTOPEN ");
+ features++;
+ }
+
+ if (server->kernel_features & MK_KERNEL_SO_REUSEPORT) {
+ if (server->scheduler_mode == MK_SCHEDULER_FAIR_BALANCING) {
+ offset += snprintf(buffer + offset, size - offset,
+ "%s!%s", ANSI_BOLD ANSI_RED, ANSI_RESET);
+ }
+ offset += snprintf(buffer + offset, size - offset, "%s", "SO_REUSEPORT ");
+ features++;
+ }
+
+ if (server->kernel_features & MK_KERNEL_TCP_AUTOCORKING) {
+ snprintf(buffer + offset, size - offset, "%s", "TCP_AUTOCORKING ");
+ features++;
+ }
+
+ return features;
+}
+#else
+/* We still need to determine if this can be safely ignored or what do we need to do here */
+
+int mk_kernel_version()
+{
+ return 1;
+}
+
+int mk_kernel_features(int version)
+{
+ return 0;
+}
+
+int mk_kernel_features_print(char* buffer, size_t size,
+ struct mk_server* server)
+{
+ return 0;
+}
+#endif
diff --git a/fluent-bit/lib/monkey/mk_server/mk_lib.c b/fluent-bit/lib/monkey/mk_server/mk_lib.c
new file mode 100644
index 000000000..c29400864
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_lib.c
@@ -0,0 +1,796 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <mk_core/mk_pthread.h>
+
+#include <monkey/mk_lib.h>
+#include <monkey/monkey.h>
+#include <monkey/mk_stream.h>
+#include <monkey/mk_thread.h>
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_fifo.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_tls.h>
+
+#define config_eq(a, b) strcasecmp(a, b)
+
+static inline int bool_val(char *v)
+{
+ if (strcasecmp(v, "On") == 0 || strcasecmp(v, "Yes") == 0) {
+ return MK_TRUE;
+ }
+ else if (strcasecmp(v, "Off") == 0 || strcasecmp(v, "No") == 0) {
+ return MK_FALSE;
+ }
+
+ return -1;
+}
+
+mk_ctx_t *mk_create()
+{
+ mk_ctx_t *ctx;
+
+ ctx = mk_mem_alloc_z(sizeof(mk_ctx_t));
+ if (!ctx) {
+ return NULL;
+ }
+
+ /* Create Monkey server instance */
+ ctx->server = mk_server_create();
+
+ /*
+ * FIFO
+ * ====
+ * Before to prepare the background service, we create a MK_FIFO interface
+ * for further communication between the caller (user) and HTTP end-point
+ * callbacks.
+ */
+ ctx->fifo = mk_fifo_create(NULL, ctx->server);
+ ctx->fifo->key = &mk_server_fifo_key;
+
+ /*
+ * FIFO: Set workers callback associated to the Monkey scheduler to prepare them
+ * before to enter the event loop
+ */
+ mk_sched_worker_cb_add(ctx->server, mk_fifo_worker_setup, ctx->fifo);
+ return ctx;
+}
+
+int mk_destroy(mk_ctx_t *ctx)
+{
+ mk_fifo_destroy(ctx->fifo);
+ mk_mem_free(ctx);
+
+ return 0;
+}
+
+static inline int mk_lib_yield(mk_request_t *req)
+{
+ int ret;
+ struct mk_thread *th;
+ struct mk_channel *channel;
+ struct mk_sched_worker *sched;
+
+ sched = mk_sched_get_thread_conf();
+ if (!sched) {
+ return -1;
+ }
+
+ th = MK_TLS_GET(mk_thread);
+ channel = req->session->channel;
+
+ channel->thread = th;
+
+ ret = mk_event_add(sched->loop,
+ channel->fd,
+ MK_EVENT_THREAD,
+ MK_EVENT_WRITE, channel->event);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* Just wait */
+ mk_thread_yield(th);
+
+ if (channel->event->status & MK_EVENT_REGISTERED) {
+ /* We got a notification, remove the event registered
+ ret = mk_event_add(sched->loop,
+ channel->fd,
+ MK_EVENT_CONNECTION,
+ MK_EVENT_READ, channel->event);
+ mk_thread_yield(th);
+ */
+
+ ret = mk_event_del(sched->loop, channel->event);
+ }
+
+ return 0;
+}
+
+static void mk_lib_worker(void *data)
+{
+ int fd;
+ int bytes;
+ uint64_t val;
+ struct mk_server *server;
+ struct mk_event *event;
+
+ mk_ctx_t *ctx = data;
+ server = ctx->server;
+
+ /* Start the service */
+ mk_server_setup(server);
+ mk_server_loop(server);
+
+ /*
+ * Give a second to the parent context to avoid consume an event
+ * we should not read at the moment (SIGNAL_START).
+ */
+ sleep(1);
+
+ /* Wait for events */
+ mk_event_wait(server->lib_evl);
+ mk_event_foreach(event, server->lib_evl) {
+ fd = event->fd;
+
+#ifdef _WIN32
+ bytes = recv(fd, &val, sizeof(uint64_t), MSG_WAITALL);
+#else
+ bytes = read(fd, &val, sizeof(uint64_t));
+#endif
+
+ if (bytes <= 0) {
+ return;
+ }
+
+ if (val == MK_SERVER_SIGNAL_STOP) {
+ break;
+ }
+ }
+
+ mk_event_loop_destroy(server->lib_evl);
+ mk_exit_all(server);
+ pthread_kill(pthread_self(), 0);
+
+ return;
+}
+
+int mk_start(mk_ctx_t *ctx)
+{
+ int fd;
+ int bytes;
+ int ret;
+ uint64_t val;
+ pthread_t tid;
+ struct mk_event *event;
+ struct mk_server *server;
+
+ server = ctx->server;
+
+ ret = mk_utils_worker_spawn(mk_lib_worker, ctx, &tid);
+ if (ret == -1) {
+ return -1;
+ }
+ ctx->worker_tid = tid;
+
+ /* Wait for the started signal so we can return to the caller */
+ mk_event_wait(server->lib_evl_start);
+ mk_event_foreach(event, server->lib_evl_start) {
+ fd = event->fd;
+
+ /* When using libevent _mk_event_channel_create creates a unix socket
+ * instead of a pipe and windows doesn't us calling read / write on a
+ * socket instead of recv / send
+ */
+#ifdef _WIN32
+ bytes = recv(fd, &val, sizeof(uint64_t), MSG_WAITALL);
+#else
+ bytes = read(fd, &val, sizeof(uint64_t));
+#endif
+
+ if (bytes <= 0) {
+ ret = -1;
+ break;
+ }
+
+ if (val == MK_SERVER_SIGNAL_START) {
+ ret = 0;
+ break;
+ }
+ else {
+ ret = -1;
+ break;
+ }
+ }
+
+ mk_event_loop_destroy(server->lib_evl_start);
+ if (ret == -1) {
+ mk_stop(ctx);
+ }
+
+ return ret;
+}
+
+int mk_stop(mk_ctx_t *ctx)
+{
+ int n;
+ uint64_t val;
+ int8_t scheduler_mode ;
+ struct mk_server *server = ctx->server;
+
+ /* Keep track of the scheduler mode on stack, since the
+ * the worker may free the server before we need the info.
+ */
+ scheduler_mode = server->scheduler_mode;
+
+ val = MK_SERVER_SIGNAL_STOP;
+
+ /* Send a stop signal to the main lib channel to abort.
+ *
+ * MK_SCHEDULER_FAIR_BALANCING: this signal will be
+ * consumed by mk_server_loop_balancer.
+ *
+ * MK_SCHEDULER_REUSEPORT: this signal will be consumed
+ * by mk_lib_worker.
+ */
+#ifdef _WIN32
+ n = send(server->lib_ch_manager[1], &val, sizeof(val), 0);
+#else
+ n = write(server->lib_ch_manager[1], &val, sizeof(val));
+#endif
+ if (n <= 0) {
+ perror("write");
+ return -1;
+ }
+
+ /* In MK_SCHEDULER_FAIR_BALANCING mode, we need one more
+ * stop signal to abort mk_lib_worker.
+ */
+ if (scheduler_mode == MK_SCHEDULER_FAIR_BALANCING) {
+ /* Give mk_server_loop_balancer time to clean up. */
+ sleep(1);
+
+ /* Send a signal for mk_lib_worker to abort */
+#ifdef _WIN32
+ n = send(server->lib_ch_manager[1], &val, sizeof(val), 0);
+#else
+ n = write(server->lib_ch_manager[1], &val, sizeof(val));
+#endif
+ if (n <= 0) {
+ perror("write");
+ return -1;
+ }
+ }
+
+ /* Wait for the child thread to exit */
+ pthread_join(ctx->worker_tid, NULL);
+ return 0;
+}
+
+/*
+ * Instruct Monkey core to invoke a callback function inside each worker
+ * started by the scheduler.
+ */
+int mk_worker_callback(mk_ctx_t *ctx,
+ void (*cb_func) (void *),
+ void *data)
+{
+ return mk_sched_worker_cb_add(ctx->server, cb_func, data);
+}
+
+int mk_config_set_property(struct mk_server *server, char *k, char *v)
+{
+ int b;
+ int ret;
+ int num;
+ unsigned long len;
+
+ if (config_eq(k, "Listen") == 0) {
+ ret = mk_config_listen_parse(v, server);
+ if (ret != 0) {
+ return -1;
+ }
+ }
+ else if (config_eq(k, "Workers") == 0) {
+ num = atoi(v);
+ if (num <= 0) {
+ server->workers = mk_utils_get_system_core_count();
+ }
+ else {
+ server->workers = num;
+ }
+ }
+ else if (config_eq(k, "Timeout") == 0) {
+ num = atoi(v);
+ if (num <= 0) {
+ return -1;
+ }
+ server->timeout = num;
+ }
+ else if (config_eq(k, "KeepAlive") == 0) {
+ b = bool_val(v);
+ if (b == -1) {
+ return -1;
+ }
+ server->keep_alive = b;
+ }
+ else if (config_eq(k, "MaxKeepAliveRequest") == 0) {
+ num = atoi(v);
+ if (num <= 0) {
+ return -1;
+ }
+ server->max_keep_alive_request = num;
+ }
+ else if (config_eq(k, "KeepAliveTimeout") == 0) {
+ num = atoi(v);
+ if (num <= 0) {
+ return -1;
+ }
+ server->keep_alive_timeout = num;
+ }
+ else if (config_eq(k, "UserDir") == 0) {
+ server->conf_user_pub = mk_string_dup(v);
+ }
+ else if (config_eq(k, "IndexFile") == 0) {
+ server->index_files = mk_string_split_line(v);
+ if (!server->index_files) {
+ return -1;
+ }
+ }
+ else if (config_eq(k, "HideVersion") == 0) {
+ b = bool_val(v);
+ if (b == -1) {
+ return -1;
+ }
+ server->hideversion = b;
+ }
+ else if (config_eq(k, "Resume") == 0) {
+ b = bool_val(v);
+ if (b == -1) {
+ return -1;
+ }
+ server->resume = b;
+ }
+ else if (config_eq(k, "MaxRequestSize") == 0) {
+ num = atoi(v);
+ if (num <= 0) {
+ return -1;
+ }
+ server->max_request_size = num;
+ }
+ else if (config_eq(k, "SymLink") == 0) {
+ b = bool_val(v);
+ if (b == -1) {
+ return -1;
+ }
+ server->symlink = b;
+ }
+ else if (config_eq(k, "DefaultMimeType") == 0) {
+ mk_string_build(&server->mimetype_default_str, &len, "%s\r\n", v);
+ }
+ else if (config_eq(k, "FDT") == 0) {
+ b = bool_val(v);
+ if (b == -1) {
+ return -1;
+ }
+ server->fdt = b;
+ }
+
+ return 0;
+}
+
+int mk_config_set(mk_ctx_t *ctx, ...)
+{
+ int ret;
+ char *key;
+ char *value;
+ va_list va;
+
+ va_start(va, ctx);
+
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ if (!value) {
+ /* Wrong parameter */
+ va_end(va);
+ return -1;
+ }
+
+ ret = mk_config_set_property(ctx->server, key, value);
+ if (ret != 0) {
+ va_end(va);
+ return -1;
+ }
+ }
+
+ va_end(va);
+ return 0;
+}
+
+/* Given a vhost id, return the vhost context */
+struct mk_vhost *mk_vhost_lookup(mk_ctx_t *ctx, int id)
+{
+ struct mk_vhost *host;
+ struct mk_list *head;
+
+ mk_list_foreach(head, &ctx->server->hosts) {
+ host = mk_list_entry(head, struct mk_vhost, _head);
+ if (host->id == id) {
+ return host;
+ }
+ }
+
+ return NULL;
+}
+
+int mk_vhost_create(mk_ctx_t *ctx, char *name)
+{
+ struct mk_vhost *h;
+ struct mk_vhost_alias *halias;
+
+ /* Virtual host */
+ h = mk_mem_alloc_z(sizeof(struct mk_vhost));
+ if (!h) {
+ return -1;
+ }
+
+ /* Assign a virtual host id, we just set based on list size */
+ h->id = mk_list_size(&ctx->server->hosts);
+ mk_list_init(&h->error_pages);
+ mk_list_init(&h->server_names);
+ mk_list_init(&h->handlers);
+
+ /* Host alias */
+ halias = mk_mem_alloc_z(sizeof(struct mk_vhost_alias));
+ if (!halias) {
+ mk_mem_free(h);
+ return -1;
+ }
+
+ /* Host name */
+ if (!name) {
+ halias->name = mk_string_dup("127.0.0.1");
+ }
+ else {
+ halias->name = mk_string_dup(name);
+ }
+ mk_list_add(&halias->_head, &h->server_names);
+ mk_list_add(&h->_head, &ctx->server->hosts);
+
+ /* Return the host id, that number is enough for further operations */
+ return h->id;
+}
+
+static int mk_vhost_set_property(struct mk_vhost *vh, char *k, char *v)
+{
+ struct mk_vhost_alias *ha;
+
+ if (config_eq(k, "Name") == 0) {
+ ha = mk_mem_alloc(sizeof(struct mk_vhost_alias));
+ if (!ha) {
+ return -1;
+ }
+ ha->name = mk_string_dup(v);
+ ha->len = strlen(v);
+ mk_list_add(&ha->_head, &vh->server_names);
+ }
+ else if (config_eq(k, "DocumentRoot") == 0) {
+ vh->documentroot.data = mk_string_dup(v);
+ vh->documentroot.len = strlen(v);
+ }
+
+ return 0;
+}
+
+int mk_vhost_set(mk_ctx_t *ctx, int vid, ...)
+{
+ int ret;
+ char *key;
+ char *value;
+ va_list va;
+ struct mk_vhost *vh;
+
+ /* Lookup the virtual host */
+ vh = mk_vhost_lookup(ctx, vid);
+ if (!vh) {
+ return -1;
+ }
+
+ va_start(va, vid);
+
+ while ((key = va_arg(va, char *))) {
+ value = va_arg(va, char *);
+ if (!value) {
+ /* Wrong parameter */
+ return -1;
+ }
+
+ ret = mk_vhost_set_property(vh, key, value);
+ if (ret != 0) {
+ va_end(va);
+ return -1;
+ }
+ }
+
+ va_end(va);
+ return 0;
+}
+
+int mk_vhost_handler(mk_ctx_t *ctx, int vid, char *regex,
+ void (*cb)(mk_request_t *, void *), void *data)
+{
+ struct mk_vhost *vh;
+ struct mk_vhost_handler *handler;
+ void (*_cb) (struct mk_http_request *, void *);
+
+ /* Lookup the virtual host */
+ vh = mk_vhost_lookup(ctx, vid);
+ if (!vh) {
+ return -1;
+ }
+
+ _cb = cb;
+ handler = mk_vhost_handler_match(regex, _cb, data);
+ if (!handler) {
+ return -1;
+ }
+ mk_list_add(&handler->_head, &vh->handlers);
+
+ return 0;
+}
+
+/* Flush streams data associated to a request in question */
+int mk_http_flush(mk_request_t *req)
+{
+ int ret;
+ size_t out_bytes = 0;
+
+ ret = mk_channel_stream_write(&req->stream, &out_bytes);
+ return ret;
+}
+
+int mk_http_status(mk_request_t *req, int status)
+{
+ req->headers.status = status;
+ return 0;
+}
+
+/* Append a response header */
+int mk_http_header(mk_request_t *req,
+ char *key, int key_len,
+ char *val, int val_len)
+{
+ int pos;
+ int len;
+ char *buf;
+ struct response_headers *h;
+
+ h = &req->headers;
+ if (!h->_extra_rows) {
+ h->_extra_rows = mk_iov_create(MK_PLUGIN_HEADER_EXTRA_ROWS * 2, 0);
+ if (!h->_extra_rows) {
+ return -1;
+ }
+ }
+
+ len = key_len + val_len + 4;
+ buf = mk_mem_alloc(len);
+ if (!buf) {
+ /* we don't free extra_rows as it's released later */
+ return -1;
+ }
+
+ /* Compose the buffer */
+ memcpy(buf, key, key_len);
+ pos = key_len;
+ buf[pos++] = ':';
+ buf[pos++] = ' ';
+ memcpy(buf + pos, val, val_len);
+ pos += val_len;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+
+ /* Add the new buffer */
+ mk_iov_add(h->_extra_rows, buf, pos, MK_TRUE);
+
+ return 0;
+}
+
+static inline int chunk_header(long num, char *out)
+{
+ int i = 1;
+ int j, c;
+ int remainder;
+ int quotient;
+ char tmp[32];
+ char hex[] = "0123456789ABCDEF";
+
+ if (num == 0) {
+ out[0] = '0';
+ out[1] = '\r';
+ out[2] = '\n';
+ out[3] = '\r';
+ out[4] = '\n';
+ out[5] = '\0';
+ return 5;
+ }
+
+ quotient = num;
+ while (quotient != 0) {
+ remainder = quotient % 16;
+ tmp[i++] = hex[remainder];
+ quotient = quotient / 16;
+ }
+
+ c = 0;
+ for (j = i -1 ; j > 0; j--, c++) {
+ out[c] = tmp[j];
+ }
+
+ out[c++] = '\r';
+ out[c++] = '\n';
+ out[c] = '\0';
+
+ return c;
+}
+
+static void free_chunk_header(struct mk_stream_input *input)
+{
+ mk_mem_free(input->buffer);
+ input->buffer = NULL;
+}
+
+
+/* Check if response headers were processed, otherwise prepare them */
+static int headers_setup(mk_request_t *req)
+{
+ /*
+ * Let's keep it simple for now: if the headers have not been sent, do it
+ * now and then send the body content just queued.
+ */
+ if (req->headers.sent == MK_FALSE) {
+ /* Force chunked-transfer encoding */
+ if (req->protocol == MK_HTTP_PROTOCOL_11) {
+ req->headers.transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
+ }
+ else {
+ req->headers.content_length = -1;
+ }
+ mk_header_prepare(req->session, req, req->session->server);
+ }
+ return 0;
+}
+
+/* Enqueue some data for the body response */
+int mk_http_send(mk_request_t *req, char *buf, size_t len,
+ void (*cb_finish)(mk_request_t *))
+{
+ int chunk_len;
+ int ret;
+ char *tmp;
+ char chunk_pre[32];
+ (void) cb_finish;
+
+ if (req->session->channel->status != MK_CHANNEL_OK) {
+ return -1;
+ }
+
+ if (req->headers.status == -1) {
+ /* Cannot append data if the status have not been set */
+ mk_err("HTTP: set the response status first");
+ return -1;
+ }
+
+ /* Chunk encoding prefix */
+ if (req->protocol == MK_HTTP_PROTOCOL_11) {
+ chunk_len = chunk_header(len, chunk_pre);
+ tmp = mk_string_dup(chunk_pre);
+ if (!tmp) {
+ return -1;
+ }
+ ret = mk_stream_in_raw(&req->stream, NULL,
+ tmp, chunk_len, NULL, free_chunk_header);
+ if (ret != 0) {
+ return -1;
+ }
+ }
+
+ /* Append raw data */
+ if (len > 0) {
+ ret = mk_stream_in_raw(&req->stream, NULL,
+ buf, len, NULL, NULL);
+ if (ret == 0) {
+ /* Update count of bytes */
+ req->stream_size += len;
+ }
+ }
+
+ if (req->protocol == MK_HTTP_PROTOCOL_11 && len > 0) {
+ ret = mk_stream_in_raw(&req->stream, NULL,
+ "\r\n", 2, NULL, NULL);
+ }
+
+ /* Validate if the response headers are ready */
+ headers_setup(req);
+
+ /* Flush channel data */
+ ret = mk_http_flush(req);
+
+ /*
+ * Flush have been done, before to return our original caller, we want to yield
+ * and give some execution time to the event loop to avoid possible blocking
+ * since the caller might be using this mk_http_send() in a loop.
+ */
+ mk_lib_yield(req);
+ return ret;
+}
+
+int mk_http_done(mk_request_t *req)
+{
+ if (req->session->channel->status != MK_CHANNEL_OK) {
+ return -1;
+ }
+
+ /* Validate if the response headers are ready */
+ headers_setup(req);
+
+ if (req->headers.transfer_encoding == MK_HEADER_TE_TYPE_CHUNKED) {
+ /* Append end-of-chunk bytes */
+ mk_http_send(req, NULL, 0, NULL);
+ }
+ else {
+ mk_http_send(req, NULL, 0, NULL);
+ }
+
+ if (req->session->close_now == MK_TRUE) {
+ mk_lib_yield(req);
+ }
+
+ return 0;
+}
+
+/* Create a messaging queue end-point */
+int mk_mq_create(mk_ctx_t *ctx, char *name, void (*cb), void *data)
+{
+ int id;
+
+ id = mk_fifo_queue_create(ctx->fifo, name, cb, data);
+ return id;
+}
+
+/* Write a message to a specific queue ID */
+int mk_mq_send(mk_ctx_t *ctx, int qid, void *data, size_t size)
+{
+ return mk_fifo_send(ctx->fifo, qid, data, size);
+}
+
+int mk_main()
+{
+ while (1) {
+ sleep(60);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_mimetype.c b/fluent-bit/lib/monkey/mk_server/mk_mimetype.c
new file mode 100644
index 000000000..b86b4ef1a
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_mimetype.c
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <mk_core/mk_unistd.h>
+
+#include <monkey/monkey.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_http.h>
+
+struct mk_mimetype *mimetype_default;
+
+static int rbtree_compare(const void *lhs, const void *rhs)
+{
+ return strcmp((const char *)lhs, (const char *)rhs);
+}
+
+/* Match mime type for requested resource */
+struct mk_mimetype *mk_mimetype_lookup(struct mk_server *server, char *name)
+{
+ int cmp;
+ struct rb_tree_node *node = server->mimetype_rb_head.root;
+
+ while (node) {
+ struct mk_mimetype *entry = container_of(node, struct mk_mimetype, _rb_head);
+ cmp = strcmp(name, entry->name);
+ if (cmp < 0)
+ node = node->left;
+ else if (cmp > 0)
+ node = node->right;
+ else {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+int mk_mimetype_add(struct mk_server *server, char *name, const char *type)
+{
+ int len = strlen(type) + 3;
+ char *p;
+ struct mk_mimetype *new_mime;
+
+ /* make sure we register the extension in lower case */
+ p = name;
+ for ( ; *p; ++p) *p = tolower(*p);
+
+ new_mime = mk_mem_alloc_z(sizeof(struct mk_mimetype));
+ if (!new_mime) {
+ return -1;
+ }
+ new_mime->name = mk_string_dup(name);
+ if (!new_mime->name) {
+ mk_mem_free(new_mime);
+ return -1;
+ }
+ new_mime->type.data = mk_mem_alloc(len);
+ if (!new_mime->type.data) {
+ mk_mem_free(new_mime->name);
+ mk_mem_free(new_mime);
+ return -1;
+ }
+ new_mime->type.len = len - 1;
+ new_mime->header_type.data = mk_mem_alloc(len + 32);
+ if (!new_mime->header_type.data) {
+ mk_mem_free(new_mime->name);
+ mk_mem_free(new_mime->type.data);
+ mk_mem_free(new_mime);
+ return -1;
+ }
+ new_mime->header_type.len = snprintf(new_mime->header_type.data,
+ len + 32,
+ "Content-Type: %s\r\n",
+ type);
+ strcpy(new_mime->type.data, type);
+ strcat(new_mime->type.data, MK_CRLF);
+ new_mime->type.data[len-1] = '\0';
+
+ /* Insert the node into the RBT */
+ rb_tree_insert(&server->mimetype_rb_head,
+ new_mime->name, &new_mime->_rb_head);
+
+ /* Add to linked list head */
+ mk_list_add(&new_mime->_head, &server->mimetype_list);
+
+ return 0;
+}
+
+int mk_mimetype_init(struct mk_server *server)
+{
+ char *name;
+ int ret;
+
+ /* Initialize the heads */
+ mk_list_init(&server->mimetype_list);
+ rb_tree_new(&server->mimetype_rb_head, rbtree_compare);
+
+ name = mk_string_dup(MIMETYPE_DEFAULT_NAME);
+ if (server->mimetype_default_str) {
+ ret = mk_mimetype_add(server, name, server->mimetype_default_str);
+ }
+ else {
+ ret = mk_mimetype_add(server, name, MIMETYPE_DEFAULT_TYPE);
+ }
+ if (ret < 0) {
+ mk_mem_free(name);
+ return -1;
+ }
+ server->mimetype_default = mk_list_entry_first(&server->mimetype_list,
+ struct mk_mimetype,
+ _head);
+ mk_mem_free(name);
+ return 0;
+}
+
+/* Load the two mime arrays into memory */
+int mk_mimetype_read_config(struct mk_server *server)
+{
+ char path[MK_MAX_PATH];
+ struct mk_rconf *cnf;
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *entry;
+ struct mk_list *head;
+ struct file_info f_info;
+ int ret;
+
+ if (!server->conf_mimetype) {
+ return -1;
+ }
+
+ /* Read mime types configuration file */
+ snprintf(path, MK_MAX_PATH, "%s/%s",
+ server->path_conf_root,
+ server->conf_mimetype);
+
+ ret = mk_file_get_info(path, &f_info, MK_FILE_EXISTS);
+ if (ret == -1 || f_info.is_file == MK_FALSE) {
+ snprintf(path, MK_MAX_PATH, "%s", server->conf_mimetype);
+ }
+ cnf = mk_rconf_open(path);
+ if (!cnf) {
+ mk_warn("[mime] skipping mimetype configuration file");
+ return -1;
+ }
+
+ /* Get MimeTypes tag */
+ section = mk_rconf_section_get(cnf, "MIMETYPES");
+ if (!section) {
+ mk_err("[mime] Invalid mime type file");
+ return -1;
+ }
+
+ mk_list_foreach(head, &section->entries) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+ if (!entry->key || !entry->val) {
+ continue;
+ }
+
+ if (mk_mimetype_add(server, entry->key, entry->val) != 0) {
+ mk_err("[mime] Error loading Mime Types");
+ return -1;
+ }
+ }
+
+ mk_rconf_free(cnf);
+
+ return 0;
+}
+
+struct mk_mimetype *mk_mimetype_find(struct mk_server *server, mk_ptr_t *filename)
+{
+ int j, len;
+
+ j = len = filename->len;
+
+ /* looking for extension */
+ while (j >= 0 && filename->data[j] != '.') {
+ j--;
+ }
+
+ if (j <= 0) {
+ return NULL;
+ }
+
+ return mk_mimetype_lookup(server, filename->data + j + 1);
+}
+
+void mk_mimetype_free_all(struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_list *tmp;
+ struct mk_mimetype *mime;
+
+ mk_list_foreach_safe(head, tmp, &server->mimetype_list) {
+ mime = mk_list_entry(head, struct mk_mimetype, _head);
+ mk_ptr_free(&mime->type);
+ mk_mem_free(mime->name);
+ mk_mem_free(mime->header_type.data);
+ mk_mem_free(mime);
+ }
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_net.c b/fluent-bit/lib/monkey/mk_server/mk_net.c
new file mode 100644
index 000000000..de183de48
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_net.c
@@ -0,0 +1,284 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2016 Monkey Software LLC <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_core.h>
+#include <monkey/mk_net.h>
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_thread.h>
+#include <monkey/mk_tls.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <afunix.h>
+#else
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+/* Initialize the network stack*/
+int mk_net_init()
+{
+#ifdef _WIN32
+ int result;
+ WSADATA wsa_data;
+ static int initialized = 0;
+
+ if(0 != initialized) {
+ return 0;
+ }
+
+ result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+
+ if(0 != result) {
+ if(WSAEINPROGRESS == result)
+ {
+ Sleep(100); /* Let the other thread finish initializing the stack */
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ initialized = 1;
+#endif
+
+ return 0;
+}
+
+/* Connect to a TCP socket server */
+static int mk_net_fd_connect(int fd, char *host, unsigned long port)
+{
+ int ret;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ char _port[6];
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ snprintf(_port, sizeof(_port), "%lu", port);
+ ret = getaddrinfo(host, _port, &hints, &res);
+ if (ret != 0) {
+ return -1;
+ }
+
+ ret = connect(fd, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+
+ return ret;
+}
+
+struct mk_net_connection *mk_net_conn_create(char *addr, int port)
+{
+ int fd;
+ int ret;
+ int error = 0;
+ socklen_t len = sizeof(error);
+ struct mk_sched_worker *sched;
+ struct mk_net_connection *conn;
+
+ /* Allocate connection context */
+ conn = mk_mem_alloc(sizeof(struct mk_net_connection));
+ if (!conn) {
+ return NULL;
+ }
+
+ /* Create socket */
+ fd = mk_socket_create(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ mk_mem_free(conn);
+ return NULL;
+ }
+
+ /* Make socket async */
+ mk_socket_set_nonblocking(fd);
+ conn->fd = fd;
+
+ ret = mk_net_fd_connect(conn->fd, addr, port);
+ if (ret == -1) {
+ if (errno != EINPROGRESS) {
+ close(fd);
+ mk_mem_free(conn);
+ return NULL;
+ }
+
+ MK_EVENT_NEW(&conn->event);
+
+ sched = mk_sched_get_thread_conf();
+ // FIXME: not including the thread
+ //conn->thread = mk_thread_get();
+ ret = mk_event_add(sched->loop, conn->fd, MK_EVENT_THREAD,
+ MK_EVENT_WRITE, &conn->event);
+ if (ret == -1) {
+ close(fd);
+ mk_mem_free(conn);
+ return NULL;
+ }
+
+ /*
+ * Return the control to the parent caller, we need to wait for
+ * the event loop to get back to us.
+ */
+ mk_thread_yield(conn->thread);
+
+ /* We got a notification, remove the event registered */
+ ret = mk_event_del(sched->loop, &conn->event);
+
+ /* Check the connection status */
+ if (conn->event.mask & MK_EVENT_WRITE) {
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ close(fd);
+ mk_mem_free(conn);
+ return NULL;
+ }
+
+ if (error != 0) {
+ /* Connection is broken, not much to do here */
+ fprintf(stderr, "Async connection failed %s:%i\n",
+ conn->host, conn->port);
+ close(fd);
+ mk_mem_free(conn);
+ return NULL;
+ }
+ MK_EVENT_NEW(&conn->event);
+ return conn;
+ }
+ else {
+ close(fd);
+ mk_mem_free(conn);
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+int mk_net_conn_write(struct mk_channel *channel,
+ void *data, size_t len)
+{
+ int ret = 0;
+ int error;
+ ssize_t bytes;
+ size_t total = 0;
+ size_t send;
+ socklen_t slen = sizeof(error);
+ struct mk_thread *th = MK_TLS_GET(mk_thread);
+ struct mk_sched_worker *sched;
+
+ sched = mk_sched_get_thread_conf();
+ if (!sched) {
+ return -1;
+ }
+
+ retry:
+ error = 0;
+
+ if (len - total > 524288) {
+ send = 524288;
+ }
+ else {
+ send = (len - total);
+ }
+
+ send = len - total;
+ bytes = channel->io->write(channel->io->plugin, channel->fd, (uint8_t *)data + total, send);
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ MK_EVENT_NEW(channel->event);
+ channel->thread = th;
+ ret = mk_event_add(sched->loop,
+ channel->fd,
+ MK_EVENT_THREAD,
+ MK_EVENT_WRITE, channel->event);
+ if (ret == -1) {
+ /*
+ * If we failed here there no much that we can do, just
+ * let the caller we failed
+ */
+ return -1;
+ }
+
+ /*
+ * Return the control to the parent caller, we need to wait for
+ * the event loop to get back to us.
+ */
+ mk_thread_yield(th);
+
+ /* We got a notification, remove the event registered */
+ ret = mk_event_del(sched->loop, channel->event);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* Check the connection status */
+ if (channel->event->mask & MK_EVENT_WRITE) {
+ ret = getsockopt(channel->fd, SOL_SOCKET, SO_ERROR, &error, &slen);
+ if (ret == -1) {
+ fprintf(stderr, "[io] could not validate socket status");
+ return -1;
+ }
+
+ if (error != 0) {
+ return -1;
+ }
+
+ MK_EVENT_NEW(channel->event);
+ goto retry;
+ }
+ else {
+ return -1;
+ }
+
+ }
+ else {
+ return -1;
+ }
+ }
+
+ /* Update counters */
+ total += bytes;
+ if (total < len) {
+ channel->thread = th;
+ ret = mk_event_add(sched->loop,
+ channel->fd,
+ MK_EVENT_THREAD,
+ MK_EVENT_WRITE, channel->event);
+ if (ret == -1) {
+ /*
+ * If we failed here there no much that we can do, just
+ * let the caller we failed
+ */
+ return -1;
+ }
+
+ mk_thread_yield(th);
+ goto retry;
+ }
+
+ if (channel->event->status & MK_EVENT_REGISTERED) {
+ /* We got a notification, remove the event registered */
+ ret = mk_event_del(sched->loop, channel->event);
+ }
+
+ return total;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_plugin.c b/fluent-bit/lib/monkey/mk_server/mk_plugin.c
new file mode 100644
index 000000000..50e2886b7
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_plugin.c
@@ -0,0 +1,804 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_http.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_static_plugins.h>
+#include <monkey/mk_plugin_stage.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_net.h>
+
+#ifndef _WIN32
+#include <dlfcn.h>
+#include <err.h>
+#endif
+
+enum {
+ bufsize = 256
+};
+
+static struct plugin_stagemap *plg_stagemap;
+struct plugin_network_io *plg_netiomap;
+
+struct mk_plugin *mk_plugin_lookup(char *shortname, struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_plugin *p = NULL;
+
+ mk_list_foreach(head, &server->plugins) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+ if (strcmp(p->shortname, shortname) == 0){
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+void *mk_plugin_load_dynamic(const char *path)
+{
+ void *handle;
+
+#ifdef _WIN32
+ handle = (void *) LoadLibraryA(path);
+#else
+ handle = dlopen(path, RTLD_LAZY);
+
+ if (!handle) {
+ mk_warn("dlopen() %s", dlerror());
+ }
+#endif
+
+ return handle;
+}
+
+void *mk_plugin_load_symbol(void *handler, const char *symbol)
+{
+ void *s;
+
+#ifdef _WIN32
+ s = GetProcAddress((HMODULE)handler, symbol);
+#else
+ dlerror();
+ s = dlsym(handler, symbol);
+ if (dlerror() != NULL) {
+ return NULL;
+ }
+#endif
+
+ return s;
+}
+
+/* Initialize a plugin, trigger the init_plugin callback */
+static int mk_plugin_init(struct plugin_api *api,
+ struct mk_plugin *plugin,
+ struct mk_server *server)
+{
+ int ret;
+ unsigned long len;
+ char path[1024];
+ char *conf_dir = NULL;
+ struct file_info f_info;
+
+ MK_TRACE("Load Plugin: '%s'", plugin->shortname);
+
+ snprintf(path, 1024, "%s/%s",
+ server->path_conf_root, server->conf_plugins);
+ ret = mk_file_get_info(path, &f_info, MK_FILE_READ);
+ if (ret == -1 || f_info.is_directory == MK_FALSE) {
+ snprintf(path, 1024, "%s", server->conf_plugins);
+ }
+
+ /* Build plugin configuration path */
+ mk_string_build(&conf_dir,
+ &len,
+ "%s/%s/",
+ path, plugin->shortname);
+
+ /* Init plugin */
+ plugin->api = api;
+ plugin->server_ctx = server;
+
+ if (plugin->network != NULL) {
+ plugin->network->plugin = plugin;
+ }
+
+ ret = plugin->init_plugin(plugin, conf_dir);
+ mk_mem_free(conf_dir);
+
+ return ret;
+}
+
+
+/*
+ * Load a plugin into Monkey core, 'type' defines if it's a MK_PLUGIN_STATIC or
+ * a MK_PLUGIN_DYNAMIC. 'shortname' is mandatory and 'path' is only used when
+ * MK_PLUGIN_DYNAMIC is set and represents the absolute path of the shared
+ * library.
+ */
+struct mk_plugin *mk_plugin_load(int type, const char *shortname,
+ void *data, struct mk_server *server)
+{
+ char *path;
+ char symbol[64];
+ void *handler;
+ struct mk_list *head;
+ struct mk_plugin *tmp;
+ struct mk_plugin *plugin = NULL;
+ struct mk_plugin_stage *stage;
+
+ /* Set main struct name to reference */
+ if (type == MK_PLUGIN_DYNAMIC) {
+ path = (char *) data;
+ handler = mk_plugin_load_dynamic(path);
+ if (!handler) {
+ return NULL;
+ }
+
+ snprintf(symbol, sizeof(symbol) - 1, "mk_plugin_%s", shortname);
+ plugin = mk_plugin_load_symbol(handler, symbol);
+ if (!plugin) {
+ mk_warn("Plugin '%s' is not registering properly", path);
+#ifdef _WIN32
+ FreeLibrary((HMODULE)handler);
+#else
+ dlclose(handler);
+#endif
+ return NULL;
+ }
+
+ /* Make sure this is not loaded twice (ref #218) */
+ mk_list_foreach(head, &server->plugins) {
+ tmp = mk_list_entry(head, struct mk_plugin, _head);
+ if (tmp->load_type == MK_PLUGIN_STATIC &&
+ strcmp(tmp->name, plugin->name) == 0){
+ mk_warn("Plugin '%s' have been built-in.",
+ tmp->shortname);
+#ifdef _WIN32
+ FreeLibrary((HMODULE)handler);
+#else
+ dlclose(handler);
+#endif
+ return NULL;
+ }
+ }
+
+ plugin->load_type = MK_PLUGIN_DYNAMIC;
+ plugin->handler = handler;
+ plugin->path = mk_string_dup(path);
+ }
+ else if (type == MK_PLUGIN_STATIC) {
+ plugin = (struct mk_plugin *) data;
+ plugin->load_type = MK_PLUGIN_STATIC;
+ }
+
+ if (!plugin) {
+ return NULL;
+ }
+
+ /* Validate all callbacks are set */
+ if (!plugin->shortname || !plugin->name || !plugin->version ||
+ !plugin->init_plugin || !plugin->exit_plugin) {
+ mk_warn("Plugin '%s' is not registering all fields properly",
+ shortname);
+ return NULL;
+ }
+
+ if (plugin->hooks & MK_PLUGIN_NETWORK_LAYER) {
+ mk_bug(!plugin->network);
+ }
+
+ mk_list_init(&plugin->stage_list);
+ if (plugin->hooks & MK_PLUGIN_STAGE) {
+ struct mk_plugin_stage *st;
+
+ stage = plugin->stage;
+ if (stage->stage10) {
+ st = mk_mem_alloc(sizeof(struct mk_plugin_stage));
+ st->stage10 = stage->stage10;
+ st->plugin = plugin;
+ mk_list_add(&st->_head, &server->stage10_handler);
+ mk_list_add(&st->_parent_head, &plugin->stage_list);
+ }
+ if (stage->stage20) {
+ st = mk_mem_alloc(sizeof(struct mk_plugin_stage));
+ st->stage20 = stage->stage20;
+ st->plugin = plugin;
+ mk_list_add(&st->_head, &server->stage20_handler);
+ mk_list_add(&st->_parent_head, &plugin->stage_list);
+ }
+ if (stage->stage30) {
+ st = mk_mem_alloc(sizeof(struct mk_plugin_stage));
+ st->stage30 = stage->stage30;
+ st->plugin = plugin;
+ mk_list_add(&st->_head, &server->stage30_handler);
+ mk_list_add(&st->_parent_head, &plugin->stage_list);
+ }
+ if (stage->stage40) {
+ st = mk_mem_alloc(sizeof(struct mk_plugin_stage));
+ st->stage40 = stage->stage40;
+ st->plugin = plugin;
+ mk_list_add(&st->_head, &server->stage40_handler);
+ mk_list_add(&st->_parent_head, &plugin->stage_list);
+ }
+ if (stage->stage50) {
+ st = mk_mem_alloc(sizeof(struct mk_plugin_stage));
+ st->stage50 = stage->stage50;
+ st->plugin = plugin;
+ mk_list_add(&st->_head, &server->stage50_handler);
+ mk_list_add(&st->_parent_head, &plugin->stage_list);
+ }
+ }
+
+ if (type == MK_PLUGIN_DYNAMIC) {
+ /* Add Plugin to the end of the list */
+ mk_list_add(&plugin->_head, &server->plugins);
+ }
+
+ return plugin;
+}
+
+void mk_plugin_unregister(struct mk_plugin *p)
+{
+ mk_mem_free(p->path);
+ mk_list_del(&p->_head);
+ if (p->load_type == MK_PLUGIN_DYNAMIC) {
+#ifdef _WIN32
+ FreeLibrary((HMODULE)p->handler);
+#else
+ dlclose(p->handler);
+#endif
+ }
+
+}
+
+void mk_plugin_api_init(struct mk_server *server)
+{
+ struct plugin_api *api;
+
+ /* Create an instance of the API */
+ api = mk_mem_alloc_z(sizeof(struct plugin_api));
+
+#ifndef _WIN32
+ __builtin_prefetch(api);
+#endif
+
+ /* Setup and connections list */
+ /* FIXME: api->config = server; */
+
+ /* API plugins funcions */
+
+ /* Error helper */
+ api->_error = mk_print;
+
+ /* HTTP callbacks */
+ api->http_request_end = mk_plugin_http_request_end;
+ api->http_request_error = mk_plugin_http_error;
+
+ /* Memory callbacks */
+ api->pointer_set = mk_ptr_set;
+ api->pointer_print = mk_ptr_print;
+ api->pointer_to_buf = mk_ptr_to_buf;
+ api->plugin_load_symbol = mk_plugin_load_symbol;
+ api->mem_alloc = mk_mem_alloc;
+ api->mem_alloc_z = mk_mem_alloc_z;
+ api->mem_realloc = mk_mem_realloc;
+ api->mem_free = mk_mem_free;
+
+ /* String Callbacks */
+ api->str_build = mk_string_build;
+ api->str_dup = mk_string_dup;
+ api->str_search = mk_string_search;
+ api->str_search_n = mk_string_search_n;
+ api->str_char_search = mk_string_char_search;
+ api->str_copy_substr = mk_string_copy_substr;
+ api->str_itop = mk_string_itop;
+ api->str_split_line = mk_string_split_line;
+ api->str_split_free = mk_string_split_free;
+
+ /* File Callbacks */
+ api->file_to_buffer = mk_file_to_buffer;
+ api->file_get_info = mk_file_get_info;
+
+ /* HTTP Callbacks */
+ api->header_prepare = mk_plugin_header_prepare;
+ api->header_add = mk_plugin_header_add;
+ api->header_get = mk_http_header_get;
+ api->header_set_http_status = mk_header_set_http_status;
+
+ /* Channels / Streams */
+ api->channel_new = mk_channel_new;
+ api->channel_flush = mk_channel_flush;
+ api->channel_write = mk_channel_write;
+ api->channel_append_stream = mk_channel_append_stream;
+
+ /* IOV callbacks */
+ api->iov_create = mk_iov_create;
+ api->iov_realloc = mk_iov_realloc;
+ api->iov_free = mk_iov_free;
+ api->iov_free_marked = mk_iov_free_marked;
+ api->iov_add = mk_iov_add;
+ api->iov_set_entry = mk_iov_set_entry;
+ api->iov_send = mk_iov_send;
+ api->iov_print = mk_iov_print;
+
+ /* events mechanism */
+ api->ev_loop_create = mk_event_loop_create;
+ api->ev_add = mk_event_add;
+ api->ev_del = mk_event_del;
+ api->ev_timeout_create = mk_event_timeout_create;
+ api->ev_channel_create = mk_event_channel_create;
+ api->ev_wait = mk_event_wait;
+ api->ev_backend = mk_event_backend;
+
+ /* Mimetype */
+ api->mimetype_lookup = mk_mimetype_lookup;
+
+ /* Socket callbacks */
+ api->socket_cork_flag = mk_socket_set_cork_flag;
+ api->socket_connect = mk_socket_connect;
+ api->socket_open = mk_socket_open;
+ api->socket_reset = mk_socket_reset;
+ api->socket_set_tcp_fastopen = mk_socket_set_tcp_fastopen;
+ api->socket_set_tcp_reuseport = mk_socket_set_tcp_reuseport;
+ api->socket_set_tcp_nodelay = mk_socket_set_tcp_nodelay;
+ api->socket_set_nonblocking = mk_socket_set_nonblocking;
+ api->socket_create = mk_socket_create;
+ api->socket_ip_str = mk_socket_ip_str;
+
+ /* Async network */
+ api->net_conn_create = mk_net_conn_create;
+
+ /* Config Callbacks */
+ api->config_create = mk_rconf_create;
+ api->config_open = mk_rconf_open;
+ api->config_free = mk_rconf_free;
+ api->config_section_get = mk_rconf_section_get;
+ api->config_section_get_key = mk_rconf_section_get_key;
+
+ /* Scheduler and Event callbacks */
+ api->sched_loop = mk_sched_loop;
+ api->sched_get_connection = mk_sched_get_connection;
+ api->sched_event_free = mk_sched_event_free;
+ api->sched_remove_client = mk_plugin_sched_remove_client;
+ api->sched_worker_info = mk_plugin_sched_get_thread_conf;
+
+ /* Worker functions */
+ api->worker_spawn = mk_utils_worker_spawn;
+ api->worker_rename = mk_utils_worker_rename;
+
+ /* Time functions */
+ api->time_unix = mk_plugin_time_now_unix;
+ api->time_to_gmt = mk_utils_utime2gmt;
+ api->time_human = mk_plugin_time_now_human;
+
+ api->stacktrace = (void *) mk_utils_stacktrace;
+ api->kernel_version = mk_kernel_version;
+ api->kernel_features_print = mk_kernel_features_print;
+ api->plugins = &server->plugins;
+
+ /* handler */
+ api->handler_param_get = mk_handler_param_get;
+
+ server->api = api;
+}
+
+void mk_plugin_load_static(struct mk_server *server)
+{
+ /* Load static plugins */
+ mk_list_init(&server->plugins);
+ mk_static_plugins(&server->plugins);
+}
+
+void mk_plugin_load_all(struct mk_server *server)
+{
+ int ret;
+ char *tmp;
+ char *path;
+ char shortname[64];
+ struct mk_plugin *p;
+ struct mk_rconf *cnf;
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *entry;
+ struct mk_list *head;
+ struct mk_list *htmp;
+ struct file_info f_info;
+
+ mk_plugin_load_static(server);
+ mk_list_foreach_safe(head, htmp, &server->plugins) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+
+ /* Load the static plugin */
+ p = mk_plugin_load(MK_PLUGIN_STATIC,
+ p->shortname,
+ (void *) p,
+ server);
+ if (!p) {
+ continue;
+ }
+ ret = mk_plugin_init(server->api, p, server);
+ if (ret == -1) {
+ /* Free plugin, do not register, error initializing */
+ mk_warn("Plugin initialization failed: %s", p->shortname);
+ mk_plugin_unregister(p);
+ continue;
+ }
+ else if (ret == -2) {
+ /* Do not register, just skip it */
+ mk_plugin_unregister(p);
+ continue;
+ }
+ }
+
+ /* In case there are not dynamic plugins */
+ if (!server->conf_plugin_load) {
+ return;
+ }
+
+ /* Read configuration file */
+ path = mk_mem_alloc_z(1024);
+ snprintf(path, 1024, "%s/%s", server->path_conf_root,
+ server->conf_plugin_load);
+ ret = mk_file_get_info(path, &f_info, MK_FILE_READ);
+ if (ret == -1 || f_info.is_file == MK_FALSE) {
+ snprintf(path, 1024, "%s", server->conf_plugin_load);
+ }
+
+ cnf = mk_rconf_open(path);
+ if (!cnf) {
+ mk_warn("No dynamic plugins loaded.");
+ mk_mem_free(path);
+ return;
+ }
+
+ /* Read section 'PLUGINS' */
+ section = mk_rconf_section_get(cnf, "PLUGINS");
+ if (!section) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read key entries */
+ mk_list_foreach_safe(head, htmp, &section->entries) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+ if (strcasecmp(entry->key, "Load") == 0) {
+
+ /* Get plugin 'shortname' */
+ tmp = memrchr(entry->val, '-', strlen(entry->val));
+ ++tmp;
+ memset(shortname, '\0', sizeof(shortname) - 1);
+ strncpy(shortname, tmp, strlen(tmp) - 3);
+
+ /* Load the dynamic plugin */
+ p = mk_plugin_load(MK_PLUGIN_DYNAMIC,
+ shortname,
+ entry->val,
+ server);
+ if (!p) {
+ mk_warn("Invalid plugin '%s'", entry->val);
+ continue;
+ }
+
+ ret = mk_plugin_init(server->api, p, server);
+ if (ret < 0) {
+ /* Free plugin, do not register */
+ MK_TRACE("Unregister plugin '%s'", p->shortname);
+ mk_plugin_unregister(p);
+ continue;
+ }
+ }
+ }
+
+ /* Look for plugins thread key data */
+ mk_plugin_preworker_calls(server);
+ mk_vhost_map_handlers(server);
+ mk_mem_free(path);
+ mk_rconf_free(cnf);
+}
+
+static void mk_plugin_exit_stages(struct mk_plugin *p)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_plugin_stage *st;
+
+ mk_list_foreach_safe(head, tmp, &p->stage_list) {
+ st = mk_list_entry(head, struct mk_plugin_stage, _parent_head);
+
+ /* remove from direct config->stageN head list */
+ mk_list_del(&st->_head);
+
+ /* remove from plugin->stage_lists */
+ mk_list_del(&st->_parent_head);
+ mk_mem_free(st);
+ }
+}
+
+/* Invoke all plugins 'exit' hook and free resources by the plugin interface */
+void mk_plugin_exit_all(struct mk_server *server)
+{
+ struct mk_plugin *plugin;
+ struct mk_list *head, *tmp;
+
+ /* Plugins */
+ mk_list_foreach(head, &server->plugins) {
+ plugin = mk_list_entry(head, struct mk_plugin, _head);
+ plugin->exit_plugin(plugin);
+ }
+
+ /* Plugin interface it self */
+ mk_list_foreach_safe(head, tmp, &server->plugins) {
+ plugin = mk_list_entry(head, struct mk_plugin, _head);
+ mk_list_del(&plugin->_head);
+ mk_plugin_exit_stages(plugin);
+
+ if (plugin->load_type == MK_PLUGIN_DYNAMIC) {
+ mk_mem_free(plugin->path);
+#ifdef _WIN32
+ FreeLibrary((HMODULE)plugin->handler);
+#else
+ dlclose(plugin ->handler);
+#endif
+ }
+ else if (plugin->load_type == MK_PLUGIN_STATIC) {
+ if (plugin->network != NULL) {
+ mk_mem_free(plugin->network);
+ }
+
+ mk_mem_free(plugin);
+ }
+ }
+
+ mk_mem_free(server->api);
+ mk_mem_free(plg_stagemap);
+}
+
+/*
+ * When a worker is exiting, it invokes this function to release any plugin
+ * associated data.
+ */
+void mk_plugin_exit_worker()
+{
+}
+
+/* This function is called by every created worker
+ * for plugins which need to set some data under a thread
+ * context
+ */
+void mk_plugin_core_process(struct mk_server *server)
+{
+ struct mk_plugin *node;
+ struct mk_list *head;
+
+ mk_list_foreach(head, &server->plugins) {
+ node = mk_list_entry(head, struct mk_plugin, _head);
+
+ /* Init plugin */
+ if (node->master_init) {
+ node->master_init(server);
+ }
+ }
+}
+
+/* This function is called by every created worker
+ * for plugins which need to set some data under a thread
+ * context
+ */
+void mk_plugin_core_thread(struct mk_server *server)
+{
+
+ struct mk_plugin *node;
+ struct mk_list *head;
+
+ mk_list_foreach(head, &server->plugins) {
+ node = mk_list_entry(head, struct mk_plugin, _head);
+
+ /* Init plugin thread context */
+ if (node->worker_init) {
+ node->worker_init(server);
+ }
+ }
+}
+
+/* This function is called by Monkey *outside* of the
+ * thread context for plugins, so here's the right
+ * place to set pthread keys or similar
+ */
+void mk_plugin_preworker_calls(struct mk_server *server)
+{
+ int ret;
+ struct mk_plugin *node;
+ struct mk_list *head;
+
+ mk_list_foreach(head, &server->plugins) {
+ node = mk_list_entry(head, struct mk_plugin, _head);
+
+ /* Init pthread keys */
+ if (node->thread_key) {
+ MK_TRACE("[%s] Set thread key", node->shortname);
+
+ ret = pthread_key_create(node->thread_key, NULL);
+ if (ret != 0) {
+ mk_err("Plugin Error: could not create key for %s",
+ node->shortname);
+ }
+ }
+ }
+}
+
+int mk_plugin_http_error(int http_status, struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ struct mk_plugin *plugin)
+{
+ return mk_http_error(http_status, cs, sr, plugin->server_ctx);
+}
+
+
+int mk_plugin_http_request_end(struct mk_plugin *plugin,
+ struct mk_http_session *cs, int close)
+{
+ int ret;
+ int con;
+ struct mk_http_request *sr;
+ struct mk_server *server = plugin->server_ctx;
+
+ MK_TRACE("[FD %i] PLUGIN HTTP REQUEST END", cs->socket);
+
+ cs->status = MK_REQUEST_STATUS_INCOMPLETE;
+ if (mk_list_is_empty(&cs->request_list) == 0) {
+ MK_TRACE("[FD %i] Tried to end non-existing request.", cs->socket);
+ return -1;
+ }
+
+ sr = mk_list_entry_last(&cs->request_list, struct mk_http_request, _head);
+ mk_plugin_stage_run_40(cs, sr, server);
+
+ if (close == MK_TRUE) {
+ cs->close_now = MK_TRUE;
+ }
+
+ /* Let's check if we should ask to finalize the connection or not */
+ ret = mk_http_request_end(cs, server);
+ MK_TRACE("[FD %i] HTTP session end = %i", cs->socket, ret);
+ if (ret < 0) {
+ con = mk_sched_event_close(cs->conn, mk_sched_get_thread_conf(),
+ MK_EP_SOCKET_DONE, server);
+ if (con != 0) {
+ return con;
+ }
+ else {
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/* Plugin epoll event handlers
+ * ---------------------------
+ * this functions are called by connection.c functions as mk_conn_read(),
+ * mk_conn_write(),mk_conn_error(), mk_conn_close() and mk_conn_timeout().
+ *
+ * Return Values:
+ * -------------
+ * MK_PLUGIN_RET_EVENT_NOT_ME: There's no plugin hook associated
+ */
+
+void mk_plugin_event_bad_return(const char *hook, int ret)
+{
+ mk_err("[%s] Not allowed return value %i", hook, ret);
+}
+
+int mk_plugin_time_now_unix(struct mk_server *server)
+{
+ return server->clock_context->log_current_utime;
+}
+
+mk_ptr_t *mk_plugin_time_now_human(struct mk_server *server)
+{
+ return &server->clock_context->log_current_time;
+}
+
+int mk_plugin_sched_remove_client(int socket, struct mk_server *server)
+{
+ struct mk_sched_conn *conn;
+ struct mk_sched_worker *sched;
+
+ MK_TRACE("[FD %i] remove client", socket);
+
+ sched = mk_sched_get_thread_conf();
+ conn = mk_sched_get_connection(sched, socket);
+ if (!conn) {
+ return -1;
+ }
+
+ return mk_sched_remove_client(conn, sched, server);
+}
+
+int mk_plugin_header_prepare(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ return mk_header_prepare(cs, sr, plugin->server_ctx);
+}
+
+
+int mk_plugin_header_add(struct mk_http_request *sr, char *row, int len)
+{
+ mk_bug(!sr);
+
+ if (!sr->headers._extra_rows) {
+ /*
+ * We allocate space for a fixed number of IOV entries:
+ *
+ * MK_PLUGIN_HEADER_EXTRA_ROWS = X
+ *
+ * we use (MK_PLUGIN_HEADER_EXTRA_ROWS * 2) thinking in an ending CRLF
+ */
+ sr->headers._extra_rows = mk_iov_create(MK_PLUGIN_HEADER_EXTRA_ROWS * 2, 0);
+ mk_bug(!sr->headers._extra_rows);
+ }
+
+ mk_iov_add(sr->headers._extra_rows, row, len,
+ MK_FALSE);
+ mk_iov_add(sr->headers._extra_rows,
+ mk_iov_crlf.data, mk_iov_crlf.len,
+ MK_FALSE);
+ return 0;
+}
+
+struct mk_sched_worker *mk_plugin_sched_get_thread_conf()
+{
+ return MK_TLS_GET(mk_tls_sched_worker_node);
+}
+
+struct mk_plugin *mk_plugin_cap(char cap, struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_plugin *plugin;
+
+ mk_list_foreach(head, &server->plugins) {
+ plugin = mk_list_entry(head, struct mk_plugin, _head);
+ if (plugin->capabilities & cap) {
+ return plugin;
+ }
+ }
+
+ return NULL;
+}
+
+struct mk_vhost_handler_param *mk_handler_param_get(int id,
+ struct mk_list *params)
+{
+ int i = 0;
+ struct mk_list *head;
+
+ mk_list_foreach(head, params) {
+ if (i == id) {
+ return mk_list_entry(head, struct mk_vhost_handler_param, _head);
+ }
+ i++;
+ }
+
+ return NULL;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_scheduler.c b/fluent-bit/lib/monkey/mk_server/mk_scheduler.c
new file mode 100644
index 000000000..a680d3cdf
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_scheduler.c
@@ -0,0 +1,866 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_info.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_scheduler_tls.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_thread.h>
+#include <monkey/mk_cache.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_linuxtrace.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_plugin_stage.h>
+#include <monkey/mk_http_thread.h>
+
+#include <signal.h>
+
+#ifndef _WIN32
+#include <sys/syscall.h>
+#endif
+
+extern struct mk_sched_handler mk_http_handler;
+extern struct mk_sched_handler mk_http2_handler;
+
+pthread_mutex_t mutex_worker_init = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t mutex_worker_exit = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Returns the worker id which should take a new incomming connection,
+ * it returns the worker id with less active connections. Just used
+ * if config->scheduler_mode is MK_SCHEDULER_FAIR_BALANCING.
+ */
+static inline int _next_target(struct mk_server *server)
+{
+ int i;
+ int target = 0;
+ unsigned long long tmp = 0, cur = 0;
+ struct mk_sched_ctx *ctx = server->sched_ctx;
+ struct mk_sched_worker *worker;
+
+ cur = (ctx->workers[0].accepted_connections -
+ ctx->workers[0].closed_connections);
+ if (cur == 0) {
+ return 0;
+ }
+
+ /* Finds the lowest load worker */
+ for (i = 1; i < server->workers; i++) {
+ worker = &ctx->workers[i];
+ tmp = worker->accepted_connections - worker->closed_connections;
+ if (tmp < cur) {
+ target = i;
+ cur = tmp;
+
+ if (cur == 0)
+ break;
+ }
+ }
+
+ /*
+ * If sched_ctx->workers[target] worker is full then the whole server too,
+ * because it has the lowest load.
+ */
+ if (mk_unlikely(server->server_capacity > 0 &&
+ server->server_capacity <= cur)) {
+ MK_TRACE("Too many clients: %i", server->server_capacity);
+
+ /* Instruct to close the connection anyways, we lie, it will die */
+ return -1;
+ }
+
+ return target;
+}
+
+struct mk_sched_worker *mk_sched_next_target(struct mk_server *server)
+{
+ int t;
+ struct mk_sched_ctx *ctx = server->sched_ctx;
+
+ t = _next_target(server);
+ if (mk_likely(t != -1)) {
+ return &ctx->workers[t];
+ }
+
+ return NULL;
+}
+
+/*
+ * This function is invoked when the core triggers a MK_SCHED_SIGNAL_FREE_ALL
+ * event through the signal channels, it means the server will stop working
+ * so this is the last call to release all memory resources in use. Of course
+ * this takes place in a thread context.
+ */
+void mk_sched_worker_free(struct mk_server *server)
+{
+ int i;
+ pthread_t tid;
+ struct mk_sched_ctx *ctx = server->sched_ctx;
+ struct mk_sched_worker *worker = NULL;
+
+ pthread_mutex_lock(&mutex_worker_exit);
+
+ /*
+ * Fix Me: needs to implement API to make plugins release
+ * their resources first at WORKER LEVEL
+ */
+
+ /* External */
+ mk_plugin_exit_worker();
+ mk_vhost_fdt_worker_exit(server);
+ mk_cache_worker_exit();
+
+ /* Scheduler stuff */
+ tid = pthread_self();
+ for (i = 0; i < server->workers; i++) {
+ worker = &ctx->workers[i];
+ if (worker->tid == tid) {
+ break;
+ }
+ worker = NULL;
+ }
+
+ mk_bug(!worker);
+
+ /* FIXME!: there is nothing done here with the worker context */
+
+ /* Free master array (av queue & busy queue) */
+ mk_mem_free(MK_TLS_GET(mk_tls_sched_cs));
+ mk_mem_free(MK_TLS_GET(mk_tls_sched_cs_incomplete));
+ mk_mem_free(MK_TLS_GET(mk_tls_sched_worker_notif));
+ pthread_mutex_unlock(&mutex_worker_exit);
+}
+
+struct mk_sched_handler *mk_sched_handler_cap(char cap)
+{
+ if (cap == MK_CAP_HTTP) {
+ return &mk_http_handler;
+ }
+
+#ifdef MK_HAVE_HTTP2
+ else if (cap == MK_CAP_HTTP2) {
+ return &mk_http2_handler;
+ }
+#endif
+
+ return NULL;
+}
+
+/*
+ * Register a new client connection into the scheduler, this call takes place
+ * inside the worker/thread context.
+ */
+struct mk_sched_conn *mk_sched_add_connection(int remote_fd,
+ struct mk_server_listen *listener,
+ struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ int ret;
+ int size;
+ struct mk_sched_handler *handler;
+ struct mk_sched_conn *conn;
+ struct mk_event *event;
+
+ /* Before to continue, we need to run plugin stage 10 */
+ ret = mk_plugin_stage_run_10(remote_fd, server);
+
+ /* Close connection, otherwise continue */
+ if (ret == MK_PLUGIN_RET_CLOSE_CONX) {
+ listener->network->network->close(listener->network, remote_fd);
+ MK_LT_SCHED(remote_fd, "PLUGIN_CLOSE");
+ return NULL;
+ }
+
+ handler = listener->protocol;
+ if (handler->sched_extra_size > 0) {
+ void *data;
+ size = (sizeof(struct mk_sched_conn) + handler->sched_extra_size);
+ data = mk_mem_alloc_z(size);
+ conn = (struct mk_sched_conn *) data;
+ }
+ else {
+ conn = mk_mem_alloc_z(sizeof(struct mk_sched_conn));
+ }
+
+ if (!conn) {
+ mk_err("[server] Could not register client");
+ return NULL;
+ }
+
+ event = &conn->event;
+ event->fd = remote_fd;
+ event->type = MK_EVENT_CONNECTION;
+ event->mask = MK_EVENT_EMPTY;
+ event->status = MK_EVENT_NONE;
+ conn->arrive_time = server->clock_context->log_current_utime;
+ conn->protocol = handler;
+ conn->net = listener->network->network;
+ conn->is_timeout_on = MK_FALSE;
+ conn->server_listen = listener;
+
+ /* Stream channel */
+ conn->channel.type = MK_CHANNEL_SOCKET; /* channel type */
+ conn->channel.fd = remote_fd; /* socket conn */
+ conn->channel.io = conn->net; /* network layer */
+ conn->channel.event = event; /* parent event ref */
+ mk_list_init(&conn->channel.streams);
+
+ /*
+ * Register the connections into the timeout_queue:
+ *
+ * When a new connection arrives, we cannot assume it contains some data
+ * to read, meaning the event loop may not get notifications and the protocol
+ * handler will never be called. So in order to avoid DDoS we always register
+ * this session in the timeout_queue for further lookup.
+ *
+ * The protocol handler is in charge to remove the session from the
+ * timeout_queue.
+ */
+ mk_sched_conn_timeout_add(conn, sched);
+
+ /* Linux trace message */
+ MK_LT_SCHED(remote_fd, "REGISTERED");
+
+ return conn;
+}
+
+static void mk_sched_thread_lists_init()
+{
+ struct mk_list *sched_cs_incomplete;
+
+ /* mk_tls_sched_cs_incomplete */
+ sched_cs_incomplete = mk_mem_alloc(sizeof(struct mk_list));
+ mk_list_init(sched_cs_incomplete);
+ MK_TLS_SET(mk_tls_sched_cs_incomplete, sched_cs_incomplete);
+}
+
+/* Register thread information. The caller thread is the thread information's owner */
+static int mk_sched_register_thread(struct mk_server *server)
+{
+ struct mk_sched_ctx *ctx = server->sched_ctx;
+ struct mk_sched_worker *worker;
+
+ /*
+ * If this thread slept inside this section, some other thread may touch
+ * server->worker_id.
+ * So protect it with a mutex, only one thread may handle server->worker_id.
+ *
+ * Note : Let's use the platform agnostic atomics we implemented in cmetrics here
+ * instead of a lock.
+ */
+ worker = &ctx->workers[server->worker_id];
+ worker->idx = server->worker_id++;
+ worker->tid = pthread_self();
+
+#if defined(__linux__)
+ /*
+ * Under Linux does not exists the difference between process and
+ * threads, everything is a thread in the kernel task struct, and each
+ * one has it's own numerical identificator: PID .
+ *
+ * Here we want to know what's the PID associated to this running
+ * task (which is different from parent Monkey PID), it can be
+ * retrieved with gettid() but Glibc does not export to userspace
+ * the syscall, we need to call it directly through syscall(2).
+ */
+ worker->pid = syscall(__NR_gettid);
+#elif defined(__APPLE__)
+ uint64_t tid;
+ pthread_threadid_np(NULL, &tid);
+ worker->pid = tid;
+#else
+ worker->pid = 0xdeadbeef;
+#endif
+
+ /* Initialize lists */
+ mk_list_init(&worker->timeout_queue);
+ worker->request_handler = NULL;
+
+ return worker->idx;
+}
+
+static void mk_signal_thread_sigpipe_safe()
+{
+#ifndef _WIN32
+ sigset_t old;
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGPIPE);
+ pthread_sigmask(SIG_BLOCK, &set, &old);
+#endif
+}
+
+/* created thread, all these calls are in the thread context */
+void *mk_sched_launch_worker_loop(void *data)
+{
+ int ret;
+ int wid;
+ unsigned long len;
+ char *thread_name = 0;
+ struct mk_list *head;
+ struct mk_sched_worker_cb *wcb;
+ struct mk_sched_worker *sched = NULL;
+ struct mk_sched_notif *notif = NULL;
+ struct mk_sched_thread_conf *thinfo = data;
+ struct mk_sched_ctx *ctx;
+ struct mk_server *server;
+
+ server = thinfo->server;
+ ctx = server->sched_ctx;
+
+ /* Avoid SIGPIPE signals on this thread */
+ mk_signal_thread_sigpipe_safe();
+
+ /* Init specific thread cache */
+ mk_sched_thread_lists_init();
+ mk_cache_worker_init();
+
+ /* Virtual hosts: initialize per thread-vhost data */
+ mk_vhost_fdt_worker_init(server);
+
+ /* Register working thread */
+ wid = mk_sched_register_thread(server);
+ sched = &ctx->workers[wid];
+ sched->loop = mk_event_loop_create(MK_EVENT_QUEUE_SIZE);
+ if (!sched->loop) {
+ mk_err("Error creating Scheduler loop");
+ exit(EXIT_FAILURE);
+ }
+
+
+ sched->mem_pagesize = mk_utils_get_system_page_size();
+
+ /*
+ * Create the notification instance and link it to the worker
+ * thread-scope list.
+ */
+ notif = mk_mem_alloc_z(sizeof(struct mk_sched_notif));
+ MK_TLS_SET(mk_tls_sched_worker_notif, notif);
+
+ /* Register the scheduler channel to signal active workers */
+ ret = mk_event_channel_create(sched->loop,
+ &sched->signal_channel_r,
+ &sched->signal_channel_w,
+ notif);
+ if (ret < 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ mk_list_init(&sched->event_free_queue);
+ mk_list_init(&sched->threads);
+ mk_list_init(&sched->threads_purge);
+
+ /*
+ * ULONG_MAX BUG test only
+ * =======================
+ * to test the workaround we can use the following value:
+ *
+ * thinfo->closed_connections = 1000;
+ */
+
+ //thinfo->ctx = thconf->ctx;
+
+ /* Rename worker */
+ mk_string_build(&thread_name, &len, "monkey: wrk/%i", sched->idx);
+ mk_utils_worker_rename(thread_name);
+ mk_mem_free(thread_name);
+
+ /* Export known scheduler node to context thread */
+ MK_TLS_SET(mk_tls_sched_worker_node, sched);
+ mk_plugin_core_thread(server);
+
+ if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
+ sched->listeners = mk_server_listen_init(server);
+ if (!sched->listeners) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Unlock the conditional initializator */
+ pthread_mutex_lock(&server->pth_mutex);
+ server->pth_init = MK_TRUE;
+ pthread_cond_signal(&server->pth_cond);
+ pthread_mutex_unlock(&server->pth_mutex);
+
+ /* Invoke custom worker-callbacks defined by the scheduler (lib) */
+ mk_list_foreach(head, &server->sched_worker_callbacks) {
+ wcb = mk_list_entry(head, struct mk_sched_worker_cb, _head);
+ wcb->cb_func(wcb->data);
+ }
+
+ mk_mem_free(thinfo);
+
+ /* init server thread loop */
+ mk_server_worker_loop(server);
+
+ return 0;
+}
+
+/* Create thread which will be listening for incomings requests */
+int mk_sched_launch_thread(struct mk_server *server, pthread_t *tout)
+{
+ pthread_t tid;
+ pthread_attr_t attr;
+ struct mk_sched_thread_conf *thconf;
+
+ server->pth_init = MK_FALSE;
+
+ /*
+ * This lock is used for the 'pth_cond' conditional. Once the worker
+ * thread is ready it will signal the condition.
+ */
+ pthread_mutex_lock(&server->pth_mutex);
+
+ /* Thread data */
+ thconf = mk_mem_alloc_z(sizeof(struct mk_sched_thread_conf));
+ thconf->server = server;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ if (pthread_create(&tid, &attr, mk_sched_launch_worker_loop,
+ (void *) thconf) != 0) {
+ mk_libc_error("pthread_create");
+ pthread_mutex_unlock(&server->pth_mutex);
+ return -1;
+ }
+
+ *tout = tid;
+
+ /* Block until the child thread is ready */
+ while (!server->pth_init) {
+ pthread_cond_wait(&server->pth_cond, &server->pth_mutex);
+ }
+
+ pthread_mutex_unlock(&server->pth_mutex);
+
+ return 0;
+}
+
+/*
+ * The scheduler nodes are an array of struct mk_sched_worker type,
+ * each worker thread belongs to a scheduler node, on this function we
+ * allocate a scheduler node per number of workers defined.
+ */
+int mk_sched_init(struct mk_server *server)
+{
+ int size;
+ struct mk_sched_ctx *ctx;
+
+ ctx = mk_mem_alloc_z(sizeof(struct mk_sched_ctx));
+ if (!ctx) {
+ mk_libc_error("malloc");
+ return -1;
+ }
+
+ size = (sizeof(struct mk_sched_worker) * server->workers);
+ ctx->workers = mk_mem_alloc(size);
+ if (!ctx->workers) {
+ mk_libc_error("malloc");
+ mk_mem_free(ctx);
+ return -1;
+ }
+ memset(ctx->workers, '\0', size);
+
+ /* Initialize helpers */
+ pthread_mutex_init(&server->pth_mutex, NULL);
+ pthread_cond_init(&server->pth_cond, NULL);
+ server->pth_init = MK_FALSE;
+
+ /* Map context into server context */
+ server->sched_ctx = ctx;
+
+ /* The mk_thread_prepare call was replaced by mk_http_thread_initialize_tls
+ * which is called earlier.
+ */
+
+ return 0;
+}
+
+int mk_sched_exit(struct mk_server *server)
+{
+ struct mk_sched_ctx *ctx;
+
+ ctx = server->sched_ctx;
+ mk_sched_worker_cb_free(server);
+ mk_mem_free(ctx->workers);
+ mk_mem_free(ctx);
+
+ return 0;
+}
+
+void mk_sched_set_request_list(struct rb_root *list)
+{
+ MK_TLS_SET(mk_tls_sched_cs, list);
+}
+
+int mk_sched_remove_client(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ struct mk_event *event;
+
+ /*
+ * Close socket and change status: we must invoke mk_epoll_del()
+ * because when the socket is closed is cleaned from the queue by
+ * the Kernel at its leisure, and we may get false events if we rely
+ * on that.
+ */
+ event = &conn->event;
+ MK_TRACE("[FD %i] Scheduler remove", event->fd);
+
+ mk_event_del(sched->loop, event);
+
+ /* Invoke plugins in stage 50 */
+ mk_plugin_stage_run_50(event->fd, server);
+
+ sched->closed_connections++;
+
+ /* Unlink from the red-black tree */
+ //rb_erase(&conn->_rb_head, &sched->rb_queue);
+ mk_sched_conn_timeout_del(conn);
+
+ /* Close at network layer level */
+ conn->net->close(conn->net->plugin, event->fd);
+
+ /* Release and return */
+ mk_channel_clean(&conn->channel);
+ mk_sched_event_free(&conn->event);
+ conn->status = MK_SCHED_CONN_CLOSED;
+
+ MK_LT_SCHED(remote_fd, "DELETE_CLIENT");
+ return 0;
+}
+
+/* FIXME: nobody is using this function, check back later */
+struct mk_sched_conn *mk_sched_get_connection(struct mk_sched_worker *sched,
+ int remote_fd)
+{
+ (void) sched;
+ (void) remote_fd;
+ return NULL;
+}
+
+/*
+ * For a given connection number, remove all resources associated: it can be
+ * used on any context such as: timeout, I/O errors, request finished,
+ * exceptions, etc.
+ */
+int mk_sched_drop_connection(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ mk_sched_threads_destroy_conn(sched, conn);
+ return mk_sched_remove_client(conn, sched, server);
+}
+
+int mk_sched_check_timeouts(struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ int client_timeout;
+ struct mk_sched_conn *conn;
+ struct mk_list *head;
+ struct mk_list *temp;
+
+ /* PENDING CONN TIMEOUT */
+ mk_list_foreach_safe(head, temp, &sched->timeout_queue) {
+ conn = mk_list_entry(head, struct mk_sched_conn, timeout_head);
+ if (conn->event.type & MK_EVENT_IDLE) {
+ continue;
+ }
+
+ client_timeout = conn->arrive_time + server->timeout;
+
+ /* Check timeout */
+ if (client_timeout <= server->clock_context->log_current_utime) {
+ MK_TRACE("Scheduler, closing fd %i due TIMEOUT",
+ conn->event.fd);
+ MK_LT_SCHED(conn->event.fd, "TIMEOUT_CONN_PENDING");
+ conn->protocol->cb_close(conn, sched, MK_SCHED_CONN_TIMEOUT,
+ server);
+ mk_sched_drop_connection(conn, sched, server);
+ }
+ }
+
+ return 0;
+}
+
+static int sched_thread_cleanup(struct mk_sched_worker *sched,
+ struct mk_list *list)
+{
+ int c = 0;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_http_thread *mth;
+ (void) sched;
+
+ mk_list_foreach_safe(head, tmp, list) {
+ mth = mk_list_entry(head, struct mk_http_thread, _head);
+ mk_http_thread_destroy(mth);
+ c++;
+ }
+
+ return c;
+
+}
+
+int mk_sched_threads_purge(struct mk_sched_worker *sched)
+{
+ int c = 0;
+
+ c = sched_thread_cleanup(sched, &sched->threads_purge);
+ return c;
+}
+
+int mk_sched_threads_destroy_all(struct mk_sched_worker *sched)
+{
+ int c = 0;
+
+ c = sched_thread_cleanup(sched, &sched->threads_purge);
+ c += sched_thread_cleanup(sched, &sched->threads);
+
+ return c;
+}
+
+/*
+ * Destroy the thread contexts associated to the particular
+ * connection.
+ *
+ * Return the number of contexts destroyed.
+ */
+int mk_sched_threads_destroy_conn(struct mk_sched_worker *sched,
+ struct mk_sched_conn *conn)
+{
+ int c = 0;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_http_thread *mth;
+ (void) sched;
+
+ mk_list_foreach_safe(head, tmp, &sched->threads) {
+ mth = mk_list_entry(head, struct mk_http_thread, _head);
+ if (mth->session->conn == conn) {
+ mk_http_thread_destroy(mth);
+ c++;
+ }
+ }
+ return c;
+}
+
+/*
+ * Scheduler events handler: lookup for event handler and invoke
+ * proper callbacks.
+ */
+int mk_sched_event_read(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ int ret = 0;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("[FD %i] Connection Handler / read", conn->event.fd);
+#endif
+
+ /*
+ * When the event loop notify that there is some readable information
+ * from the socket, we need to invoke the protocol handler associated
+ * to this connection and also pass as a reference the 'read()' function
+ * that handle 'read I/O' operations, e.g:
+ *
+ * - plain sockets through liana will use just read(2)
+ * - ssl though mbedtls should use mk_mbedtls_read(..)
+ */
+ ret = conn->protocol->cb_read(conn, sched, server);
+ if (ret == -1) {
+ if (errno == EAGAIN) {
+ MK_TRACE("[FD %i] EAGAIN: need to read more data", conn->event.fd);
+ return 1;
+ }
+ return -1;
+ }
+
+ return ret;
+}
+
+int mk_sched_event_write(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ struct mk_server *server)
+{
+ int ret = -1;
+ size_t count;
+ struct mk_event *event;
+
+ MK_TRACE("[FD %i] Connection Handler / write", conn->event.fd);
+
+ ret = mk_channel_write(&conn->channel, &count);
+ if (ret == MK_CHANNEL_FLUSH || ret == MK_CHANNEL_BUSY) {
+ return 0;
+ }
+ else if (ret == MK_CHANNEL_DONE || ret == MK_CHANNEL_EMPTY) {
+ if (conn->protocol->cb_done) {
+ ret = conn->protocol->cb_done(conn, sched, server);
+ }
+ if (ret == -1) {
+ return -1;
+ }
+ else if (ret == 0) {
+ event = &conn->event;
+ mk_event_add(sched->loop, event->fd,
+ MK_EVENT_CONNECTION,
+ MK_EVENT_READ,
+ conn);
+ }
+ return 0;
+ }
+ else if (ret & MK_CHANNEL_ERROR) {
+ return -1;
+ }
+
+ /* avoid to make gcc cry :_( */
+ return -1;
+}
+
+int mk_sched_event_close(struct mk_sched_conn *conn,
+ struct mk_sched_worker *sched,
+ int type, struct mk_server *server)
+{
+ MK_TRACE("[FD %i] Connection Handler, closed", conn->event.fd);
+ mk_event_del(sched->loop, &conn->event);
+
+ if (type != MK_EP_SOCKET_DONE) {
+ conn->protocol->cb_close(conn, sched, type, server);
+ }
+ /*
+ * Remove the socket from the scheduler and make sure
+ * to disable all notifications.
+ */
+ mk_sched_drop_connection(conn, sched, server);
+ return 0;
+}
+
+void mk_sched_event_free(struct mk_event *event)
+{
+ struct mk_sched_worker *sched = mk_sched_get_thread_conf();
+
+ if ((event->type & MK_EVENT_IDLE) != 0) {
+ return;
+ }
+
+ event->type |= MK_EVENT_IDLE;
+ mk_list_add(&event->_head, &sched->event_free_queue);
+}
+
+/* Register a new callback function to invoke when a worker is created */
+int mk_sched_worker_cb_add(struct mk_server *server,
+ void (*cb_func) (void *),
+ void *data)
+{
+ struct mk_sched_worker_cb *wcb;
+
+ wcb = mk_mem_alloc(sizeof(struct mk_sched_worker_cb));
+ if (!wcb) {
+ return -1;
+ }
+
+ wcb->cb_func = cb_func;
+ wcb->data = data;
+ mk_list_add(&wcb->_head, &server->sched_worker_callbacks);
+ return 0;
+}
+
+void mk_sched_worker_cb_free(struct mk_server *server)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_sched_worker_cb *wcb;
+
+ mk_list_foreach_safe(head, tmp, &server->sched_worker_callbacks) {
+ wcb = mk_list_entry(head, struct mk_sched_worker_cb, _head);
+ mk_list_del(&wcb->_head);
+ mk_mem_free(wcb);
+ }
+}
+
+int mk_sched_send_signal(struct mk_sched_worker *worker, uint64_t val)
+{
+ ssize_t n;
+
+ /* When using libevent _mk_event_channel_create creates a unix socket
+ * instead of a pipe and windows doesn't us calling read / write on a
+ * socket instead of recv / send
+ */
+
+#ifdef _WIN32
+ n = send(worker->signal_channel_w, &val, sizeof(uint64_t), 0);
+#else
+ n = write(worker->signal_channel_w, &val, sizeof(uint64_t));
+#endif
+
+ if (n < 0) {
+ mk_libc_error("write");
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int mk_sched_broadcast_signal(struct mk_server *server, uint64_t val)
+{
+ int i;
+ int count = 0;
+ struct mk_sched_ctx *ctx;
+ struct mk_sched_worker *worker;
+
+ ctx = server->sched_ctx;
+ for (i = 0; i < server->workers; i++) {
+ worker = &ctx->workers[i];
+
+ count += mk_sched_send_signal(worker, val);
+ }
+
+ return count;
+}
+
+/*
+ * Wait for all workers to finish: this function assumes that previously a
+ * MK_SCHED_SIGNAL_FREE_ALL was sent to the worker channels.
+ */
+int mk_sched_workers_join(struct mk_server *server)
+{
+ int i;
+ int count = 0;
+ struct mk_sched_ctx *ctx;
+ struct mk_sched_worker *worker;
+
+ ctx = server->sched_ctx;
+ for (i = 0; i < server->workers; i++) {
+ worker = &ctx->workers[i];
+ pthread_join(worker->tid, NULL);
+ count++;
+ }
+
+ return count;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_server.c b/fluent-bit/lib/monkey/mk_server/mk_server.c
new file mode 100644
index 000000000..a84ef4485
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_server.c
@@ -0,0 +1,679 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_info.h>
+#include <monkey/monkey.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_server.h>
+#include <monkey/mk_server_tls.h>
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_fifo.h>
+#include <monkey/mk_http_thread.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+pthread_key_t mk_server_fifo_key;
+
+static int mk_server_lib_notify_event_loop_break(struct mk_sched_worker *sched);
+
+/* Return the number of clients that can be attended */
+unsigned int mk_server_capacity(struct mk_server *server)
+{
+ int ret;
+ int cur;
+
+#ifndef _WIN32
+ struct rlimit lim;
+
+ /* Limit by system */
+ getrlimit(RLIMIT_NOFILE, &lim);
+ cur = lim.rlim_cur;
+
+ if (server->fd_limit > cur) {
+ lim.rlim_cur = server->fd_limit;
+ lim.rlim_max = server->fd_limit;
+
+ ret = setrlimit(RLIMIT_NOFILE, &lim);
+ if (ret == -1) {
+ mk_warn("Could not increase FDLimit to %i.", server->fd_limit);
+ }
+ else {
+ cur = server->fd_limit;
+ }
+ }
+ else if (server->fd_limit > 0) {
+ cur = server->fd_limit;
+ }
+
+#else
+ ret = 0;
+ cur = INT_MAX;
+
+ /* This is not the right way to plug this, according to raymond chen the only limit
+ * to fd count is free memory in their winsock provider and there are no other limits
+ * that I know of but I should still look for a more elegant solution. (even if it
+ * was just ignoring the server_capacity limit in scheduler.c: _next_target)
+ */
+#endif
+
+ return cur;
+}
+
+static inline
+struct mk_sched_conn *mk_server_listen_handler(struct mk_sched_worker *sched,
+ void *data,
+ struct mk_server *server)
+{
+ int ret;
+ int client_fd = -1;
+ struct mk_sched_conn *conn;
+ struct mk_server_listen *listener = data;
+
+ client_fd = mk_socket_accept(listener->server_fd);
+ if (mk_unlikely(client_fd == -1)) {
+ MK_TRACE("[server] Accept connection failed: %s", strerror(errno));
+ goto error;
+ }
+
+ conn = mk_sched_add_connection(client_fd, listener, sched, server);
+ if (mk_unlikely(!conn)) {
+ goto error;
+ }
+
+ ret = mk_event_add(sched->loop, client_fd,
+ MK_EVENT_CONNECTION, MK_EVENT_READ, conn);
+ if (mk_unlikely(ret != 0)) {
+ mk_err("[server] Error registering file descriptor: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ sched->accepted_connections++;
+ MK_TRACE("[server] New connection arrived: FD %i", client_fd);
+ return conn;
+
+error:
+ if (client_fd != -1) {
+ listener->network->network->close(listener->network, client_fd);
+ }
+
+ return NULL;
+}
+
+void mk_server_listen_free()
+{
+ struct mk_list *list;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_server_listen *listener;
+
+ list = MK_TLS_GET(mk_tls_server_listen);
+ mk_list_foreach_safe(head, tmp, list) {
+ listener = mk_list_entry(head, struct mk_server_listen, _head);
+ mk_list_del(&listener->_head);
+ mk_mem_free(listener);
+ }
+}
+
+void mk_server_listen_exit(struct mk_list *list)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_server_listen *listen;
+
+ if (!list) {
+ return;
+ }
+
+ mk_list_foreach_safe(head, tmp, list) {
+ listen = mk_list_entry(head, struct mk_server_listen, _head);
+ mk_event_closesocket(listen->server_fd);
+ mk_list_del(&listen->_head);
+ mk_mem_free(listen);
+ }
+
+ mk_mem_free(list);
+}
+
+struct mk_list *mk_server_listen_init(struct mk_server *server)
+{
+ int server_fd;
+ int reuse_port = MK_FALSE;
+ struct mk_list *head;
+ struct mk_list *listeners;
+ struct mk_event *event;
+ struct mk_server_listen *listener;
+ struct mk_sched_handler *protocol;
+ struct mk_plugin *plugin;
+ struct mk_config_listener *listen;
+
+ if (!server) {
+ goto error;
+ }
+
+ listeners = mk_mem_alloc(sizeof(struct mk_list));
+ mk_list_init(listeners);
+
+ if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
+ reuse_port = MK_TRUE;
+ }
+
+ mk_list_foreach(head, &server->listeners) {
+ listen = mk_list_entry(head, struct mk_config_listener, _head);
+
+ server_fd = mk_socket_server(listen->port,
+ listen->address,
+ reuse_port,
+ server);
+ if (server_fd >= 0) {
+ if (mk_socket_set_tcp_defer_accept(server_fd) != 0) {
+#if defined (__linux__)
+ mk_warn("[server] Could not set TCP_DEFER_ACCEPT");
+#endif
+ }
+
+ listener = mk_mem_alloc_z(sizeof(struct mk_server_listen));
+
+ /* configure the internal event_state */
+ event = &listener->event;
+ event->fd = server_fd;
+ event->type = MK_EVENT_LISTENER;
+ event->mask = MK_EVENT_EMPTY;
+ event->status = MK_EVENT_NONE;
+
+ /* continue with listener setup and linking */
+ listener->server_fd = server_fd;
+ listener->listen = listen;
+
+ if (listen->flags & MK_CAP_HTTP) {
+ protocol = mk_sched_handler_cap(MK_CAP_HTTP);
+ if (!protocol) {
+ mk_err("HTTP protocol not supported");
+ exit(EXIT_FAILURE);
+ }
+ listener->protocol = protocol;
+ }
+
+#ifdef MK_HAVE_HTTP2
+ if (listen->flags & MK_CAP_HTTP2) {
+ protocol = mk_sched_handler_cap(MK_CAP_HTTP2);
+ if (!protocol) {
+ mk_err("HTTP2 protocol not supported");
+ exit(EXIT_FAILURE);
+ }
+ listener->protocol = protocol;
+ }
+#endif
+ listener->network = mk_plugin_cap(MK_CAP_SOCK_PLAIN, server);
+
+ if (listen->flags & MK_CAP_SOCK_TLS) {
+ plugin = mk_plugin_cap(MK_CAP_SOCK_TLS, server);
+ if (!plugin) {
+ mk_err("SSL/TLS not supported");
+ exit(EXIT_FAILURE);
+ }
+ listener->network = plugin;
+ }
+
+ mk_list_add(&listener->_head, listeners);
+ }
+ else {
+ mk_err("[server] Failed to bind server socket to %s:%s.",
+ listen->address,
+ listen->port);
+ return NULL;
+ }
+ }
+
+ if (reuse_port == MK_TRUE) {
+ MK_TLS_SET(mk_tls_server_listen, listeners);
+ }
+
+ return listeners;
+
+error:
+ return NULL;
+}
+
+/* Here we launch the worker threads to attend clients */
+void mk_server_launch_workers(struct mk_server *server)
+{
+ int i;
+ pthread_t skip;
+
+ /* Launch workers */
+ for (i = 0; i < server->workers; i++) {
+ /* Spawn the thread */
+ mk_sched_launch_thread(server, &skip);
+ }
+}
+
+/*
+ * When using the FIFO interface, this function get's the FIFO worker
+ * context and register the pipe file descriptor into the main event
+ * loop.
+ *
+ * note: this function is invoked by each worker thread.
+ */
+static int mk_server_fifo_worker_setup(struct mk_event_loop *evl)
+{
+ int ret;
+ struct mk_fifo_worker *fw;
+
+ fw = pthread_getspecific(mk_server_fifo_key);
+ if (!fw) {
+ return -1;
+ }
+
+ ret = mk_event_add(evl, fw->channel[0],
+ MK_EVENT_FIFO, MK_EVENT_READ,
+ fw);
+ if (ret != 0) {
+ mk_err("[server] Error registering fifo worker channel: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * The loop_balancer() runs in the main process context and is considered
+ * the old-fashion way to handle connections. It have an event queue waiting
+ * for connections, once one arrives, it decides which worker (thread) may
+ * handle it registering the accept(2)ed file descriptor on the worker
+ * event monitored queue.
+ */
+void mk_server_loop_balancer(struct mk_server *server)
+{
+ size_t bytes;
+ uint64_t val;
+ int operation_flag;
+ struct mk_list *head;
+ struct mk_list *listeners;
+ struct mk_server_listen *listener;
+ struct mk_event *event;
+ struct mk_event_loop *evl;
+ struct mk_sched_worker *sched;
+ struct mk_event management_event;
+
+ /* Init the listeners */
+ listeners = mk_server_listen_init(server);
+ if (!listeners) {
+ mk_err("Failed to initialize listen sockets.");
+ return;
+ }
+
+ /* Create an event loop context */
+ evl = mk_event_loop_create(MK_EVENT_QUEUE_SIZE);
+ if (!evl) {
+ mk_err("Could not initialize event loop");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Register the listeners */
+ mk_list_foreach(head, listeners) {
+ listener = mk_list_entry(head, struct mk_server_listen, _head);
+ mk_event_add(evl, listener->server_fd,
+ MK_EVENT_LISTENER, MK_EVENT_READ,
+ listener);
+ }
+
+ memset(&management_event, 0, sizeof(struct mk_event));
+
+ mk_event_add(evl,
+ server->lib_ch_manager[0],
+ MK_EVENT_NOTIFICATION,
+ MK_EVENT_READ,
+ &management_event);
+
+ operation_flag = MK_TRUE;
+ while (operation_flag) {
+ mk_event_wait(evl);
+ mk_event_foreach(event, evl) {
+ if (event->mask & MK_EVENT_READ) {
+ /* This signal is sent by mk_stop and both this and
+ * mk_lib_worker are expecting it.
+ */
+ if (server->lib_ch_manager[0] == event->fd) {
+#ifdef _WIN32
+ bytes = recv(event->fd, &val, sizeof(uint64_t), MSG_WAITALL);
+#else
+ bytes = read(event->fd, &val, sizeof(uint64_t));
+#endif
+
+ if (bytes <= 0) {
+ return;
+ }
+
+ if (val == MK_SERVER_SIGNAL_STOP) {
+ operation_flag = MK_FALSE;
+
+ break;
+ }
+
+ continue;
+ }
+
+ /*
+ * Accept connection: determinate which thread may work on this
+ * new connection.
+ */
+ sched = mk_sched_next_target(server);
+ if (sched != NULL) {
+ mk_server_listen_handler(sched, event, server);
+
+ mk_server_lib_notify_event_loop_break(sched);
+
+#ifdef MK_HAVE_TRACE
+ int i;
+ struct mk_sched_ctx *ctx = server->sched_ctx;
+
+ for (i = 0; i < server->workers; i++) {
+ MK_TRACE("Worker Status");
+ MK_TRACE(" WID %i / conx = %llu",
+ ctx->workers[i].idx,
+ ctx->workers[i].accepted_connections -
+ ctx->workers[i].closed_connections);
+ }
+#endif
+ }
+ else {
+ mk_warn("[server] Over capacity.");
+ }
+ }
+ else if (event->mask & MK_EVENT_CLOSE) {
+ mk_err("[server] Error on socket %d: %s",
+ event->fd, strerror(errno));
+ }
+ }
+ }
+ mk_event_loop_destroy(evl);
+ mk_server_listen_exit(listeners);
+}
+
+/*
+ * This function is called when the scheduler is running in the REUSEPORT
+ * mode. That means that each worker is listening on shared TCP ports.
+ *
+ * When using shared TCP ports the Kernel decides to which worker the
+ * connection will be assigned.
+ */
+void mk_server_worker_loop(struct mk_server *server)
+{
+ int ret = -1;
+ int timeout_fd;
+ uint64_t val;
+ struct mk_event *event;
+ struct mk_event_loop *evl;
+ struct mk_list *list;
+ struct mk_list *head;
+ struct mk_sched_conn *conn;
+ struct mk_sched_worker *sched;
+ struct mk_server_listen *listener;
+ struct mk_server_timeout *server_timeout;
+
+ /* Get thread conf */
+ sched = mk_sched_get_thread_conf();
+ evl = sched->loop;
+
+ /*
+ * The worker will NOT process any connection until the master
+ * process through mk_server_loop() send us the green light
+ * signal MK_SERVER_SIGNAL_START.
+ */
+ mk_event_wait(evl);
+ mk_event_foreach(event, evl) {
+ if ((event->mask & MK_EVENT_READ) &&
+ event->type == MK_EVENT_NOTIFICATION) {
+ if (event->fd == sched->signal_channel_r) {
+ /* When using libevent _mk_event_channel_create creates a unix socket
+ * instead of a pipe and windows doesn't us calling read / write on a
+ * socket instead of recv / send
+ */
+#ifdef _WIN32
+ ret = recv(event->fd, &val, sizeof(val), MSG_WAITALL);
+#else
+ ret = read(event->fd, &val, sizeof(val));
+#endif
+ if (ret < 0) {
+ mk_libc_error("read");
+ continue;
+ }
+ if (val == MK_SERVER_SIGNAL_START) {
+ MK_TRACE("Worker %i started (SIGNAL_START)", sched->idx);
+ break;
+ }
+ }
+ }
+ }
+
+ if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
+ /* Register listeners */
+ list = MK_TLS_GET(mk_tls_server_listen);
+ mk_list_foreach(head, list) {
+ listener = mk_list_entry(head, struct mk_server_listen, _head);
+ mk_event_add(sched->loop, listener->server_fd,
+ MK_EVENT_LISTENER, MK_EVENT_READ,
+ listener);
+ }
+ }
+
+ /*
+ * If running in library mode, register the FIFO pipe file descriptors
+ * into the main event loop.
+ */
+ if (server->lib_mode == MK_TRUE) {
+ mk_server_fifo_worker_setup(evl);
+ }
+
+ /* create a new timeout file descriptor */
+ server_timeout = mk_mem_alloc_z(sizeof(struct mk_server_timeout));
+ MK_TLS_SET(mk_tls_server_timeout, server_timeout);
+ timeout_fd = mk_event_timeout_create(evl, server->timeout, 0, server_timeout);
+
+ while (1) {
+ mk_event_wait(evl);
+ mk_event_foreach(event, evl) {
+ ret = 0;
+ if (event->type & MK_EVENT_IDLE) {
+ continue;
+ }
+
+ if (event->type == MK_EVENT_CONNECTION) {
+ conn = (struct mk_sched_conn *) event;
+
+ if (event->mask & MK_EVENT_WRITE) {
+ MK_TRACE("[FD %i] Event WRITE", event->fd);
+ ret = mk_sched_event_write(conn, sched, server);
+ }
+
+ if (event->mask & MK_EVENT_READ) {
+ MK_TRACE("[FD %i] Event READ", event->fd);
+ ret = mk_sched_event_read(conn, sched, server);
+ }
+
+
+ if (event->mask & MK_EVENT_CLOSE && ret != -1) {
+ MK_TRACE("[FD %i] Event CLOSE", event->fd);
+ ret = -1;
+ }
+
+ if (ret < 0 && conn->status != MK_SCHED_CONN_CLOSED) {
+ MK_TRACE("[FD %i] Event FORCE CLOSE | ret = %i",
+ event->fd, ret);
+ mk_sched_event_close(conn, sched, MK_EP_SOCKET_CLOSED,
+ server);
+ }
+ }
+ else if (event->type == MK_EVENT_LISTENER) {
+ /*
+ * A new connection have been accepted..or failed, despite
+ * the result, we let the loop continue processing the other
+ * events triggered.
+ */
+ conn = mk_server_listen_handler(sched, event, server);
+ if (conn) {
+ //conn->event.mask = MK_EVENT_READ
+ //goto speed;
+ }
+ continue;
+ }
+ else if (event->type == MK_EVENT_CUSTOM) {
+ /*
+ * We got an event associated to a custom interface, that
+ * means a plugin registered some file descriptor on this
+ * event loop and an event was triggered. We pass the control
+ * to the defined event handler.
+ */
+ event->handler(event);
+ }
+ else if (event->type == MK_EVENT_NOTIFICATION) {
+#ifdef _WIN32
+ ret = recv(event->fd, &val, sizeof(val), MSG_WAITALL);
+#else
+ ret = read(event->fd, &val, sizeof(val));
+#endif
+ if (ret < 0) {
+ mk_libc_error("read");
+ continue;
+ }
+
+ if (event->fd == sched->signal_channel_r) {
+ if (val == MK_SCHED_SIGNAL_DEADBEEF) {
+ //FIXME:mk_sched_sync_counters();
+ continue;
+ }
+ else if (val == MK_SCHED_SIGNAL_FREE_ALL) {
+ if (timeout_fd > 0) {
+ mk_event_timeout_destroy(evl, server_timeout);
+ }
+ mk_mem_free(MK_TLS_GET(mk_tls_server_timeout));
+ mk_server_listen_exit(sched->listeners);
+ mk_event_loop_destroy(evl);
+ mk_sched_worker_free(server);
+ return;
+ }
+ else if (val == MK_SCHED_SIGNAL_EVENT_LOOP_BREAK) {
+ /* NOTE: This is just a notification that's sent to break out
+ * of the libevent loop in windows after accepting a new
+ * client
+ */
+ MK_TRACE("New client accepted, awesome!");
+ }
+ }
+ else if (event->fd == timeout_fd) {
+ mk_sched_check_timeouts(sched, server);
+ }
+ continue;
+ }
+ else if (event->type == MK_EVENT_THREAD) {
+ mk_http_thread_event(event);
+ continue;
+ }
+ else if (event->type == MK_EVENT_FIFO) {
+ mk_fifo_worker_read(event);
+ continue;
+ }
+ }
+ mk_sched_threads_purge(sched);
+ mk_sched_event_free_all(sched);
+ }
+}
+
+static int mk_server_lib_notify_event_loop_break(struct mk_sched_worker *sched)
+{
+ uint64_t val;
+
+ /* Check the channel is valid (enabled by library mode) */
+ if (sched->signal_channel_w <= 0) {
+ return -1;
+ }
+
+ val = MK_SCHED_SIGNAL_EVENT_LOOP_BREAK;
+
+#ifdef _WIN32
+ return send(sched->signal_channel_w, &val, sizeof(uint64_t), 0);
+#else
+ return write(sched->signal_channel_w, &val, sizeof(uint64_t));
+#endif
+}
+
+static int mk_server_lib_notify_started(struct mk_server *server)
+{
+ uint64_t val;
+
+ /* Check the channel is valid (enabled by library mode) */
+ if (server->lib_ch_start[1] <= 0) {
+ return -1;
+ }
+
+ val = MK_SERVER_SIGNAL_START;
+
+#ifdef _WIN32
+ return send(server->lib_ch_start[1], &val, sizeof(uint64_t), 0);
+#else
+ return write(server->lib_ch_start[1], &val, sizeof(uint64_t));
+#endif
+}
+
+void mk_server_loop(struct mk_server *server)
+{
+ uint64_t val;
+
+ /* Rename worker */
+ mk_utils_worker_rename("monkey: server");
+
+ if (server->lib_mode == MK_FALSE) {
+ mk_info("HTTP Server started");
+ }
+
+ /* Wake up workers */
+ val = MK_SERVER_SIGNAL_START;
+ mk_sched_broadcast_signal(server, val);
+
+ /* Signal lib caller (if any) */
+ mk_server_lib_notify_started(server);
+
+ /*
+ * When using REUSEPORT mode on the Scheduler, we need to signal
+ * them so they can start processing connections.
+ */
+ if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
+ /* do thing :) */
+ }
+ else {
+ /* FIXME!: this old mode needs some checks on library mode */
+ mk_server_loop_balancer(server);
+ }
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_socket.c b/fluent-bit/lib/monkey/mk_server/mk_socket.c
new file mode 100644
index 000000000..0277ab1c3
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_socket.c
@@ -0,0 +1,402 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#ifndef SOL_TCP
+#define SOL_TCP IPPROTO_TCP
+#endif
+
+#include <monkey/monkey.h>
+#include <monkey/mk_info.h>
+#include <monkey/mk_socket.h>
+#include <monkey/mk_kernel.h>
+#include <monkey/mk_net.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_plugin.h>
+
+#include <time.h>
+
+/*
+ * Example from:
+ * http://www.baus.net/on-tcp_cork
+ */
+int mk_socket_set_cork_flag(int fd, int state)
+{
+ MK_TRACE("Socket, set Cork Flag FD %i to %s", fd, (state ? "ON" : "OFF"));
+
+#if defined (TCP_CORK)
+ return setsockopt(fd, SOL_TCP, TCP_CORK, &state, sizeof(state));
+#elif defined (TCP_NOPUSH)
+ return setsockopt(fd, SOL_SOCKET, TCP_NOPUSH, &state, sizeof(state));
+#endif
+
+ return 0;
+}
+
+int mk_socket_set_nonblocking(int sockfd)
+{
+
+ MK_TRACE("Socket, set FD %i to non-blocking", sockfd);
+
+#ifdef _WIN32
+ u_long flags;
+
+ flags = 0;
+ if (SOCKET_ERROR == ioctlsocket(sockfd, FIONBIO, &flags)) {
+ mk_err("Can't set to non-blocking mode socket %i", sockfd);
+ return -1;
+ }
+#else
+ if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK) == -1) {
+ mk_err("Can't set to non-blocking mode socket %i", sockfd);
+ return -1;
+ }
+ fcntl(sockfd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ return 0;
+}
+
+/*
+ * Enable the TCP_FASTOPEN feature for server side implemented in
+ * Linux Kernel >= 3.7, for more details read here:
+ *
+ * TCP Fast Open: expediting web services: http://lwn.net/Articles/508865/
+ */
+int mk_socket_set_tcp_fastopen(int sockfd)
+{
+#if defined (__linux__)
+ int qlen = 5;
+ return setsockopt(sockfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
+#endif
+
+ (void) sockfd;
+ return -1;
+}
+
+int mk_socket_set_tcp_nodelay(int sockfd)
+{
+ int on = 1;
+
+ return setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &on, sizeof(on));
+}
+
+int mk_socket_set_tcp_defer_accept(int sockfd)
+{
+#if defined (__linux__)
+ int timeout = 0;
+
+ return setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int));
+#else
+ (void) sockfd;
+ return -1;
+#endif
+}
+
+int mk_socket_set_tcp_reuseport(int sockfd)
+{
+ int on = 1;
+ return setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
+}
+
+int mk_socket_create(int domain, int type, int protocol)
+{
+ int fd;
+
+#ifdef SOCK_CLOEXEC
+ fd = socket(domain, type | SOCK_CLOEXEC, protocol);
+#else
+ fd = socket(domain, type, protocol);
+
+#ifndef _WIN32
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+#endif
+
+ if (fd == -1) {
+ mk_libc_error("socket");
+ return -1;
+ }
+
+ return fd;
+}
+
+int mk_socket_open(char *path, int async)
+{
+ int ret;
+ int socket_fd;
+ struct sockaddr_un address;
+
+ socket_fd = mk_socket_create(PF_UNIX, SOCK_STREAM, 0);
+ if (socket_fd == -1) {
+ return -1;
+ }
+
+ memset(&address, '\0', sizeof(struct sockaddr_un));
+ address.sun_family = AF_UNIX;
+ snprintf(address.sun_path, sizeof(address.sun_path), "%s", path);
+
+ if (async == MK_TRUE) {
+ mk_socket_set_nonblocking(socket_fd);
+ }
+
+ ret = connect(socket_fd, (struct sockaddr *) &address,
+ sizeof(struct sockaddr_un));
+ if (ret == -1) {
+ if (errno == EINPROGRESS) {
+ return socket_fd;
+ }
+ else {
+#ifdef MK_HAVE_TRACE
+ mk_libc_error("connect");
+#endif
+ close(socket_fd);
+ return -1;
+ }
+ }
+
+ return socket_fd;
+}
+
+
+int mk_socket_connect(char *host, int port, int async)
+{
+ int ret;
+ int socket_fd = -1;
+ char *port_str = 0;
+ unsigned long len;
+ struct addrinfo hints;
+ struct addrinfo *res, *rp;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ mk_string_build(&port_str, &len, "%d", port);
+
+ ret = getaddrinfo(host, port_str, &hints, &res);
+ mk_mem_free(port_str);
+ if(ret != 0) {
+ mk_err("Can't get addr info: %s", gai_strerror(ret));
+ return -1;
+ }
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+ socket_fd = mk_socket_create(rp->ai_family,
+ rp->ai_socktype, rp->ai_protocol);
+
+ if (socket_fd == -1) {
+ mk_warn("Error creating client socket, retrying");
+ continue;
+ }
+
+ if (async == MK_TRUE) {
+ mk_socket_set_nonblocking(socket_fd);
+ }
+
+ ret = connect(socket_fd,
+ (struct sockaddr *) rp->ai_addr, rp->ai_addrlen);
+ if (ret == -1) {
+ if (errno == EINPROGRESS) {
+ break;
+ }
+ else {
+ printf("%s", strerror(errno));
+ perror("connect");
+ exit(1);
+ close(socket_fd);
+ continue;
+ }
+ }
+ break;
+ }
+ freeaddrinfo(res);
+
+ if (rp == NULL)
+ return -1;
+
+ return socket_fd;
+}
+
+int mk_socket_reset(int socket)
+{
+ int status = 1;
+
+ if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int)) == -1) {
+ mk_libc_error("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+int mk_socket_bind(int socket_fd, const struct sockaddr *addr,
+ socklen_t addrlen, int backlog, struct mk_server *server)
+{
+ int ret;
+
+ ret = bind(socket_fd, addr, addrlen);
+ if( ret == -1 ) {
+ mk_warn("Error binding socket");
+ return ret;
+ }
+
+ /*
+ * Enable TCP_FASTOPEN by default: if for some reason this call fail,
+ * it will not affect the behavior of the server, in order to succeed,
+ * Monkey must be running in a Linux system with Kernel >= 3.7 and the
+ * tcp_fastopen flag enabled here:
+ *
+ * # cat /proc/sys/net/ipv4/tcp_fastopen
+ *
+ * To enable this feature just do:
+ *
+ * # echo 1 > /proc/sys/net/ipv4/tcp_fastopen
+ */
+ if (server->kernel_features & MK_KERNEL_TCP_FASTOPEN) {
+ ret = mk_socket_set_tcp_fastopen(socket_fd);
+ if (ret == -1) {
+ mk_warn("Could not set TCP_FASTOPEN");
+ }
+ }
+
+ ret = listen(socket_fd, backlog);
+ if(ret == -1 ) {
+ return -1;
+ }
+
+ return ret;
+}
+
+/* Just IPv4 for now... */
+int mk_socket_server(char *port, char *listen_addr,
+ int reuse_port, struct mk_server *server)
+{
+ int ret;
+ int socket_fd = -1;
+ struct addrinfo hints;
+ struct addrinfo *res, *rp;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ mk_net_init();
+
+ ret = getaddrinfo(listen_addr, port, &hints, &res);
+ if(ret != 0) {
+ mk_err("Can't get addr info: %s", gai_strerror(ret));
+ return -1;
+ }
+
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+ socket_fd = mk_socket_create(rp->ai_family,
+ rp->ai_socktype, rp->ai_protocol);
+ if (socket_fd == -1) {
+ mk_warn("Error creating server socket, retrying");
+ continue;
+ }
+
+ ret = mk_socket_set_tcp_nodelay(socket_fd);
+ if (ret == -1) {
+ mk_warn("Could not set TCP_NODELAY");
+ }
+
+ mk_socket_reset(socket_fd);
+
+ /* Check if reuse port can be enabled on this socket */
+ if (reuse_port == MK_TRUE &&
+ (server->kernel_features & MK_KERNEL_SO_REUSEPORT)) {
+ ret = mk_socket_set_tcp_reuseport(socket_fd);
+ if (ret == -1) {
+ mk_warn("Could not use SO_REUSEPORT, using fair balancing mode");
+ server->scheduler_mode = MK_SCHEDULER_FAIR_BALANCING;
+ }
+ }
+
+ ret = mk_socket_bind(socket_fd, rp->ai_addr, rp->ai_addrlen,
+ MK_SOMAXCONN, server);
+ if(ret == -1) {
+ mk_err("Cannot listen on %s:%s", listen_addr, port);
+ freeaddrinfo(res);
+ return -1;
+ }
+ break;
+ }
+ freeaddrinfo(res);
+
+ if (rp == NULL)
+ return -1;
+
+ return socket_fd;
+}
+
+int mk_socket_ip_str(int socket_fd, char **buf, int size, unsigned long *len)
+{
+ int ret;
+ struct sockaddr_storage addr;
+ socklen_t s_len = sizeof(addr);
+
+ ret = getpeername(socket_fd, (struct sockaddr *) &addr, &s_len);
+
+ if (mk_unlikely(ret == -1)) {
+ MK_TRACE("[FD %i] Can't get addr for this socket", socket_fd);
+ return -1;
+ }
+
+ errno = 0;
+
+ if(addr.ss_family == AF_INET) {
+ if((inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr,
+ *buf, size)) == NULL) {
+ mk_warn("mk_socket_ip_str: Can't get the IP text form (%i)", errno);
+ return -1;
+ }
+ }
+ else if(addr.ss_family == AF_INET6) {
+ if((inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+ *buf, size)) == NULL) {
+ mk_warn("mk_socket_ip_str: Can't get the IP text form (%i)", errno);
+ return -1;
+ }
+ }
+
+ *len = strlen(*buf);
+ return 0;
+}
+
+int mk_socket_accept(int server_fd)
+{
+ int remote_fd;
+
+ struct sockaddr sock_addr;
+ socklen_t socket_size = sizeof(struct sockaddr);
+
+#ifdef MK_HAVE_ACCEPT4
+ remote_fd = accept4(server_fd, &sock_addr, &socket_size,
+ SOCK_NONBLOCK | SOCK_CLOEXEC);
+#else
+ remote_fd = accept(server_fd, &sock_addr, &socket_size);
+ mk_socket_set_nonblocking(remote_fd);
+#endif
+
+ return remote_fd;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_stream.c b/fluent-bit/lib/monkey/mk_server/mk_stream.c
new file mode 100644
index 000000000..df0f1b0b8
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_stream.c
@@ -0,0 +1,338 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_stream.h>
+#include <assert.h>
+
+/* Create a new channel */
+struct mk_channel *mk_channel_new(int type, int fd)
+{
+ struct mk_channel *channel;
+
+ channel = mk_mem_alloc(sizeof(struct mk_channel));
+ if (!channel) {
+ return NULL;
+ }
+ channel->type = type;
+ channel->fd = fd;
+ channel->status = MK_CHANNEL_OK;
+ mk_list_init(&channel->streams);
+
+ return channel;
+}
+
+int mk_channel_release(struct mk_channel *channel)
+{
+ mk_mem_free(channel);
+ return 0;
+}
+
+static inline size_t channel_write_in_file(struct mk_channel *channel,
+ struct mk_stream_input *in)
+{
+ ssize_t bytes = 0;
+
+ MK_TRACE("[CH %i] STREAM_FILE [fd=%i], bytes=%lu",
+ channel->fd, in->fd, in->bytes_total);
+
+ /* Direct write */
+ bytes = mk_sched_conn_sendfile(channel,
+ in->fd,
+ &in->bytes_offset,
+ in->bytes_total
+ );
+ MK_TRACE("[CH=%d] [FD=%i] WRITE STREAM FILE: %lu bytes",
+ channel->fd, in->fd, bytes);
+
+ return bytes;
+}
+
+size_t mk_stream_size(struct mk_stream *stream)
+{
+ return (stream->bytes_total - stream->bytes_offset);
+}
+
+/*
+ * It 'intent' to write a few streams over the channel and alter the
+ * channel notification side if required: READ -> WRITE.
+ */
+int mk_channel_flush(struct mk_channel *channel)
+{
+ int ret = 0;
+ size_t count = 0;
+ size_t total = 0;
+ uint32_t stop = (MK_CHANNEL_DONE | MK_CHANNEL_ERROR | MK_CHANNEL_EMPTY);
+
+ do {
+ ret = mk_channel_write(channel, &count);
+ total += count;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("Channel flush: %d bytes", count);
+ if (ret & MK_CHANNEL_DONE) {
+ MK_TRACE("Channel was empty");
+ }
+ if (ret & MK_CHANNEL_ERROR) {
+ MK_TRACE("Channel error");
+ }
+ if (ret & MK_CHANNEL_EMPTY) {
+ MK_TRACE("Channel empty");
+ }
+#endif
+ } while (total <= 4096 && ((ret & stop) == 0));
+
+ if (ret == MK_CHANNEL_DONE) {
+ MK_TRACE("Channel done");
+ return ret;
+ }
+ else if (ret & (MK_CHANNEL_FLUSH | MK_CHANNEL_BUSY)) {
+ MK_TRACE("Channel FLUSH | BUSY");
+ if ((channel->event->mask & MK_EVENT_WRITE) == 0) {
+ mk_event_add(mk_sched_loop(),
+ channel->fd,
+ MK_EVENT_CONNECTION,
+ MK_EVENT_WRITE,
+ channel->event);
+ }
+ }
+
+ return ret;
+}
+
+int mk_stream_in_release(struct mk_stream_input *in)
+{
+ if (in->cb_finished) {
+ in->cb_finished(in);
+ }
+
+ mk_stream_input_unlink(in);
+ if (in->dynamic == MK_TRUE) {
+ mk_mem_free(in);
+ }
+
+ return 0;
+}
+
+int mk_channel_stream_write(struct mk_stream *stream, size_t *count)
+{
+ ssize_t bytes = 0;
+ struct mk_iov *iov;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_channel *channel;
+ struct mk_stream_input *input;
+
+ channel = stream->channel;
+
+ /* Validate channel status */
+ if (channel->status != MK_CHANNEL_OK) {
+ return -MK_CHANNEL_ERROR;
+ }
+
+ /* Iterate inputs and process stream */
+ mk_list_foreach_safe(head, tmp, &stream->inputs) {
+ input = mk_list_entry(head, struct mk_stream_input, _head);
+ if (input->type == MK_STREAM_FILE) {
+ bytes = channel_write_in_file(channel, input);
+ }
+ else if (input->type == MK_STREAM_IOV) {
+ iov = input->buffer;
+ if (!iov) {
+ return MK_CHANNEL_EMPTY;
+ }
+
+ bytes = mk_sched_conn_writev(channel, iov);
+
+ MK_TRACE("[CH %i] STREAM_IOV, wrote %d bytes",
+ channel->fd, bytes);
+ if (bytes > 0) {
+ /* Perform the adjustment on mk_iov */
+ mk_iov_consume(iov, bytes);
+ }
+ }
+ else if (input->type == MK_STREAM_RAW) {
+ bytes = mk_sched_conn_write(channel,
+ input->buffer, input->bytes_total);
+ MK_TRACE("[CH %i] STREAM_RAW, bytes=%lu/%lu\n",
+ channel->fd, bytes, input->bytes_total);
+ }
+
+ if (bytes > 0) {
+ *count = bytes;
+ mk_stream_input_consume(input, bytes);
+
+ /* notification callback, optional */
+ if (stream->cb_bytes_consumed) {
+ stream->cb_bytes_consumed(stream, bytes);
+ }
+
+ if (input->cb_consumed) {
+ input->cb_consumed(input, bytes);
+ }
+
+ if (input->bytes_total == 0) {
+ MK_TRACE("Input done, unlinking (channel=%p)", channel);
+ mk_stream_in_release(input);
+ }
+ MK_TRACE("[CH %i] CHANNEL_FLUSH", channel->fd);
+ }
+ else if (bytes < 0) {
+ mk_stream_in_release(input);
+ return -MK_CHANNEL_ERROR;
+ }
+ else if (bytes == 0) {
+ mk_stream_in_release(input);
+ return -MK_CHANNEL_ERROR;
+ }
+ }
+
+ return bytes;
+}
+
+/* It perform a direct stream I/O write through the network layer */
+int mk_channel_write(struct mk_channel *channel, size_t *count)
+{
+ ssize_t bytes = -1;
+ struct mk_iov *iov;
+ struct mk_stream *stream = NULL;
+ struct mk_stream_input *input;
+
+ errno = 0;
+
+ if (mk_list_is_empty(&channel->streams) == 0) {
+ MK_TRACE("[CH %i] CHANNEL_EMPTY", channel->fd);
+ return MK_CHANNEL_EMPTY;
+ }
+
+ /* Get the input source */
+ stream = mk_list_entry_first(&channel->streams, struct mk_stream, _head);
+ if (mk_list_is_empty(&stream->inputs) == 0) {
+ return MK_CHANNEL_EMPTY;
+ }
+ input = mk_list_entry_first(&stream->inputs, struct mk_stream_input, _head);
+
+ /*
+ * Based on the Stream Input type we consume on that way, not all inputs
+ * requires to read from buffer, e.g: Static File, Pipes.
+ */
+ if (channel->type == MK_CHANNEL_SOCKET) {
+ if (input->type == MK_STREAM_FILE) {
+ bytes = channel_write_in_file(channel, input);
+ }
+ else if (input->type == MK_STREAM_IOV) {
+ iov = input->buffer;
+ if (!iov) {
+ return MK_CHANNEL_EMPTY;
+ }
+
+ bytes = mk_sched_conn_writev(channel, iov);
+
+ MK_TRACE("[CH %i] STREAM_IOV, wrote %d bytes",
+ channel->fd, bytes);
+ if (bytes > 0) {
+ /* Perform the adjustment on mk_iov */
+ mk_iov_consume(iov, bytes);
+ }
+ }
+ else if (input->type == MK_STREAM_RAW) {
+ bytes = mk_sched_conn_write(channel,
+ input->buffer, input->bytes_total);
+ MK_TRACE("[CH %i] STREAM_RAW, bytes=%lu/%lu",
+ channel->fd, bytes, input->bytes_total);
+ if (bytes > 0) {
+ /* DEPRECATED: consume_raw(input, bytes); */
+ }
+ }
+
+ if (bytes > 0) {
+ *count = bytes;
+ mk_stream_input_consume(input, bytes);
+
+ /* notification callback, optional */
+ if (stream->cb_bytes_consumed) {
+ stream->cb_bytes_consumed(stream, bytes);
+ }
+
+ if (input->cb_consumed) {
+ input->cb_consumed(input, bytes);
+ }
+
+ if (input->bytes_total == 0) {
+ MK_TRACE("Input done, unlinking (channel=%p)", channel);
+ mk_stream_in_release(input);
+ }
+
+ if (mk_list_is_empty(&stream->inputs) == 0) {
+ /* Everytime the stream is empty, we notify the trigger the cb */
+ if (stream->cb_finished) {
+ stream->cb_finished(stream);
+ }
+
+ if (mk_channel_is_empty(channel) == 0) {
+ MK_TRACE("[CH %i] CHANNEL_DONE", channel->fd);
+ return MK_CHANNEL_DONE;
+ }
+ else {
+ MK_TRACE("[CH %i] CHANNEL_FLUSH", channel->fd);
+ return MK_CHANNEL_FLUSH;
+ }
+ }
+
+ MK_TRACE("[CH %i] CHANNEL_FLUSH", channel->fd);
+ return MK_CHANNEL_FLUSH;
+ }
+ else if (bytes < 0) {
+ if (errno == EAGAIN) {
+ return MK_CHANNEL_BUSY;
+ }
+
+ mk_stream_in_release(input);
+ return MK_CHANNEL_ERROR;
+ }
+ else if (bytes == 0) {
+ mk_stream_in_release(input);
+ return MK_CHANNEL_ERROR;
+ }
+ }
+
+ return MK_CHANNEL_ERROR;
+}
+
+/* Remove any dynamic memory associated */
+int mk_channel_clean(struct mk_channel *channel)
+{
+ struct mk_list *tmp;
+ struct mk_list *tmp_in;
+ struct mk_list *head;
+ struct mk_list *head_in;
+ struct mk_stream *stream;
+ struct mk_stream_input *in;
+
+ mk_list_foreach_safe(head, tmp, &channel->streams) {
+ stream = mk_list_entry(head, struct mk_stream, _head);
+ mk_list_foreach_safe(head_in, tmp_in, &stream->inputs) {
+ in = mk_list_entry(head_in, struct mk_stream_input, _head);
+ mk_stream_in_release(in);
+ }
+ mk_stream_release(stream);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_user.c b/fluent-bit/lib/monkey/mk_server/mk_user.c
new file mode 100644
index 000000000..7200ff08c
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_user.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/monkey.h>
+#include <monkey/mk_user.h>
+#include <monkey/mk_http.h>
+#include <monkey/mk_http_status.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_config.h>
+
+#ifndef _WIN32
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <grp.h>
+
+int mk_user_init(struct mk_http_session *cs, struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int limit;
+ const int offset = 2; /* The user is defined after the '/~' string, so offset = 2 */
+ const int user_len = 255;
+ char user[/*user_len*/ 255]; /* VC++ Doesn't allow for this to be a const int*/
+ char *user_uri;
+ struct passwd *s_user;
+
+ if (sr->uri_processed.len <= 2) {
+ return -1;
+ }
+
+ limit = mk_string_char_search(sr->uri_processed.data + offset, '/',
+ sr->uri_processed.len);
+
+ if (limit == -1) {
+ limit = (sr->uri_processed.len) - offset;
+ }
+
+ if (limit + offset >= (user_len)) {
+ return -1;
+ }
+
+ memcpy(user, sr->uri_processed.data + offset, limit);
+ user[limit] = '\0';
+
+ MK_TRACE("user: '%s'", user);
+
+ /* Check system user */
+ if ((s_user = getpwnam(user)) == NULL) {
+ mk_http_error(MK_CLIENT_NOT_FOUND, cs, sr, server);
+ return -1;
+ }
+
+ if (sr->uri_processed.len > (unsigned int) (offset+limit)) {
+ user_uri = mk_mem_alloc(sr->uri_processed.len);
+ if (!user_uri) {
+ return -1;
+ }
+
+ memcpy(user_uri,
+ sr->uri_processed.data + (offset + limit),
+ sr->uri_processed.len - offset - limit);
+ user_uri[sr->uri_processed.len - offset - limit] = '\0';
+
+ mk_string_build(&sr->real_path.data, &sr->real_path.len,
+ "%s/%s%s",
+ s_user->pw_dir, server->conf_user_pub, user_uri);
+ mk_mem_free(user_uri);
+ }
+ else {
+ mk_string_build(&sr->real_path.data, &sr->real_path.len,
+ "%s/%s", s_user->pw_dir, server->conf_user_pub);
+ }
+
+ sr->user_home = MK_TRUE;
+ return 0;
+}
+
+/* Change process user */
+int mk_user_set_uidgid(struct mk_server *server)
+{
+ struct passwd *usr;
+
+ /* Launched by root ? */
+ if (geteuid() == 0 && server->user) {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl)) {
+ mk_warn("cannot get resource limits");
+ }
+
+ /* Check if user exists */
+ if ((usr = getpwnam(server->user)) == NULL) {
+ mk_err("Invalid user '%s'", server->user);
+ goto out;
+ }
+
+ if (initgroups(server->user, usr->pw_gid) != 0) {
+ mk_err("Initgroups() failed");
+ }
+
+ /* Change process UID and GID */
+ if (setegid(usr->pw_gid) == -1) {
+ mk_err("I cannot change the GID to %u", usr->pw_gid);
+ }
+
+ if (seteuid(usr->pw_uid) == -1) {
+ mk_err("I cannot change the UID to %u", usr->pw_uid);
+ }
+
+ server->is_seteuid = MK_TRUE;
+ }
+
+ out:
+ /* Variables set for run checks on file permission */
+ //FIXME
+ //EUID = geteuid();
+ //EGID = getegid();
+
+ return 0;
+}
+
+/* Return process to the original user */
+int mk_user_undo_uidgid(struct mk_server *server)
+{
+ if (server->is_seteuid == MK_TRUE) {
+ if (setegid(0) < 0) {
+ mk_err("Can't restore effective GID");
+ }
+ if (seteuid(0) < 0) {
+ mk_err("Can't restore effective UID");
+ }
+ }
+ return 0;
+}
+
+#else
+/*
+ None of these functionalities are going to be available in windows at the moment
+*/
+
+int mk_user_init(struct mk_http_session* cs, struct mk_http_request* sr,
+ struct mk_server* server)
+{
+ return -1;
+}
+
+int mk_user_set_uidgid(struct mk_server* server)
+{
+ mk_err("Cannot impersonate users in windows");
+
+ return 0;
+}
+
+int mk_user_undo_uidgid(struct mk_server* server)
+{
+ return 0;
+}
+#endif
diff --git a/fluent-bit/lib/monkey/mk_server/mk_utils.c b/fluent-bit/lib/monkey/mk_server/mk_utils.c
new file mode 100644
index 000000000..443c0ec35
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_utils.c
@@ -0,0 +1,589 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/* local headers */
+#include <monkey/monkey.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_config.h>
+#include <monkey/mk_socket.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_user.h>
+#include <monkey/mk_cache.h>
+#include <monkey/mk_tls.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <time.h>
+#include <inttypes.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+/* stacktrace */
+#ifndef _WIN32
+#include <dlfcn.h>
+#endif
+
+#ifdef MK_HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+#define MK_UTILS_GMT_DATEFORMAT "%a, %d %b %Y %H:%M:%S GMT"
+
+#ifdef _WIN32
+static struct tm* localtime_r(const time_t* timep, struct tm* result)
+{
+ localtime_s(result, timep);
+
+ return result;
+}
+
+static struct tm* gmtime_r(const time_t* timep, struct tm* result)
+{
+ gmtime_s(result, timep);
+
+ return result;
+}
+
+static time_t timegm(struct tm* timeptr)
+{
+ return _mkgmtime(timeptr);
+}
+#endif
+
+#ifdef _WIN32
+int mk_utils_get_system_core_count()
+{
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION *proc_info_buffer;
+ unsigned int result_entry_count;
+ unsigned int entry_index;
+ DWORD result_length;
+ int result_code;
+ int core_count;
+
+ core_count = 1;
+ result_length = 0;
+ proc_info_buffer = NULL;
+
+ result_code = GetLogicalProcessorInformation(proc_info_buffer, &result_length);
+ /* We're passing a null buffer, result_code has to be false */
+
+ if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
+ result_entry_count = result_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
+ proc_info_buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION *) _alloca(result_length);
+
+ if(NULL != proc_info_buffer) {
+ result_code = GetLogicalProcessorInformation(proc_info_buffer, &result_length);
+
+ if (0 != result_code) {
+ core_count = 0;
+
+ for(entry_index = 0 ; entry_index < result_entry_count ; entry_index++) {
+ if(RelationProcessorCore == proc_info_buffer[entry_index].Relationship) {
+ core_count++;
+ }
+ }
+ }
+ }
+
+ /* Athread stack allocation error is a pretty serious
+ * error so in that case we let someone else handle it by returning a
+ * sane default (1 core)
+ */
+ }
+
+ return core_count;
+}
+
+int mk_utils_get_system_page_size()
+{
+ SYSTEM_INFO si;
+
+ GetSystemInfo(&si);
+
+ return si.dwPageSize;
+}
+
+#else
+int mk_utils_get_system_core_count()
+{
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+int mk_utils_get_system_page_size()
+{
+ return sysconf(_SC_PAGESIZE);
+}
+
+#endif
+
+
+/* Date helpers */
+static const char mk_date_wd[][6] = {"Sun, ", "Mon, ", "Tue, ", "Wed, ", "Thu, ", "Fri, ", "Sat, "};
+static const char mk_date_ym[][5] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", "Jul ",
+ "Aug ", "Sep ", "Oct ", "Nov ", "Dec "};
+
+static int mk_utils_gmt_cache_get(char **data, time_t date)
+{
+ unsigned int i;
+ struct mk_gmt_cache *gcache = MK_TLS_GET(mk_tls_cache_gmtext);
+
+ if (mk_unlikely(!gcache)) {
+ return MK_FALSE;
+ }
+
+ for (i = 0; i < MK_GMT_CACHES; i++) {
+ if (date == gcache[i].time) {
+ memcpy(*data, gcache[i].text, 32);
+ gcache[i].hits++;
+ return MK_TRUE;
+ }
+ }
+
+ return MK_FALSE;
+}
+
+static void mk_utils_gmt_cache_add(char *data, time_t time)
+{
+ unsigned int i, min = 0;
+ struct mk_gmt_cache *gcache = MK_TLS_GET(mk_tls_cache_gmtext);
+
+ for (i = 1; i < MK_GMT_CACHES; i++) {
+ if (gcache[i].hits < gcache[min].hits)
+ min = i;
+ }
+
+ gcache[min].hits = 1;
+ gcache[min].time = time;
+ memcpy(gcache[min].text, data, 32);
+}
+
+/*
+ *This function given a unix time, set in a mk_ptr_t
+ * the date in the RFC1123 format like:
+ *
+ * Wed, 23 Jun 2010 22:32:01 GMT
+ *
+ * it also adds a 'CRLF' at the end
+ */
+int mk_utils_utime2gmt(char **data, time_t date)
+{
+ const int size = 31;
+ unsigned short year, mday, hour, min, sec;
+ char *buf=0;
+ struct tm *gtm;
+
+ if (date == 0) {
+ if ((date = time(NULL)) == -1) {
+ return -1;
+ }
+ }
+ else {
+ /* Maybe it's converted already? */
+ if (mk_utils_gmt_cache_get(data, date) == MK_TRUE) {
+ return size;
+ }
+ }
+
+ /* Convert unix time to struct tm */
+ gtm = MK_TLS_GET(mk_tls_cache_gmtime);
+
+ /* If this function was invoked from a non-thread context it should exit */
+ mk_bug(!gtm);
+ gtm = gmtime_r(&date, gtm);
+ if (!gtm) {
+ return -1;
+ }
+
+ /* struct tm -> tm_year counts number of years after 1900 */
+ year = gtm->tm_year + 1900;
+
+ /* Signed division is slow, by using unsigned we gain 25% speed */
+ mday = gtm->tm_mday;
+ hour = gtm->tm_hour;
+ min = gtm->tm_min;
+ sec = gtm->tm_sec;
+
+ /* Compose template */
+ buf = *data;
+
+ /* Week day */
+ memcpy(buf, mk_date_wd[gtm->tm_wday], 5);
+ buf += 5;
+
+ /* Day of the month */
+ *buf++ = ('0' + (mday / 10));
+ *buf++ = ('0' + (mday % 10));
+ *buf++ = ' ';
+
+ /* Month */
+ memcpy(buf, mk_date_ym[gtm->tm_mon], 4);
+ buf += 4;
+
+ /* Year */
+ *buf++ = ('0' + (year / 1000) % 10);
+ *buf++ = ('0' + (year / 100) % 10);
+ *buf++ = ('0' + (year / 10) % 10);
+ *buf++ = ('0' + (year % 10));
+ *buf++ = ' ';
+
+ /* Hour */
+ *buf++ = ('0' + (hour / 10));
+ *buf++ = ('0' + (hour % 10));
+ *buf++ = ':';
+
+ /* Minutes */
+ *buf++ = ('0' + (min / 10));
+ *buf++ = ('0' + (min % 10));
+ *buf++ = ':';
+
+ /* Seconds */
+ *buf++ = ('0' + (sec / 10));
+ *buf++ = ('0' + (sec % 10));
+
+ /* GMT Time zone + CRLF */
+ memcpy(buf, " GMT\r\n\0", 7);
+
+ /* Add new entry to the cache */
+ mk_utils_gmt_cache_add(*data, date);
+
+ /* Set mk_ptr_t data len */
+ return size;
+}
+
+time_t mk_utils_gmt2utime(char *date)
+{
+ time_t new_unix_time;
+ struct tm t_data;
+ memset(&t_data, 0, sizeof(struct tm));
+
+
+#ifdef _WIN32
+#pragma message("Since there is no strptime in windows we'll parse the date in a really crude way just to get it out of the way")
+
+ if (0 != strcmp(MK_UTILS_GMT_DATEFORMAT, "%a, %d %b %Y %H:%M:%S GMT")) {
+ return -1;
+ }
+
+ {
+ char *token;
+
+ token = strtok(date, " "); /* "%a, " */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ token = strtok(NULL, " "); /* "%d " */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ t_data.tm_mday = strtol(token, NULL, 10);
+
+ token = strtok(NULL, " "); /* "%b " */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ if(0 == _strnicmp(token, "jan", 3)){
+ t_data.tm_mon = 0;
+ }
+ else if(0 == _strnicmp(token, "feb", 3)){
+ t_data.tm_mon = 1;
+ }
+ else if(0 == _strnicmp(token, "mar", 3)){
+ t_data.tm_mon = 2;
+ }
+ else if(0 == _strnicmp(token, "apr", 3)){
+ t_data.tm_mon = 3;
+ }
+ else if(0 == _strnicmp(token, "may", 3)){
+ t_data.tm_mon = 4;
+ }
+ else if(0 == _strnicmp(token, "jun", 3)){
+ t_data.tm_mon = 5;
+ }
+ else if(0 == _strnicmp(token, "jul", 3)){
+ t_data.tm_mon = 6;
+ }
+ else if(0 == _strnicmp(token, "aug", 3)){
+ t_data.tm_mon = 7;
+ }
+ else if(0 == _strnicmp(token, "sep", 3)){
+ t_data.tm_mon = 8;
+ }
+ else if(0 == _strnicmp(token, "oct", 3)){
+ t_data.tm_mon = 9;
+ }
+ else if(0 == _strnicmp(token, "nov", 3)){
+ t_data.tm_mon = 10;
+ }
+ else if(0 == _strnicmp(token, "dec", 3)){
+ t_data.tm_mon = 11;
+ }
+ else {
+ return -1;
+ }
+
+ token = strtok(NULL, " "); /* "%Y " */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ t_data.tm_year = strtol(token, NULL, 10);
+
+ token = strtok(NULL, ":"); /* "%H:" */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ t_data.tm_hour = strtol(token, NULL, 10);
+
+ token = strtok(NULL, ":"); /* "%M:" */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ t_data.tm_min = strtol(token, NULL, 10);
+
+ token = strtok(NULL, " "); /* "%S " */
+
+ if (NULL == token) {
+ return -1;
+ }
+
+ t_data.tm_sec = strtol(token, NULL, 10);
+ }
+
+#else
+ if (!strptime(date, MK_UTILS_GMT_DATEFORMAT, (struct tm*)&t_data)) {
+ return -1;
+ }
+#endif
+
+ new_unix_time = timegm((struct tm *) &t_data);
+
+ return (new_unix_time);
+}
+
+int mk_buffer_cat(mk_ptr_t *p, char *buf1, int len1, char *buf2, int len2)
+{
+ /* Validate lengths */
+ if (mk_unlikely(len1 < 0 || len2 < 0)) {
+ return -1;
+ }
+
+ /* alloc space */
+ p->data = (char *) mk_mem_alloc(len1 + len2 + 1);
+
+ /* copy data */
+ memcpy(p->data, buf1, len1);
+ memcpy(p->data + len1, buf2, len2);
+ p->data[len1 + len2] = '\0';
+
+ /* assign len */
+ p->len = len1 + len2;
+
+ return 0;
+}
+
+/* Convert hexadecimal to int */
+int mk_utils_hex2int(char *hex, int len)
+{
+ int i = 0;
+ int res = 0;
+ char c;
+
+ while ((c = *hex++) && i < len) {
+ res *= 0x10;
+
+ if (c >= 'a' && c <= 'f') {
+ res += (c - 0x57);
+ }
+ else if (c >= 'A' && c <= 'F') {
+ res += (c - 0x37);
+ }
+ else if (c >= '0' && c <= '9') {
+ res += (c - 0x30);
+ }
+ else {
+ return -1;
+ }
+ i++;
+ }
+
+ if (res < 0) {
+ return -1;
+ }
+
+ return res;
+}
+
+/* If the URI contains hexa format characters it will return
+ * convert the Hexa values to ASCII character
+ */
+char *mk_utils_url_decode(mk_ptr_t uri)
+{
+ int tmp, hex_result;
+ unsigned int i;
+ int buf_idx = 0;
+ char *buf;
+ char hex[3];
+
+ if ((tmp = mk_string_char_search(uri.data, '%', uri.len)) < 0) {
+ return NULL;
+ }
+
+ i = tmp;
+
+ buf = mk_mem_alloc_z(uri.len + 1);
+ if (i > 0) {
+ memcpy(buf, uri.data, i);
+ buf_idx = i;
+ }
+
+ while (i < uri.len) {
+ if (uri.data[i] == '%' && i + 2 < uri.len) {
+ memcpy(hex, uri.data + i + 1, 2);
+ hex[2] = '\0';
+
+ hex_result = mk_utils_hex2int(hex, 2);
+
+ if (hex_result != -1) {
+ buf[buf_idx] = hex_result;
+ }
+ else {
+ mk_mem_free(buf);
+ return NULL;
+ }
+ i += 2;
+ }
+ else {
+ buf[buf_idx] = uri.data[i];
+ }
+ i++;
+ buf_idx++;
+ }
+ buf[buf_idx] = '\0';
+
+ return buf;
+}
+
+#ifndef MK_HAVE_BACKTRACE
+void mk_utils_stacktrace(void) {}
+#else
+void mk_utils_stacktrace(void)
+{
+ unsigned int i;
+ int ret;
+ size_t size;
+ void *arr[10];
+ Dl_info d;
+
+ printf("[stack trace]\n");
+ size = backtrace(arr, 10);
+
+ for (i = 1; i < size && i < 10; i++) {
+ ret = dladdr(arr[i], &d);
+ if (ret == 0 || !d.dli_sname) {
+ printf(" #%i 0x%016" PRIxPTR " in \?\?\?\?\?\?\?()\n",
+ (i - 1), (uintptr_t) arr[i]);
+ continue;
+ }
+
+ printf(" #%i 0x%016" PRIxPTR " in %s() from %s\n",
+ (i - 1), (uintptr_t) arr[i], d.dli_sname, d.dli_fname);
+ }
+}
+#endif
+
+
+
+/*
+ * This hash generation function is taken originally from Redis source code:
+ *
+ * https://github.com/antirez/redis/blob/unstable/src/dict.c#L109
+ *
+ * ----
+ * MurmurHash2, by Austin Appleby
+ * Note - This code makes a few assumptions about how your machine behaves -
+ * 1. We can read a 4-byte value from any address without crashing
+ * 2. sizeof(int) == 4
+ *
+ * And it has a few limitations -
+ *
+ * 1. It will not work incrementally.
+ * 2. It will not produce the same results on little-endian and big-endian
+ * machines.
+ */
+unsigned int mk_utils_gen_hash(const void *key, int len)
+{
+ /* 'm' and 'r' are mixing constants generated offline.
+ They're not really 'magic', they just happen to work well. */
+ uint32_t seed = 5381;
+ const uint32_t m = 0x5bd1e995;
+ const int r = 24;
+
+ /* Initialize the hash to a 'random' value */
+ uint32_t h = seed ^ len;
+
+ /* Mix 4 bytes at a time into the hash */
+ const unsigned char *data = (const unsigned char *)key;
+
+ while(len >= 4) {
+ uint32_t k = *(uint32_t*) data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ /* Handle the last few bytes of the input array */
+ switch(len) {
+ case 3: h ^= data[2] << 16; // fallthrough
+ case 2: h ^= data[1] << 8; // fallthrough
+ case 1: h ^= data[0]; h *= m;
+ };
+
+ /* Do a few final mixes of the hash to ensure the last few
+ * bytes are well-incorporated. */
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return (unsigned int) h;
+}
diff --git a/fluent-bit/lib/monkey/mk_server/mk_vhost.c b/fluent-bit/lib/monkey/mk_server/mk_vhost.c
new file mode 100644
index 000000000..2dc35fb1f
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_vhost.c
@@ -0,0 +1,821 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_info.h>
+#include <monkey/monkey.h>
+#include <monkey/mk_core.h>
+#include <monkey/mk_vhost.h>
+#include <monkey/mk_vhost_tls.h>
+#include <monkey/mk_utils.h>
+#include <monkey/mk_http_status.h>
+#include <monkey/mk_info.h>
+
+#include <mk_core/mk_dirent.h>
+
+#include <re.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int str_to_regex(char *str, regex_t *reg)
+{
+ char *p = str;
+ regex_t *result;
+
+ while (*p) {
+ if (*p == ' ') *p = '|';
+ p++;
+ }
+
+ result = re_compile(str);
+
+ memcpy(reg, result, REGEXP_SIZE);
+
+ return 0;
+}
+
+/*
+ * This function is triggered upon thread creation (inside the thread
+ * context), here we configure per-thread data.
+ */
+int mk_vhost_fdt_worker_init(struct mk_server *server)
+{
+ int i;
+ int j;
+ struct mk_vhost *h;
+ struct mk_list *list;
+ struct mk_list *head;
+ struct vhost_fdt_host *fdt;
+ struct vhost_fdt_hash_table *ht;
+ struct vhost_fdt_hash_chain *hc;
+
+ if (server->fdt == MK_FALSE) {
+ return -1;
+ }
+
+ /*
+ * We are under a thread context and the main configuration is
+ * already in place. Now for every existent virtual host we are
+ * going to create the File Descriptor Table (FDT) which aims to
+ * hold references of 'open and shared' file descriptors under
+ * the Virtual Host context.
+ */
+
+ /*
+ * Under an initialization context we need to protect this critical
+ * section
+ */
+ pthread_mutex_lock(&server->vhost_fdt_mutex);
+
+ /*
+ * Initialize the thread FDT/Hosts list and create an entry per
+ * existent virtual host
+ */
+ list = mk_mem_alloc_z(sizeof(struct mk_list));
+ mk_list_init(list);
+
+ mk_list_foreach(head, &server->hosts) {
+ h = mk_list_entry(head, struct mk_vhost, _head);
+
+ fdt = mk_mem_alloc(sizeof(struct vhost_fdt_host));
+ fdt->host = h;
+
+ /* Initialize hash table */
+ for (i = 0; i < VHOST_FDT_HASHTABLE_SIZE; i++) {
+ ht = &fdt->hash_table[i];
+ ht->av_slots = VHOST_FDT_HASHTABLE_CHAINS;
+
+ /* for each chain under the hash table, set the fd */
+ for (j = 0; j < VHOST_FDT_HASHTABLE_CHAINS; j++) {
+ hc = &ht->chain[j];
+ hc->fd = -1;
+ hc->hash = 0;
+ hc->readers = 0;
+ }
+ }
+ mk_list_add(&fdt->_head, list);
+ }
+
+ MK_TLS_SET(mk_tls_vhost_fdt, list);
+ pthread_mutex_unlock(&server->vhost_fdt_mutex);
+
+ return 0;
+}
+
+int mk_vhost_fdt_worker_exit(struct mk_server *server)
+{
+ struct mk_list *list;
+ struct mk_list *head;
+ struct mk_list *tmp;
+ struct vhost_fdt_host *fdt;
+
+ if (server->fdt == MK_FALSE) {
+ return -1;
+ }
+
+ list = MK_TLS_GET(mk_tls_vhost_fdt);
+ mk_list_foreach_safe(head, tmp, list) {
+ fdt = mk_list_entry(head, struct vhost_fdt_host, _head);
+ mk_list_del(&fdt->_head);
+ mk_mem_free(fdt);
+ }
+
+ mk_mem_free(list);
+ return 0;
+}
+
+
+static inline
+struct vhost_fdt_hash_table *mk_vhost_fdt_table_lookup(int id, struct mk_vhost *host)
+{
+ struct mk_list *head;
+ struct mk_list *list;
+ struct vhost_fdt_host *fdt_host;
+ struct vhost_fdt_hash_table *ht = NULL;
+
+ list = MK_TLS_GET(mk_tls_vhost_fdt);
+ mk_list_foreach(head, list) {
+ fdt_host = mk_list_entry(head, struct vhost_fdt_host, _head);
+ if (fdt_host->host == host) {
+ ht = &fdt_host->hash_table[id];
+ return ht;
+ }
+ }
+
+ return ht;
+}
+
+static inline
+struct vhost_fdt_hash_chain
+*mk_vhost_fdt_chain_lookup(unsigned int hash, struct vhost_fdt_hash_table *ht)
+{
+ int i;
+ struct vhost_fdt_hash_chain *hc = NULL;
+
+ for (i = 0; i < VHOST_FDT_HASHTABLE_CHAINS; i++) {
+ hc = &ht->chain[i];
+ if (hc->hash == hash) {
+ return hc;
+ }
+ }
+
+ return NULL;
+}
+
+
+static inline int mk_vhost_fdt_open(int id, unsigned int hash,
+ struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int i;
+ int fd = -1;
+ struct vhost_fdt_hash_table *ht = NULL;
+ struct vhost_fdt_hash_chain *hc;
+
+ if (server->fdt == MK_FALSE) {
+ return open(sr->real_path.data, sr->file_info.flags_read_only);
+ }
+
+ ht = mk_vhost_fdt_table_lookup(id, sr->host_conf);
+ if (mk_unlikely(!ht)) {
+ return open(sr->real_path.data, sr->file_info.flags_read_only);
+ }
+
+ /* We got the hash table, now look around the chains array */
+ hc = mk_vhost_fdt_chain_lookup(hash, ht);
+ if (hc) {
+ /* Increment the readers and return the shared FD */
+ hc->readers++;
+ sr->vhost_fdt_id = id;
+ sr->vhost_fdt_hash = hash;
+ sr->vhost_fdt_enabled = MK_TRUE;
+ return hc->fd;
+ }
+
+ /*
+ * Get here means that no entry exists in the hash table for the
+ * requested file descriptor and hash, we must try to open the file
+ * and register the entry in the table.
+ */
+ fd = open(sr->real_path.data, sr->file_info.flags_read_only);
+ if (fd == -1) {
+ return -1;
+ }
+
+ /* If chains are full, just return the new FD, bad luck... */
+ if (ht->av_slots <= 0) {
+ return fd;
+ }
+
+ /* Register the new entry in an available slot */
+ for (i = 0; i < VHOST_FDT_HASHTABLE_CHAINS; i++) {
+ hc = &ht->chain[i];
+ if (hc->fd == -1) {
+ hc->fd = fd;
+ hc->hash = hash;
+ hc->readers++;
+ ht->av_slots--;
+
+ sr->vhost_fdt_id = id;
+ sr->vhost_fdt_hash = hash;
+ sr->vhost_fdt_enabled = MK_TRUE;
+
+ return fd;
+ }
+ }
+
+ return fd;
+}
+
+static inline int mk_vhost_fdt_close(struct mk_http_request *sr,
+ struct mk_server *server)
+{
+ int id;
+ unsigned int hash;
+ struct vhost_fdt_hash_table *ht = NULL;
+ struct vhost_fdt_hash_chain *hc;
+
+ if (server->fdt == MK_FALSE || sr->vhost_fdt_enabled == MK_FALSE) {
+ if (sr->in_file.fd > 0) {
+ return close(sr->in_file.fd);
+ }
+ return -1;
+ }
+
+ id = sr->vhost_fdt_id;
+ hash = sr->vhost_fdt_hash;
+
+ ht = mk_vhost_fdt_table_lookup(id, sr->host_conf);
+ if (mk_unlikely(!ht)) {
+ return close(sr->in_file.fd);
+ }
+
+ /* We got the hash table, now look around the chains array */
+ hc = mk_vhost_fdt_chain_lookup(hash, ht);
+ if (hc) {
+ /* Increment the readers and check if we should close */
+ hc->readers--;
+ sr->vhost_fdt_enabled = MK_FALSE;
+
+ if (hc->readers == 0) {
+ hc->fd = -1;
+ hc->hash = 0;
+ ht->av_slots++;
+ return close(sr->in_file.fd);
+ }
+ else {
+ return 0;
+ }
+ }
+ return close(sr->in_file.fd);
+}
+
+
+int mk_vhost_open(struct mk_http_request *sr, struct mk_server *server)
+{
+ int id;
+ int off;
+ unsigned int hash;
+
+ off = sr->host_conf->documentroot.len;
+ hash = mk_utils_gen_hash(sr->real_path.data + off,
+ sr->real_path.len - off);
+ id = (hash % VHOST_FDT_HASHTABLE_SIZE);
+
+ return mk_vhost_fdt_open(id, hash, sr, server);
+}
+
+int mk_vhost_close(struct mk_http_request *sr, struct mk_server *server)
+{
+ return mk_vhost_fdt_close(sr, server);
+}
+
+struct mk_vhost_handler *mk_vhost_handler_match(char *match,
+ void (*cb)(struct mk_http_request *,
+ void *),
+ void *data)
+{
+ int ret;
+ struct mk_vhost_handler *h;
+
+ h = mk_mem_alloc(sizeof(struct mk_vhost_handler));
+ if (!h) {
+ return NULL;
+ }
+ h->name = NULL;
+ h->cb = cb;
+ h->data = data;
+ h->match = mk_mem_alloc(REGEXP_SIZE);
+ if (!h->match) {
+ mk_mem_free(h);
+ return NULL;
+ }
+ mk_list_init(&h->params);
+
+ ret = str_to_regex(match, h->match);
+ if (ret == -1) {
+ mk_mem_free(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+/*
+ * Open a virtual host configuration file and return a structure with
+ * definitions.
+ */
+struct mk_vhost *mk_vhost_read(char *path)
+{
+ int ret;
+ char *tmp;
+ char *host_low;
+ struct stat checkdir;
+ struct mk_vhost *host;
+ struct mk_vhost_alias *new_alias;
+ struct mk_vhost_error_page *err_page;
+ struct mk_rconf *cnf;
+ struct mk_rconf_section *section_host;
+ struct mk_rconf_section *section_ep;
+ struct mk_rconf_section *section_handlers;
+ struct mk_rconf_entry *entry_ep;
+ struct mk_string_line *entry;
+ struct mk_list *head, *list, *line;
+ struct mk_vhost_handler *h_handler;
+ struct mk_vhost_handler_param *h_param;
+
+ /* Read configuration file */
+ cnf = mk_rconf_open(path);
+ if (!cnf) {
+ mk_err("Configuration error, aborting.");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read 'HOST' section */
+ section_host = mk_rconf_section_get(cnf, "HOST");
+ if (!section_host) {
+ mk_err("Invalid config file %s", path);
+ return NULL;
+ }
+
+ /* Alloc configuration node */
+ host = mk_mem_alloc_z(sizeof(struct mk_vhost));
+ host->config = cnf;
+ host->file = mk_string_dup(path);
+
+ /* Init list for host name aliases */
+ mk_list_init(&host->server_names);
+
+ /* Init list for custom error pages */
+ mk_list_init(&host->error_pages);
+
+ /* Init list for content handlers */
+ mk_list_init(&host->handlers);
+
+ /* Lookup Servername */
+ list = mk_rconf_section_get_key(section_host, "Servername", MK_RCONF_LIST);
+ if (!list) {
+ mk_err("Hostname does not contain a Servername");
+ exit(EXIT_FAILURE);
+ }
+
+ mk_list_foreach(head, list) {
+ entry = mk_list_entry(head, struct mk_string_line, _head);
+ if (entry->len > MK_HOSTNAME_LEN - 1) {
+ continue;
+ }
+
+ /* Hostname to lowercase */
+ host_low = mk_string_tolower(entry->val);
+
+ /* Alloc node */
+ new_alias = mk_mem_alloc_z(sizeof(struct mk_vhost_alias));
+ new_alias->name = mk_mem_alloc_z(entry->len + 1);
+ strncpy(new_alias->name, host_low, entry->len);
+ mk_mem_free(host_low);
+
+ new_alias->len = entry->len;
+
+ mk_list_add(&new_alias->_head, &host->server_names);
+ }
+ mk_string_split_free(list);
+
+ /* Lookup document root handled by a mk_ptr_t */
+ host->documentroot.data = mk_rconf_section_get_key(section_host,
+ "DocumentRoot",
+ MK_RCONF_STR);
+ if (!host->documentroot.data) {
+ mk_err("Missing DocumentRoot entry on %s file", path);
+ mk_rconf_free(cnf);
+ mk_mem_free(host->file);
+ mk_mem_free(host);
+ return NULL;
+ }
+
+ host->documentroot.len = strlen(host->documentroot.data);
+
+ /* Validate document root configured */
+ if (stat(host->documentroot.data, &checkdir) == -1) {
+ mk_err("Invalid path to DocumentRoot in %s", path);
+ }
+ else if (!(checkdir.st_mode & S_IFDIR)) {
+ mk_err("DocumentRoot variable in %s has an invalid directory path", path);
+ }
+
+ if (mk_list_is_empty(&host->server_names) == 0) {
+ mk_rconf_free(cnf);
+ mk_mem_free(host->file);
+ mk_mem_free(host);
+ return NULL;
+ }
+
+ /* Check Virtual Host redirection */
+ host->header_redirect.data = NULL;
+ host->header_redirect.len = 0;
+
+ tmp = mk_rconf_section_get_key(section_host,
+ "Redirect",
+ MK_RCONF_STR);
+ if (tmp) {
+ host->header_redirect.data = mk_string_dup(tmp);
+ host->header_redirect.len = strlen(tmp);
+ mk_mem_free(tmp);
+ }
+
+ /* Error Pages */
+ section_ep = mk_rconf_section_get(cnf, "ERROR_PAGES");
+ if (section_ep) {
+ mk_list_foreach(head, &section_ep->entries) {
+ entry_ep = mk_list_entry(head, struct mk_rconf_entry, _head);
+
+ int ep_status = -1;
+ char *ep_file = NULL;
+ unsigned long len;
+
+ ep_status = atoi(entry_ep->key);
+ ep_file = entry_ep->val;
+
+ /* Validate input values */
+ if (ep_status < MK_CLIENT_BAD_REQUEST ||
+ ep_status > MK_SERVER_HTTP_VERSION_UNSUP ||
+ ep_file == NULL) {
+ continue;
+ }
+
+ /* Alloc error page node */
+ err_page = mk_mem_alloc_z(sizeof(struct mk_vhost_error_page));
+ err_page->status = ep_status;
+ err_page->file = mk_string_dup(ep_file);
+ err_page->real_path = NULL;
+ mk_string_build(&err_page->real_path, &len, "%s/%s",
+ host->documentroot.data, err_page->file);
+
+ MK_TRACE("Map error page: status %i -> %s", err_page->status, err_page->file);
+
+ /* Link page to the error page list */
+ mk_list_add(&err_page->_head, &host->error_pages);
+ }
+ }
+
+ /* Handlers */
+ int i;
+ int params;
+ struct mk_list *head_line;
+
+ section_handlers = mk_rconf_section_get(cnf, "HANDLERS");
+ if (!section_handlers) {
+ return host;
+ }
+ mk_list_foreach(head, &section_handlers->entries) {
+ entry_ep = mk_list_entry(head, struct mk_rconf_entry, _head);
+ if (strncasecmp(entry_ep->key, "Match", strlen(entry_ep->key)) == 0) {
+ line = mk_string_split_line(entry_ep->val);
+ if (!line) {
+ continue;
+ }
+ h_handler = mk_mem_alloc(sizeof(struct mk_vhost_handler));
+ if (!h_handler) {
+ exit(EXIT_FAILURE);
+ }
+ h_handler->match = mk_mem_alloc(REGEXP_SIZE);
+ if (!h_handler->match) {
+ mk_mem_free(h_handler);
+ exit(EXIT_FAILURE);
+ }
+ h_handler->cb = NULL;
+ mk_list_init(&h_handler->params);
+
+ i = 0;
+ params = 0;
+ mk_list_foreach(head_line, line) {
+ entry = mk_list_entry(head_line, struct mk_string_line, _head);
+ switch (i) {
+ case 0:
+ ret = str_to_regex(entry->val, h_handler->match);
+ if (ret == -1) {
+ return NULL;
+ }
+ break;
+ case 1:
+ h_handler->name = mk_string_dup(entry->val);
+ break;
+ default:
+ /* link parameters */
+ h_param = mk_mem_alloc(sizeof(struct mk_vhost_handler_param));
+ h_param->p.data = mk_string_dup(entry->val);
+ h_param->p.len = entry->len;
+ mk_list_add(&h_param->_head, &h_handler->params);
+ params++;
+ };
+ i++;
+ }
+ h_handler->n_params = params;
+ mk_string_split_free(line);
+
+ if (i < 2) {
+ mk_err("[Host Handlers] invalid Match value\n");
+ exit(EXIT_FAILURE);
+ }
+ mk_list_add(&h_handler->_head, &host->handlers);
+ }
+ }
+
+
+ return host;
+}
+
+int mk_vhost_map_handlers(struct mk_server *server)
+{
+ int n = 0;
+ struct mk_list *head;
+ struct mk_list *head_handler;
+ struct mk_vhost *host;
+ struct mk_vhost_handler *h_handler;
+ struct mk_plugin *p;
+
+ mk_list_foreach(head, &server->hosts) {
+ host = mk_list_entry(head, struct mk_vhost, _head);
+ mk_list_foreach(head_handler, &host->handlers) {
+ h_handler = mk_list_entry(head_handler,
+ struct mk_vhost_handler, _head);
+
+ /* Lookup plugin by name */
+ p = mk_plugin_lookup(h_handler->name, server);
+ if (!p) {
+ mk_err("Plugin '%s' was not loaded", h_handler->name);
+ continue;
+ }
+
+ if (p->hooks != MK_PLUGIN_STAGE) {
+ mk_err("Plugin '%s' is not a handler", h_handler->name);
+ continue;
+ }
+
+ h_handler->handler = p;
+ n++;
+ }
+ }
+
+ return n;
+}
+
+void mk_vhost_set_single(char *path, struct mk_server *server)
+{
+ struct mk_vhost *host;
+ struct mk_vhost_alias *halias;
+ struct stat checkdir;
+
+ /* Set the default host */
+ host = mk_mem_alloc_z(sizeof(struct mk_vhost));
+ mk_list_init(&host->error_pages);
+ mk_list_init(&host->server_names);
+
+ /* Prepare the unique alias */
+ halias = mk_mem_alloc_z(sizeof(struct mk_vhost_alias));
+ halias->name = mk_string_dup("127.0.0.1");
+ mk_list_add(&halias->_head, &host->server_names);
+
+ host->documentroot.data = mk_string_dup(path);
+ host->documentroot.len = strlen(path);
+ host->header_redirect.data = NULL;
+
+ /* Validate document root configured */
+ if (stat(host->documentroot.data, &checkdir) == -1) {
+ mk_err("Invalid path to DocumentRoot in %s", path);
+ exit(EXIT_FAILURE);
+ }
+ else if (!(checkdir.st_mode & S_IFDIR)) {
+ mk_err("DocumentRoot variable in %s has an invalid directory path", path);
+ exit(EXIT_FAILURE);
+ }
+ mk_list_add(&host->_head, &server->hosts);
+ mk_list_init(&host->handlers);
+}
+
+/* Given a configuration directory, start reading the virtual host entries */
+void mk_vhost_init(char *path, struct mk_server *server)
+{
+ DIR *dir;
+ unsigned long len;
+ char *buf = 0;
+ char *sites = 0;
+ char *file;
+ struct mk_vhost *p_host; /* debug */
+ struct dirent *ent;
+ struct file_info f_info;
+ int ret;
+
+ if (!server->conf_sites) {
+ mk_warn("[vhost] skipping default site");
+ return;
+ }
+
+ /* Read default virtual host file */
+ mk_string_build(&sites, &len, "%s/%s/",
+ path, server->conf_sites);
+ ret = mk_file_get_info(sites, &f_info, MK_FILE_EXISTS);
+ if (ret == -1 || f_info.is_directory == MK_FALSE) {
+ mk_mem_free(sites);
+ sites = server->conf_sites;
+ }
+
+ mk_string_build(&buf, &len, "%s/default", sites);
+
+ p_host = mk_vhost_read(buf);
+ if (!p_host) {
+ mk_err("Error parsing main configuration file 'default'");
+ }
+ mk_list_add(&p_host->_head, &server->hosts);
+ server->nhosts++;
+ mk_mem_free(buf);
+ buf = NULL;
+
+
+ /* Read all virtual hosts defined in sites/ */
+ if (!(dir = opendir(sites))) {
+ mk_mem_free(sites);
+ mk_err("Could not open %s", sites);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reading content */
+ while ((ent = readdir(dir)) != NULL) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+ if (strcmp((char *) ent->d_name, "..") == 0) {
+ continue;
+ }
+ if (ent->d_name[strlen(ent->d_name) - 1] == '~') {
+ continue;
+ }
+ if (strcasecmp((char *) ent->d_name, "default") == 0) {
+ continue;
+ }
+ file = NULL;
+ mk_string_build(&file, &len, "%s/%s", sites, ent->d_name);
+
+ p_host = mk_vhost_read(file);
+ mk_mem_free(file);
+ if (!p_host) {
+ continue;
+ }
+ else {
+ mk_list_add(&p_host->_head, &server->hosts);
+ server->nhosts++;
+ }
+ }
+ closedir(dir);
+ mk_mem_free(sites);
+}
+
+
+/* Lookup a registered virtual host based on the given 'host' input */
+int mk_vhost_get(mk_ptr_t host, struct mk_vhost **vhost,
+ struct mk_vhost_alias **alias,
+ struct mk_server *server)
+{
+ struct mk_vhost *entry_host;
+ struct mk_vhost_alias *entry_alias;
+ struct mk_list *head_vhost, *head_alias;
+
+ mk_list_foreach(head_vhost, &server->hosts) {
+ entry_host = mk_list_entry(head_vhost, struct mk_vhost, _head);
+ mk_list_foreach(head_alias, &entry_host->server_names) {
+ entry_alias = mk_list_entry(head_alias, struct mk_vhost_alias, _head);
+ if (entry_alias->len == host.len &&
+ strncmp(entry_alias->name, host.data, host.len) == 0) {
+ *vhost = entry_host;
+ *alias = entry_alias;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static void mk_vhost_handler_free(struct mk_vhost_handler *h)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_vhost_handler_param *param;
+
+ /* Release Params */
+ mk_list_foreach_safe(head, tmp, &h->params) {
+ param = mk_list_entry(head, struct mk_vhost_handler_param, _head);
+ mk_list_del(&param->_head);
+ mk_mem_free(param->p.data);
+ mk_mem_free(param);
+ }
+
+ mk_mem_free(h->match);
+ mk_mem_free(h->name);
+ mk_mem_free(h);
+}
+
+int mk_vhost_destroy(struct mk_vhost *vh)
+{
+ struct mk_vhost_alias *halias = NULL;
+ struct mk_vhost_handler *hhandler;
+ struct mk_vhost_error_page *ep;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ if (vh) {
+ /* Free aliases or servernames */
+ mk_list_foreach_safe(head, tmp, &vh->server_names) {
+ halias = mk_list_entry(head, struct mk_vhost_alias, _head);
+ if (halias) {
+ mk_list_del(&halias->_head);
+ if (halias->name) {
+ mk_mem_free(halias->name);
+ }
+ mk_mem_free(halias);
+ }
+ }
+
+ /* Handlers */
+ mk_list_foreach_safe(head, tmp, &vh->handlers) {
+ hhandler = mk_list_entry(head, struct mk_vhost_handler, _head);
+ if (hhandler) {
+ mk_vhost_handler_free(hhandler);
+ }
+ }
+
+ /* Free error pages */
+ mk_list_foreach_safe(head, tmp, &vh->error_pages) {
+ ep = mk_list_entry(head, struct mk_vhost_error_page, _head);
+ if (ep) {
+ mk_list_del(&ep->_head);
+ if (ep->file) {
+ mk_mem_free(ep->file);
+ }
+ if (ep->real_path) {
+ mk_mem_free(ep->real_path);
+ }
+ mk_mem_free(ep);
+ }
+ }
+ mk_ptr_free(&vh->documentroot);
+
+ /* Free source configuration */
+ if (vh->config) {
+ mk_rconf_free(vh->config);
+ }
+ mk_list_del(&vh->_head);
+ if (vh->file) {
+ mk_mem_free(vh->file);
+ }
+
+ mk_mem_free(vh);
+ }
+ return 0;
+}
+
+void mk_vhost_free_all(struct mk_server *server)
+{
+ struct mk_vhost *host;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ mk_list_foreach_safe(head, tmp, &server->hosts) {
+ host = mk_list_entry(head, struct mk_vhost, _head);
+ mk_vhost_destroy(host);
+ }
+}
diff --git a/fluent-bit/lib/monkey/mk_server/monkey.c b/fluent-bit/lib/monkey/mk_server/monkey.c
new file mode 100644
index 000000000..68513d21b
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/monkey.c
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <mk_core/mk_pthread.h>
+#include <mk_core/mk_event.h>
+
+#include <monkey/mk_scheduler.h>
+#include <monkey/mk_plugin.h>
+#include <monkey/mk_clock.h>
+#include <monkey/mk_thread.h>
+#include <monkey/mk_mimetype.h>
+#include <monkey/mk_http_thread.h>
+
+pthread_once_t mk_server_tls_setup_once = PTHREAD_ONCE_INIT;
+
+static void mk_set_up_tls_keys()
+{
+ MK_INIT_INITIALIZE_TLS_UNIVERSAL();
+ MK_INIT_INITIALIZE_TLS();
+
+ mk_http_thread_initialize_tls();
+}
+
+void mk_server_info(struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_plugin *p;
+ struct mk_config_listener *l;
+
+#ifdef _WIN32
+ printf(MK_BANNER_ENTRY "Process ID is %ld\n", (long)GetCurrentProcessId());
+#else
+ printf(MK_BANNER_ENTRY "Process ID is %ld\n", (long) getpid());
+#endif
+ mk_list_foreach(head, &server->listeners) {
+ l = mk_list_entry(head, struct mk_config_listener, _head);
+ printf(MK_BANNER_ENTRY "Server listening on %s:%s\n",
+ l->address, l->port);
+ }
+ printf(MK_BANNER_ENTRY
+ "%i threads, may handle up to %i client connections\n",
+ server->workers, server->server_capacity);
+
+ /* List loaded plugins */
+ printf(MK_BANNER_ENTRY "Loaded Plugins: ");
+ mk_list_foreach(head, &server->plugins) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+ printf("%s ", p->shortname);
+ }
+ printf("\n");
+
+#ifdef __linux__
+ char tmp[64];
+
+ if (mk_kernel_features_print(tmp, sizeof(tmp), server) > 0) {
+ printf(MK_BANNER_ENTRY "Linux Features: %s\n", tmp);
+ }
+#endif
+
+ fflush(stdout);
+}
+
+/* Initialize Monkey Server */
+struct mk_server *mk_server_create()
+{
+ int ret;
+ int kern_version;
+ int kern_features;
+ struct mk_server *server;
+
+ server = mk_mem_alloc_z(sizeof(struct mk_server));
+ if (!server) {
+ return NULL;
+ }
+
+ /* I'll try to leave both initializations here because
+ * it should be possible to run in windows using the accept
+ * backend in which case it doesn't make sense to tie the net stack
+ * initialization to libevent.
+ */
+ mk_net_init();
+ mk_event_init();
+
+ /* Library mode: event loop */
+ server->lib_mode = MK_TRUE;
+ server->lib_evl = mk_event_loop_create(8);
+ if (!server->lib_evl) {
+ mk_mem_free(server);
+ return NULL;
+ }
+
+ /* Library mode: channel manager */
+
+ memset(&server->lib_ch_event, 0, sizeof(struct mk_event));
+
+ ret = mk_event_channel_create(server->lib_evl,
+ &server->lib_ch_manager[0],
+ &server->lib_ch_manager[1],
+ &server->lib_ch_event);
+
+ if (ret != 0) {
+ mk_event_loop_destroy(server->lib_evl);
+ mk_mem_free(server);
+ return NULL;
+ }
+
+ /* Library mode: start event loop */
+ server->lib_evl_start = mk_event_loop_create(1);
+ if (!server->lib_evl_start) {
+ mk_event_loop_destroy(server->lib_evl);
+ mk_mem_free(server);
+ return NULL;
+ }
+
+ memset(&server->lib_ch_start_event, 0, sizeof(struct mk_event));
+
+ ret = mk_event_channel_create(server->lib_evl_start,
+ &server->lib_ch_start[0],
+ &server->lib_ch_start[1],
+ &server->lib_ch_start_event);
+
+ if (ret != 0) {
+ mk_event_loop_destroy(server->lib_evl);
+ mk_event_loop_destroy(server->lib_evl_start);
+ mk_mem_free(server);
+ return NULL;
+ }
+
+ /* Initialize linked list heads */
+ mk_list_init(&server->plugins);
+ mk_list_init(&server->sched_worker_callbacks);
+ mk_list_init(&server->stage10_handler);
+ mk_list_init(&server->stage20_handler);
+ mk_list_init(&server->stage30_handler);
+ mk_list_init(&server->stage40_handler);
+ mk_list_init(&server->stage50_handler);
+ server->scheduler_mode = -1;
+
+ mk_core_init();
+
+ /* Init thread keys */
+ pthread_once(&mk_server_tls_setup_once, mk_set_up_tls_keys);
+
+ /* Init Kernel version data */
+ kern_version = mk_kernel_version();
+ kern_features = mk_kernel_features(kern_version);
+
+ server->kernel_version = kern_version;
+ server->kernel_features = kern_features;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("Monkey TRACE is enabled");
+ //pthread_mutex_init(&mutex_trace, (pthread_mutexattr_t *) NULL);
+#endif
+
+#ifdef LINUX_TRACE
+ mk_info("Linux Trace enabled");
+#endif
+
+ mk_config_set_init_values(server);
+
+ mk_mimetype_init(server);
+
+ pthread_mutex_init(&server->vhost_fdt_mutex, NULL);
+
+ return server;
+}
+
+int mk_server_setup(struct mk_server *server)
+{
+ int ret;
+ pthread_t tid;
+
+ /* Core and Scheduler setup */
+ mk_config_start_configure(server);
+ mk_config_signature(server);
+
+ mk_sched_init(server);
+
+
+ /* Clock init that must happen before starting threads */
+ mk_clock_sequential_init(server);
+
+ /* Load plugins */
+ mk_plugin_api_init(server);
+ mk_plugin_load_all(server);
+
+ /* Workers: logger and clock */
+ ret = mk_utils_worker_spawn((void *) mk_clock_worker_init, server, &tid);
+ if (ret != 0) {
+ return -1;
+ }
+
+ /* Configuration sanity check */
+ mk_config_sanity_check(server);
+
+ /* Invoke Plugin PRCTX hooks */
+ mk_plugin_core_process(server);
+
+ /* Launch monkey http workers */
+ mk_server_launch_workers(server);
+
+ return 0;
+}
+
+void mk_exit_all(struct mk_server *server)
+{
+ uint64_t val;
+
+ /* Distribute worker signals to stop working */
+ val = MK_SCHED_SIGNAL_FREE_ALL;
+ mk_sched_broadcast_signal(server, val);
+
+ /* Wait for all workers to finish */
+ mk_sched_workers_join(server);
+
+ /* Continue exiting */
+ mk_plugin_exit_all(server);
+ mk_clock_exit(server);
+
+ mk_sched_exit(server);
+ mk_config_free_all(server);
+}