You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Randy Terbush <ra...@zyzzyva.com> on 1997/07/05 17:40:10 UTC

Re: [PATCH] CGI unbuffering for 1.3

> 	Whee, I made my cgi unbuffering work with 1.3 What this does:
> createas a select() loops between the cgi and the client, so that if
> the cgi pauses its output, the client sees a puase. If nph- is on,
> then the only difference is that the select() loops starts as soon as
> the cgi starts, instead of waiting until
> scan_script_headers/send_http_hedares happens.
> 
> 	I am guessing that this patch will work as-is on NT except for
> my send_fb function, which is the select() loop. It's been tested so
> far on freebsd.
> 
> 	I coudln't figure out how to prototype spawn_child_err_buff()
> in alloc.c because in alloc.h buff.h isn't included yet.
> 
> 	Comments? Should I stick this in? This CGI code has been in
> Stronghold since just after 1.2 went into feature freeze. SO it's
> pretty stable on UNIX, in its stronghold incarnation. I may have messed
> up and created some instability when I modified the stuff for 1.3,
> though.

I'm not clear on why we need to create parallel functions rather 
than change the existing ones. Note that I'm not saying we don't, 
but am not clear on why we can't change the existing functions.

Also, I question why send_fb_length() returns a long. It appears to 
promote the return of bwrite() from an int to a long. bwrite should 
probably be returning a size_t as I look at this.

Also, as Roy oft points out, single character variable names are 
kind of a pain to follow. Especially the use of 'o'.

Lastly (a nit) there are comments that reference directories that 
don't necessarily exist. /usr/local/etc/httpd. There has been some 
discussion of this lately. I would like to see us lose this legacy 
in the source reorganization that Paul has been working on. This is 
an incredibily stupid place to install the web server IMO.


> Index: alloc.c
> ===================================================================
> RCS file: /export/home/cvs/apache/src/alloc.c,v
> retrieving revision 1.35
> diff -c -r1.35 alloc.c
> *** alloc.c	1997/06/29 19:19:34	1.35
> --- alloc.c	1997/07/04 05:37:28
> ***************
> *** 1025,1030 ****
> --- 1025,1222 ----
>   #define enc_pipe(fds) pipe(fds)
>   #endif /* WIN32 */
>   
> + 
> + 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 pid;
> +   int in_fds[2];
> +   int out_fds[2];
> +   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;
> +       return 0;
> +   }
> +   
> +   if (pipe_out && enc_pipe (out_fds) < 0) {
> +     save_errno = errno;
> +     if (pipe_in) {
> +       close (in_fds[0]); close (in_fds[1]);
> +     }
> +     unblock_alarms();
> +     errno = save_errno;
> +     return 0;
> +   }
> + 
> +   if (pipe_err && enc_pipe (err_fds) < 0) {
> +     save_errno = errno;
> +     if (pipe_in) {
> +       close (in_fds[0]); close (in_fds[1]);
> +     }
> +     if (pipe_out) {
> +       close (out_fds[0]); close (out_fds[1]);
> +     }
> +     unblock_alarms();
> +     errno = save_errno;
> +     return 0;
> +   }
> + 
> + #ifdef WIN32
> + 
> +   {
> +       HANDLE thread_handle;
> +       int hStdIn, hStdOut, hStdErr;
> +       int old_priority;
> +       
> +       (void)acquire_mutex(spawn_mutex);
> +       thread_handle = GetCurrentThread(); /* doesn't need to be closed */
> +       old_priority = GetThreadPriority(thread_handle);
> +       SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST);
> +       /* Now do the right thing with your pipes */
> +       if(pipe_in)
> +       {
> +           hStdIn = dup(fileno(stdin));
> +           dup2(in_fds[0], fileno(stdin));
> +           close(in_fds[0]);
> +       }
> +       if(pipe_out)
> +       {
> +           hStdOut = dup(fileno(stdout));
> +           dup2(out_fds[1], fileno(stdout));
> +           close(out_fds[1]);
> +       }
> +       if(pipe_err)
> +       {
> +           hStdErr = dup(fileno(stderr));
> +           dup2(err_fds[1], fileno(stderr));
> +           close(err_fds[1]);
> +       }
> + 
> +       pid = (*func)(data);
> +       if(!pid)
> +       {
> +           save_errno = errno;
> +           close(in_fds[1]);
> +           close(out_fds[0]);
> +           close(err_fds[0]);
> +       }
> + 
> +       /* restore the original stdin, stdout and stderr */
> +       if(pipe_in)
> +           dup2(hStdIn, fileno(stdin));
> +       if(pipe_out)
> +           dup2(hStdOut, fileno(stdout));
> +       if(pipe_err)
> +           dup2(hStdErr, fileno(stderr));
> + 
> +       if(pid)
> +       {
> +           note_subprocess(p, pid, kill_how);
> +           if(pipe_in)
> +           {
> +               *pipe_in = bcreate(p, B_WR);
> + 	      bpushfd(*pipe_in, in_fds[1], in_fds[1]);
> +           }
> +           if(pipe_out)
> +           {
> +               *pipe_out = bcreate(p, B_RD);
> + 	      bpushfd(*pipe_out, out_fds[0], out_fds[0]);
> +           }
> +           if(pipe_err)
> +           {
> +               *pipe_err = bcreate(p, B_RD);
> + 	      bpushfd(*pipe_err, err_fds[0], err_fds[0]);
> +           }
> +       }
> +       SetThreadPriority(thread_handle, old_priority);
> +       (void)release_mutex(spawn_mutex);
> +       /*
> +        * go on to the end of the function, where you can
> +        * unblock alarms and return the pid
> +        */
> + 
> +   }
> + #else
> + 
> +   if ((pid = fork()) < 0) {
> +     save_errno = errno;
> +     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();
> +     errno = save_errno;
> +     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 (1);		/* Should only get here if the exec in func() failed */
> +   }
> + 
> +   /* 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]);
> +   }
> + #endif /* WIN32 */
> + 
> +   unblock_alarms();
> +   return pid;
> + }
> + 
>   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)
> Index: alloc.h
> ===================================================================
> RCS file: /export/home/cvs/apache/src/alloc.h,v
> retrieving revision 1.23
> diff -c -r1.23 alloc.h
> *** alloc.h	1997/06/28 20:24:27	1.23
> --- alloc.h	1997/07/04 05:37:31
> ***************
> *** 243,248 ****
> --- 243,253 ----
>   int spawn_child_err (pool *, int (*)(void *), void *,
>   		 enum kill_conditions, FILE **pipe_in, FILE **pipe_out,
>                    FILE **pipe_err);
> + #if 0
> + int spawn_child_err_buff (pool *, int (*)(void *), void *,
> +            	  enum kill_conditions, BUFF **pipe_in, BUFF **pipe_out,
> +                   BUFF **pipe_err);
> + #endif
>   #define spawn_child(p,f,v,k,in,out) spawn_child_err(p,f,v,k,in,out,NULL)
>   
>   /* magic numbers --- min free bytes to consider a free pool block useable,
> Index: http_protocol.c
> ===================================================================
> RCS file: /export/home/cvs/apache/src/http_protocol.c,v
> retrieving revision 1.131
> diff -c -r1.131 http_protocol.c
> *** http_protocol.c	1997/07/01 06:46:02	1.131
> --- http_protocol.c	1997/07/04 05:38:01
> ***************
> *** 1516,1521 ****
> --- 1516,1597 ----
>   
>       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(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;
> + }
> + 
> + 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); }
>   
> Index: http_protocol.h
> ===================================================================
> RCS file: /export/home/cvs/apache/src/http_protocol.h,v
> retrieving revision 1.19
> diff -c -r1.19 http_protocol.h
> *** http_protocol.h	1997/04/07 10:58:38	1.19
> --- http_protocol.h	1997/07/04 05:38:06
> ***************
> *** 110,115 ****
> --- 110,118 ----
>   
>   long send_fd(FILE *f, request_rec *r);
>   long send_fd_length(FILE *f, request_rec *r, long length);
> + 
> + long send_fb(BUFF *f, request_rec *r);
> + 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.42
> diff -c -r1.42 mod_cgi.c
> *** mod_cgi.c	1997/06/29 17:39:50	1.42
> --- mod_cgi.c	1997/07/04 05:38:40
> ***************
> *** 183,189 ****
>   }
>   
>   static int log_script(request_rec *r, cgi_server_conf *conf, int ret,
> ! 	       char *dbuf, char *sbuf, FILE *script_in, FILE *script_err)
>   {
>       table *hdrs_arr = r->headers_in;
>       table_entry *hdrs = (table_entry *)hdrs_arr->elts;
> --- 183,189 ----
>   }
>   
>   static int log_script(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;
> ***************
> *** 197,205 ****
>   	((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname),
>   		     "a")) == NULL)) {
>         /* Soak up script output */
> !       while (fgets(argsbuffer, MAX_STRING_LEN-1, script_in) != NULL)
>   	continue;
> !       while (fgets(argsbuffer, MAX_STRING_LEN-1, script_err) != NULL)
>   	continue;
>         return ret;
>       }
> --- 197,205 ----
>   	((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;
>       }
> ***************
> *** 207,213 ****
>       /* "%% [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);
> --- 207,213 ----
>       /* "%% [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);
> ***************
> *** 216,222 ****
>         fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
>       }
>       if ((r->method_number == M_POST || r->method_number == M_PUT)
> ! 	&& dbuf && *dbuf) {
>         fprintf(f, "\n%s\n", dbuf);
>       }
>   
> --- 216,222 ----
>         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);
>       }
>   
> ***************
> *** 233,259 ****
>         fprintf(f, "%s\n", sbuf);
>   
>       *argsbuffer = '\0';
> !     fgets(argsbuffer, HUGE_STRING_LEN-1, script_in);
>       if (*argsbuffer) {
>         fputs("%stdout\n", f);
>         fputs(argsbuffer, f);
> !       while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL)
>   	fputs(argsbuffer, f);
>         fputs("\n", f);
>       }
>   
>       *argsbuffer = '\0';
> !     fgets(argsbuffer, HUGE_STRING_LEN-1, script_err);
>       if (*argsbuffer) {
>         fputs("%stderr\n", f);
>         fputs(argsbuffer, f);
> !       while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err) != NULL)
>   	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);
>   
>       pfclose(r->pool, f);
>       return ret;
> --- 233,259 ----
>         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;
> ***************
> *** 277,283 ****
>       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    
> --- 277,282 ----
> ***************
> *** 312,321 ****
>       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.
>        */
> --- 311,316 ----
> ***************
> *** 352,358 ****
>   {
>       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;
> --- 347,353 ----
>   {
>       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;
> ***************
> *** 421,435 ****
>   	   * 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
>           log_reason ("couldn't spawn child process", r->filename, r);
>           return SERVER_ERROR;
>       }
> --- 416,425 ----
>   	   * waiting for free_proc_chain to cleanup in the middle of an
>   	   * SSI request -djg
>   	   */
> ! 	  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,477 ****
>   		dbpos += dbsize;
>   	    }
>   	    reset_timeout(r);
> ! 	    if (fwrite(argsbuffer, sizeof(char), 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)
> --- 461,467 ----
>   		dbpos += dbsize;
>   	    }
>   	    reset_timeout(r);
> ! 	    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,499 ****
>   	    }
>   	}
>   
> ! 	fflush (script_out);
>   	signal (SIGPIPE, handler);
>   	
>   	kill_timeout (r);
>       }
>       
> !     pfclose (r->main ? r->main->pool : 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");
> --- 470,489 ----
>   	    }
>   	}
>   
> ! 	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(r, conf, ret, dbuf, sbuf, script_in, script_err);
>   	
>   	location = table_get (r->headers_out, "Location");
> ***************
> *** 502,512 ****
>   	  
>   	    /* Soak up all the script output */
>   	    hard_timeout ("read from script", r);
> ! 	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_in)
> ! 	           > 0)
>   	        continue;
> ! 	    while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err)
> ! 	           > 0)
>   	        continue;
>   	    kill_timeout (r);
>   
> --- 492,500 ----
>   	  
>   	    /* 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);
>   
> ***************
> *** 535,558 ****
>   	
>   	send_http_header(r);
>   	if (!r->header_only)
> ! 	    send_fd(script_in, r);
> ! 	pfclose (r->main ? r->main->pool : r->pool, 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;
>   	kill_timeout(r);
> ! 	pfclose (r->main ? r->main->pool : r->pool, 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
>   	waitpid(child_pid, (int*)0, 0);
>   #endif
>       }    
> --- 523,542 ----
>   	
>   	send_http_header(r);
>   	if (!r->header_only)
> ! 	    send_fb(script_in, r);
>   
>   	soft_timeout("soaking script stderr", r);
> ! 	while(bgets(argsbuffer, HUGE_STRING_LEN-1, script_err))
> ! 	      continue;
>   	kill_timeout(r);
> ! 	     
> ! 	bclose(script_in);
> ! 	bclose(script_out);
>       }
>   
> !     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.62
> diff -c -r1.62 util_script.c
> *** util_script.c	1997/06/29 19:19:37	1.62
> --- util_script.c	1997/07/04 05:38:56
> ***************
> *** 312,317 ****
> --- 312,406 ----
>       }
>   }
>   
> + 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) <= 0) {
> + 	    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) <= 0)
> + 		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];
> Index: util_script.h
> ===================================================================
> RCS file: /export/home/cvs/apache/src/util_script.h,v
> retrieving revision 1.17
> diff -c -r1.17 util_script.h
> *** util_script.h	1997/06/15 19:22:35	1.17
> --- util_script.h	1997/07/04 05:38:59
> ***************
> *** 64,69 ****
> --- 64,70 ----
>   void add_common_vars(request_rec *r);
>   #define scan_script_header(a1,a2) scan_script_header_err(a1,a2,NULL)
>   int scan_script_header_err(request_rec *r, FILE *f, char *buffer);
> + int scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer);
>   void send_size(size_t size, request_rec *r);
>   int call_exec (request_rec *r, char *argv0, char **env, int shellcmd);
>   
> 
> 
> -- 
> Sameer Parekh					Voice:   510-986-8770
> President					FAX:     510-986-8777
> C2Net
> http://www.c2.net/				sameer@c2.net




Re: [PATCH] CGI unbuffering for 1.3

Posted by Brian Behlendorf <br...@organic.com>.
At 05:57 PM 7/7/97 -0700, you wrote:
>So anyhow, this is where I'm heading with Sameer's code.  This hasn't
>even been tested yet, so don't bother patching it in.

All sounds great!

>That satisfy the complaints about it?  It's still ugly to have both BUFF *
>and FILE * crap.  I'd really love to lose both in favour of sfio.

Me too, but let's do it after 1.3.....

	Brian


--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
"Why not?" - TL           brian@organic.com - hyperreal.org - apache.org

Re: [PATCH] CGI unbuffering for 1.3

Posted by Dean Gaudet <dg...@arctic.org>.
So anyhow, this is where I'm heading with Sameer's code.  This hasn't
even been tested yet, so don't bother patching it in.

- clean up spawn_child_err{,_buff} moving shared code into a _core routine

- fix a possible fd leak in spawn_child_err, if fdopen() failed it would
  lose a descriptor or three

- remove the fb->foobar tweaking in send_fb_length by enhancing bread() to
  deal with a partially filled buffer while in unbuffered mode

- remove the select() from send_fb_length, because as is it's not required
    the bread will block anyhow since it's a blocking fd.  This is going
    to change, I need select() and some more garbage in here to deal with
    a client disappearing while the CGI is off thinking.

- do the shared code thing with scan_script_header_err{,_buff}

That satisfy the complaints about it?  It's still ugly to have both BUFF *
and FILE * crap.  I'd really love to lose both in favour of sfio.

Dean

Index: alloc.c
===================================================================
RCS file: /export/home/cvs/apache/src/alloc.c,v
retrieving revision 1.36
diff -u -r1.36 alloc.c
--- alloc.c	1997/07/06 05:37:18	1.36
+++ alloc.c	1997/07/08 01:19:03
@@ -1025,10 +1025,16 @@
 #define enc_pipe(fds) pipe(fds)
 #endif /* WIN32 */
 
+/* for fdopen, to get binary mode */
+#if defined (__EMX__) || defined (WIN32)
+#define BINMODE	"b"
+#else
+#define BINMODE
+#endif
 
-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)
+static int spawn_child_err_core (pool *p, int (*func)(void *), void *data,
+		     enum kill_conditions kill_how,
+		     int *pipe_in, int *pipe_out, int *pipe_err)
 {
   int pid;
   int in_fds[2];
@@ -1036,13 +1042,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;
   }
   
@@ -1051,7 +1051,6 @@
     if (pipe_in) {
       close (in_fds[0]); close (in_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1064,7 +1063,6 @@
     if (pipe_out) {
       close (out_fds[0]); close (out_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1120,20 +1118,14 @@
       if(pid)
       {
           note_subprocess(p, pid, kill_how);
-          if(pipe_in)
-          {
-              *pipe_in = bcreate(p, B_WR);
-	      bpushfd(*pipe_in, in_fds[1], in_fds[1]);
+          if(pipe_in) {
+	      *pipe_in = in_fds[1];
           }
-          if(pipe_out)
-          {
-              *pipe_out = bcreate(p, B_RD);
-	      bpushfd(*pipe_out, out_fds[0], out_fds[0]);
+          if(pipe_out) {
+	      *pipe_out = out_fds[0];
           }
-          if(pipe_err)
-          {
-              *pipe_err = bcreate(p, B_RD);
-	      bpushfd(*pipe_err, err_fds[0], err_fds[0]);
+          if(pipe_err) {
+              *pipe_err = err_fds[0];
           }
       }
       SetThreadPriority(thread_handle, old_priority);
@@ -1157,7 +1149,6 @@
     if (pipe_err) {
       close (err_fds[0]); close (err_fds[1]);
     }
-    unblock_alarms();
     errno = save_errno;
     return 0;
   }
@@ -1196,237 +1187,106 @@
   
   if (pipe_out) {
     close (out_fds[1]);
-    *pipe_out = bcreate(p, B_RD);
-    bpushfd(*pipe_out, out_fds[0], out_fds[0]);
+    *pipe_out = 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]);
+    *pipe_in = 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]);
+    *pipe_err = err_fds[0];
   }
 #endif /* WIN32 */
 
-  unblock_alarms();
   return pid;
 }
 
+
 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 pid;
-  int in_fds[2];
-  int out_fds[2];
-  int err_fds[2];
-  int save_errno;
+    int fd_in, fd_out, fd_err;
+    int pid, save_errno;
 
-  block_alarms();
-  
-  if (pipe_in && enc_pipe (in_fds) < 0)
-  {
-      save_errno = errno;
-      unblock_alarms();
-      errno = save_errno;
-      return 0;
-  }
-  
-  if (pipe_out && enc_pipe (out_fds) < 0) {
-    save_errno = errno;
-    if (pipe_in) {
-      close (in_fds[0]); close (in_fds[1]);
-    }
-    unblock_alarms();
-    errno = save_errno;
-    return 0;
-  }
+    block_alarms();
 
-  if (pipe_err && enc_pipe (err_fds) < 0) {
-    save_errno = errno;
-    if (pipe_in) {
-      close (in_fds[0]); close (in_fds[1]);
+    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) {
-      close (out_fds[0]); close (out_fds[1]);
+	*pipe_out = fdopen (fd_out, "r" BINMODE);
+	if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
+	else close (fd_out);
     }
-    unblock_alarms();
-    errno = save_errno;
-    return 0;
-  }
 
-#ifdef WIN32
+    if (pipe_in) {
+	*pipe_in = fdopen (fd_in, "w" BINMODE);
+	if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
+	else close (fd_in);
+    }
 
-  {
-      HANDLE thread_handle;
-      int hStdIn, hStdOut, hStdErr;
-      int old_priority;
-      
-      (void)acquire_mutex(spawn_mutex);
-      thread_handle = GetCurrentThread(); /* doesn't need to be closed */
-      old_priority = GetThreadPriority(thread_handle);
-      SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST);
-      /* Now do the right thing with your pipes */
-      if(pipe_in)
-      {
-          hStdIn = dup(fileno(stdin));
-          dup2(in_fds[0], fileno(stdin));
-          close(in_fds[0]);
-      }
-      if(pipe_out)
-      {
-          hStdOut = dup(fileno(stdout));
-          dup2(out_fds[1], fileno(stdout));
-          close(out_fds[1]);
-      }
-      if(pipe_err)
-      {
-          hStdErr = dup(fileno(stderr));
-          dup2(err_fds[1], fileno(stderr));
-          close(err_fds[1]);
-      }
+    if (pipe_err) {
+	*pipe_err = fdopen (fd_err, "r" BINMODE);
+	if (*pipe_err) note_cleanups_for_file (p, *pipe_err);
+	else close (fd_err);
+    }
 
-      pid = (*func)(data);
-      if(!pid)
-      {
-          save_errno = errno;
-          close(in_fds[1]);
-          close(out_fds[0]);
-          close(err_fds[0]);
-      }
+    unblock_alarms();
+    return pid;
+}
 
-      /* restore the original stdin, stdout and stderr */
-      if(pipe_in)
-          dup2(hStdIn, fileno(stdin));
-      if(pipe_out)
-          dup2(hStdOut, fileno(stdout));
-      if(pipe_err)
-          dup2(hStdErr, fileno(stderr));
 
-      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_out)
-          {
-              *pipe_out = fdopen(out_fds[0], "rb");
-              if(*pipe_out)
-                  note_cleanups_for_file(p, *pipe_out);
-          }
-          if(pipe_err)
-          {
-              *pipe_err = fdopen(err_fds[0], "rb");
-              if(*pipe_err)
-                  note_cleanups_for_file(p, *pipe_err);
-          }
-      }
-      SetThreadPriority(thread_handle, old_priority);
-      (void)release_mutex(spawn_mutex);
-      /*
-       * go on to the end of the function, where you can
-       * unblock alarms and return the pid
-       */
+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;
 
-  }
-#else
+    block_alarms();
 
-  if ((pid = fork()) < 0) {
-    save_errno = errno;
-    if (pipe_in) {
-      close (in_fds[0]); close (in_fds[1]);
+    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) {
-      close (out_fds[0]); close (out_fds[1]);
-    }
-    if (pipe_err) {
-      close (err_fds[0]); close (err_fds[1]);
-    }
-    unblock_alarms();
-    errno = save_errno;
-    return 0;
-  }
-
-  if (!pid) {
-    /* Child process */
-    
-    if (pipe_out) {
-      close (out_fds[0]);
-      dup2 (out_fds[1], STDOUT_FILENO);
-      close (out_fds[1]);
+	*pipe_out = bcreate(p, B_RD);
+	bpushfd(*pipe_out, fd_out, fd_out);
     }
 
     if (pipe_in) {
-      close (in_fds[1]);
-      dup2 (in_fds[0], STDIN_FILENO);
-      close (in_fds[0]);
+	*pipe_in = bcreate(p, B_WR);
+	bpushfd(*pipe_in, fd_in, fd_in);
     }
 
     if (pipe_err) {
-      close (err_fds[0]);
-      dup2 (err_fds[1], STDERR_FILENO);
-      close (err_fds[1]);
+	*pipe_err = bcreate(p, B_RD);
+	bpushfd(*pipe_err, fd_err, fd_err);
     }
 
-    /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
-    signal (SIGCHLD, SIG_DFL);	/* Was that it? */
-    
-    func (data);
-    exit (1);		/* Should only get here if the exec in func() failed */
-  }
-
-  /* Parent process */
-
-  note_subprocess (p, pid, kill_how);
-  
-  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);
-  }
-
-  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);
-  }
-
-  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);
-  }
-#endif /* WIN32 */
-
-  unblock_alarms();
-  return pid;
+    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.35
diff -u -r1.35 buff.c
--- buff.c	1997/06/30 21:16:17	1.35
+++ buff.c	1997/07/08 01:19:06
@@ -419,12 +419,12 @@
 {
     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);
 	}
     }
@@ -526,9 +526,16 @@
     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 == -1 && errno != EAGAIN) doerror(fb, B_RD);
 	return i;
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.133
diff -u -r1.133 http_protocol.c
--- http_protocol.c	1997/07/06 05:37:19	1.133
+++ http_protocol.c	1997/07/08 01:19:11
@@ -1516,6 +1516,7 @@
 
     return (chunk_start + len_read);
 }
+
 long send_fb_length(BUFF *fb, request_rec *r, long length)
 {
     char buf[IOBUFSIZE];
@@ -1527,33 +1528,16 @@
 
     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;
+    bsetflag (fb, B_RD, 0);
 
+    while(1) {
 	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;
+	      && fb->flags & B_ERROR && errno == EINTR) {
+	    /* no-op */
+	}
 	
 	if (n < 1) {
             break;
Index: util_script.c
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.c,v
retrieving revision 1.63
diff -u -r1.63 util_script.c
--- util_script.c	1997/07/06 05:37:20	1.63
+++ util_script.c	1997/07/08 01:19:13
@@ -312,7 +312,9 @@
     }
 }
 
-int scan_script_header_err_buff(request_rec *r, BUFF *fb, 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,8 @@
     
     while(1) {
 
-	if (bgets(w, MAX_STRING_LEN-1, fb) <= 0) {
+	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;
         }
@@ -353,7 +356,7 @@
 
 	    if (!buffer)
 	      /* Soak up all the script output --- may save an outright kill */
-	      while (bgets(w, MAX_STRING_LEN-1, fb) <= 0)
+	      while ((*getsfunc)(w, MAX_STRING_LEN-1, getsfunc_data))
 		continue;
 	    
 	    kill_timeout (r);
@@ -401,95 +404,27 @@
     }
 }
 
-int scan_script_header_err(request_rec *r, FILE *f, 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 (fgets(w, MAX_STRING_LEN-1, f) == NULL) {
-	    kill_timeout (r);
-	    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 (fgets(w, MAX_STRING_LEN-1, f) != NULL)
-		continue;
-	    
-	    kill_timeout (r);
-	    log_reason (malformed, r->filename, r);
-	    return SERVER_ERROR;
-        }
+static int getsfunc_FILE (char *buf, int len, void *f)
+{
+    return fgets (buf, len, (FILE *)f) != NULL;
+}
 
-        *l++ = '\0';
-	while (*l && isspace (*l)) ++l;
-	
-        if(!strcasecmp(w,"Content-type")) {
+int scan_script_header_err(request_rec *r, FILE *f, char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_FILE, f);
+}
 
-	    /* 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);
-        }   
+static int getsfunc_BUFF (char *w, int len, void *fb)
+{
+    return bgets (w, len, (BUFF *)fb) > 0;
+}
 
-/* 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_buff(request_rec *r, BUFF *fb, char *buffer)
+{
+    return scan_script_header_err_core (r, buffer, getsfunc_BUFF, fb);
 }
+
 
 void send_size(size_t size, request_rec *r) {
     char ss[20];