summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/plugins/cgi
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/monkey/plugins/cgi')
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt7
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/cgi.c501
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/cgi.h133
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/event.c170
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/request.c72
5 files changed, 883 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt
new file mode 100644
index 000000000..121144aa1
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(src
+ cgi.c
+ event.c
+ request.c
+ )
+
+MONKEY_PLUGIN(cgi "${src}")
diff --git a/fluent-bit/lib/monkey/plugins/cgi/cgi.c b/fluent-bit/lib/monkey/plugins/cgi/cgi.c
new file mode 100644
index 000000000..58c7a647f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/cgi.c
@@ -0,0 +1,501 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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_stream.h>
+#include "cgi.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+void cgi_finish(struct cgi_request *r)
+{
+ /*
+ * Unregister & close the CGI child process pipe reader fd from the
+ * thread event loop, otherwise we may get unexpected notifications.
+ */
+ mk_api->ev_del(mk_api->sched_loop(), (struct mk_event *) r);
+ close(r->fd);
+ if (r->chunked && r->active == MK_TRUE) {
+ PLUGIN_TRACE("CGI sending Chunked EOF");
+ channel_write(r, "0\r\n\r\n", 5);
+ }
+
+ /* Try to kill any child process */
+ if (r->child > 0) {
+ kill(r->child, SIGKILL);
+ r->child = 0;
+ }
+
+ /* Invalidte our socket handler */
+ requests_by_socket[r->socket] = NULL;
+ if (r->active == MK_TRUE) {
+ mk_api->http_request_end(r->plugin, r->cs, r->hangup);
+ }
+ cgi_req_del(r);
+}
+
+int swrite(const int fd, const void *buf, const size_t count)
+{
+ ssize_t pos = count, ret = 0;
+
+ while (pos > 0 && ret >= 0) {
+ ret = write(fd, buf, pos);
+ if (ret < 0) {
+ return ret;
+ }
+
+ pos -= ret;
+ buf += ret;
+ }
+ return count;
+}
+
+int channel_write(struct cgi_request *r, void *buf, size_t count)
+{
+ int ret;
+
+ if (r->active == MK_FALSE) {
+ return -1;
+ }
+
+ MK_TRACE("channel write: %d bytes", count);
+ mk_stream_in_raw(&r->sr->stream,
+ NULL,
+ buf, count,
+ NULL, NULL);
+
+ ret = mk_api->channel_flush(r->sr->session->channel);
+ if (ret & MK_CHANNEL_ERROR) {
+ r->active = MK_FALSE;
+ cgi_finish(r);
+ }
+ return 0;
+}
+
+static void cgi_write_post(void *p)
+{
+ const struct post_t * const in = p;
+
+ swrite(in->fd, in->buf, in->len);
+ close(in->fd);
+}
+
+static int do_cgi(const char *const __restrict__ file,
+ const char *const __restrict__ url,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs,
+ struct mk_plugin *plugin,
+ char *interpreter,
+ char *mimetype)
+{
+ int ret;
+ int devnull;
+ const int socket = cs->socket;
+ struct file_info finfo;
+ struct cgi_request *r = NULL;
+ struct mk_event *event;
+ char *env[30];
+ int writepipe[2], readpipe[2];
+ (void) plugin;
+
+ /* Unchanging env vars */
+ env[0] = "PATH_INFO=";
+ env[1] = "GATEWAY_INTERFACE=CGI/1.1";
+ env[2] = "REDIRECT_STATUS=200";
+ const int env_start = 3;
+ char *protocol;
+ unsigned long len;
+
+ /* Dynamic env vars */
+ unsigned short envpos = env_start;
+
+ char method[SHORTLEN];
+ char *query = NULL;
+ char request_uri[PATHLEN];
+ char script_filename[PATHLEN];
+ char script_name[PATHLEN];
+ char query_string[PATHLEN];
+ char remote_addr[INET6_ADDRSTRLEN+SHORTLEN];
+ char tmpaddr[INET6_ADDRSTRLEN], *ptr = tmpaddr;
+ char remote_port[SHORTLEN];
+ char content_length[SHORTLEN];
+ char content_type[SHORTLEN];
+ char server_software[SHORTLEN];
+ char server_protocol[SHORTLEN];
+ char http_host[SHORTLEN];
+
+ /* Check the interpreter exists */
+ if (interpreter) {
+ ret = mk_api->file_get_info(interpreter, &finfo, MK_FILE_EXEC);
+ if (ret == -1 ||
+ (finfo.is_file == MK_FALSE && finfo.is_link == MK_FALSE) ||
+ finfo.exec_access == MK_FALSE) {
+ return 500;
+ }
+ }
+
+ if (mimetype) {
+ sr->content_type.data = mimetype;
+ sr->content_type.len = strlen(mimetype);
+ }
+
+ snprintf(method, SHORTLEN, "REQUEST_METHOD=%.*s", (int) sr->method_p.len, sr->method_p.data);
+ env[envpos++] = method;
+
+ snprintf(server_software, SHORTLEN, "SERVER_SOFTWARE=%s",
+ mk_api->config->server_signature);
+ env[envpos++] = server_software;
+
+ snprintf(http_host, SHORTLEN, "HTTP_HOST=%.*s", (int) sr->host.len, sr->host.data);
+ env[envpos++] = http_host;
+
+ if (sr->protocol == MK_HTTP_PROTOCOL_11)
+ protocol = MK_HTTP_PROTOCOL_11_STR;
+ else
+ protocol = MK_HTTP_PROTOCOL_10_STR;
+
+ snprintf(server_protocol, SHORTLEN, "SERVER_PROTOCOL=%s", protocol);
+ env[envpos++] = server_protocol;
+
+ if (sr->query_string.len) {
+ query = mk_api->mem_alloc_z(sr->query_string.len + 1);
+ memcpy(query, sr->query_string.data, sr->query_string.len);
+ snprintf(request_uri, PATHLEN, "REQUEST_URI=%s?%s", url, query);
+ }
+ else {
+ snprintf(request_uri, PATHLEN, "REQUEST_URI=%s", url);
+ }
+ env[envpos++] = request_uri;
+
+ snprintf(script_filename, PATHLEN, "SCRIPT_FILENAME=%s", file);
+ env[envpos++] = script_filename;
+
+ snprintf(script_name, PATHLEN, "SCRIPT_NAME=%s", url);
+ env[envpos++] = script_name;
+
+ if (query) {
+ snprintf(query_string, PATHLEN, "QUERY_STRING=%s", query);
+ env[envpos++] = query_string;
+ mk_api->mem_free(query);
+ }
+
+ if (mk_api->socket_ip_str(socket, &ptr, INET6_ADDRSTRLEN, &len) < 0)
+ tmpaddr[0] = '\0';
+ snprintf(remote_addr, INET6_ADDRSTRLEN+SHORTLEN, "REMOTE_ADDR=%s", tmpaddr);
+ env[envpos++] = remote_addr;
+
+ snprintf(remote_port, SHORTLEN, "REMOTE_PORT=%ld", sr->port);
+ env[envpos++] = remote_port;
+
+ if (sr->data.len) {
+ snprintf(content_length, SHORTLEN, "CONTENT_LENGTH=%lu", sr->data.len);
+ env[envpos++] = content_length;
+ }
+
+ if (sr->content_type.len) {
+ snprintf(content_type, SHORTLEN, "CONTENT_TYPE=%.*s", (int)sr->content_type.len, sr->content_type.data);
+ env[envpos++] = content_type;
+ }
+
+
+ /* Must be NULL-terminated */
+ env[envpos] = NULL;
+
+ /* pipes, from monkey's POV */
+ if (pipe(writepipe) || pipe(readpipe)) {
+ mk_err("Failed to create pipe");
+ return 403;
+ }
+
+ pid_t pid = vfork();
+ if (pid < 0) {
+ mk_err("Failed to fork");
+ return 403;
+ }
+
+ /* Child */
+ if (pid == 0) {
+ close(writepipe[1]);
+ close(readpipe[0]);
+
+ /* Our stdin is the read end of monkey's writing */
+ if (dup2(writepipe[0], 0) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(writepipe[0]);
+
+ /* Our stdout is the write end of monkey's reading */
+ if (dup2(readpipe[1], 1) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(readpipe[1]);
+
+ /* Our stderr goes to /dev/null */
+ devnull = open("/dev/null", O_WRONLY);
+ if (devnull == -1) {
+ perror("open");
+ _exit(1);
+ }
+
+ if (dup2(devnull, 2) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(devnull);
+
+ char *argv[3] = { NULL };
+
+ char *tmp = mk_api->str_dup(file);
+ if (chdir(dirname(tmp)))
+ _exit(1);
+
+ char *tmp2 = mk_api->str_dup(file);
+ argv[0] = basename(tmp2);
+
+ /* Restore signals for the child */
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ if (!interpreter) {
+ execve(file, argv, env);
+ }
+ else {
+ argv[0] = basename(interpreter);
+ argv[1] = (char *) file;
+ execve(interpreter, argv, env);
+ }
+ /* Exec failed, return */
+ _exit(1);
+ }
+
+ /* Yay me */
+ close(writepipe[0]);
+ close(readpipe[1]);
+
+ /* If we have POST data to write, spawn a thread to do that */
+ if (sr->data.len) {
+ struct post_t p;
+ pthread_t tid;
+
+ p.fd = writepipe[1];
+ p.buf = sr->data.data;
+ p.len = sr->data.len;
+
+ ret = mk_api->worker_spawn(cgi_write_post, &p, &tid);
+ if (ret != 0) {
+ return 403;
+ }
+ }
+ else {
+ close(writepipe[1]);
+ }
+
+ r = cgi_req_create(readpipe[0], socket, plugin, sr, cs);
+ if (!r) {
+ return 403;
+ }
+ r->child = pid;
+
+ /*
+ * Hang up?: by default Monkey assumes the CGI scripts generate
+ * content dynamically (no Content-Length header), so for such HTTP/1.0
+ * clients we should close the connection as KeepAlive is not supported
+ * by specification, only on HTTP/1.1 where the Chunked Transfer encoding
+ * exists.
+ */
+ if (r->sr->protocol >= MK_HTTP_PROTOCOL_11) {
+ r->hangup = MK_FALSE;
+ }
+
+ /* Set transfer encoding */
+ if (r->sr->protocol >= MK_HTTP_PROTOCOL_11 &&
+ (r->sr->headers.status < MK_REDIR_MULTIPLE ||
+ r->sr->headers.status > MK_REDIR_USE_PROXY)) {
+ r->sr->headers.transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
+ r->chunked = 1;
+ }
+
+ /* Register the 'request' context */
+ cgi_req_add(r);
+
+ /* Prepare the built-in event structure */
+ event = &r->event;
+ event->fd = readpipe[0];
+ event->type = MK_EVENT_CUSTOM;
+ event->mask = MK_EVENT_EMPTY;
+ event->data = r;
+ event->handler = cb_cgi_read;
+
+ /* Register the event into the worker event-loop */
+ ret = mk_api->ev_add(mk_api->sched_loop(),
+ readpipe[0],
+ MK_EVENT_CUSTOM, MK_EVENT_READ, r);
+ if (ret != 0) {
+ return 403;
+ }
+
+
+ /* XXX Fixme: this needs to be atomic */
+ requests_by_socket[socket] = r;
+ return 200;
+}
+
+int mk_cgi_plugin_init(struct plugin_api **api, char *confdir)
+{
+ struct rlimit lim;
+ (void) confdir;
+
+ mk_api = *api;
+ mk_list_init(&cgi_global_matches);
+ pthread_key_create(&cgi_request_list, NULL);
+
+ /*
+ * We try to perform some quick lookup over the list of CGI
+ * instances. We do this with a fixed length array, if you use CGI
+ * you don't care too much about performance anyways.
+ */
+ getrlimit(RLIMIT_NOFILE, &lim);
+ requests_by_socket = mk_api->mem_alloc_z(sizeof(struct cgi_request *) * lim.rlim_cur);
+
+ /* Make sure we act good if the child dies */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ return 0;
+}
+
+int mk_cgi_plugin_exit()
+{
+ regfree(&match_regex);
+ mk_api->mem_free(requests_by_socket);
+
+ return 0;
+}
+
+int mk_cgi_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ char *interpreter = NULL;
+ char *mimetype = NULL;
+ struct mk_vhost_handler_param *param;
+ (void) plugin;
+
+ const char *const file = sr->real_path.data;
+
+ if (!sr->file_info.is_file) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* start running the CGI */
+ if (cgi_req_get(cs->socket)) {
+ PLUGIN_TRACE("Error, someone tried to retry\n");
+ return MK_PLUGIN_RET_CONTINUE;
+ }
+
+ if (n_params > 0) {
+ /* Interpreter */
+ param = mk_api->handler_param_get(0, params);
+ if (param) {
+ interpreter = param->p.data;
+ }
+
+ /* Mimetype */
+ param = mk_api->handler_param_get(0, params);
+ if (param) {
+ mimetype = param->p.data;
+ }
+ }
+
+ int status = do_cgi(file, sr->uri_processed.data,
+ sr, cs, plugin, interpreter, mimetype);
+
+ /* These are just for the other plugins, such as logger; bogus data */
+ mk_api->header_set_http_status(sr, status);
+ if (status != 200) {
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ sr->headers.cgi = SH_CGI;
+ return MK_PLUGIN_RET_CONTINUE;
+}
+
+/*
+ * Invoked everytime a remote client drop the active connection, this
+ * callback is triggered by the Monkey Scheduler
+ */
+int mk_cgi_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ struct cgi_request *r;
+ (void) sr;
+ (void) plugin;
+
+ PLUGIN_TRACE("CGI / Parent connection closed (hangup)");
+ r = requests_by_socket[cs->socket];
+ if (!r) {
+ return -1;
+ }
+
+ r->active = MK_FALSE;
+ cgi_finish(r);
+ return 0;
+}
+
+void mk_cgi_worker_init()
+{
+ struct mk_list *list = mk_api->mem_alloc_z(sizeof(struct mk_list));
+
+ mk_list_init(list);
+ pthread_setspecific(cgi_request_list, (void *) list);
+}
+
+
+struct mk_plugin_stage mk_plugin_stage_cgi = {
+ .stage30 = &mk_cgi_stage30,
+ .stage30_hangup = &mk_cgi_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_cgi = {
+ /* Identification */
+ .shortname = "cgi",
+ .name = "Common Gateway Interface",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_cgi_plugin_init,
+ .exit_plugin = mk_cgi_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = mk_cgi_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_cgi
+};
diff --git a/fluent-bit/lib/monkey/plugins/cgi/cgi.h b/fluent-bit/lib/monkey/plugins/cgi/cgi.h
new file mode 100644
index 000000000..27123b7ae
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/cgi.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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.
+ */
+
+#ifndef MK_CGI_H
+#define MK_CGI_H
+
+#include <monkey/mk_api.h>
+
+#include <sys/types.h>
+#include <regex.h>
+#include <signal.h>
+#include <libgen.h>
+
+enum {
+ BUFLEN = 4096,
+ PATHLEN = 1024,
+ SHORTLEN = 64
+};
+
+regex_t match_regex;
+
+struct cgi_request **requests_by_socket;
+
+struct post_t {
+ int fd;
+ void *buf;
+ unsigned long len;
+};
+
+struct cgi_match_t {
+ regex_t match;
+ char *bin;
+ mk_ptr_t content_type;
+
+ struct mk_list _head;
+};
+
+struct cgi_vhost_t {
+ struct mk_vhost *host;
+ struct mk_list matches;
+};
+
+struct cgi_vhost_t *cgi_vhosts;
+struct mk_list cgi_global_matches;
+
+
+struct cgi_request {
+ /* Built-in reference for the event loop */
+ struct mk_event event;
+
+ char in_buf[BUFLEN];
+
+ struct mk_list _head;
+
+ struct mk_plugin *plugin;
+ struct mk_http_request *sr;
+ struct mk_http_session *cs;
+
+ unsigned int in_len;
+
+ int fd; /* Pipe the CGI proc */
+ int socket; /* Client connection */
+ int hangup; /* Should close connection when done ? */
+ int active; /* Active session ? */
+ pid_t child; /* child process ID */
+ unsigned char status_done;
+ unsigned char all_headers_done;
+ unsigned char chunked;
+};
+
+/* Global list per worker */
+pthread_key_t cgi_request_list;
+
+extern struct cgi_request **requests_by_socket;
+
+void cgi_finish(struct cgi_request *r);
+
+int swrite(const int fd, const void *buf, const size_t count);
+int channel_write(struct cgi_request *r, void *buf, size_t count);
+
+struct cgi_request *cgi_req_create(int fd, int socket,
+ struct mk_plugin *plugin,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs);
+void cgi_req_add(struct cgi_request *r);
+int cgi_req_del(struct cgi_request *r);
+
+// Get the CGI request by the client socket
+static inline struct cgi_request *cgi_req_get(int socket)
+{
+ struct cgi_request *r = requests_by_socket[socket];
+ return r;
+}
+
+// Get the CGI request by the CGI app's fd
+static inline struct cgi_request *cgi_req_get_by_fd(int fd)
+{
+ struct mk_list *list, *node;
+ struct cgi_request *r;
+
+ list = pthread_getspecific(cgi_request_list);
+ if (mk_list_is_empty(list) == 0)
+ return NULL;
+
+ mk_list_foreach(node, list) {
+ r = mk_list_entry(node, struct cgi_request, _head);
+ if (r->fd == fd)
+ return r;
+ }
+
+ return NULL;
+}
+
+int cb_cgi_read(void *data);
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/cgi/event.c b/fluent-bit/lib/monkey/plugins/cgi/event.c
new file mode 100644
index 000000000..b8d395260
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/event.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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 "cgi.h"
+
+/*
+ * The reason for this function is that some CGI apps
+ *
+ * use LFLF and some use CRLFCRLF.
+ *
+ * If that app then sends content that has the other break
+ * in the beginning, monkey can accidentally send part of the
+ * content as headers.
+ */
+
+static char *getearliestbreak(const char buf[], const unsigned bufsize,
+ unsigned char * const advance)
+ {
+ char * const crend = memmem(buf, bufsize, MK_IOV_CRLFCRLF,
+ sizeof(MK_IOV_CRLFCRLF) - 1);
+ char * const lfend = memmem(buf, bufsize, MK_IOV_LFLF,
+ sizeof(MK_IOV_LFLF) - 1);
+
+ if (!crend && !lfend)
+ return NULL;
+
+ /* If only one found, return that one */
+ if (!crend) {
+ *advance = 2;
+ return lfend;
+ }
+ if (!lfend)
+ return crend;
+
+ /* Both found, return the earlier one - the latter one is part of content */
+ if (lfend < crend) {
+ *advance = 2;
+ return lfend;
+ }
+ return crend;
+}
+
+int process_cgi_data(struct cgi_request *r)
+{
+ int ret;
+ int len;
+ int status;
+ char *buf = r->in_buf;
+ char *outptr = r->in_buf;
+ char *end;
+ char *endl;
+ unsigned char advance;
+
+ mk_api->socket_cork_flag(r->cs->socket, TCP_CORK_OFF);
+ if (!r->status_done && r->in_len >= 8) {
+ if (memcmp(buf, "Status: ", 8) == 0) {
+ status = atoi(buf + 8);
+ mk_api->header_set_http_status(r->sr, status);
+ endl = memchr(buf + 8, '\n', r->in_len - 8);
+ if (!endl) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ else {
+ endl++;
+ outptr = endl;
+ r->in_len -= endl - buf;
+ }
+ }
+ else if (memcmp(buf, "HTTP", 4) == 0) {
+ status = atoi(buf + 9);
+ mk_api->header_set_http_status(r->sr, status);
+
+ endl = memchr(buf + 8, '\n', r->in_len - 8);
+ if (!endl) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ else {
+ endl++;
+ outptr = endl;
+ r->in_len -= endl - buf;
+ }
+ }
+ mk_api->header_prepare(r->plugin, r->cs, r->sr);
+ r->status_done = 1;
+ }
+
+ if (!r->all_headers_done) {
+ advance = 4;
+
+ /* Write the rest of the headers without chunking */
+ end = getearliestbreak(outptr, r->in_len, &advance);
+ if (!end) {
+ /* Let's return until we have the headers break */
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ end += advance;
+ len = end - outptr;
+ channel_write(r, outptr, len);
+ outptr += len;
+ r->in_len -= len;
+
+ r->all_headers_done = 1;
+ if (r->in_len == 0) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ }
+
+ if (r->chunked) {
+ char tmp[16];
+ len = snprintf(tmp, 16, "%x\r\n", r->in_len);
+ ret = channel_write(r, tmp, len);
+ if (ret < 0)
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+
+ ret = channel_write(r, outptr, r->in_len);
+ if (ret < 0) {
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+
+ r->in_len = 0;
+ if (r->chunked) {
+ channel_write(r, MK_CRLF, 2);
+ }
+ return MK_PLUGIN_RET_EVENT_OWNED;
+}
+
+int cb_cgi_read(void *data)
+{
+ int n;
+ struct cgi_request *r = data;
+
+ if (r->active == MK_FALSE) {
+ return -1;
+ }
+
+ if ((BUFLEN - r->in_len) < 1) {
+ PLUGIN_TRACE("CLOSE BY SIZE");
+ cgi_finish(r);
+ return -1;
+ }
+
+ n = read(r->fd, r->in_buf + r->in_len, BUFLEN - r->in_len);
+ PLUGIN_TRACE("FD=%i CGI READ=%d", r->fd, n);
+ if (n <= 0) {
+ /* It most of cases this means the child process finished */
+ cgi_finish(r);
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+ r->in_len += n;
+ process_cgi_data(r);
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/cgi/request.c b/fluent-bit/lib/monkey/plugins/cgi/request.c
new file mode 100644
index 000000000..c51a13831
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/request.c
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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 "cgi.h"
+
+struct cgi_request *cgi_req_create(int fd, int socket,
+ struct mk_plugin *plugin,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs)
+{
+ struct cgi_request *cgi;
+
+ cgi = mk_api->mem_alloc_z(sizeof(struct cgi_request));
+ if (!cgi) {
+ return NULL;
+ }
+
+ cgi->fd = fd;
+ cgi->socket = socket;
+ cgi->plugin = plugin;
+ cgi->sr = sr;
+ cgi->cs = cs;
+ cgi->hangup = MK_TRUE;
+ cgi->active = MK_TRUE;
+ cgi->in_len = 0;
+
+ cgi->event.mask = MK_EVENT_EMPTY;
+ cgi->event.status = MK_EVENT_NONE;
+
+ return cgi;
+}
+
+void cgi_req_add(struct cgi_request *r)
+{
+ struct mk_list *list;
+
+ list = pthread_getspecific(cgi_request_list);
+ mk_list_add(&r->_head, list);
+}
+
+int cgi_req_del(struct cgi_request *r)
+{
+ PLUGIN_TRACE("Delete request child_fd=%i child_pid=%lu",
+ r->fd, r->child);
+
+ mk_list_del(&r->_head);
+ if (r->active == MK_FALSE) {
+ mk_api->sched_event_free(&r->event);
+ }
+ else {
+ mk_mem_free(r);
+ }
+
+ return 0;
+}