version 1.1, 2013/10/14 10:32:48
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "server.h" |
#include "server.h" |
#include "stat_cache.h" |
#include "stat_cache.h" |
#include "keyvalue.h" |
#include "keyvalue.h" |
#include "log.h" |
#include "log.h" |
#include "connections.h" |
#include "connections.h" |
#include "joblist.h" |
#include "joblist.h" |
|
#include "response.h" |
#include "http_chunk.h" |
#include "http_chunk.h" |
|
#include "network_backends.h" |
|
|
#include "plugin.h" |
#include "plugin.h" |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
|
#include "sys-mmap.h" |
|
|
#ifdef __WIN32 |
#ifdef __WIN32 |
# include <winsock2.h> |
# include <winsock2.h> |
#else |
#else |
# include <sys/socket.h> |
# include <sys/socket.h> |
# include <sys/wait.h> |
# include <sys/wait.h> |
# include <sys/mman.h> |
|
# include <netinet/in.h> |
# include <netinet/in.h> |
# include <arpa/inet.h> |
# include <arpa/inet.h> |
#endif |
#endif |
Line 32
|
Line 36
|
#include <stdio.h> |
#include <stdio.h> |
#include <fcntl.h> |
#include <fcntl.h> |
|
|
#ifdef HAVE_SYS_FILIO_H |
|
# include <sys/filio.h> |
|
#endif |
|
|
|
#include "version.h" |
|
|
|
enum {EOL_UNSET, EOL_N, EOL_RN}; |
enum {EOL_UNSET, EOL_N, EOL_RN}; |
|
|
typedef struct { |
typedef struct { |
Line 56 typedef struct {
|
Line 54 typedef struct {
|
typedef struct { |
typedef struct { |
array *cgi; |
array *cgi; |
unsigned short execute_x_only; |
unsigned short execute_x_only; |
|
unsigned short xsendfile_allow; |
|
array *xsendfile_docroot; |
} plugin_config; |
} plugin_config; |
|
|
typedef struct { |
typedef struct { |
Line 73 typedef struct {
|
Line 73 typedef struct {
|
typedef struct { |
typedef struct { |
pid_t pid; |
pid_t pid; |
int fd; |
int fd; |
|
int fdtocgi; |
int fde_ndx; /* index into the fd-event buffer */ |
int fde_ndx; /* index into the fd-event buffer */ |
|
int fde_ndx_tocgi; /* index into the fd-event buffer */ |
|
|
connection *remote_conn; /* dumb pointer */ |
connection *remote_conn; /* dumb pointer */ |
plugin_data *plugin_data; /* dumb pointer */ |
plugin_data *plugin_data; /* dumb pointer */ |
Line 85 typedef struct {
|
Line 87 typedef struct {
|
static handler_ctx * cgi_handler_ctx_init(void) { |
static handler_ctx * cgi_handler_ctx_init(void) { |
handler_ctx *hctx = calloc(1, sizeof(*hctx)); |
handler_ctx *hctx = calloc(1, sizeof(*hctx)); |
|
|
assert(hctx); | force_assert(hctx); |
|
|
hctx->response = buffer_init(); |
hctx->response = buffer_init(); |
hctx->response_header = buffer_init(); |
hctx->response_header = buffer_init(); |
|
hctx->fd = -1; |
|
hctx->fdtocgi = -1; |
|
|
return hctx; |
return hctx; |
} |
} |
Line 100 static void cgi_handler_ctx_free(handler_ctx *hctx) {
|
Line 104 static void cgi_handler_ctx_free(handler_ctx *hctx) {
|
free(hctx); |
free(hctx); |
} |
} |
|
|
enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR}; | enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR}; |
|
|
INIT_FUNC(mod_cgi_init) { |
INIT_FUNC(mod_cgi_init) { |
plugin_data *p; |
plugin_data *p; |
|
|
p = calloc(1, sizeof(*p)); |
p = calloc(1, sizeof(*p)); |
|
|
assert(p); | force_assert(p); |
|
|
p->tmp_buf = buffer_init(); |
p->tmp_buf = buffer_init(); |
p->parse_response = buffer_init(); |
p->parse_response = buffer_init(); |
Line 127 FREE_FUNC(mod_cgi_free) {
|
Line 131 FREE_FUNC(mod_cgi_free) {
|
for (i = 0; i < srv->config_context->used; i++) { |
for (i = 0; i < srv->config_context->used; i++) { |
plugin_config *s = p->config_storage[i]; |
plugin_config *s = p->config_storage[i]; |
|
|
|
if (NULL == s) continue; |
|
|
array_free(s->cgi); |
array_free(s->cgi); |
|
array_free(s->xsendfile_docroot); |
|
|
free(s); |
free(s); |
} |
} |
Line 152 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
|
Line 159 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
|
config_values_t cv[] = { |
config_values_t cv[] = { |
{ "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
{ "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
{ "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ |
{ "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ |
|
{ "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 */ |
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET} |
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET} |
}; |
}; |
|
|
if (!p) return HANDLER_ERROR; |
if (!p) return HANDLER_ERROR; |
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); | p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); |
| force_assert(p->config_storage); |
|
|
for (i = 0; i < srv->config_context->used; i++) { |
for (i = 0; i < srv->config_context->used; i++) { |
|
data_config const* config = (data_config const*)srv->config_context->data[i]; |
plugin_config *s; |
plugin_config *s; |
|
|
s = calloc(1, sizeof(plugin_config)); |
s = calloc(1, sizeof(plugin_config)); |
assert(s); | force_assert(s); |
|
|
s->cgi = array_init(); |
s->cgi = array_init(); |
s->execute_x_only = 0; |
s->execute_x_only = 0; |
|
s->xsendfile_allow= 0; |
|
s->xsendfile_docroot = array_init(); |
|
|
cv[0].destination = s->cgi; |
cv[0].destination = s->cgi; |
cv[1].destination = &(s->execute_x_only); |
cv[1].destination = &(s->execute_x_only); |
|
cv[2].destination = &(s->xsendfile_allow); |
|
cv[3].destination = s->xsendfile_docroot; |
|
|
p->config_storage[i] = s; |
p->config_storage[i] = s; |
|
|
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { | if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { |
return HANDLER_ERROR; |
return HANDLER_ERROR; |
} |
} |
|
|
|
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); |
|
} |
|
} |
} |
} |
|
|
return HANDLER_GO_ON; |
return HANDLER_GO_ON; |
Line 196 static int cgi_pid_add(server *srv, plugin_data *p, pi
|
Line 230 static int cgi_pid_add(server *srv, plugin_data *p, pi
|
if (r->size == 0) { |
if (r->size == 0) { |
r->size = 16; |
r->size = 16; |
r->ptr = malloc(sizeof(*r->ptr) * r->size); |
r->ptr = malloc(sizeof(*r->ptr) * r->size); |
|
force_assert(r->ptr); |
} else if (r->used == r->size) { |
} else if (r->used == r->size) { |
r->size += 16; |
r->size += 16; |
r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); |
r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); |
|
force_assert(r->ptr); |
} |
} |
|
|
r->ptr[r->used++] = pid; |
r->ptr[r->used++] = pid; |
Line 235 static int cgi_response_parse(server *srv, connection
|
Line 271 static int cgi_response_parse(server *srv, connection
|
|
|
UNUSED(srv); |
UNUSED(srv); |
|
|
buffer_copy_string_buffer(p->parse_response, in); | buffer_copy_buffer(p->parse_response, in); |
|
|
for (s = p->parse_response->ptr; |
for (s = p->parse_response->ptr; |
NULL != (ns = strchr(s, '\n')); |
NULL != (ns = strchr(s, '\n')); |
Line 298 static int cgi_response_parse(server *srv, connection
|
Line 334 static int cgi_response_parse(server *srv, connection
|
break; |
break; |
case 6: |
case 6: |
if (0 == strncasecmp(key, "Status", key_len)) { |
if (0 == strncasecmp(key, "Status", key_len)) { |
con->http_status = strtol(value, NULL, 10); | int status = strtol(value, NULL, 10); |
con->parsed_response |= HTTP_STATUS; | if (status >= 100 && status < 1000) { |
| con->http_status = status; |
| con->parsed_response |= HTTP_STATUS; |
| } else { |
| con->http_status = 502; |
| } |
} |
} |
break; |
break; |
case 8: |
case 8: |
Line 315 static int cgi_response_parse(server *srv, connection
|
Line 356 static int cgi_response_parse(server *srv, connection
|
break; |
break; |
case 14: |
case 14: |
if (0 == strncasecmp(key, "Content-Length", key_len)) { |
if (0 == strncasecmp(key, "Content-Length", key_len)) { |
con->response.content_length = strtol(value, NULL, 10); | con->response.content_length = strtoul(value, NULL, 10); |
con->parsed_response |= HTTP_CONTENT_LENGTH; |
con->parsed_response |= HTTP_CONTENT_LENGTH; |
} |
} |
break; |
break; |
Line 344 static int cgi_demux_response(server *srv, handler_ctx
|
Line 385 static int cgi_demux_response(server *srv, handler_ctx
|
int toread; |
int toread; |
|
|
#if defined(__WIN32) |
#if defined(__WIN32) |
buffer_prepare_copy(hctx->response, 4 * 1024); | buffer_string_prepare_copy(hctx->response, 4 * 1024); |
#else |
#else |
if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { | if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) { |
buffer_prepare_copy(hctx->response, 4 * 1024); | buffer_string_prepare_copy(hctx->response, 4 * 1024); |
} else { |
} else { |
if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; |
if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; |
buffer_prepare_copy(hctx->response, toread + 1); | buffer_string_prepare_copy(hctx->response, toread); |
} |
} |
#endif |
#endif |
|
|
if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { |
if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { |
if (errno == EAGAIN || errno == EINTR) { |
if (errno == EAGAIN || errno == EINTR) { |
/* would block, wait for signal */ |
/* would block, wait for signal */ |
|
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); |
return FDEVENT_HANDLED_NOT_FINISHED; |
return FDEVENT_HANDLED_NOT_FINISHED; |
} |
} |
/* error */ |
/* error */ |
Line 366 static int cgi_demux_response(server *srv, handler_ctx
|
Line 408 static int cgi_demux_response(server *srv, handler_ctx
|
|
|
if (n == 0) { |
if (n == 0) { |
/* read finished */ |
/* read finished */ |
|
|
con->file_finished = 1; |
|
|
|
/* send final chunk */ |
|
http_chunk_append_mem(srv, con, NULL, 0); |
|
joblist_append(srv, con); |
|
|
|
return FDEVENT_HANDLED_FINISHED; |
return FDEVENT_HANDLED_FINISHED; |
} |
} |
|
|
hctx->response->ptr[n] = '\0'; | buffer_commit(hctx->response, n); |
hctx->response->used = n+1; | |
|
|
/* split header from body */ |
/* split header from body */ |
|
|
Line 385 static int cgi_demux_response(server *srv, handler_ctx
|
Line 419 static int cgi_demux_response(server *srv, handler_ctx
|
int is_header = 0; |
int is_header = 0; |
int is_header_end = 0; |
int is_header_end = 0; |
size_t last_eol = 0; |
size_t last_eol = 0; |
size_t i; | size_t i, header_len; |
|
|
buffer_append_string_buffer(hctx->response_header, hctx->response); |
buffer_append_string_buffer(hctx->response_header, hctx->response); |
|
|
Line 412 static int cgi_demux_response(server *srv, handler_ctx
|
Line 446 static int cgi_demux_response(server *srv, handler_ctx
|
|
|
/* nph (non-parsed headers) */ |
/* nph (non-parsed headers) */ |
if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1; |
if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1; |
| |
for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) { | header_len = buffer_string_length(hctx->response_header); |
| for (i = 0; !is_header_end && i < header_len; i++) { |
char c = hctx->response_header->ptr[i]; |
char c = hctx->response_header->ptr[i]; |
|
|
switch (c) { |
switch (c) { |
Line 453 static int cgi_demux_response(server *srv, handler_ctx
|
Line 488 static int cgi_demux_response(server *srv, handler_ctx
|
if (is_header_end) { |
if (is_header_end) { |
if (!is_header) { |
if (!is_header) { |
/* no header, but a body */ |
/* no header, but a body */ |
| if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { |
if (con->request.http_version == HTTP_VERSION_1_1) { | return FDEVENT_HANDLED_ERROR; |
con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; | |
} |
} |
|
|
http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); |
|
joblist_append(srv, con); |
|
} else { |
} else { |
const char *bstart; |
const char *bstart; |
size_t blen; |
size_t blen; |
| |
| /* the body starts after the EOL */ |
| bstart = hctx->response_header->ptr + i; |
| blen = header_len - i; |
| |
/** |
/** |
* i still points to the char after the terminating EOL EOL |
* i still points to the char after the terminating EOL EOL |
* |
* |
* put it on the last \n again |
* put it on the last \n again |
*/ |
*/ |
i--; |
i--; |
| |
/* the body starts after the EOL */ | |
bstart = hctx->response_header->ptr + (i + 1); | |
blen = (hctx->response_header->used - 1) - (i + 1); | |
| |
/* string the last \r?\n */ |
/* string the last \r?\n */ |
if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) { |
if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) { |
i--; |
i--; |
} |
} |
|
|
hctx->response_header->ptr[i] = '\0'; | buffer_string_set_length(hctx->response_header, i); |
hctx->response_header->used = i + 1; /* the string + \0 */ | |
| |
/* parse the response header */ |
/* parse the response header */ |
cgi_response_parse(srv, con, p, hctx->response_header); |
cgi_response_parse(srv, con, p, hctx->response_header); |
|
|
/* enable chunked-transfer-encoding */ | if (con->http_status >= 300 && con->http_status < 400) { |
if (con->request.http_version == HTTP_VERSION_1_1 && | /*(con->parsed_response & HTTP_LOCATION)*/ |
!(con->parsed_response & HTTP_CONTENT_LENGTH)) { | data_string *ds; |
con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; | 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; |
| } |
} |
} |
|
|
|
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; |
|
} |
|
} |
|
|
if (blen > 0) { |
if (blen > 0) { |
http_chunk_append_mem(srv, con, bstart, blen + 1); | if (0 != http_chunk_append_mem(srv, con, bstart, blen)) { |
joblist_append(srv, con); | return FDEVENT_HANDLED_ERROR; |
| } |
} |
} |
} |
} |
|
|
con->file_started = 1; |
con->file_started = 1; |
|
} 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; |
|
} |
} |
} |
} else { |
} else { |
http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); | if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { |
joblist_append(srv, con); | return FDEVENT_HANDLED_ERROR; |
| } |
| 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; |
| } |
} |
} |
|
|
#if 0 |
#if 0 |
Line 513 static int cgi_demux_response(server *srv, handler_ctx
|
Line 602 static int cgi_demux_response(server *srv, handler_ctx
|
return FDEVENT_HANDLED_NOT_FINISHED; |
return FDEVENT_HANDLED_NOT_FINISHED; |
} |
} |
|
|
static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { | 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; |
| } |
| |
| static void cgi_connection_close(server *srv, handler_ctx *hctx) { |
int status; |
int status; |
pid_t pid; |
pid_t pid; |
plugin_data *p; | plugin_data *p = hctx->plugin_data; |
connection *con; | connection *con = hctx->remote_conn; |
|
|
if (NULL == hctx) return HANDLER_GO_ON; |
|
|
|
p = hctx->plugin_data; |
|
con = hctx->remote_conn; |
|
|
|
if (con->mode != p->id) return HANDLER_GO_ON; |
|
|
|
#ifndef __WIN32 |
#ifndef __WIN32 |
|
|
/* the connection to the browser went away, but we still have a connection |
/* the connection to the browser went away, but we still have a connection |
Line 542 static handler_t cgi_connection_close(server *srv, han
|
Line 635 static handler_t cgi_connection_close(server *srv, han
|
if (close(hctx->fd)) { |
if (close(hctx->fd)) { |
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); |
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); |
} |
} |
|
} |
|
|
hctx->fd = -1; | if (hctx->fdtocgi != -1) { |
hctx->fde_ndx = -1; | cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ |
} |
} |
|
|
pid = hctx->pid; |
pid = hctx->pid; |
|
|
con->plugin_ctx[p->id] = NULL; |
con->plugin_ctx[p->id] = NULL; |
|
|
/* is this a good idea ? */ |
|
cgi_handler_ctx_free(hctx); |
cgi_handler_ctx_free(hctx); |
|
|
/* if waitpid hasn't been called by response.c yet, do it here */ |
/* if waitpid hasn't been called by response.c yet, do it here */ |
Line 578 static handler_t cgi_connection_close(server *srv, han
|
Line 671 static handler_t cgi_connection_close(server *srv, han
|
* -> we get here with waitpid == ECHILD |
* -> we get here with waitpid == ECHILD |
* |
* |
*/ |
*/ |
if (errno == ECHILD) return HANDLER_GO_ON; | if (errno != ECHILD) { |
| log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); |
log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); | |
return HANDLER_ERROR; | |
default: | |
/* Send an error if we haven't sent any data yet */ | |
if (0 == con->file_started) { | |
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); | |
con->http_status = 500; | |
con->mode = DIRECT; | |
} else { | |
con->file_finished = 1; | |
} |
} |
| /* anyway: don't wait for it anymore */ |
| pid = 0; |
| break; |
| default: |
if (WIFEXITED(status)) { |
if (WIFEXITED(status)) { |
#if 0 |
#if 0 |
log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); |
log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); |
#endif |
#endif |
return HANDLER_GO_ON; |
|
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); |
log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); |
return HANDLER_GO_ON; |
|
} |
} |
|
|
|
pid = 0; |
|
break; |
} |
} |
|
|
|
if (pid) { |
|
kill(pid, SIGTERM); |
|
|
kill(pid, SIGTERM); | /* cgi-script is still alive, queue the PID for removal */ |
| cgi_pid_add(srv, p, pid); |
/* cgi-script is still alive, queue the PID for removal */ | } |
cgi_pid_add(srv, p, pid); | |
} |
} |
#endif |
#endif |
return HANDLER_GO_ON; | |
| /* finish response (if not already con->file_started, con->file_finished) */ |
| if (con->mode == p->id) { |
| http_response_backend_done(srv, con); |
| } |
} |
} |
|
|
static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { |
static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { |
plugin_data *p = p_d; |
plugin_data *p = p_d; |
|
handler_ctx *hctx = con->plugin_ctx[p->id]; |
|
if (hctx) cgi_connection_close(srv, hctx); |
|
|
return cgi_connection_close(srv, con->plugin_ctx[p->id]); | return HANDLER_GO_ON; |
} |
} |
|
|
|
|
static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { | static int cgi_write_request(server *srv, handler_ctx *hctx, int fd); |
| |
| |
| static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) { |
handler_ctx *hctx = ctx; |
handler_ctx *hctx = ctx; |
connection *con = hctx->remote_conn; |
connection *con = hctx->remote_conn; |
|
|
|
/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/ |
joblist_append(srv, con); |
joblist_append(srv, con); |
|
|
if (hctx->fd == -1) { | if (revents & FDEVENT_OUT) { |
log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd"); | if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) { |
| cgi_connection_close(srv, hctx); |
| return HANDLER_ERROR; |
| } |
| /* more request body to be sent to CGI */ |
| } |
|
|
|
if (revents & FDEVENT_HUP) { |
|
/* skip sending remaining data to CGI */ |
|
if (con->request.content_length) { |
|
chunkqueue *cq = con->request_content_queue; |
|
chunkqueue_mark_written(cq, chunkqueue_length(cq)); |
|
if (cq->bytes_in != (off_t)con->request.content_length) { |
|
con->keep_alive = 0; |
|
} |
|
} |
|
|
|
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ |
|
} else if (revents & FDEVENT_ERR) { |
|
/* kill all connections to the cgi process */ |
|
#if 1 |
|
log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR"); |
|
#endif |
|
cgi_connection_close(srv, hctx); |
return HANDLER_ERROR; |
return HANDLER_ERROR; |
} |
} |
|
|
if (revents & FDEVENT_IN) { | return HANDLER_FINISHED; |
| } |
| |
| |
| static int cgi_recv_response(server *srv, handler_ctx *hctx) { |
switch (cgi_demux_response(srv, hctx)) { |
switch (cgi_demux_response(srv, hctx)) { |
case FDEVENT_HANDLED_NOT_FINISHED: |
case FDEVENT_HANDLED_NOT_FINISHED: |
break; |
break; |
Line 646 static handler_t cgi_handle_fdevent(server *srv, void
|
Line 770 static handler_t cgi_handle_fdevent(server *srv, void
|
|
|
/* if we get a IN|HUP and have read everything don't exec the close twice */ |
/* if we get a IN|HUP and have read everything don't exec the close twice */ |
return HANDLER_FINISHED; |
return HANDLER_FINISHED; |
|
case FDEVENT_HANDLED_COMEBACK: |
|
cgi_connection_close(srv, hctx); |
|
return HANDLER_COMEBACK; |
case FDEVENT_HANDLED_ERROR: |
case FDEVENT_HANDLED_ERROR: |
/* Send an error if we haven't sent any data yet */ |
|
if (0 == con->file_started) { |
|
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); |
|
con->http_status = 500; |
|
con->mode = DIRECT; |
|
} else { |
|
con->file_finished = 1; |
|
} |
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: "); |
log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: "); |
break; | |
| cgi_connection_close(srv, hctx); |
| return HANDLER_FINISHED; |
} |
} |
} |
|
|
|
if (revents & FDEVENT_OUT) { | return HANDLER_GO_ON; |
/* nothing to do */ | } |
| |
| |
| static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { |
| handler_ctx *hctx = ctx; |
| connection *con = hctx->remote_conn; |
| |
| joblist_append(srv, con); |
| |
| if (revents & FDEVENT_IN) { |
| handler_t rc = cgi_recv_response(srv, hctx);/*(might invalidate hctx)*/ |
| if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ |
} |
} |
|
|
/* perhaps this issue is already handled */ |
/* perhaps this issue is already handled */ |
if (revents & FDEVENT_HUP) { |
if (revents & FDEVENT_HUP) { |
/* check if we still have a unfinished header package which is a body in reality */ | if (con->file_started) { |
if (con->file_started == 0 && | /* drain any remaining data from kernel pipe buffers |
hctx->response_header->used) { | * even if (con->conf.stream_response_body |
| * & FDEVENT_STREAM_RESPONSE_BUFMIN) |
| * since event loop will spin on fd FDEVENT_HUP event |
| * until unregistered. */ |
| handler_t rc; |
| do { |
| rc = cgi_recv_response(srv,hctx);/*(might invalidate hctx)*/ |
| } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ |
| return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ |
| } else if (!buffer_string_is_empty(hctx->response_header)) { |
| /* unfinished header package which is a body in reality */ |
con->file_started = 1; |
con->file_started = 1; |
http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); | if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { |
joblist_append(srv, con); | cgi_connection_close(srv, hctx); |
} | return HANDLER_ERROR; |
| } |
if (con->file_finished == 0) { | |
http_chunk_append_mem(srv, con, NULL, 0); | |
joblist_append(srv, con); | |
} | |
| |
con->file_finished = 1; | |
| |
if (chunkqueue_is_empty(con->write_queue)) { | |
/* there is nothing left to write */ | |
connection_set_state(srv, con, CON_STATE_RESPONSE_END); | |
} else { |
} else { |
/* used the write-handler to finish the request on demand */ |
|
|
|
} |
|
|
|
# if 0 |
# if 0 |
log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents); | log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents); |
# endif |
# endif |
| } |
/* rtsigs didn't liked the close */ | |
cgi_connection_close(srv, hctx); |
cgi_connection_close(srv, hctx); |
} else if (revents & FDEVENT_ERR) { |
} else if (revents & FDEVENT_ERR) { |
con->file_finished = 1; |
|
|
|
/* kill all connections to the cgi process */ |
/* kill all connections to the cgi process */ |
cgi_connection_close(srv, hctx); |
cgi_connection_close(srv, hctx); |
#if 1 |
#if 1 |
Line 717 static int cgi_env_add(char_array *env, const char *ke
|
Line 840 static int cgi_env_add(char_array *env, const char *ke
|
if (!key || !val) return -1; |
if (!key || !val) return -1; |
|
|
dst = malloc(key_len + val_len + 2); |
dst = malloc(key_len + val_len + 2); |
|
force_assert(dst); |
memcpy(dst, key, key_len); |
memcpy(dst, key, key_len); |
dst[key_len] = '='; |
dst[key_len] = '='; |
memcpy(dst + key_len + 1, val, val_len); |
memcpy(dst + key_len + 1, val, val_len); |
Line 725 static int cgi_env_add(char_array *env, const char *ke
|
Line 849 static int cgi_env_add(char_array *env, const char *ke
|
if (env->size == 0) { |
if (env->size == 0) { |
env->size = 16; |
env->size = 16; |
env->ptr = malloc(env->size * sizeof(*env->ptr)); |
env->ptr = malloc(env->size * sizeof(*env->ptr)); |
|
force_assert(env->ptr); |
} else if (env->size == env->used) { |
} else if (env->size == env->used) { |
env->size += 16; |
env->size += 16; |
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); |
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); |
|
force_assert(env->ptr); |
} |
} |
|
|
env->ptr[env->used++] = dst; |
env->ptr[env->used++] = dst; |
Line 735 static int cgi_env_add(char_array *env, const char *ke
|
Line 861 static int cgi_env_add(char_array *env, const char *ke
|
return 0; |
return 0; |
} |
} |
|
|
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { | /* returns: 0: continue, -1: fatal error, -2: connection reset */ |
| /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), |
| * also mmaps and sends complete chunk instead of only small parts - the files |
| * are supposed to be temp files with reasonable chunk sizes. |
| * |
| * Also always use mmap; the files are "trusted", as we created them. |
| */ |
| static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) { |
| chunk* const c = cq->first; |
| off_t offset, toSend, file_end; |
| ssize_t r; |
| size_t mmap_offset, mmap_avail; |
| char *data; |
| |
| force_assert(NULL != c); |
| force_assert(FILE_CHUNK == c->type); |
| force_assert(c->offset >= 0 && c->offset <= c->file.length); |
| |
| offset = c->file.start + c->offset; |
| toSend = c->file.length - c->offset; |
| file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ |
| |
| if (0 == toSend) { |
| chunkqueue_remove_finished_chunks(cq); |
| return 0; |
| } |
| |
| if (0 != network_open_file_chunk(srv, con, cq)) return -1; |
| |
| /* (re)mmap the buffer if range is not covered completely */ |
| if (MAP_FAILED == c->file.mmap.start |
| || offset < c->file.mmap.offset |
| || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { |
| |
| if (MAP_FAILED != c->file.mmap.start) { |
| munmap(c->file.mmap.start, c->file.mmap.length); |
| c->file.mmap.start = MAP_FAILED; |
| } |
| |
| c->file.mmap.offset = mmap_align_offset(offset); |
| c->file.mmap.length = file_end - c->file.mmap.offset; |
| |
| if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { |
| if (toSend > 65536) toSend = 65536; |
| data = malloc(toSend); |
| force_assert(data); |
| if (-1 == lseek(c->file.fd, offset, SEEK_SET) |
| || 0 >= (toSend = read(c->file.fd, data, toSend))) { |
| if (-1 == toSend) { |
| log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:", |
| strerror(errno), c->file.name, c->file.fd, offset); |
| } else { /*(0 == toSend)*/ |
| log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):", |
| c->file.name, c->file.fd, offset); |
| } |
| free(data); |
| return -1; |
| } |
| } |
| } |
| |
| if (MAP_FAILED != c->file.mmap.start) { |
| force_assert(offset >= c->file.mmap.offset); |
| mmap_offset = offset - c->file.mmap.offset; |
| force_assert(c->file.mmap.length > mmap_offset); |
| mmap_avail = c->file.mmap.length - mmap_offset; |
| force_assert(toSend <= (off_t) mmap_avail); |
| |
| data = c->file.mmap.start + mmap_offset; |
| } |
| |
| r = write(fd, data, toSend); |
| |
| if (MAP_FAILED == c->file.mmap.start) free(data); |
| |
| if (r < 0) { |
| switch (errno) { |
| case EAGAIN: |
| case EINTR: |
| return 0; |
| case EPIPE: |
| case ECONNRESET: |
| return -2; |
| default: |
| log_error_write(srv, __FILE__, __LINE__, "ssd", |
| "write failed:", strerror(errno), fd); |
| return -1; |
| } |
| } |
| |
| if (r >= 0) { |
| chunkqueue_mark_written(cq, r); |
| } |
| |
| return r; |
| } |
| |
| static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) { |
| connection *con = hctx->remote_conn; |
| chunkqueue *cq = con->request_content_queue; |
| chunk *c; |
| |
| /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. |
| * solution: if this is still a problem on windows, then substitute |
| * socketpair() for pipe() and closesocket() for close() on windows. |
| */ |
| |
| for (c = cq->first; c; c = cq->first) { |
| ssize_t r = -1; |
| |
| switch(c->type) { |
| case FILE_CHUNK: |
| r = cgi_write_file_chunk_mmap(srv, con, fd, cq); |
| break; |
| |
| case MEM_CHUNK: |
| if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { |
| switch(errno) { |
| case EAGAIN: |
| case EINTR: |
| /* ignore and try again */ |
| r = 0; |
| break; |
| case EPIPE: |
| case ECONNRESET: |
| /* connection closed */ |
| r = -2; |
| break; |
| default: |
| /* fatal error */ |
| log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno)); |
| r = -1; |
| break; |
| } |
| } else if (r > 0) { |
| chunkqueue_mark_written(cq, r); |
| } |
| break; |
| } |
| |
| if (0 == r) break; /*(might block)*/ |
| |
| switch (r) { |
| case -1: |
| /* fatal error */ |
| return -1; |
| case -2: |
| /* connection reset */ |
| log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI"); |
| /* skip all remaining data */ |
| chunkqueue_mark_written(cq, chunkqueue_length(cq)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (cq->bytes_out == (off_t)con->request.content_length) { |
| /* sent all request body input */ |
| /* close connection to the cgi-script */ |
| if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ |
| if (close(fd)) { |
| log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno)); |
| } |
| } else { |
| cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ |
| } |
| } else { |
| off_t cqlen = cq->bytes_in - cq->bytes_out; |
| if (cq->bytes_in < (off_t)con->request.content_length && cqlen < 65536 - 16384) { |
| /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ |
| if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { |
| con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; |
| con->is_readable = 1; /* trigger optimistic read from client */ |
| } |
| } |
| if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ |
| hctx->fdtocgi = fd; |
| hctx->fde_ndx_tocgi = -1; |
| fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); |
| } |
| if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ |
| if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) { |
| fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0); |
| } |
| } else { |
| /* more request body remains to be sent to CGI so register for fdevents */ |
| fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { |
pid_t pid; |
pid_t pid; |
|
|
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
Line 748 static int cgi_create_env(server *srv, connection *con
|
Line 1068 static int cgi_create_env(server *srv, connection *con
|
|
|
#ifndef __WIN32 |
#ifndef __WIN32 |
|
|
if (cgi_handler->used > 1) { | if (!buffer_string_is_empty(cgi_handler)) { |
/* stat the exec file */ |
/* stat the exec file */ |
if (-1 == (stat(cgi_handler->ptr, &st))) { |
if (-1 == (stat(cgi_handler->ptr, &st))) { |
log_error_write(srv, __FILE__, __LINE__, "sbss", |
log_error_write(srv, __FILE__, __LINE__, "sbss", |
Line 777 static int cgi_create_env(server *srv, connection *con
|
Line 1097 static int cgi_create_env(server *srv, connection *con
|
char **args; |
char **args; |
int argc; |
int argc; |
int i = 0; |
int i = 0; |
char buf[32]; | char buf[LI_ITOSTRING_LENGTH]; |
size_t n; |
size_t n; |
char_array env; |
char_array env; |
char *c; |
char *c; |
Line 803 static int cgi_create_env(server *srv, connection *con
|
Line 1123 static int cgi_create_env(server *srv, connection *con
|
env.size = 0; |
env.size = 0; |
env.used = 0; |
env.used = 0; |
|
|
if (buffer_is_empty(con->conf.server_tag)) { | cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)); | |
} else { | |
cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); | |
} | |
|
|
if (!buffer_is_empty(con->server_name)) { | if (!buffer_string_is_empty(con->server_name)) { |
size_t len = con->server_name->used - 1; | size_t len = buffer_string_length(con->server_name); |
|
|
if (con->server_name->ptr[0] == '[') { |
if (con->server_name->ptr[0] == '[') { |
const char *colon = strstr(con->server_name->ptr, "]:"); |
const char *colon = strstr(con->server_name->ptr, "]:"); |
Line 823 static int cgi_create_env(server *srv, connection *con
|
Line 1139 static int cgi_create_env(server *srv, connection *con
|
cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); |
} else { |
} else { |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
s = inet_ntop(srv_sock->addr.plain.sa_family, | s = inet_ntop( |
srv_sock->addr.plain.sa_family == AF_INET6 ? | srv_sock->addr.plain.sa_family, |
(const void *) &(srv_sock->addr.ipv6.sin6_addr) : | srv_sock->addr.plain.sa_family == AF_INET6 ? |
(const void *) &(srv_sock->addr.ipv4.sin_addr), | (const void *) &(srv_sock->addr.ipv6.sin6_addr) : |
b2, sizeof(b2)-1); | (const void *) &(srv_sock->addr.ipv4.sin_addr), |
| b2, sizeof(b2)-1); |
#else |
#else |
s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); |
s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); |
#endif |
#endif |
|
force_assert(s); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); |
} |
} |
cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); |
cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); |
|
|
s = get_http_version_name(con->request.http_version); |
s = get_http_version_name(con->request.http_version); |
| force_assert(s); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); |
|
|
LI_ltostr(buf, | li_utostrn(buf, sizeof(buf), |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) |
ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) |
#else |
#else |
Line 851 static int cgi_create_env(server *srv, connection *con
|
Line 1169 static int cgi_create_env(server *srv, connection *con
|
switch (srv_sock->addr.plain.sa_family) { |
switch (srv_sock->addr.plain.sa_family) { |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
case AF_INET6: |
case AF_INET6: |
s = inet_ntop(srv_sock->addr.plain.sa_family, | s = inet_ntop( |
(const void *) &(srv_sock->addr.ipv6.sin6_addr), | srv_sock->addr.plain.sa_family, |
b2, sizeof(b2)-1); | (const void *) &(srv_sock->addr.ipv6.sin6_addr), |
| b2, sizeof(b2)-1); |
break; |
break; |
case AF_INET: |
case AF_INET: |
s = inet_ntop(srv_sock->addr.plain.sa_family, | s = inet_ntop( |
(const void *) &(srv_sock->addr.ipv4.sin_addr), | srv_sock->addr.plain.sa_family, |
b2, sizeof(b2)-1); | (const void *) &(srv_sock->addr.ipv4.sin_addr), |
| b2, sizeof(b2)-1); |
break; |
break; |
#else |
#else |
case AF_INET: |
case AF_INET: |
Line 869 static int cgi_create_env(server *srv, connection *con
|
Line 1189 static int cgi_create_env(server *srv, connection *con
|
s = ""; |
s = ""; |
break; |
break; |
} |
} |
|
force_assert(s); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); |
cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); |
|
|
s = get_http_method_name(con->request.http_method); |
s = get_http_method_name(con->request.http_method); |
|
force_assert(s); |
cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); |
cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); |
|
|
if (!buffer_is_empty(con->request.pathinfo)) { | if (!buffer_string_is_empty(con->request.pathinfo)) { |
cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); |
cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); |
} |
} |
cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); | if (!buffer_string_is_empty(con->uri.query)) { |
if (!buffer_is_empty(con->uri.query)) { | |
cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); |
cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); |
|
} else { |
|
cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); |
} |
} |
if (!buffer_is_empty(con->request.orig_uri)) { | cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); |
cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); | if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { |
| cgi_env_add(&env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)); |
} |
} |
|
/* set REDIRECT_STATUS for php compiled with --force-redirect |
|
* (if REDIRECT_STATUS has not already been set by error handler) */ |
|
if (0 == con->error_handler_saved_status) { |
|
cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); |
|
} |
|
|
|
|
switch (con->dst_addr.plain.sa_family) { |
switch (con->dst_addr.plain.sa_family) { |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
case AF_INET6: |
case AF_INET6: |
s = inet_ntop(con->dst_addr.plain.sa_family, | s = inet_ntop( |
(const void *) &(con->dst_addr.ipv6.sin6_addr), | con->dst_addr.plain.sa_family, |
b2, sizeof(b2)-1); | (const void *) &(con->dst_addr.ipv6.sin6_addr), |
| b2, sizeof(b2)-1); |
break; |
break; |
case AF_INET: |
case AF_INET: |
s = inet_ntop(con->dst_addr.plain.sa_family, | s = inet_ntop( |
(const void *) &(con->dst_addr.ipv4.sin_addr), | con->dst_addr.plain.sa_family, |
b2, sizeof(b2)-1); | (const void *) &(con->dst_addr.ipv4.sin_addr), |
| b2, sizeof(b2)-1); |
break; |
break; |
#else |
#else |
case AF_INET: |
case AF_INET: |
Line 907 static int cgi_create_env(server *srv, connection *con
|
Line 1238 static int cgi_create_env(server *srv, connection *con
|
s = ""; |
s = ""; |
break; |
break; |
} |
} |
|
force_assert(s); |
cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); |
cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); |
|
|
LI_ltostr(buf, | li_utostrn(buf, sizeof(buf), |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) |
ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) |
#else |
#else |
Line 922 static int cgi_create_env(server *srv, connection *con
|
Line 1254 static int cgi_create_env(server *srv, connection *con
|
cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); |
cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); |
} |
} |
|
|
/* request.content_length < SSIZE_MAX, see request.c */ | li_itostrn(buf, sizeof(buf), con->request.content_length); |
LI_ltostr(buf, con->request.content_length); | |
cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); |
cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); |
cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); |
cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); |
cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); |
cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); |
Line 949 static int cgi_create_env(server *srv, connection *con
|
Line 1280 static int cgi_create_env(server *srv, connection *con
|
|
|
ds = (data_string *)con->request.headers->data[n]; |
ds = (data_string *)con->request.headers->data[n]; |
|
|
if (ds->value->used && ds->key->used) { | if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { |
size_t j; | /* Do not emit HTTP_PROXY in environment. |
| * Some executables use HTTP_PROXY to configure |
buffer_reset(p->tmp_buf); | * outgoing proxy. See also https://httpoxy.org/ */ |
| if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { |
if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { | continue; |
buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); | |
p->tmp_buf->used--; /* strip \0 after HTTP_ */ | |
} |
} |
|
|
buffer_prepare_append(p->tmp_buf, ds->key->used + 2); | buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 1); |
|
|
for (j = 0; j < ds->key->used - 1; j++) { |
|
char cr = '_'; |
|
if (light_isalpha(ds->key->ptr[j])) { |
|
/* upper-case */ |
|
cr = ds->key->ptr[j] & ~32; |
|
} else if (light_isdigit(ds->key->ptr[j])) { |
|
/* copy */ |
|
cr = ds->key->ptr[j]; |
|
} |
|
p->tmp_buf->ptr[p->tmp_buf->used++] = cr; |
|
} |
|
p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; |
|
|
|
cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); |
cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); |
} |
} |
} |
} |
Line 983 static int cgi_create_env(server *srv, connection *con
|
Line 1299 static int cgi_create_env(server *srv, connection *con
|
|
|
ds = (data_string *)con->environment->data[n]; |
ds = (data_string *)con->environment->data[n]; |
|
|
if (ds->value->used && ds->key->used) { | if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { |
size_t j; | buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 0); |
|
|
buffer_reset(p->tmp_buf); |
|
|
|
buffer_prepare_append(p->tmp_buf, ds->key->used + 2); |
|
|
|
for (j = 0; j < ds->key->used - 1; j++) { |
|
char cr = '_'; |
|
if (light_isalpha(ds->key->ptr[j])) { |
|
/* upper-case */ |
|
cr = ds->key->ptr[j] & ~32; |
|
} else if (light_isdigit(ds->key->ptr[j])) { |
|
/* copy */ |
|
cr = ds->key->ptr[j]; |
|
} |
|
p->tmp_buf->ptr[p->tmp_buf->used++] = cr; |
|
} |
|
p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; |
|
|
|
cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); |
cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); |
} |
} |
} |
} |
Line 1017 static int cgi_create_env(server *srv, connection *con
|
Line 1316 static int cgi_create_env(server *srv, connection *con
|
/* set up args */ |
/* set up args */ |
argc = 3; |
argc = 3; |
args = malloc(sizeof(*args) * argc); |
args = malloc(sizeof(*args) * argc); |
|
force_assert(args); |
i = 0; |
i = 0; |
|
|
if (cgi_handler->used > 1) { | if (!buffer_string_is_empty(cgi_handler)) { |
args[i++] = cgi_handler->ptr; |
args[i++] = cgi_handler->ptr; |
} |
} |
args[i++] = con->physical.path->ptr; |
args[i++] = con->physical.path->ptr; |
Line 1027 static int cgi_create_env(server *srv, connection *con
|
Line 1327 static int cgi_create_env(server *srv, connection *con
|
|
|
/* search for the last / */ |
/* search for the last / */ |
if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { |
if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { |
*c = '\0'; | /* handle special case of file in root directory */ |
| const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr; |
|
|
|
/* temporarily shorten con->physical.path to directory without terminating '/' */ |
|
*c = '\0'; |
/* change to the physical directory */ |
/* change to the physical directory */ |
if (-1 == chdir(con->physical.path->ptr)) { | if (-1 == chdir(physdir)) { |
log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); |
log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); |
} |
} |
*c = '/'; |
*c = '/'; |
Line 1044 static int cgi_create_env(server *srv, connection *con
|
Line 1347 static int cgi_create_env(server *srv, connection *con
|
/* exec the cgi */ |
/* exec the cgi */ |
execve(args[0], args, env.ptr); |
execve(args[0], args, env.ptr); |
|
|
/* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */ | /* most log files may have been closed/redirected by this point, |
| * though stderr might still point to lighttpd.breakage.log */ |
/* */ | perror(args[0]); |
SEGFAULT(); | _exit(1); |
break; | |
} |
} |
case -1: |
case -1: |
/* error */ |
/* error */ |
Line 1058 static int cgi_create_env(server *srv, connection *con
|
Line 1360 static int cgi_create_env(server *srv, connection *con
|
close(to_cgi_fds[0]); |
close(to_cgi_fds[0]); |
close(to_cgi_fds[1]); |
close(to_cgi_fds[1]); |
return -1; |
return -1; |
break; |
|
default: { |
default: { |
handler_ctx *hctx; | /* parent process */ |
/* father */ | |
|
|
close(from_cgi_fds[1]); |
close(from_cgi_fds[1]); |
close(to_cgi_fds[0]); |
close(to_cgi_fds[0]); |
|
|
if (con->request.content_length) { | /* register PID and wait for them asynchronously */ |
chunkqueue *cq = con->request_content_queue; | |
chunk *c; | |
|
|
assert(chunkqueue_length(cq) == (off_t)con->request.content_length); | hctx->pid = pid; |
| hctx->fd = from_cgi_fds[0]; |
| hctx->fde_ndx = -1; |
|
|
|
if (0 == con->request.content_length) { |
|
close(to_cgi_fds[1]); |
|
} else { |
/* there is content to send */ |
/* there is content to send */ |
for (c = cq->first; c; c = cq->first) { | if (-1 == fdevent_fcntl_set(srv->ev, to_cgi_fds[1])) { |
int r = 0; | log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); |
| close(to_cgi_fds[1]); |
| cgi_connection_close(srv, hctx); |
| return -1; |
| } |
|
|
/* copy all chunks */ | if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { |
switch(c->type) { | close(to_cgi_fds[1]); |
case FILE_CHUNK: | cgi_connection_close(srv, hctx); |
| return -1; |
if (c->file.mmap.start == MAP_FAILED) { | |
if (-1 == c->file.fd && /* open the file if not already open */ | |
-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { | |
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); | |
| |
close(from_cgi_fds[0]); | |
close(to_cgi_fds[1]); | |
return -1; | |
} | |
| |
c->file.mmap.length = c->file.length; | |
| |
if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { | |
log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", | |
strerror(errno), c->file.name, c->file.fd); | |
| |
close(from_cgi_fds[0]); | |
close(to_cgi_fds[1]); | |
return -1; | |
} | |
| |
close(c->file.fd); | |
c->file.fd = -1; | |
| |
/* chunk_reset() or chunk_free() will cleanup for us */ | |
} | |
| |
if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { | |
switch(errno) { | |
case ENOSPC: | |
con->http_status = 507; | |
break; | |
case EINTR: | |
continue; | |
default: | |
con->http_status = 403; | |
break; | |
} | |
} | |
break; | |
case MEM_CHUNK: | |
if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { | |
switch(errno) { | |
case ENOSPC: | |
con->http_status = 507; | |
break; | |
case EINTR: | |
continue; | |
default: | |
con->http_status = 403; | |
break; | |
} | |
} | |
break; | |
case UNUSED_CHUNK: | |
break; | |
} | |
| |
if (r > 0) { | |
c->offset += r; | |
cq->bytes_out += r; | |
} else { | |
log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno)); | |
con->http_status = 500; | |
break; | |
} | |
chunkqueue_remove_finished_chunks(cq); | |
} |
} |
} |
} |
|
|
close(to_cgi_fds[1]); |
|
|
|
/* register PID and wait for them asyncronously */ |
|
con->mode = p->id; |
|
buffer_reset(con->physical.path); |
|
|
|
hctx = cgi_handler_ctx_init(); |
|
|
|
hctx->remote_conn = con; |
|
hctx->plugin_data = p; |
|
hctx->pid = pid; |
|
hctx->fd = from_cgi_fds[0]; |
|
hctx->fde_ndx = -1; |
|
|
|
con->plugin_ctx[p->id] = hctx; |
|
|
|
fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); |
fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); |
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); |
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); |
|
|
if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { |
if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { |
log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); |
log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); |
| cgi_connection_close(srv, hctx); |
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); | |
fdevent_unregister(srv->ev, hctx->fd); | |
| |
log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd); | |
| |
close(hctx->fd); | |
| |
cgi_handler_ctx_free(hctx); | |
| |
con->plugin_ctx[p->id] = NULL; | |
| |
return -1; |
return -1; |
} |
} |
|
|
Line 1196 static int cgi_create_env(server *srv, connection *con
|
Line 1409 static int cgi_create_env(server *srv, connection *con
|
#endif |
#endif |
} |
} |
|
|
|
static buffer * cgi_get_handler(array *a, buffer *fn) { |
|
size_t k, s_len = buffer_string_length(fn); |
|
for (k = 0; k < a->used; ++k) { |
|
data_string *ds = (data_string *)a->data[k]; |
|
size_t ct_len = buffer_string_length(ds->key); |
|
|
|
if (buffer_is_empty(ds->key)) continue; |
|
if (s_len < ct_len) continue; |
|
|
|
if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { |
|
return ds->value; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
#define PATCH(x) \ |
#define PATCH(x) \ |
p->conf.x = s->x; |
p->conf.x = s->x; |
static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) { |
static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) { |
Line 1204 static int mod_cgi_patch_connection(server *srv, conne
|
Line 1434 static int mod_cgi_patch_connection(server *srv, conne
|
|
|
PATCH(cgi); |
PATCH(cgi); |
PATCH(execute_x_only); |
PATCH(execute_x_only); |
|
PATCH(xsendfile_allow); |
|
PATCH(xsendfile_docroot); |
|
|
/* skip the first, the global context */ |
/* skip the first, the global context */ |
for (i = 1; i < srv->config_context->used; i++) { |
for (i = 1; i < srv->config_context->used; i++) { |
Line 1221 static int mod_cgi_patch_connection(server *srv, conne
|
Line 1453 static int mod_cgi_patch_connection(server *srv, conne
|
PATCH(cgi); |
PATCH(cgi); |
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) { |
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) { |
PATCH(execute_x_only); |
PATCH(execute_x_only); |
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile"))) { |
|
PATCH(xsendfile_allow); |
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile-docroot"))) { |
|
PATCH(xsendfile_docroot); |
} |
} |
} |
} |
} |
} |
Line 1230 static int mod_cgi_patch_connection(server *srv, conne
|
Line 1466 static int mod_cgi_patch_connection(server *srv, conne
|
#undef PATCH |
#undef PATCH |
|
|
URIHANDLER_FUNC(cgi_is_handled) { |
URIHANDLER_FUNC(cgi_is_handled) { |
size_t k, s_len; |
|
plugin_data *p = p_d; |
plugin_data *p = p_d; |
buffer *fn = con->physical.path; |
buffer *fn = con->physical.path; |
stat_cache_entry *sce = NULL; |
stat_cache_entry *sce = NULL; |
|
|
if (con->mode != DIRECT) return HANDLER_GO_ON; |
if (con->mode != DIRECT) return HANDLER_GO_ON; |
|
|
if (fn->used == 0) return HANDLER_GO_ON; | if (buffer_is_empty(fn)) return HANDLER_GO_ON; |
|
|
mod_cgi_patch_connection(srv, con, p); |
mod_cgi_patch_connection(srv, con, p); |
|
|
Line 1245 URIHANDLER_FUNC(cgi_is_handled) {
|
Line 1480 URIHANDLER_FUNC(cgi_is_handled) {
|
if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; |
if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; |
if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; |
if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; |
|
|
s_len = fn->used - 1; | if (NULL != cgi_get_handler(p->conf.cgi, fn)) { |
| handler_ctx *hctx = cgi_handler_ctx_init(); |
for (k = 0; k < p->conf.cgi->used; k++) { | hctx->remote_conn = con; |
data_string *ds = (data_string *)p->conf.cgi->data[k]; | hctx->plugin_data = p; |
size_t ct_len = ds->key->used - 1; | con->plugin_ctx[p->id] = hctx; |
| con->mode = p->id; |
if (ds->key->used == 0) continue; | |
if (s_len < ct_len) continue; | |
| |
if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { | |
if (cgi_create_env(srv, con, p, ds->value)) { | |
con->mode = DIRECT; | |
con->http_status = 500; | |
| |
buffer_reset(con->physical.path); | |
return HANDLER_FINISHED; | |
} | |
/* one handler is enough for the request */ | |
break; | |
} | |
} |
} |
|
|
return HANDLER_GO_ON; |
return HANDLER_GO_ON; |
Line 1329 TRIGGER_FUNC(cgi_trigger) {
|
Line 1550 TRIGGER_FUNC(cgi_trigger) {
|
|
|
/* |
/* |
* - HANDLER_GO_ON : not our job |
* - HANDLER_GO_ON : not our job |
* - HANDLER_FINISHED: got response header | * - HANDLER_FINISHED: got response |
* - HANDLER_WAIT_FOR_EVENT: waiting for response header | * - HANDLER_WAIT_FOR_EVENT: waiting for response |
*/ |
*/ |
SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { |
SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { |
int status; |
|
plugin_data *p = p_d; |
plugin_data *p = p_d; |
handler_ctx *hctx = con->plugin_ctx[p->id]; |
handler_ctx *hctx = con->plugin_ctx[p->id]; |
|
chunkqueue *cq = con->request_content_queue; |
|
|
if (con->mode != p->id) return HANDLER_GO_ON; |
if (con->mode != p->id) return HANDLER_GO_ON; |
if (NULL == hctx) return HANDLER_GO_ON; |
if (NULL == hctx) return HANDLER_GO_ON; |
|
|
#if 0 | if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) |
log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid); | && con->file_started) { |
#endif | if (chunkqueue_length(con->write_queue) > 65536 - 4096) { |
| fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); |
if (hctx->pid == 0) { | } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) { |
/* cgi already dead */ | /* optimistic read from backend, which might re-enable FDEVENT_IN */ |
if (!con->file_started) return HANDLER_WAIT_FOR_EVENT; | handler_t rc = cgi_recv_response(srv, hctx); /*(might invalidate hctx)*/ |
return HANDLER_FINISHED; | if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ |
| } |
} |
} |
|
|
#ifndef __WIN32 | if (cq->bytes_in != (off_t)con->request.content_length) { |
switch(waitpid(hctx->pid, &status, WNOHANG)) { | /*(64k - 4k to attempt to avoid temporary files |
case 0: | * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ |
/* we only have for events here if we don't have the header yet, | if (cq->bytes_in - cq->bytes_out > 65536 - 4096 |
* otherwise the event-handler will send us the incoming data */ | && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ |
if (con->file_started) return HANDLER_FINISHED; | con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; |
| if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; |
return HANDLER_WAIT_FOR_EVENT; | } else { |
case -1: | handler_t r = connection_handle_read_post_state(srv, con); |
if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT; | if (!chunkqueue_is_empty(cq)) { |
| if (fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT) { |
if (errno == ECHILD && con->file_started == 0) { | return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r; |
/* | } |
* second round but still not response | } |
*/ | if (r != HANDLER_GO_ON) return r; |
return HANDLER_WAIT_FOR_EVENT; | |
} |
} |
|
} |
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); | if (-1 == hctx->fd) { |
con->mode = DIRECT; | buffer *handler = cgi_get_handler(p->conf.cgi, con->physical.path); |
con->http_status = 500; | if (!handler) return HANDLER_GO_ON; /*(should not happen; checked in cgi_is_handled())*/ |
| if (cgi_create_env(srv, con, p, hctx, handler)) { |
| con->http_status = 500; |
| con->mode = DIRECT; |
|
|
hctx->pid = 0; | return HANDLER_FINISHED; |
| |
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); | |
fdevent_unregister(srv->ev, hctx->fd); | |
| |
if (close(hctx->fd)) { | |
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); | |
} |
} |
| #if 0 |
cgi_handler_ctx_free(hctx); | log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid); |
| #endif |
con->plugin_ctx[p->id] = NULL; | } else if (!chunkqueue_is_empty(con->request_content_queue)) { |
| if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) { |
return HANDLER_FINISHED; | cgi_connection_close(srv, hctx); |
default: | return HANDLER_ERROR; |
/* cgi process exited | |
*/ | |
| |
hctx->pid = 0; | |
| |
/* we already have response headers? just continue */ | |
if (con->file_started) return HANDLER_FINISHED; | |
| |
if (WIFEXITED(status)) { | |
/* clean exit - just continue */ | |
return HANDLER_WAIT_FOR_EVENT; | |
} |
} |
|
|
/* cgi proc died, and we didn't get any data yet - send error message and close cgi con */ |
|
log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); |
|
|
|
con->http_status = 500; |
|
con->mode = DIRECT; |
|
|
|
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); |
|
fdevent_unregister(srv->ev, hctx->fd); |
|
|
|
if (close(hctx->fd)) { |
|
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); |
|
} |
|
|
|
cgi_handler_ctx_free(hctx); |
|
|
|
con->plugin_ctx[p->id] = NULL; |
|
return HANDLER_FINISHED; |
|
} |
} |
#else | |
return HANDLER_ERROR; | /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ |
#endif | return HANDLER_WAIT_FOR_EVENT; |
} |
} |
|
|
|
|
Line 1432 int mod_cgi_plugin_init(plugin *p) {
|
Line 1622 int mod_cgi_plugin_init(plugin *p) {
|
p->connection_reset = cgi_connection_close_callback; |
p->connection_reset = cgi_connection_close_callback; |
p->handle_subrequest_start = cgi_is_handled; |
p->handle_subrequest_start = cgi_is_handled; |
p->handle_subrequest = mod_cgi_handle_subrequest; |
p->handle_subrequest = mod_cgi_handle_subrequest; |
#if 0 |
|
p->handle_fdevent = cgi_handle_fdevent; |
|
#endif |
|
p->handle_trigger = cgi_trigger; |
p->handle_trigger = cgi_trigger; |
p->init = mod_cgi_init; |
p->init = mod_cgi_init; |
p->cleanup = mod_cgi_free; |
p->cleanup = mod_cgi_free; |