2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "server.h"
|
|
|
|
#include "stat_cache.h"
|
|
|
|
#include "keyvalue.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "connections.h"
|
|
|
|
#include "joblist.h"
|
2016-04-22 03:57:14 +00:00
|
|
|
#include "response.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "http_chunk.h"
|
2015-08-23 11:53:48 +00:00
|
|
|
#include "network_backends.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#include <sys/types.h>
|
2016-02-10 19:33:57 +00:00
|
|
|
#include "sys-mmap.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#ifdef __WIN32
|
2009-10-11 14:31:42 +00:00
|
|
|
# include <winsock2.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
#else
|
2009-10-11 14:31:42 +00:00
|
|
|
# include <sys/socket.h>
|
|
|
|
# include <sys/wait.h>
|
|
|
|
# include <netinet/in.h>
|
|
|
|
# include <arpa/inet.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fdevent.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2005-12-20 12:47:39 +00:00
|
|
|
#include <fcntl.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
enum {EOL_UNSET, EOL_N, EOL_RN};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char **ptr;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
size_t size;
|
|
|
|
size_t used;
|
|
|
|
} char_array;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pid_t *ptr;
|
|
|
|
size_t used;
|
|
|
|
size_t size;
|
|
|
|
} buffer_pid_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
array *cgi;
|
2009-07-04 20:23:00 +00:00
|
|
|
unsigned short execute_x_only;
|
2016-04-22 03:57:14 +00:00
|
|
|
unsigned short xsendfile_allow;
|
|
|
|
array *xsendfile_docroot;
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_config;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
|
|
|
buffer_pid_t cgi_pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *tmp_buf;
|
|
|
|
buffer *parse_response;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
plugin_config **config_storage;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
plugin_config conf;
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pid_t pid;
|
|
|
|
int fd;
|
2016-04-12 04:21:03 +00:00
|
|
|
int fdtocgi;
|
2005-02-20 14:27:00 +00:00
|
|
|
int fde_ndx; /* index into the fd-event buffer */
|
2016-04-12 04:21:03 +00:00
|
|
|
int fde_ndx_tocgi; /* index into the fd-event buffer */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
connection *remote_conn; /* dumb pointer */
|
|
|
|
plugin_data *plugin_data; /* dumb pointer */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *response;
|
|
|
|
buffer *response_header;
|
|
|
|
} handler_ctx;
|
|
|
|
|
2012-08-31 14:11:41 +00:00
|
|
|
static handler_ctx * cgi_handler_ctx_init(void) {
|
2005-02-20 14:27:00 +00:00
|
|
|
handler_ctx *hctx = calloc(1, sizeof(*hctx));
|
|
|
|
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(hctx);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
hctx->response = buffer_init();
|
|
|
|
hctx->response_header = buffer_init();
|
2016-04-07 00:46:43 +00:00
|
|
|
hctx->fd = -1;
|
2016-04-12 04:21:03 +00:00
|
|
|
hctx->fdtocgi = -1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return hctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cgi_handler_ctx_free(handler_ctx *hctx) {
|
|
|
|
buffer_free(hctx->response);
|
|
|
|
buffer_free(hctx->response_header);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(hctx);
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:29:02 +00:00
|
|
|
enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR};
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
INIT_FUNC(mod_cgi_init) {
|
|
|
|
plugin_data *p;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p = calloc(1, sizeof(*p));
|
|
|
|
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p->tmp_buf = buffer_init();
|
|
|
|
p->parse_response = buffer_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FREE_FUNC(mod_cgi_free) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (p->config_storage) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s = p->config_storage[i];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-05-14 09:38:33 +00:00
|
|
|
if (NULL == s) continue;
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
array_free(s->cgi);
|
2016-04-22 03:57:14 +00:00
|
|
|
array_free(s->xsendfile_docroot);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
free(p->config_storage);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
if (r->ptr) free(r->ptr);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_free(p->tmp_buf);
|
|
|
|
buffer_free(p->parse_response);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
size_t i = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
config_values_t cv[] = {
|
2005-02-20 14:27:00 +00:00
|
|
|
{ "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
|
2009-07-04 20:23:00 +00:00
|
|
|
{ "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
|
2016-05-07 05:18:34 +00:00
|
|
|
{ "cgi.x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
|
|
|
|
{ "cgi.x-sendfile-docroot", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
|
2005-02-20 14:27:00 +00:00
|
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!p) return HANDLER_ERROR;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2013-11-13 11:43:26 +00:00
|
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(p->config_storage);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
2015-11-07 12:51:11 +00:00
|
|
|
data_config const* config = (data_config const*)srv->config_context->data[i];
|
2005-02-20 14:27:00 +00:00
|
|
|
plugin_config *s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-08-16 13:07:46 +00:00
|
|
|
s = calloc(1, sizeof(plugin_config));
|
2014-02-16 13:08:20 +00:00
|
|
|
force_assert(s);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
s->cgi = array_init();
|
2009-07-04 20:23:00 +00:00
|
|
|
s->execute_x_only = 0;
|
2016-04-22 03:57:14 +00:00
|
|
|
s->xsendfile_allow= 0;
|
|
|
|
s->xsendfile_docroot = array_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
cv[0].destination = s->cgi;
|
2009-07-04 20:23:00 +00:00
|
|
|
cv[1].destination = &(s->execute_x_only);
|
2016-04-22 03:57:14 +00:00
|
|
|
cv[2].destination = &(s->xsendfile_allow);
|
|
|
|
cv[3].destination = s->xsendfile_docroot;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p->config_storage[i] = s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-11-07 12:51:11 +00:00
|
|
|
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
2016-04-22 03:57:14 +00:00
|
|
|
|
|
|
|
if (s->xsendfile_docroot->used) {
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < s->xsendfile_docroot->used; ++j) {
|
|
|
|
data_string *ds = (data_string *)s->xsendfile_docroot->data[j];
|
|
|
|
if (ds->type != TYPE_STRING) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s",
|
|
|
|
"unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )");
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
if (ds->value->ptr[0] != '/') {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "SBs",
|
|
|
|
"cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
buffer_path_simplify(ds->value, ds->value);
|
|
|
|
buffer_append_slash(ds->value);
|
|
|
|
}
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
|
|
|
|
int m = -1;
|
|
|
|
size_t i;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
for (i = 0; i < r->used; i++) {
|
|
|
|
if (r->ptr[i] > m) m = r->ptr[i];
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (r->size == 0) {
|
|
|
|
r->size = 16;
|
|
|
|
r->ptr = malloc(sizeof(*r->ptr) * r->size);
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(r->ptr);
|
2005-02-20 14:27:00 +00:00
|
|
|
} else if (r->used == r->size) {
|
|
|
|
r->size += 16;
|
|
|
|
r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
|
2015-10-16 19:44:06 +00:00
|
|
|
force_assert(r->ptr);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
r->ptr[r->used++] = pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
|
|
|
|
size_t i;
|
|
|
|
buffer_pid_t *r = &(p->cgi_pid);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
for (i = 0; i < r->used; i++) {
|
|
|
|
if (r->ptr[i] == pid) break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (i != r->used) {
|
|
|
|
/* found */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (i != r->used - 1) {
|
|
|
|
r->ptr[i] = r->ptr[r->used - 1];
|
|
|
|
}
|
|
|
|
r->used--;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
|
2005-02-20 14:27:00 +00:00
|
|
|
char *ns;
|
|
|
|
const char *s;
|
|
|
|
int line = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
UNUSED(srv);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 12:37:10 +00:00
|
|
|
buffer_copy_buffer(p->parse_response, in);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
for (s = p->parse_response->ptr;
|
2007-08-17 21:04:20 +00:00
|
|
|
NULL != (ns = strchr(s, '\n'));
|
|
|
|
s = ns + 1, line++) {
|
2005-02-20 14:27:00 +00:00
|
|
|
const char *key, *value;
|
|
|
|
int key_len;
|
|
|
|
data_string *ds;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/* strip the \n */
|
2005-02-20 14:27:00 +00:00
|
|
|
ns[0] = '\0';
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
|
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
if (line == 0 &&
|
2005-02-20 14:27:00 +00:00
|
|
|
0 == strncmp(s, "HTTP/1.", 7)) {
|
|
|
|
/* non-parsed header ... we parse them anyway */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if ((s[7] == '1' ||
|
|
|
|
s[7] == '0') &&
|
|
|
|
s[8] == ' ') {
|
|
|
|
int status;
|
|
|
|
/* after the space should be a status code for us */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
status = strtol(s+9, NULL, 10);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-18 11:35:27 +00:00
|
|
|
if (status >= 100 &&
|
|
|
|
status < 1000) {
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we expected 3 digits and didn't got them */
|
|
|
|
con->parsed_response |= HTTP_STATUS;
|
|
|
|
con->http_status = status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2007-08-17 21:04:20 +00:00
|
|
|
/* parse the headers */
|
2005-02-20 14:27:00 +00:00
|
|
|
key = s;
|
|
|
|
if (NULL == (value = strchr(s, ':'))) {
|
|
|
|
/* we expect: "<key>: <value>\r\n" */
|
|
|
|
continue;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
key_len = value - key;
|
|
|
|
value += 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* skip LWS */
|
|
|
|
while (*value == ' ' || *value == '\t') value++;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
|
|
|
|
ds = data_response_init();
|
|
|
|
}
|
|
|
|
buffer_copy_string_len(ds->key, key, key_len);
|
|
|
|
buffer_copy_string(ds->value, value);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
array_insert_unique(con->response.headers, (data_unset *)ds);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
switch(key_len) {
|
|
|
|
case 4:
|
|
|
|
if (0 == strncasecmp(key, "Date", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_DATE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (0 == strncasecmp(key, "Status", key_len)) {
|
2016-03-26 10:58:49 +00:00
|
|
|
int status = strtol(value, NULL, 10);
|
|
|
|
if (status >= 100 && status < 1000) {
|
|
|
|
con->http_status = status;
|
|
|
|
con->parsed_response |= HTTP_STATUS;
|
|
|
|
} else {
|
|
|
|
con->http_status = 502;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (0 == strncasecmp(key, "Location", key_len)) {
|
|
|
|
con->parsed_response |= HTTP_LOCATION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (0 == strncasecmp(key, "Connection", key_len)) {
|
|
|
|
con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
|
|
|
|
con->parsed_response |= HTTP_CONNECTION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if (0 == strncasecmp(key, "Content-Length", key_len)) {
|
2016-03-26 10:58:49 +00:00
|
|
|
con->response.content_length = strtoul(value, NULL, 10);
|
2005-02-20 14:27:00 +00:00
|
|
|
con->parsed_response |= HTTP_CONTENT_LENGTH;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* CGI/1.1 rev 03 - 7.2.1.2 */
|
|
|
|
if ((con->parsed_response & HTTP_LOCATION) &&
|
|
|
|
!(con->parsed_response & HTTP_STATUS)) {
|
|
|
|
con->http_status = 302;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
|
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
connection *con = hctx->remote_conn;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
while(1) {
|
|
|
|
int n;
|
2011-03-13 17:44:39 +00:00
|
|
|
int toread;
|
|
|
|
|
|
|
|
#if defined(__WIN32)
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, 4 * 1024);
|
2011-03-13 17:44:39 +00:00
|
|
|
#else
|
2016-07-17 20:13:31 +00:00
|
|
|
if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) {
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, 4 * 1024);
|
2011-03-13 17:44:39 +00:00
|
|
|
} else {
|
|
|
|
if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
|
2015-02-08 19:10:39 +00:00
|
|
|
buffer_string_prepare_copy(hctx->response, toread);
|
2011-03-13 17:44:39 +00:00
|
|
|
}
|
|
|
|
#endif
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
|
|
|
|
if (errno == EAGAIN || errno == EINTR) {
|
|
|
|
/* would block, wait for signal */
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
2005-02-20 14:27:00 +00:00
|
|
|
return FDEVENT_HANDLED_NOT_FINISHED;
|
|
|
|
}
|
|
|
|
/* error */
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (n == 0) {
|
|
|
|
/* read finished */
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 19:10:44 +00:00
|
|
|
buffer_commit(hctx->response, n);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* split header from body */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (con->file_started == 0) {
|
2007-08-17 21:04:20 +00:00
|
|
|
int is_header = 0;
|
|
|
|
int is_header_end = 0;
|
|
|
|
size_t last_eol = 0;
|
2015-02-08 19:10:44 +00:00
|
|
|
size_t i, header_len;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_append_string_buffer(hctx->response_header, hctx->response);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* we have to handle a few cases:
|
|
|
|
*
|
|
|
|
* nph:
|
|
|
|
*
|
|
|
|
* HTTP/1.0 200 Ok\n
|
|
|
|
* Header: Value\n
|
|
|
|
* \n
|
|
|
|
*
|
|
|
|
* CGI:
|
|
|
|
* Header: Value\n
|
|
|
|
* Status: 200\n
|
|
|
|
* \n
|
|
|
|
*
|
|
|
|
* and different mixes of \n and \r\n combinations
|
|
|
|
*
|
|
|
|
* Some users also forget about CGI and just send a response and hope
|
|
|
|
* we handle it. No headers, no header-content seperator
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* nph (non-parsed headers) */
|
2007-08-17 21:04:20 +00:00
|
|
|
if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
|
|
|
header_len = buffer_string_length(hctx->response_header);
|
|
|
|
for (i = 0; !is_header_end && i < header_len; i++) {
|
2007-08-17 21:04:20 +00:00
|
|
|
char c = hctx->response_header->ptr[i];
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case ':':
|
|
|
|
/* we found a colon
|
|
|
|
*
|
|
|
|
* looks like we have a normal header
|
|
|
|
*/
|
|
|
|
is_header = 1;
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
/* EOL */
|
|
|
|
if (is_header == 0) {
|
|
|
|
/* we got a EOL but we don't seem to got a HTTP header */
|
|
|
|
|
|
|
|
is_header_end = 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* check if we saw a \n(\r)?\n sequence
|
|
|
|
*/
|
|
|
|
if (last_eol > 0 &&
|
|
|
|
((i - last_eol == 1) ||
|
|
|
|
(i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
|
|
|
|
is_header_end = 1;
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
last_eol = i;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (is_header_end) {
|
|
|
|
if (!is_header) {
|
2005-02-20 14:27:00 +00:00
|
|
|
/* no header, but a body */
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
} else {
|
2007-08-17 21:04:20 +00:00
|
|
|
const char *bstart;
|
|
|
|
size_t blen;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
|
|
|
/* the body starts after the EOL */
|
|
|
|
bstart = hctx->response_header->ptr + i;
|
|
|
|
blen = header_len - i;
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/**
|
|
|
|
* i still points to the char after the terminating EOL EOL
|
|
|
|
*
|
|
|
|
* put it on the last \n again
|
|
|
|
*/
|
|
|
|
i--;
|
2015-02-08 19:10:44 +00:00
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
/* string the last \r?\n */
|
|
|
|
if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
|
|
|
|
i--;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2015-02-08 19:10:44 +00:00
|
|
|
buffer_string_set_length(hctx->response_header, i);
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* parse the response header */
|
2007-08-17 21:04:20 +00:00
|
|
|
cgi_response_parse(srv, con, p, hctx->response_header);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-07-14 18:29:02 +00:00
|
|
|
if (con->http_status >= 300 && con->http_status < 400) {
|
|
|
|
/*(con->parsed_response & HTTP_LOCATION)*/
|
|
|
|
data_string *ds;
|
|
|
|
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
|
|
|
|
&& ds->value->ptr[0] == '/') {
|
|
|
|
if (++con->loops_per_request > 5) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
|
|
|
|
con->http_status = 500; /* Internal Server Error */
|
|
|
|
con->mode = DIRECT;
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_copy_buffer(con->request.uri, ds->value);
|
|
|
|
|
|
|
|
if (con->request.content_length) {
|
|
|
|
if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
|
|
|
|
con->keep_alive = 0;
|
|
|
|
}
|
|
|
|
con->request.content_length = 0;
|
|
|
|
chunkqueue_reset(con->request_content_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (con->http_status != 307 && con->http_status != 308) {
|
|
|
|
/* Note: request body (if any) sent to initial dynamic handler
|
|
|
|
* and is not available to the internal redirect */
|
|
|
|
con->request.http_method = HTTP_METHOD_GET;
|
|
|
|
}
|
|
|
|
|
|
|
|
connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
|
|
|
|
|
|
|
|
con->mode = DIRECT;
|
|
|
|
return FDEVENT_HANDLED_COMEBACK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-22 03:57:14 +00:00
|
|
|
if (p->conf.xsendfile_allow) {
|
|
|
|
data_string *ds;
|
|
|
|
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
|
|
|
|
http_response_xsendfile(srv, con, ds->value, p->conf.xsendfile_docroot);
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-17 21:04:20 +00:00
|
|
|
if (blen > 0) {
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
con->file_started = 1;
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
} else {
|
|
|
|
/*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
|
|
|
|
if (header_len > MAX_HTTP_REQUEST_HEADER) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
|
|
|
|
con->http_status = 502; /* Bad Gateway */
|
|
|
|
con->mode = DIRECT;
|
|
|
|
return FDEVENT_HANDLED_FINISHED;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-05-25 20:45:09 +00:00
|
|
|
if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
|
|
|
|
return FDEVENT_HANDLED_ERROR;
|
|
|
|
}
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
|
|
|
|
&& chunkqueue_length(con->write_queue) > 65536 - 4096) {
|
|
|
|
if (!con->is_writable) {
|
|
|
|
/*(defer removal of FDEVENT_IN interest since
|
|
|
|
* connection_state_machine() might be able to send data
|
|
|
|
* immediately, unless !con->is_writable, where
|
|
|
|
* connection_state_machine() might not loop back to call
|
|
|
|
* mod_cgi_handle_subrequest())*/
|
|
|
|
fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
#if 0
|
2005-02-20 14:27:00 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
|
|
|
|
#endif
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return FDEVENT_HANDLED_NOT_FINISHED;
|
|
|
|
}
|
|
|
|
|
2016-04-12 04:21:03 +00:00
|
|
|
static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) {
|
|
|
|
/*(closes only hctx->fdtocgi)*/
|
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi);
|
|
|
|
fdevent_unregister(srv->ev, hctx->fdtocgi);
|
|
|
|
|
|
|
|
if (close(hctx->fdtocgi)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", hctx->fdtocgi, strerror(errno));
|
|
|
|
}
|
|
|
|
hctx->fdtocgi = -1;
|
|
|
|
}
|
|
|
|
|
2016-04-07 00:46:43 +00:00
|
|
|
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
|
2005-02-20 14:27:00 +00:00
|
|
|
int status;
|
|
|
|
pid_t pid;
|
2016-03-04 18:54:26 +00:00
|
|
|
plugin_data *p = hctx->plugin_data;
|
|
|
|
connection *con = hctx->remote_conn;
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
#ifndef __WIN32
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* the connection to the browser went away, but we still have a connection
|
2006-10-04 13:26:23 +00:00
|
|
|
* to the CGI script
|
2005-02-20 14:27:00 +00:00
|
|
|
*
|
|
|
|
* close cgi-connection
|
|
|
|
*/
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-06-04 15:19:50 +00:00
|
|
|
if (hctx->fd != -1) {
|
|
|
|
/* close connection to the cgi-script */
|
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
|
|
|
|
fdevent_unregister(srv->ev, hctx->fd);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-06-04 15:19:50 +00:00
|
|
|
if (close(hctx->fd)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
|
|
|
|
}
|
2016-04-12 04:21:03 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-04-12 04:21:03 +00:00
|
|
|
if (hctx->fdtocgi != -1) {
|
|
|
|
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
pid = hctx->pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
con->plugin_ctx[p->id] = NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
cgi_handler_ctx_free(hctx);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* if waitpid hasn't been called by response.c yet, do it here */
|
|
|
|
if (pid) {
|
|
|
|
/* check if the CGI-script is already gone */
|
|
|
|
switch(waitpid(pid, &status, WNOHANG)) {
|
|
|
|
case 0:
|
|
|
|
/* not finished yet */
|
|
|
|
#if 0
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
/* */
|
|
|
|
if (errno == EINTR) break;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* errno == ECHILD happens if _subrequest catches the process-status before
|
2005-02-20 14:27:00 +00:00
|
|
|
* we have read the response of the cgi process
|
2006-10-04 13:26:23 +00:00
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
* -> catch status
|
|
|
|
* -> WAIT_FOR_EVENT
|
|
|
|
* -> read response
|
|
|
|
* -> we get here with waitpid == ECHILD
|
2006-10-04 13:26:23 +00:00
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
*/
|
2016-03-04 18:54:24 +00:00
|
|
|
if (errno != ECHILD) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
|
2005-04-20 18:07:58 +00:00
|
|
|
}
|
2016-03-04 18:54:24 +00:00
|
|
|
/* anyway: don't wait for it anymore */
|
|
|
|
pid = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2005-02-20 14:27:00 +00:00
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
#if 0
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-03-04 18:54:24 +00:00
|
|
|
pid = 0;
|
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-03-04 18:54:24 +00:00
|
|
|
if (pid) {
|
|
|
|
kill(pid, SIGTERM);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-03-04 18:54:24 +00:00
|
|
|
/* cgi-script is still alive, queue the PID for removal */
|
|
|
|
cgi_pid_add(srv, p, pid);
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|