You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by sameer <sa...@c2.net> on 1997/01/10 19:22:46 UTC
unbuffered cgi
Here's a patch to do unbuffered CGI.
I didn't remove stuff like spawn_child_err,
scan_script_headers_err, even though I replaced them with
spawn_child_err_buff, scan_script_headers_err_buff, because I don't
know if they are needed elsewhere. Personally I think that the
non-buff functions should be removed and anything that calls the
non-buff functions should be modded to use buff, but that's a bigger
change than we can deal with at this time.
There are header inclusion issues.. I have to #include httpd.h
in alloc.c in order to give alloc.c functions BUFF * support. I
couldn't think of a better way.
Please review this for inclusion in the supported patches
directory.
I really like Brian's idea of cutting off nph stdin, so I will
try that. Anyone know if that breaks existing nph installations?
Index: alloc.c
===================================================================
RCS file: /export/home/cvs/apache/src/alloc.c,v
retrieving revision 1.21
diff -c -r1.21 alloc.c
*** alloc.c 1997/01/01 18:10:13 1.21
--- alloc.c 1997/01/10 17:42:23
***************
*** 58,65 ****
* rst --- 4/95 --- 6/95
*/
! #include "conf.h"
! #include "alloc.h"
#include <stdarg.h>
--- 58,64 ----
* rst --- 4/95 --- 6/95
*/
! #include "httpd.h"
#include <stdarg.h>
***************
*** 868,873 ****
--- 867,977 ----
new->kill_how = how;
new->next = a->subprocesses;
a->subprocesses = new;
+ }
+
+
+ int spawn_child_err_buff (pool *p, void (*func)(void *), void *data,
+ enum kill_conditions kill_how,
+ BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err)
+ {
+ int pid;
+ int in_fds[2];
+ int out_fds[2];
+ int err_fds[2];
+
+ block_alarms();
+
+ if (pipe_in && pipe (in_fds) < 0)
+ {
+ unblock_alarms();
+ return 0;
+ }
+
+ if (pipe_out && pipe (out_fds) < 0) {
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ unblock_alarms();
+ return 0;
+ }
+
+ if (pipe_err && pipe (err_fds) < 0) {
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ if (pipe_out) {
+ close (out_fds[0]); close (out_fds[1]);
+ }
+ unblock_alarms();
+ return 0;
+ }
+
+ if ((pid = fork()) < 0) {
+ if (pipe_in) {
+ close (in_fds[0]); close (in_fds[1]);
+ }
+ if (pipe_out) {
+ close (out_fds[0]); close (out_fds[1]);
+ }
+ if (pipe_err) {
+ close (err_fds[0]); close (err_fds[1]);
+ }
+ unblock_alarms();
+ return 0;
+ }
+
+ if (!pid) {
+ /* Child process */
+
+ if (pipe_out) {
+ close (out_fds[0]);
+ dup2 (out_fds[1], STDOUT_FILENO);
+ close (out_fds[1]);
+ }
+
+ if (pipe_in) {
+ close (in_fds[1]);
+ dup2 (in_fds[0], STDIN_FILENO);
+ close (in_fds[0]);
+ }
+
+ if (pipe_err) {
+ close (err_fds[0]);
+ dup2 (err_fds[1], STDERR_FILENO);
+ close (err_fds[1]);
+ }
+
+ /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
+ signal (SIGCHLD, SIG_DFL); /* Was that it? */
+
+ func (data);
+ exit (0); /* Should never get here... */
+ }
+
+ /* Parent process */
+
+ note_subprocess (p, pid, kill_how);
+
+ if (pipe_out) {
+ close (out_fds[1]);
+ *pipe_out = bcreate(p, B_RD);
+ bpushfd(*pipe_out, out_fds[0], out_fds[0]);
+ }
+
+ if (pipe_in) {
+ close (in_fds[0]);
+ *pipe_in = bcreate(p, B_WR);
+ bpushfd(*pipe_in, in_fds[1], in_fds[1]);
+ }
+
+ if (pipe_err) {
+ close (err_fds[1]);
+ *pipe_err = bcreate(p, B_RD);
+ bpushfd(*pipe_err, err_fds[0], err_fds[0]);
+ }
+
+ unblock_alarms();
+ return pid;
}
int spawn_child_err (pool *p, void (*func)(void *), void *data,
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.87
diff -c -r1.87 http_protocol.c
*** http_protocol.c 1997/01/01 18:10:21 1.87
--- http_protocol.c 1997/01/10 17:42:23
***************
*** 1325,1330 ****
--- 1325,1393 ----
return (chunk_start + len_read);
}
+ long send_fb_length(BUFF *fb, request_rec *r, long length)
+ {
+ char buf[IOBUFSIZE];
+ long total_bytes_sent;
+ register int n, w, o;
+ conn_rec *c = r->connection;
+
+ if (length == 0) return 0;
+
+ total_bytes_sent = 0;
+
+ /* Clear out the buffer */
+ if(fb->incnt != 0)
+ {
+ int o;
+ o = bwrite(c->client, fb->inptr, fb->incnt);
+ fb->incnt -= o;
+ fb->inptr += o;
+ total_bytes_sent += o;
+ }
+
+ /* Make unbuffered */
+ fb->flags &= ~B_RD;
+
+ while(1)
+ {
+ fd_set fds;
+
+ bflush(c->client);
+
+ FD_ZERO(&fds);
+ FD_SET(fb->fd_in, &fds);
+
+ select(FD_SETSIZE, &fds, NULL, NULL, NULL);
+
+ while((n = bread(fb, buf, IOBUFSIZE)) < 1
+ && fb->flags & B_ERROR && errno == EINTR)
+ continue;
+
+ if (n < 1) {
+ break;
+ }
+ o=0;
+ total_bytes_sent += n;
+
+ while(n && !r->connection->aborted) {
+ w=bwrite(c->client, &buf[o], n);
+ if(w <= 0)
+ break;
+ reset_timeout(r); /* reset timeout after successfule write */
+ n-=w;
+ o+=w;
+ }
+ }
+
+ if (length > 0) bflush(c->client);
+
+ SET_BYTES_SENT(r);
+ return total_bytes_sent;
+ }
+
+ long send_fb(BUFF *fb, request_rec *r) { return send_fb_length(fb, r, -1); }
+
long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); }
long send_fd_length(FILE *f, request_rec *r, long length)
Index: mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.27
diff -c -r1.27 mod_cgi.c
*** mod_cgi.c 1997/01/01 18:10:30 1.27
--- mod_cgi.c 1997/01/10 17:42:24
***************
*** 154,159 ****
--- 154,160 ----
{ NULL}
};
+
int log_scripterror(request_rec *r, cgi_server_conf *conf, int ret,
char *error)
{
***************
*** 172,178 ****
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
! /* "%% 500 /usr/local/etc/httpd/cgi-bin */
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fprintf(f, "%%error\n%s\n", error);
--- 173,179 ----
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
!
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fprintf(f, "%%error\n%s\n", error);
***************
*** 181,186 ****
--- 182,264 ----
return ret;
}
+ int log_script_buff(request_rec *r, cgi_server_conf *conf, int ret,
+ char *dbuf, char *sbuf, BUFF *script_in, BUFF *script_err)
+ {
+ table *hdrs_arr = r->headers_in;
+ table_entry *hdrs = (table_entry *)hdrs_arr->elts;
+ char argsbuffer[HUGE_STRING_LEN];
+ FILE *f;
+ int i;
+
+ if (!conf->logname ||
+ ((stat(server_root_relative(r->pool, conf->logname), &r->finfo) == 0)
+ && (r->finfo.st_size > conf->logbytes)) ||
+ ((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
+ "a")) == NULL)) {
+ /* Soak up script output */
+ while (bgets(argsbuffer, MAX_STRING_LEN-1, script_in))
+ continue;
+ while (bgets(argsbuffer, MAX_STRING_LEN-1, script_err))
+ continue;
+ return ret;
+ }
+
+ /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
+ fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
+ r->args ? "?" : "", r->args ? r->args : "", r->protocol);
+
+ fprintf(f, "%%%% %d %s\n", ret, r->filename);
+
+ fputs("%request\n", f);
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
+ }
+ if ((r->method_number == M_POST || r->method_number == M_PUT)
+ && *dbuf) {
+ fprintf(f, "\n%s\n", dbuf);
+ }
+
+ fputs("%response\n", f);
+ hdrs_arr = r->err_headers_out;
+ hdrs = (table_entry *)hdrs_arr->elts;
+
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
+ }
+
+ if (sbuf && *sbuf)
+ fprintf(f, "%s\n", sbuf);
+
+ *argsbuffer = '\0';
+ bgets(argsbuffer, HUGE_STRING_LEN-1, script_in);
+ if (*argsbuffer) {
+ fputs("%stdout\n", f);
+ fputs(argsbuffer, f);
+ while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
+ fputs(argsbuffer, f);
+ fputs("\n", f);
+ }
+
+ *argsbuffer = '\0';
+ bgets(argsbuffer, HUGE_STRING_LEN-1, script_err);
+ if (*argsbuffer) {
+ fputs("%stderr\n", f);
+ fputs(argsbuffer, f);
+ while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
+ fputs(argsbuffer, f);
+ fputs("\n", f);
+ }
+
+ bclose(script_in);
+ bclose(script_err);
+
+ pfclose(r->pool, f);
+ return ret;
+ }
+
int log_script(request_rec *r, cgi_server_conf *conf, int ret,
char *dbuf, char *sbuf, FILE *script_in, FILE *script_err)
{
***************
*** 206,212 ****
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
! /* "%% 500 /usr/local/etc/httpd/cgi-bin */
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fputs("%request\n", f);
--- 284,290 ----
/* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri,
r->args ? "?" : "", r->args ? r->args : "", r->protocol);
!
fprintf(f, "%%%% %d %s\n", ret, r->filename);
fputs("%request\n", f);
***************
*** 341,347 ****
{
int retval, nph, dbpos = 0;
char *argv0, *dbuf = NULL;
! FILE *script_out, *script_in, *script_err;
char argsbuffer[HUGE_STRING_LEN];
int is_included = !strcmp (r->protocol, "INCLUDED");
void *sconf = r->server->module_config;
--- 419,425 ----
{
int retval, nph, dbpos = 0;
char *argv0, *dbuf = NULL;
! BUFF *script_out, *script_in, *script_err;
char argsbuffer[HUGE_STRING_LEN];
int is_included = !strcmp (r->protocol, "INCLUDED");
void *sconf = r->server->module_config;
***************
*** 389,401 ****
cld.debug = conf->logname ? 1 : 0;
if (!(child_pid =
! spawn_child_err (r->pool, cgi_child, (void *)&cld,
! nph ? just_wait : kill_after_timeout,
#ifdef __EMX__
! &script_out, &script_in, &script_err))) {
#else
! &script_out, nph ? NULL : &script_in,
! &script_err))) {
#endif
log_reason ("couldn't spawn child process", r->filename, r);
return SERVER_ERROR;
--- 467,479 ----
cld.debug = conf->logname ? 1 : 0;
if (!(child_pid =
! spawn_child_err_buff (r->pool, cgi_child, (void *)&cld,
! nph ? just_wait : kill_after_timeout,
#ifdef __EMX__
! &script_out, &script_in, &script_err))) {
#else
! &script_out, nph ? NULL : &script_in,
! &script_err))) {
#endif
log_reason ("couldn't spawn child process", r->filename, r);
return SERVER_ERROR;
***************
*** 436,464 ****
memcpy(dbuf + dbpos, argsbuffer, dbsize);
dbpos += dbsize;
}
! if (fwrite(argsbuffer, 1, len_read, script_out) < (size_t)len_read) {
/* silly script stopped reading, soak up remaining message */
while (get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0)
; /* dump it */
break;
! }
! }
! fflush (script_out);
signal (SIGPIPE, handler);
kill_timeout (r);
}
! pfclose (r->pool, script_out);
/* Handle script return... */
if (script_in && !nph) {
char *location, sbuf[MAX_STRING_LEN];
int ret;
! if ((ret = scan_script_header_err(r, script_in, sbuf)))
! return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
location = table_get (r->headers_out, "Location");
--- 514,542 ----
memcpy(dbuf + dbpos, argsbuffer, dbsize);
dbpos += dbsize;
}
! if (bwrite(script_out, argsbuffer, len_read) < (size_t)len_read) {
/* silly script stopped reading, soak up remaining message */
while (get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0)
; /* dump it */
break;
! }
! }
! bflush (script_out);
signal (SIGPIPE, handler);
kill_timeout (r);
}
! bclose(script_out);
/* Handle script return... */
if (script_in && !nph) {
char *location, sbuf[MAX_STRING_LEN];
int ret;
! if ((ret = scan_script_header_err_buff(r, script_in, sbuf)))
! return log_script_buff(r, conf, ret, dbuf, sbuf, script_in, script_err);
location = table_get (r->headers_out, "Location");
***************
*** 466,474 ****
/* Soak up all the script output */
hard_timeout ("read from script", r);
! while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL)
continue;
! while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err) != NULL)
continue;
kill_timeout (r);
--- 544,552 ----
/* Soak up all the script output */
hard_timeout ("read from script", r);
! while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
continue;
! while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
continue;
kill_timeout (r);
***************
*** 491,508 ****
hard_timeout ("send script output", r);
send_http_header(r);
! if(!r->header_only) send_fd (script_in, r);
! /* Soak up stderr */
! while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err) != NULL)
! continue;
! kill_timeout (r);
! pfclose (r->pool, script_in);
! pfclose (r->pool, script_err);
}
if (nph) {
#ifdef __EMX__
! while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) {
bputs(argsbuffer, r->connection->client);
}
#else
--- 569,587 ----
hard_timeout ("send script output", r);
send_http_header(r);
! if(!r->header_only) send_fb (script_in, r);
!
! while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
! continue;
!
! kill_timeout (r);
! bclose (script_in);
! bclose (script_err);
}
if (nph) {
#ifdef __EMX__
! while (bgets(argsbuffer, HUGE_STRING_LEN-1, script_in)) {
bputs(argsbuffer, r->connection->client);
}
#else
Index: util_script.c
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.c,v
retrieving revision 1.37
diff -c -r1.37 util_script.c
*** util_script.c 1997/01/03 09:30:20 1.37
--- util_script.c 1997/01/10 17:42:24
***************
*** 287,292 ****
--- 287,381 ----
}
}
+ int scan_script_header_err_buff(request_rec *r, BUFF *fb, char *buffer)
+ {
+ char x[MAX_STRING_LEN];
+ char *w, *l;
+ int p;
+
+ if (buffer) *buffer = '\0';
+ w = buffer ? buffer : x;
+
+ hard_timeout ("read script header", r);
+
+ while(1) {
+
+ if (bgets(w, MAX_STRING_LEN-1, fb) == NULL) {
+ log_reason ("Premature end of script headers", r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ /* Delete terminal (CR?)LF */
+
+ p = strlen(w);
+ if (p > 0 && w[p-1] == '\n')
+ {
+ if (p > 1 && w[p-2] == '\015') w[p-2] = '\0';
+ else w[p-1] = '\0';
+ }
+
+ if(w[0] == '\0') {
+ kill_timeout (r);
+ return OK;
+ }
+
+ /* if we see a bogus header don't ignore it. Shout and scream */
+
+ if(!(l = strchr(w,':'))) {
+ char malformed[(sizeof MALFORMED_MESSAGE)+1+MALFORMED_HEADER_LENGTH_TO_SHOW];
+ strcpy(malformed, MALFORMED_MESSAGE);
+ strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
+
+ if (!buffer)
+ /* Soak up all the script output --- may save an outright kill */
+ while (bgets(w, MAX_STRING_LEN-1, fb) != NULL)
+ continue;
+
+ kill_timeout (r);
+ log_reason (malformed, r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ *l++ = '\0';
+ while (*l && isspace (*l)) ++l;
+
+ if(!strcasecmp(w,"Content-type")) {
+
+ /* Nuke trailing whitespace */
+
+ char *endp = l + strlen(l) - 1;
+ while (endp > l && isspace(*endp)) *endp-- = '\0';
+
+ r->content_type = pstrdup (r->pool, l);
+ }
+ else if(!strcasecmp(w,"Status")) {
+ sscanf(l, "%d", &r->status);
+ r->status_line = pstrdup(r->pool, l);
+ }
+ else if(!strcasecmp(w,"Location")) {
+ table_set (r->headers_out, w, l);
+ }
+ else if(!strcasecmp(w,"Content-Length")) {
+ table_set (r->headers_out, w, l);
+ }
+ else if(!strcasecmp(w,"Transfer-Encoding")) {
+ table_set (r->headers_out, w, l);
+ }
+
+ /* The HTTP specification says that it is legal to merge duplicate
+ * headers into one. Some browsers that support Cookies don't like
+ * merged headers and prefer that each Set-Cookie header is sent
+ * separately. Lets humour those browsers.
+ */
+ else if(!strcasecmp(w, "Set-Cookie")) {
+ table_add(r->err_headers_out, w, l);
+ }
+ else {
+ table_merge (r->err_headers_out, w, l);
+ }
+ }
+ }
+
int scan_script_header_err(request_rec *r, FILE *f, char *buffer)
{
char x[MAX_STRING_LEN];
--
Sameer Parekh Voice: 510-986-8770
President FAX: 510-986-8777
C2Net C2Net is having a party: http://www.c2.net/party/
http://www.c2.net/ sameer@c2.net