You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by "Roy T. Fielding" <fi...@kiwi.ics.uci.edu> on 1997/07/20 03:35:24 UTC

Re: [PATCH] nph killer, cgi buffering

>No, this is a rewrite.  Sameer's code had some problems and when it went
>in various people complained about the style.  I took all those complaints
>(and Roy's complaints and what he tried to do in his patch) and worked
>them into this patch. 

Ummm, except it looks like you forgot my comment about infinite loops
due to bgets returning -1 on error where fgets returns 0.

....Roy

Re: [PATCH] nph killer, cgi buffering (take 3)

Posted by Dean Gaudet <dg...@arctic.org>.
nph- CGIs were not compatible with HTTP/1.1 or SSL support because they
were passed a socket that talked directly to the client.  As such they
would have to implement the transport level details such as encryption or
chunking in order to work properly.  This isn't part of the CGI spec so
CGIs generally don't do this.  The most common use of nph- CGIs is when
the programmer wants an unbuffered connection to the client; regular CGIs
have always been fully buffered.  Apache now provides an unbuffered
connection to all CGIs.  Given that most CGIs are written in a language
that by default does buffering (i.e. perl) this shouldn't have a
detrimental effect on performance.  The programmer can still use nph-
CGIs, and they're still responsible for sending valid HTTP/1.1 responses. 

How's that? 

Dean

On Mon, 21 Jul 1997, Marc Slemko wrote:

> On Sat, 19 Jul 1997, Dean Gaudet wrote:
> 
> >  Changes with Apache 1.3
> > -  
> > +
> > +  *) nph- CGIs were not compatible with HTTP/1.1 or SSL support.  They
> > +     now are; provided the cgi does all the right protocol work itself,
> > +     which is not recommended at all.  Instead regular CGIs now have
> > +     far more control over the buffering between them and the client.
> > +     Apache will flush output to the client whenever it would have to
> > +     block while reading from the CGI.
> > +     [Dean Gaudet, Roy Fielding, Sameer Parekh]
> > +
> 
> I don't understand.  Your wording isn't particularily clear and seems
> disjointed.  Clarify?
> 
> 


Re: [PATCH] nph killer, cgi buffering (take 3)

Posted by Marc Slemko <ma...@worldgate.com>.
On Sat, 19 Jul 1997, Dean Gaudet wrote:

>  Changes with Apache 1.3
> -  
> +
> +  *) nph- CGIs were not compatible with HTTP/1.1 or SSL support.  They
> +     now are; provided the cgi does all the right protocol work itself,
> +     which is not recommended at all.  Instead regular CGIs now have
> +     far more control over the buffering between them and the client.
> +     Apache will flush output to the client whenever it would have to
> +     block while reading from the CGI.
> +     [Dean Gaudet, Roy Fielding, Sameer Parekh]
> +

I don't understand.  Your wording isn't particularily clear and seems
disjointed.  Clarify?


Re: [PATCH] nph killer, cgi buffering (take 3)

Posted by Dean Gaudet <dg...@arctic.org>.
And I just noticed another bug where it was bclosing the wrong stream. 
Here's take 3.  BTW I updated the CHANGES comment, the last one wasn't
exactly accurate.

Dean

Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.353
diff -u -r1.353 CHANGES
--- CHANGES	1997/07/19 22:26:55	1.353
+++ CHANGES	1997/07/20 02:36:23
@@ -1,5 +1,13 @@
 Changes with Apache 1.3
-  
+
+  *) nph- CGIs were not compatible with HTTP/1.1 or SSL support.  They
+     now are; provided the cgi does all the right protocol work itself,
+     which is not recommended at all.  Instead regular CGIs now have
+     far more control over the buffering between them and the client.
+     Apache will flush output to the client whenever it would have to
+     block while reading from the CGI.
+     [Dean Gaudet, Roy Fielding, Sameer Parekh]
+
   *) added transport handle slot (t_handle) to the BUFF structure
      [Doug MacEachern]
      
Index: alloc.c
===================================================================
RCS file: /export/home/cvs/apache/src/alloc.c,v
retrieving revision 1.40
diff -u -r1.40 alloc.c
--- alloc.c	1997/07/15 21:39:50	1.40
+++ alloc.c	1997/07/20 02:36:26
@@ -1064,9 +1064,16 @@
 #define enc_pipe(fds) pipe(fds)
 #endif /* WIN32 */
 
-API_EXPORT(int) spawn_child_err (pool *p, int (*func)(void *), void *data,
+/* for fdopen, to get binary mode */
+#if defined (__EMX__) || defined (WIN32)
+#define BINMODE	"b"
+#else
+#define BINMODE
+#endif
+
+static int spawn_child_err_core (pool *p, int (*func)(void *), void *data,
 		     enum kill_conditions kill_how,
-		     FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
+		     int *pipe_in, int *pipe_out, int *pipe_err)
 {
   int pid;
   int in_fds[2];
@@ -1074,13 +1081,7 @@
   int err_fds[2];
   int save_errno;
 
-  block_alarms();
-  
-  if (pipe_in && enc_pipe (in_fds) < 0)
-  {
-      save_errno = errno;
-      unblock_alarms();
-      errno = save_errno;
+  if (pipe_in && enc_pipe (in_fds) < 0) {
       return 0;
   }
   
@@ -1089,7 +1090,6 @@
     if (pipe_in) {
       close (in_fds[0]); close (in_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1102,7 +1102,6 @@
     if (pipe_out) {
       close (out_fds[0]); close (out_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1158,23 +1157,14 @@
       if(pid)
       {
           note_subprocess(p, pid, kill_how);
-          if(pipe_in)
-          {
-              *pipe_in = fdopen(in_fds[1], "wb");
-              if(*pipe_in)
-                  note_cleanups_for_file(p, *pipe_in);
+          if(pipe_in) {
+	      *pipe_in = in_fds[1];
           }
-          if(pipe_out)
-          {
-              *pipe_out = fdopen(out_fds[0], "rb");
-              if(*pipe_out)
-                  note_cleanups_for_file(p, *pipe_out);
+          if(pipe_out) {
+	      *pipe_out = out_fds[0];
           }
-          if(pipe_err)
-          {
-              *pipe_err = fdopen(err_fds[0], "rb");
-              if(*pipe_err)
-                  note_cleanups_for_file(p, *pipe_err);
+          if(pipe_err) {
+              *pipe_err = err_fds[0];
           }
       }
       SetThreadPriority(thread_handle, old_priority);
@@ -1198,7 +1188,6 @@
     if (pipe_err) {
       close (err_fds[0]); close (err_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1237,43 +1226,106 @@
   
   if (pipe_out) {
     close (out_fds[1]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2. */
-    *pipe_out = fdopen (out_fds[0], "rb");
-#else
-    *pipe_out = fdopen (out_fds[0], "r");
-#endif  
-  
-    if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
+    *pipe_out = out_fds[0];
   }
 
   if (pipe_in) {
     close (in_fds[0]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2 */
-    *pipe_in = fdopen (in_fds[1], "wb");
-#else
-    *pipe_in = fdopen (in_fds[1], "w");
-#endif
-    
-    if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
+    *pipe_in = in_fds[1];
   }
 
   if (pipe_err) {
     close (err_fds[1]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2. */
-    *pipe_err = fdopen (err_fds[0], "rb");
-#else
-    *pipe_err = fdopen (err_fds[0], "r");
-#endif
-  
-    if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+    *pipe_err = err_fds[0];
   }
 #endif /* WIN32 */
 
-  unblock_alarms();
   return pid;
+}
+
+
+API_EXPORT(int) spawn_child_err (pool *p, int (*func)(void *), void *data,
+		     enum kill_conditions kill_how,
+		     FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
+{
+    int fd_in, fd_out, fd_err;
+    int pid, save_errno;
+
+    block_alarms();
+
+    pid = spawn_child_err_core (p, func, data, kill_how,
+	    pipe_in ? &fd_in : NULL,
+	    pipe_out ? &fd_out : NULL,
+	    pipe_err ? &fd_err : NULL );
+
+    if (pid == 0) {
+	save_errno = errno;
+	unblock_alarms();
+	errno = save_errno;
+	return 0;
+    }
+
+    if (pipe_out) {
+	*pipe_out = fdopen (fd_out, "r" BINMODE);
+	if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
+	else close (fd_out);
+    }
+
+    if (pipe_in) {
+	*pipe_in = fdopen (fd_in, "w" BINMODE);
+	if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
+	else close (fd_in);
+    }
+
+    if (pipe_err) {
+	*pipe_err = fdopen (fd_err, "r" BINMODE);
+	if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+	else close (fd_err);
+    }
+
+    unblock_alarms();
+    return pid;
+}
+
+
+API_EXPORT(int) spawn_child_err_buff (pool *p, int (*func)(void *), void *data,
+			  enum kill_conditions kill_how,
+			  BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err)
+{
+    int fd_in, fd_out, fd_err;
+    int pid, save_errno;
+
+    block_alarms();
+
+    pid = spawn_child_err_core (p, func, data, kill_how,
+	    pipe_in ? &fd_in : NULL,
+	    pipe_out ? &fd_out : NULL,
+	    pipe_err ? &fd_err : NULL );
+
+    if (pid == 0) {
+	save_errno = errno;
+	unblock_alarms();
+	errno = save_errno;
+	return 0;
+    }
+  
+    if (pipe_out) {
+	*pipe_out = bcreate(p, B_RD);
+	bpushfd(*pipe_out, fd_out, fd_out);
+    }
+
+    if (pipe_in) {
+	*pipe_in = bcreate(p, B_WR);
+	bpushfd(*pipe_in, fd_in, fd_in);
+    }
+
+    if (pipe_err) {
+	*pipe_err = bcreate(p, B_RD);
+	bpushfd(*pipe_err, fd_err, fd_err);
+    }
+
+    unblock_alarms();
+    return pid;
 }
 
 static void free_proc_chain (struct process_chain *procs)
Index: buff.c
===================================================================
RCS file: /export/home/cvs/apache/src/buff.c,v
retrieving revision 1.37
diff -u -r1.37 buff.c
--- buff.c	1997/07/15 21:39:50	1.37
+++ buff.c	1997/07/20 02:36:28
@@ -415,18 +415,38 @@
 {
     if (value) {
 	fb->flags |= flag;
-	if( flag & B_CHUNK ) {
+	if (flag & B_CHUNK) {
 	    start_chunk(fb);
 	}
     } else {
 	fb->flags &= ~flag;
-	if( flag & B_CHUNK ) {
+	if (flag & B_CHUNK) {
 	    end_chunk(fb);
 	}
     }
     return value;
 }
 
+
+API_EXPORT(int) bnonblock(BUFF *fb, int direction)
+{
+    int fd;
+
+    fd = ( direction == B_RD ) ? fb->fd_in : fb->fd;
+#if defined(O_NONBLOCK)
+    return fcntl (fd, F_SETFL, O_NONBLOCK);
+#elif defined(F_NDELAY)
+    return fcntl (fd, F_SETFL, F_NDELAY);
+#else
+    return 0;
+#endif
+}
+
+API_EXPORT(int) bfileno(BUFF *fb, int direction)
+{
+    return (direction == B_RD) ? fb->fd_in : fb->fd;
+}
+
 /*
  * This is called instead of read() everywhere in here.  It implements
  * the B_SAFEREAD functionality -- which is to force a flush() if a read()
@@ -521,10 +541,18 @@
     if (fb->flags & B_RDERR) return -1;
     if (nbyte == 0) return 0;
 
-    if (!(fb->flags & B_RD))
-    {
-/* Unbuffered reading */
+    if (!(fb->flags & B_RD)) {
+	/* Unbuffered reading.  First check if there was something in the
+	 * buffer from before we went unbuffered. */
+	if (fb->incnt) {
+	    i = (fb->incnt > nbyte) ? nbyte : fb->incnt;
+	    memcpy (buf, fb->inptr, i);
+	    fb->incnt -= i;
+	    fb->inptr += i;
+	    return i;
+	}
 	i = saferead( fb, buf, nbyte );
+	if (i == 0) fb->flags |= B_EOF;
 	if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
 	return i;
     }
Index: buff.h
===================================================================
RCS file: /export/home/cvs/apache/src/buff.h,v
retrieving revision 1.21
diff -u -r1.21 buff.h
--- buff.h	1997/07/19 22:34:05	1.21
+++ buff.h	1997/07/20 02:36:29
@@ -158,3 +158,12 @@
 #define bputc(c, fb) ((((fb)->flags & (B_EOUT|B_WRERR|B_WR)) != B_WR || \
 		     (fb)->outcnt == (fb)->bufsiz) ? bflsbuf(c, (fb)) : \
 		     ((fb)->outbase[(fb)->outcnt++] = (c), 0))
+
+API_EXPORT(int) spawn_child_err_buff (pool *, int (*)(void *), void *,
+           	  enum kill_conditions, BUFF **pipe_in, BUFF **pipe_out,
+                  BUFF **pipe_err);
+
+/* enable non-blocking operations */
+API_EXPORT(int) bnonblock(BUFF *fb, int direction);
+/* and get an fd to select() on */
+API_EXPORT(int) bfileno(BUFF *fb, int direction);
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.143
diff -u -r1.143 http_protocol.c
--- http_protocol.c	1997/07/19 20:27:52	1.143
+++ http_protocol.c	1997/07/20 02:36:34
@@ -1626,6 +1626,85 @@
     return total_bytes_sent;
 }
 
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) send_fb(BUFF *fb, request_rec *r) {
+    return send_fb_length(fb, r, -1);
+}
+
+API_EXPORT(long) send_fb_length(BUFF *fb, request_rec *r, long length)
+{
+    char buf[IOBUFSIZE];
+    long total_bytes_sent = 0;
+    register int n, w, o, len, fd;
+    fd_set fds;
+    
+    if (length == 0) return 0;
+
+    /* Make fb unbuffered and non-blocking */
+    bsetflag (fb, B_RD, 0);
+    bnonblock (fb, B_RD);
+    fd = bfileno (fb, B_RD);
+
+    soft_timeout("send body", r);
+
+    while (!r->connection->aborted) {
+	if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+	    len = length - total_bytes_sent;
+	else len = IOBUFSIZE;
+
+	do {
+	    n = bread (fb, buf, len);
+	    if (n >= 0) break;
+	    if (n < 0 && errno != EAGAIN) break;
+	    /* we need to block, so flush the output first */
+	    bflush (r->connection->client);
+	    FD_ZERO (&fds);
+	    FD_SET (fd, &fds);
+	    /* we don't care what select says, we might as well loop back
+	     * around and try another read
+	     */
+	    ap_select (fd+1, &fds, NULL, NULL, NULL);
+	} while (!r->connection->aborted);
+
+	if (n < 1 || r->connection->aborted) {
+	    break;
+	}
+
+        o=0;
+	total_bytes_sent += n;
+
+        while (n && !r->connection->aborted) {
+            w = bwrite(r->connection->client, &buf[o], n);
+            if (w > 0) {
+                reset_timeout(r); /* reset timeout after successful write */
+                n-=w;
+                o+=w;
+            }
+            else if (w < 0) {
+                if (r->connection->aborted)
+                    break;
+                else if (errno == EAGAIN)
+                    continue;
+                else {
+                    log_unixerr("send body lost connection to",
+                                get_remote_host(r->connection,
+                                    r->per_dir_config, REMOTE_NAME),
+                                NULL, r->server);
+                    bsetflag(r->connection->client, B_EOUT, 1);
+                    r->connection->aborted = 1;
+                    break;
+                }
+            }
+        }
+    }
+
+    kill_timeout(r);
+    SET_BYTES_SENT(r);
+    return total_bytes_sent;
+}
+
 API_EXPORT(int) rputc (int c, request_rec *r)
 {
     if (r->connection->aborted) return EOF;
Index: http_protocol.h
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.h,v
retrieving revision 1.24
diff -u -r1.24 http_protocol.h
--- http_protocol.h	1997/07/19 20:27:52	1.24
+++ http_protocol.h	1997/07/20 02:36:35
@@ -110,6 +110,9 @@
 
 API_EXPORT(long) send_fd(FILE *f, request_rec *r);
 API_EXPORT(long) send_fd_length(FILE *f, request_rec *r, long length);
+
+API_EXPORT(long) send_fb(BUFF *f, request_rec *r);
+API_EXPORT(long) send_fb_length(BUFF *f, request_rec *r, long length);
      
 /* Hmmm... could macrofy these for now, and maybe forever, though the
  * definitions of the macros would get a whole lot hairier.
Index: mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.49
diff -u -r1.49 mod_cgi.c
--- mod_cgi.c	1997/07/17 22:27:34	1.49
+++ mod_cgi.c	1997/07/20 02:36:36
@@ -183,7 +183,7 @@
 }
 
 static int log_script(request_rec *r, cgi_server_conf *conf, int ret,
-	       char *dbuf, char *sbuf, FILE *script_in, FILE *script_err)
+		    char *dbuf, char *sbuf, BUFF *script_in, BUFF *script_err)
 {
     table *hdrs_arr = r->headers_in;
     table_entry *hdrs = (table_entry *)hdrs_arr->elts;
@@ -197,9 +197,9 @@
 	((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
 		     "a")) == NULL)) {
       /* Soak up script output */
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	continue;
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	continue;
       return ret;
     }
@@ -207,7 +207,7 @@
     /* "%% [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 */
+    /* "%% 500 /usr/local/etc/httpd/cgi-bin" */
     fprintf(f, "%%%% %d %s\n", ret, r->filename);
 
     fputs("%request\n", f);
@@ -216,7 +216,7 @@
       fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
     }
     if ((r->method_number == M_POST || r->method_number == M_PUT)
-	&& dbuf && *dbuf) {
+	&& *dbuf) {
       fprintf(f, "\n%s\n", dbuf);
     }
 
@@ -232,28 +232,24 @@
     if (sbuf && *sbuf)
       fprintf(f, "%s\n", sbuf);
 
-    *argsbuffer = '\0';
-    fgets(argsbuffer, HUGE_STRING_LEN-1, script_in);
-    if (*argsbuffer) {
+    if (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
       fputs("%stdout\n", f);
       fputs(argsbuffer, f);
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	fputs(argsbuffer, f);
       fputs("\n", f);
     }
 
-    *argsbuffer = '\0';
-    fgets(argsbuffer, HUGE_STRING_LEN-1, script_err);
-    if (*argsbuffer) {
+    if (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
       fputs("%stderr\n", f);
       fputs(argsbuffer, f);
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	fputs(argsbuffer, f);
       fputs("\n", f);
     }
 
-    pfclose(r->main ? r->main->pool : r->pool, script_in);
-    pfclose(r->main ? r->main->pool : r->pool, script_err);
+    bclose(script_in);
+    bclose(script_err);
 
     pfclose(r->pool, f);
     return ret;
@@ -277,7 +273,6 @@
     struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff;
     request_rec *r = cld->r;
     char *argv0 = cld->argv0;
-    int nph = cld->nph;
     int child_pid;
 
 #ifdef DEBUG_CGI    
@@ -312,10 +307,6 @@
     if (!cld->debug)
       error_log2stderr (r->server);
 
-#if !defined(__EMX__) && !defined(WIN32)
-    if (nph) client_to_stdout (r->connection);
-#endif    
-
     /* Transumute outselves into the script.
      * NB only ISINDEX scripts get decoded arguments.
      */
@@ -352,7 +343,7 @@
 {
     int retval, nph, dbpos = 0;
     char *argv0, *dbuf = NULL;
-    FILE *script_out, *script_in, *script_err;
+    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;
@@ -415,21 +406,16 @@
     cld.argv0 = argv0; cld.r = r; cld.nph = nph;
     cld.debug = conf->logname ? 1 : 0;
     
+    /*
+     * we spawn out of r->main if it's there so that we can avoid
+     * waiting for free_proc_chain to cleanup in the middle of an
+     * SSI request -djg
+     */
     if (!(child_pid =
-	  /*
-	   * we spawn out of r->main if it's there so that we can avoid
-	   * waiting for free_proc_chain to cleanup in the middle of an
-	   * SSI request -djg
-	   */
-	  spawn_child_err (r->main ? r->main->pool : r->pool, cgi_child,
-			    (void *)&cld,
-			   nph ? just_wait : kill_after_timeout,
-#if defined(__EMX__) || defined(WIN32)
-			   &script_out, &script_in, &script_err))) {
-#else
-			   &script_out, nph ? NULL : &script_in,
-	    		   &script_err))) {
-#endif
+	  spawn_child_err_buff (r->main ? r->main->pool : r->pool, cgi_child,
+				(void *)&cld,
+				kill_after_timeout,
+				&script_out, &script_in, &script_err))) {
         log_reason ("couldn't spawn child process", r->filename, r);
         return SERVER_ERROR;
     }
@@ -471,7 +457,7 @@
 		dbpos += dbsize;
 	    }
 	    reset_timeout(r);
-	    if (fwrite(argsbuffer, sizeof(char), len_read, script_out)
+	    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)
@@ -480,20 +466,20 @@
 	    }
 	}
 
-	fflush (script_out);
+	bflush (script_out);
 	signal (SIGPIPE, handler);
 	
 	kill_timeout (r);
     }
     
-    pfclose (r->main ? r->main->pool : r->pool, script_out);
+    bclose(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)))
+        if ((ret = scan_script_header_err_buff(r, script_in, sbuf)))
 	    return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
 	
 	location = table_get (r->headers_out, "Location");
@@ -502,11 +488,9 @@
 	  
 	    /* Soak up all the script output */
 	    hard_timeout ("read from script", r);
-	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_in)
-	           > 0)
+	    while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	        continue;
-	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err)
-	           > 0)
+	    while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	        continue;
 	    kill_timeout (r);
 
@@ -535,24 +519,19 @@
 	
 	send_http_header(r);
 	if (!r->header_only)
-	    send_fd(script_in, r);
-	pfclose (r->main ? r->main->pool : r->pool, script_in);
+	    send_fb(script_in, r);
+	bclose(script_in);
 
-	/* Soak up stderr */
 	soft_timeout("soaking script stderr", r);
-	while (!r->connection->aborted &&
-	  (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err) > 0))
-	    continue;
+	while(bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
+	      continue;
 	kill_timeout(r);
-	pfclose (r->main ? r->main->pool : r->pool, script_err);
+	bclose(script_err);
     }
 
-    if (nph) {
-#if defined(__EMX__) || defined(WIN32)
-        while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) {
-            bputs(argsbuffer, r->connection->client);
-        }
-#else
+    if (script_in && nph) {
+	send_fb(script_in, r);
+#if !defined(__EMX__) && !defined(WIN32)
 	waitpid(child_pid, (int*)0, 0);
 #endif
     }    
Index: util_script.c
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.c,v
retrieving revision 1.66
diff -u -r1.66 util_script.c
--- util_script.c	1997/07/15 21:39:59	1.66
+++ util_script.c	1997/07/20 02:36:38
@@ -312,7 +312,9 @@
     }
 }
 
-API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer)
+
+static int scan_script_header_err_core (request_rec *r, char *buffer,
+    int (*getsfunc)(char *, int, void *), void *getsfunc_data)
 {
     char x[MAX_STRING_LEN];
     char *w, *l;
@@ -325,7 +327,7 @@
     
     while(1) {
 
-	if (fgets(w, MAX_STRING_LEN-1, f) == NULL) {
+	if ((*getsfunc)(w, MAX_STRING_LEN-1, getsfunc_data) == 0) {
 	    kill_timeout (r);
 	    log_reason ("Premature end of script headers", r->filename, r);
 	    return SERVER_ERROR;
@@ -354,7 +356,7 @@
 
 	    if (!buffer)
 	      /* Soak up all the script output --- may save an outright kill */
-	      while (fgets(w, MAX_STRING_LEN-1, f) != NULL)
+	      while ((*getsfunc)(w, MAX_STRING_LEN-1, getsfunc_data))
 		continue;
 	    
 	    kill_timeout (r);
@@ -401,6 +403,28 @@
         }
     }
 }
+
+static int getsfunc_FILE (char *buf, int len, void *f)
+{
+    return fgets (buf, len, (FILE *)f) != NULL;
+}
+
+API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_FILE, f);
+}
+
+static int getsfunc_BUFF (char *w, int len, void *fb)
+{
+    return bgets (w, len, (BUFF *)fb) > 0;
+}
+
+API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *fb,
+    char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_BUFF, fb);
+}
+
 
 API_EXPORT(void) send_size(size_t size, request_rec *r) {
     char ss[20];
Index: util_script.h
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.h,v
retrieving revision 1.21
diff -u -r1.21 util_script.h
--- util_script.h	1997/07/15 21:40:00	1.21
+++ util_script.h	1997/07/20 02:36:39
@@ -64,6 +64,7 @@
 API_EXPORT(void) add_common_vars(request_rec *r);
 #define scan_script_header(a1,a2) scan_script_header_err(a1,a2,NULL)
 API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer);
+API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer);
 API_EXPORT(void) send_size(size_t size, request_rec *r);
 API_EXPORT(int) call_exec (request_rec *r, char *argv0, char **env, int shellcmd);
 


Re: [PATCH] nph killer, cgi buffering (take 2)

Posted by Dean Gaudet <dg...@arctic.org>.
On Sat, 19 Jul 1997, Roy T. Fielding wrote:

> Ummm, except it looks like you forgot my comment about infinite loops
> due to bgets returning -1 on error where fgets returns 0.

Ooops!  Try this on for size.

bgets returns 0 at EOF, -1 on error, so I test for > 0. 

Dean

Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.353
diff -u -r1.353 CHANGES
--- CHANGES	1997/07/19 22:26:55	1.353
+++ CHANGES	1997/07/20 02:12:12
@@ -1,5 +1,11 @@
 Changes with Apache 1.3
-  
+
+  *) The special handling for nph- CGIs has been removed, it isn't
+     compatible with HTTP/1.1 or SSL support.  mod_cgi now flushes the
+     output to the client whenever it would block trying to read from
+     the CGI, so the CGI essentially controls the buffering.
+     [Dean Gaudet, Roy Fielding, Sameer Parekh]
+
   *) added transport handle slot (t_handle) to the BUFF structure
      [Doug MacEachern]
      
Index: alloc.c
===================================================================
RCS file: /export/home/cvs/apache/src/alloc.c,v
retrieving revision 1.40
diff -u -r1.40 alloc.c
--- alloc.c	1997/07/15 21:39:50	1.40
+++ alloc.c	1997/07/20 02:12:15
@@ -1064,9 +1064,16 @@
 #define enc_pipe(fds) pipe(fds)
 #endif /* WIN32 */
 
-API_EXPORT(int) spawn_child_err (pool *p, int (*func)(void *), void *data,
+/* for fdopen, to get binary mode */
+#if defined (__EMX__) || defined (WIN32)
+#define BINMODE	"b"
+#else
+#define BINMODE
+#endif
+
+static int spawn_child_err_core (pool *p, int (*func)(void *), void *data,
 		     enum kill_conditions kill_how,
-		     FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
+		     int *pipe_in, int *pipe_out, int *pipe_err)
 {
   int pid;
   int in_fds[2];
@@ -1074,13 +1081,7 @@
   int err_fds[2];
   int save_errno;
 
-  block_alarms();
-  
-  if (pipe_in && enc_pipe (in_fds) < 0)
-  {
-      save_errno = errno;
-      unblock_alarms();
-      errno = save_errno;
+  if (pipe_in && enc_pipe (in_fds) < 0) {
       return 0;
   }
   
@@ -1089,7 +1090,6 @@
     if (pipe_in) {
       close (in_fds[0]); close (in_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1102,7 +1102,6 @@
     if (pipe_out) {
       close (out_fds[0]); close (out_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1158,23 +1157,14 @@
       if(pid)
       {
           note_subprocess(p, pid, kill_how);
-          if(pipe_in)
-          {
-              *pipe_in = fdopen(in_fds[1], "wb");
-              if(*pipe_in)
-                  note_cleanups_for_file(p, *pipe_in);
+          if(pipe_in) {
+	      *pipe_in = in_fds[1];
           }
-          if(pipe_out)
-          {
-              *pipe_out = fdopen(out_fds[0], "rb");
-              if(*pipe_out)
-                  note_cleanups_for_file(p, *pipe_out);
+          if(pipe_out) {
+	      *pipe_out = out_fds[0];
           }
-          if(pipe_err)
-          {
-              *pipe_err = fdopen(err_fds[0], "rb");
-              if(*pipe_err)
-                  note_cleanups_for_file(p, *pipe_err);
+          if(pipe_err) {
+              *pipe_err = err_fds[0];
           }
       }
       SetThreadPriority(thread_handle, old_priority);
@@ -1198,7 +1188,6 @@
     if (pipe_err) {
       close (err_fds[0]); close (err_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1237,43 +1226,106 @@
   
   if (pipe_out) {
     close (out_fds[1]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2. */
-    *pipe_out = fdopen (out_fds[0], "rb");
-#else
-    *pipe_out = fdopen (out_fds[0], "r");
-#endif  
-  
-    if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
+    *pipe_out = out_fds[0];
   }
 
   if (pipe_in) {
     close (in_fds[0]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2 */
-    *pipe_in = fdopen (in_fds[1], "wb");
-#else
-    *pipe_in = fdopen (in_fds[1], "w");
-#endif
-    
-    if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
+    *pipe_in = in_fds[1];
   }
 
   if (pipe_err) {
     close (err_fds[1]);
-#ifdef __EMX__
-    /* Need binary mode set for OS/2. */
-    *pipe_err = fdopen (err_fds[0], "rb");
-#else
-    *pipe_err = fdopen (err_fds[0], "r");
-#endif
-  
-    if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+    *pipe_err = err_fds[0];
   }
 #endif /* WIN32 */
 
-  unblock_alarms();
   return pid;
+}
+
+
+API_EXPORT(int) spawn_child_err (pool *p, int (*func)(void *), void *data,
+		     enum kill_conditions kill_how,
+		     FILE **pipe_in, FILE **pipe_out, FILE **pipe_err)
+{
+    int fd_in, fd_out, fd_err;
+    int pid, save_errno;
+
+    block_alarms();
+
+    pid = spawn_child_err_core (p, func, data, kill_how,
+	    pipe_in ? &fd_in : NULL,
+	    pipe_out ? &fd_out : NULL,
+	    pipe_err ? &fd_err : NULL );
+
+    if (pid == 0) {
+	save_errno = errno;
+	unblock_alarms();
+	errno = save_errno;
+	return 0;
+    }
+
+    if (pipe_out) {
+	*pipe_out = fdopen (fd_out, "r" BINMODE);
+	if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
+	else close (fd_out);
+    }
+
+    if (pipe_in) {
+	*pipe_in = fdopen (fd_in, "w" BINMODE);
+	if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
+	else close (fd_in);
+    }
+
+    if (pipe_err) {
+	*pipe_err = fdopen (fd_err, "r" BINMODE);
+	if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+	else close (fd_err);
+    }
+
+    unblock_alarms();
+    return pid;
+}
+
+
+API_EXPORT(int) spawn_child_err_buff (pool *p, int (*func)(void *), void *data,
+			  enum kill_conditions kill_how,
+			  BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err)
+{
+    int fd_in, fd_out, fd_err;
+    int pid, save_errno;
+
+    block_alarms();
+
+    pid = spawn_child_err_core (p, func, data, kill_how,
+	    pipe_in ? &fd_in : NULL,
+	    pipe_out ? &fd_out : NULL,
+	    pipe_err ? &fd_err : NULL );
+
+    if (pid == 0) {
+	save_errno = errno;
+	unblock_alarms();
+	errno = save_errno;
+	return 0;
+    }
+  
+    if (pipe_out) {
+	*pipe_out = bcreate(p, B_RD);
+	bpushfd(*pipe_out, fd_out, fd_out);
+    }
+
+    if (pipe_in) {
+	*pipe_in = bcreate(p, B_WR);
+	bpushfd(*pipe_in, fd_in, fd_in);
+    }
+
+    if (pipe_err) {
+	*pipe_err = bcreate(p, B_RD);
+	bpushfd(*pipe_err, fd_err, fd_err);
+    }
+
+    unblock_alarms();
+    return pid;
 }
 
 static void free_proc_chain (struct process_chain *procs)
Index: buff.c
===================================================================
RCS file: /export/home/cvs/apache/src/buff.c,v
retrieving revision 1.37
diff -u -r1.37 buff.c
--- buff.c	1997/07/15 21:39:50	1.37
+++ buff.c	1997/07/20 02:12:17
@@ -415,18 +415,38 @@
 {
     if (value) {
 	fb->flags |= flag;
-	if( flag & B_CHUNK ) {
+	if (flag & B_CHUNK) {
 	    start_chunk(fb);
 	}
     } else {
 	fb->flags &= ~flag;
-	if( flag & B_CHUNK ) {
+	if (flag & B_CHUNK) {
 	    end_chunk(fb);
 	}
     }
     return value;
 }
 
+
+API_EXPORT(int) bnonblock(BUFF *fb, int direction)
+{
+    int fd;
+
+    fd = ( direction == B_RD ) ? fb->fd_in : fb->fd;
+#if defined(O_NONBLOCK)
+    return fcntl (fd, F_SETFL, O_NONBLOCK);
+#elif defined(F_NDELAY)
+    return fcntl (fd, F_SETFL, F_NDELAY);
+#else
+    return 0;
+#endif
+}
+
+API_EXPORT(int) bfileno(BUFF *fb, int direction)
+{
+    return (direction == B_RD) ? fb->fd_in : fb->fd;
+}
+
 /*
  * This is called instead of read() everywhere in here.  It implements
  * the B_SAFEREAD functionality -- which is to force a flush() if a read()
@@ -521,10 +541,18 @@
     if (fb->flags & B_RDERR) return -1;
     if (nbyte == 0) return 0;
 
-    if (!(fb->flags & B_RD))
-    {
-/* Unbuffered reading */
+    if (!(fb->flags & B_RD)) {
+	/* Unbuffered reading.  First check if there was something in the
+	 * buffer from before we went unbuffered. */
+	if (fb->incnt) {
+	    i = (fb->incnt > nbyte) ? nbyte : fb->incnt;
+	    memcpy (buf, fb->inptr, i);
+	    fb->incnt -= i;
+	    fb->inptr += i;
+	    return i;
+	}
 	i = saferead( fb, buf, nbyte );
+	if (i == 0) fb->flags |= B_EOF;
 	if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
 	return i;
     }
Index: buff.h
===================================================================
RCS file: /export/home/cvs/apache/src/buff.h,v
retrieving revision 1.21
diff -u -r1.21 buff.h
--- buff.h	1997/07/19 22:34:05	1.21
+++ buff.h	1997/07/20 02:12:18
@@ -158,3 +158,12 @@
 #define bputc(c, fb) ((((fb)->flags & (B_EOUT|B_WRERR|B_WR)) != B_WR || \
 		     (fb)->outcnt == (fb)->bufsiz) ? bflsbuf(c, (fb)) : \
 		     ((fb)->outbase[(fb)->outcnt++] = (c), 0))
+
+API_EXPORT(int) spawn_child_err_buff (pool *, int (*)(void *), void *,
+           	  enum kill_conditions, BUFF **pipe_in, BUFF **pipe_out,
+                  BUFF **pipe_err);
+
+/* enable non-blocking operations */
+API_EXPORT(int) bnonblock(BUFF *fb, int direction);
+/* and get an fd to select() on */
+API_EXPORT(int) bfileno(BUFF *fb, int direction);
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.143
diff -u -r1.143 http_protocol.c
--- http_protocol.c	1997/07/19 20:27:52	1.143
+++ http_protocol.c	1997/07/20 02:12:23
@@ -1626,6 +1626,85 @@
     return total_bytes_sent;
 }
 
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) send_fb(BUFF *fb, request_rec *r) {
+    return send_fb_length(fb, r, -1);
+}
+
+API_EXPORT(long) send_fb_length(BUFF *fb, request_rec *r, long length)
+{
+    char buf[IOBUFSIZE];
+    long total_bytes_sent = 0;
+    register int n, w, o, len, fd;
+    fd_set fds;
+    
+    if (length == 0) return 0;
+
+    /* Make fb unbuffered and non-blocking */
+    bsetflag (fb, B_RD, 0);
+    bnonblock (fb, B_RD);
+    fd = bfileno (fb, B_RD);
+
+    soft_timeout("send body", r);
+
+    while (!r->connection->aborted) {
+	if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+	    len = length - total_bytes_sent;
+	else len = IOBUFSIZE;
+
+	do {
+	    n = bread (fb, buf, len);
+	    if (n >= 0) break;
+	    if (n < 0 && errno != EAGAIN) break;
+	    /* we need to block, so flush the output first */
+	    bflush (r->connection->client);
+	    FD_ZERO (&fds);
+	    FD_SET (fd, &fds);
+	    /* we don't care what select says, we might as well loop back
+	     * around and try another read
+	     */
+	    ap_select (fd+1, &fds, NULL, NULL, NULL);
+	} while (!r->connection->aborted);
+
+	if (n < 1 || r->connection->aborted) {
+	    break;
+	}
+
+        o=0;
+	total_bytes_sent += n;
+
+        while (n && !r->connection->aborted) {
+            w = bwrite(r->connection->client, &buf[o], n);
+            if (w > 0) {
+                reset_timeout(r); /* reset timeout after successful write */
+                n-=w;
+                o+=w;
+            }
+            else if (w < 0) {
+                if (r->connection->aborted)
+                    break;
+                else if (errno == EAGAIN)
+                    continue;
+                else {
+                    log_unixerr("send body lost connection to",
+                                get_remote_host(r->connection,
+                                    r->per_dir_config, REMOTE_NAME),
+                                NULL, r->server);
+                    bsetflag(r->connection->client, B_EOUT, 1);
+                    r->connection->aborted = 1;
+                    break;
+                }
+            }
+        }
+    }
+
+    kill_timeout(r);
+    SET_BYTES_SENT(r);
+    return total_bytes_sent;
+}
+
 API_EXPORT(int) rputc (int c, request_rec *r)
 {
     if (r->connection->aborted) return EOF;
Index: http_protocol.h
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.h,v
retrieving revision 1.24
diff -u -r1.24 http_protocol.h
--- http_protocol.h	1997/07/19 20:27:52	1.24
+++ http_protocol.h	1997/07/20 02:12:24
@@ -110,6 +110,9 @@
 
 API_EXPORT(long) send_fd(FILE *f, request_rec *r);
 API_EXPORT(long) send_fd_length(FILE *f, request_rec *r, long length);
+
+API_EXPORT(long) send_fb(BUFF *f, request_rec *r);
+API_EXPORT(long) send_fb_length(BUFF *f, request_rec *r, long length);
      
 /* Hmmm... could macrofy these for now, and maybe forever, though the
  * definitions of the macros would get a whole lot hairier.
Index: mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.49
diff -u -r1.49 mod_cgi.c
--- mod_cgi.c	1997/07/17 22:27:34	1.49
+++ mod_cgi.c	1997/07/20 02:12:25
@@ -183,7 +183,7 @@
 }
 
 static int log_script(request_rec *r, cgi_server_conf *conf, int ret,
-	       char *dbuf, char *sbuf, FILE *script_in, FILE *script_err)
+		    char *dbuf, char *sbuf, BUFF *script_in, BUFF *script_err)
 {
     table *hdrs_arr = r->headers_in;
     table_entry *hdrs = (table_entry *)hdrs_arr->elts;
@@ -197,9 +197,9 @@
 	((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
 		     "a")) == NULL)) {
       /* Soak up script output */
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	continue;
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	continue;
       return ret;
     }
@@ -207,7 +207,7 @@
     /* "%% [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 */
+    /* "%% 500 /usr/local/etc/httpd/cgi-bin" */
     fprintf(f, "%%%% %d %s\n", ret, r->filename);
 
     fputs("%request\n", f);
@@ -216,7 +216,7 @@
       fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
     }
     if ((r->method_number == M_POST || r->method_number == M_PUT)
-	&& dbuf && *dbuf) {
+	&& *dbuf) {
       fprintf(f, "\n%s\n", dbuf);
     }
 
@@ -232,28 +232,24 @@
     if (sbuf && *sbuf)
       fprintf(f, "%s\n", sbuf);
 
-    *argsbuffer = '\0';
-    fgets(argsbuffer, HUGE_STRING_LEN-1, script_in);
-    if (*argsbuffer) {
+    if (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
       fputs("%stdout\n", f);
       fputs(argsbuffer, f);
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	fputs(argsbuffer, f);
       fputs("\n", f);
     }
 
-    *argsbuffer = '\0';
-    fgets(argsbuffer, HUGE_STRING_LEN-1, script_err);
-    if (*argsbuffer) {
+    if (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
       fputs("%stderr\n", f);
       fputs(argsbuffer, f);
-      while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
+      while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	fputs(argsbuffer, f);
       fputs("\n", f);
     }
 
-    pfclose(r->main ? r->main->pool : r->pool, script_in);
-    pfclose(r->main ? r->main->pool : r->pool, script_err);
+    bclose(script_in);
+    bclose(script_err);
 
     pfclose(r->pool, f);
     return ret;
@@ -277,7 +273,6 @@
     struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff;
     request_rec *r = cld->r;
     char *argv0 = cld->argv0;
-    int nph = cld->nph;
     int child_pid;
 
 #ifdef DEBUG_CGI    
@@ -312,10 +307,6 @@
     if (!cld->debug)
       error_log2stderr (r->server);
 
-#if !defined(__EMX__) && !defined(WIN32)
-    if (nph) client_to_stdout (r->connection);
-#endif    
-
     /* Transumute outselves into the script.
      * NB only ISINDEX scripts get decoded arguments.
      */
@@ -352,7 +343,7 @@
 {
     int retval, nph, dbpos = 0;
     char *argv0, *dbuf = NULL;
-    FILE *script_out, *script_in, *script_err;
+    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;
@@ -421,15 +412,10 @@
 	   * waiting for free_proc_chain to cleanup in the middle of an
 	   * SSI request -djg
 	   */
-	  spawn_child_err (r->main ? r->main->pool : r->pool, cgi_child,
-			    (void *)&cld,
-			   nph ? just_wait : kill_after_timeout,
-#if defined(__EMX__) || defined(WIN32)
-			   &script_out, &script_in, &script_err))) {
-#else
-			   &script_out, nph ? NULL : &script_in,
-	    		   &script_err))) {
-#endif
+	  spawn_child_err_buff (r->main ? r->main->pool : r->pool, cgi_child,
+				(void *)&cld,
+				kill_after_timeout,
+				&script_out, &script_in, &script_err))) {
         log_reason ("couldn't spawn child process", r->filename, r);
         return SERVER_ERROR;
     }
@@ -471,7 +457,7 @@
 		dbpos += dbsize;
 	    }
 	    reset_timeout(r);
-	    if (fwrite(argsbuffer, sizeof(char), len_read, script_out)
+	    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)
@@ -480,20 +466,20 @@
 	    }
 	}
 
-	fflush (script_out);
+	bflush (script_out);
 	signal (SIGPIPE, handler);
 	
 	kill_timeout (r);
     }
     
-    pfclose (r->main ? r->main->pool : r->pool, script_out);
+    bclose(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)))
+        if ((ret = scan_script_header_err_buff(r, script_in, sbuf)))
 	    return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
 	
 	location = table_get (r->headers_out, "Location");
@@ -502,11 +488,9 @@
 	  
 	    /* Soak up all the script output */
 	    hard_timeout ("read from script", r);
-	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_in)
-	           > 0)
+	    while (bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
 	        continue;
-	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err)
-	           > 0)
+	    while (bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
 	        continue;
 	    kill_timeout (r);
 
@@ -535,24 +519,20 @@
 	
 	send_http_header(r);
 	if (!r->header_only)
-	    send_fd(script_in, r);
-	pfclose (r->main ? r->main->pool : r->pool, script_in);
+	    send_fb(script_in, r);
 
-	/* Soak up stderr */
 	soft_timeout("soaking script stderr", r);
-	while (!r->connection->aborted &&
-	  (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err) > 0))
-	    continue;
+	while(bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
+	      continue;
 	kill_timeout(r);
-	pfclose (r->main ? r->main->pool : r->pool, script_err);
+	     
+	bclose(script_in);
+	bclose(script_out);
     }
 
-    if (nph) {
-#if defined(__EMX__) || defined(WIN32)
-        while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) {
-            bputs(argsbuffer, r->connection->client);
-        }
-#else
+    if (script_in && nph) {
+      send_fb(script_in, r);
+#if !defined(__EMX__) && !defined(WIN32)
 	waitpid(child_pid, (int*)0, 0);
 #endif
     }    
Index: util_script.c
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.c,v
retrieving revision 1.66
diff -u -r1.66 util_script.c
--- util_script.c	1997/07/15 21:39:59	1.66
+++ util_script.c	1997/07/20 02:12:27
@@ -312,7 +312,9 @@
     }
 }
 
-API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer)
+
+static int scan_script_header_err_core (request_rec *r, char *buffer,
+    int (*getsfunc)(char *, int, void *), void *getsfunc_data)
 {
     char x[MAX_STRING_LEN];
     char *w, *l;
@@ -325,7 +327,7 @@
     
     while(1) {
 
-	if (fgets(w, MAX_STRING_LEN-1, f) == NULL) {
+	if ((*getsfunc)(w, MAX_STRING_LEN-1, getsfunc_data) == 0) {
 	    kill_timeout (r);
 	    log_reason ("Premature end of script headers", r->filename, r);
 	    return SERVER_ERROR;
@@ -354,7 +356,7 @@
 
 	    if (!buffer)
 	      /* Soak up all the script output --- may save an outright kill */
-	      while (fgets(w, MAX_STRING_LEN-1, f) != NULL)
+	      while ((*getsfunc)(w, MAX_STRING_LEN-1, getsfunc_data))
 		continue;
 	    
 	    kill_timeout (r);
@@ -401,6 +403,28 @@
         }
     }
 }
+
+static int getsfunc_FILE (char *buf, int len, void *f)
+{
+    return fgets (buf, len, (FILE *)f) != NULL;
+}
+
+API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_FILE, f);
+}
+
+static int getsfunc_BUFF (char *w, int len, void *fb)
+{
+    return bgets (w, len, (BUFF *)fb) > 0;
+}
+
+API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *fb,
+    char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_BUFF, fb);
+}
+
 
 API_EXPORT(void) send_size(size_t size, request_rec *r) {
     char ss[20];
Index: util_script.h
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.h,v
retrieving revision 1.21
diff -u -r1.21 util_script.h
--- util_script.h	1997/07/15 21:40:00	1.21
+++ util_script.h	1997/07/20 02:12:28
@@ -64,6 +64,7 @@
 API_EXPORT(void) add_common_vars(request_rec *r);
 #define scan_script_header(a1,a2) scan_script_header_err(a1,a2,NULL)
 API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char *buffer);
+API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer);
 API_EXPORT(void) send_size(size_t size, request_rec *r);
 API_EXPORT(int) call_exec (request_rec *r, char *argv0, char **env, int shellcmd);