[mod_cgi] asynchronous send of request body to CGI
Like other dynamic handler in prior commit, mod_cgi can read response before sending req body mod_cgi no longer blocks sending request body to CGI
This commit is contained in:
parent
2f21aaa973
commit
7c0f8a775d
232
src/mod_cgi.c
232
src/mod_cgi.c
|
@ -72,7 +72,9 @@ typedef struct {
|
|||
typedef struct {
|
||||
pid_t pid;
|
||||
int fd;
|
||||
int fdtocgi;
|
||||
int fde_ndx; /* index into the fd-event buffer */
|
||||
int fde_ndx_tocgi; /* index into the fd-event buffer */
|
||||
|
||||
connection *remote_conn; /* dumb pointer */
|
||||
plugin_data *plugin_data; /* dumb pointer */
|
||||
|
@ -89,6 +91,7 @@ static handler_ctx * cgi_handler_ctx_init(void) {
|
|||
hctx->response = buffer_init();
|
||||
hctx->response_header = buffer_init();
|
||||
hctx->fd = -1;
|
||||
hctx->fdtocgi = -1;
|
||||
|
||||
return hctx;
|
||||
}
|
||||
|
@ -519,6 +522,17 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
|
|||
return FDEVENT_HANDLED_NOT_FINISHED;
|
||||
}
|
||||
|
||||
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;
|
||||
pid_t pid;
|
||||
|
@ -541,16 +555,16 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) {
|
|||
if (close(hctx->fd)) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
hctx->fd = -1;
|
||||
hctx->fde_ndx = -1;
|
||||
if (hctx->fdtocgi != -1) {
|
||||
cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
|
||||
}
|
||||
|
||||
pid = hctx->pid;
|
||||
|
||||
con->plugin_ctx[p->id] = NULL;
|
||||
|
||||
/* is this a good idea ? */
|
||||
cgi_handler_ctx_free(hctx);
|
||||
|
||||
/* if waitpid hasn't been called by response.c yet, do it here */
|
||||
|
@ -631,6 +645,43 @@ static handler_t cgi_connection_close_callback(server *srv, connection *con, voi
|
|||
}
|
||||
|
||||
|
||||
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;
|
||||
connection *con = hctx->remote_conn;
|
||||
|
||||
/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
|
||||
joblist_append(srv, con);
|
||||
|
||||
if (revents & FDEVENT_OUT) {
|
||||
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 */
|
||||
chunkqueue *cq = con->request_content_queue;
|
||||
chunkqueue_mark_written(cq, chunkqueue_length(cq));
|
||||
|
||||
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_FINISHED;
|
||||
}
|
||||
|
||||
|
||||
static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
|
||||
handler_ctx *hctx = ctx;
|
||||
connection *con = hctx->remote_conn;
|
||||
|
@ -724,7 +775,7 @@ static int cgi_env_add(char_array *env, const char *key, size_t key_len, const c
|
|||
*
|
||||
* Also always use mmap; the files are "trusted", as we created them.
|
||||
*/
|
||||
static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
|
||||
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;
|
||||
|
@ -793,6 +844,89 @@ static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunk
|
|||
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 (chunkqueue_is_empty(cq)) {
|
||||
/* sent all request body input */
|
||||
/* close connection to the cgi-script */
|
||||
if (-1 == hctx->fdtocgi) { /*(entire 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 {
|
||||
/* more request body remains to be sent to CGI so register for fdevents */
|
||||
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);
|
||||
fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1099,82 +1233,30 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_
|
|||
close(from_cgi_fds[1]);
|
||||
close(to_cgi_fds[0]);
|
||||
|
||||
if (con->request.content_length) {
|
||||
chunkqueue *cq = con->request_content_queue;
|
||||
chunk *c;
|
||||
|
||||
assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
|
||||
|
||||
/* NOTE: yes, this is synchronous sending of CGI post data;
|
||||
* if you need something asynchronous (recommended with large
|
||||
* request bodies), use mod_fastcgi + fcgi-cgi.
|
||||
*
|
||||
* Also: windows doesn't support select() on pipes - wouldn't be
|
||||
* easy to fix for all platforms.
|
||||
*/
|
||||
|
||||
/* there is content to send */
|
||||
for (c = cq->first; c; c = cq->first) {
|
||||
int r = -1;
|
||||
|
||||
switch(c->type) {
|
||||
case FILE_CHUNK:
|
||||
r = cgi_write_file_chunk_mmap(srv, con, to_cgi_fds[1], cq);
|
||||
break;
|
||||
|
||||
case MEM_CHUNK:
|
||||
if ((r = write(to_cgi_fds[1], 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;
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case -1:
|
||||
/* fatal error */
|
||||
close(from_cgi_fds[0]);
|
||||
close(to_cgi_fds[1]);
|
||||
kill(pid, SIGTERM);
|
||||
cgi_pid_add(srv, p, pid);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(to_cgi_fds[1]);
|
||||
|
||||
/* register PID and wait for them asyncronously */
|
||||
/* register PID and wait for them asynchronously */
|
||||
|
||||
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 */
|
||||
if (-1 == fdevent_fcntl_set(srv->ev, to_cgi_fds[1])) {
|
||||
log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
|
||||
close(to_cgi_fds[1]);
|
||||
cgi_connection_close(srv, hctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
|
||||
close(to_cgi_fds[1]);
|
||||
cgi_connection_close(srv, hctx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
|
||||
fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
|
||||
|
||||
|
|
Loading…
Reference in New Issue