httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ryan Bloom <...@raleigh.ibm.com>
Subject Re: cgi performance in Apache-apr.
Date Tue, 20 Apr 1999 19:27:38 GMT

> I think you just about summed it up nicely ;) . The module
> youre producing is quite different to cgisock . Its
> the backward compatibility that is the main difference . Cgisock
> also takes a few liberties for the sake of performance ( no error
> pipe being the most notable ) . 
> 
> 	Is this new module in from-cvs yet Ryan ?

I am post mod_cgid to new-httpd for the first time.  I realize this is not
optimized, nor possibly even good code.  In fact, I make no promises that
it will always work.  I have gotten good results whenever I try it, but I
am not great with cgi's (just not what I enjoy writing).  This code was
peiced together over a couple of days in between working on the hybrid
server and apr, so I really haven't tried to make it great.

Comments/thoughts.  Any objections to my putting this in cvs the way it is
now?

/* ====================================================================
 * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

/*
 * http_script: keeps all script-related ramblings together.
 * 
 * Compliant to cgid/1.1 spec
 * 
 * Adapted by rst from original NCSA code by Rob McCool
 *
 * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
 * custom error responses, and DOCUMENT_ROOT because we found it useful.
 * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 
 * they fail.
 */

#define CORE_PRIVATE

#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
#include "http_conf_globals.h"
#include "buff.h"
#include "alloc.h"

module MODULE_VAR_EXPORT cgid_module;

#define MAX_FILENAME_LEN 25

static int cgid_work_func(request_rec *r, char *argv0, char **env);
static int once_through = 0;

char tmpnames[HARD_THREAD_LIMIT*HARD_SERVER_LIMIT][MAX_FILENAME_LEN];

pool *pcgi;

/* KLUDGE --- for back-combatibility, we don't have to check Execcgid
 * in ScriptAliased directories, which means we need to know if this
 * request came through ScriptAlias or not... so the Alias module
 * leaves a note for us.
 */

static int is_scriptaliased(request_rec *r)
{
    const char *t = ap_table_get(r->notes, "alias-forced-type");
    return t && (!strcasecmp(t, "cgi-script"));
}

/* Configuration stuff */

#define DEFAULT_LOGBYTES 10385760
#define DEFAULT_BUFBYTES 1024

typedef struct {
    char *logname;
    long logbytes;
    int bufbytes;
    BUFF *bin;
    BUFF *bout;
    BUFF *berror;
} cgid_server_conf;


static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env)
{
    int i, len, j;
    unsigned char *data;
    char **environ;
    char temp[MAX_STRING_LEN];
    core_dir_config *temp_core;
    void **dconf;

    r->server = ap_pcalloc(r->pool, sizeof(server_rec));

    read(fd, &j, sizeof(int));
    read(fd, &len, sizeof(int));
    data = ap_pcalloc(r->pool, len);
    i = read(fd, data, len);

    r->filename = ap_getword(r->pool, (const char **)&data, '\n');
    *argv0 = ap_getword(r->pool, (const char **)&data, '\n'); 

    r->uri = ap_getword(r->pool, (const char **)&data, '\n');
   
    environ = ap_pcalloc(r->pool, (j + 2) *sizeof(char *));
    i = 0;
    for (i = 0; i < j; i++) {
        environ[i] = ap_getword(r->pool, (const char **)&data, '\n');
    }    
    *env = environ;
    r->args = ap_getword(r->pool, (const char **)&data, '\n');
 
    read(fd, &r->server->server_uid, sizeof(uid_t));
    read(fd, &r->server->server_gid, sizeof(gid_t));

    read(fd, &i, sizeof(int));
    
    /* add 1, so that if i == 0, we still malloc something. */
    dconf = (void **)malloc(sizeof(void *) * i + 1);

    temp_core = (core_dir_config *)malloc(sizeof(core_module));

#ifdef RLIMIT_CPU
    read(fd, &j, sizeof(int));
    if (j) {
        temp_core->limit_cpu = (struct rlimit *)malloc (sizeof(struct rlimit));
        read(fd, temp_core->limit_cpu, sizeof(struct rlimit));
    }
    else {
        temp_core->limit_cpu = NULL;
    }
#endif

#if defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
    read(fd, &j, sizeof(int));
    if (j) {
        temp_core->limit_mem = (struct rlimit *)malloc (sizeof(struct rlimit));
        read(fd, temp_core->limit_mem, sizeof(struct rlimit));
    }
    else {
        temp_core->limit_mem = NULL;
    }
#endif

#ifdef RLIMIT_NPROC    
    read(fd, &j, sizeof(int));
    if (j) {
        temp_core->limit_nproc = (struct rlimit *)malloc (sizeof(struct rlimit));
        read(fd, temp_core->limit_nproc, sizeof(struct rlimit));
    }
    else {
        temp_core->limit_nproc = NULL;
    }
#endif    

    dconf[i] = (void *)temp_core;
    r->per_dir_config = dconf;
}

static void send_req(int fd, request_rec *r, char *argv0, char **env)
{
    int len;
    int i = 0;
    char *data;
    core_dir_config *conf = ap_get_module_config(r->per_dir_config, 
                                                 &core_module);

    data = ap_pstrcat(r->pool, r->filename, "\n", argv0, "\n", r->uri, "\n",
                     NULL);

    for (i =0; env[i]; i++) {
        continue;
    }    

    write(fd, &i, sizeof(int));

    for (i = 0; env[i]; i++) {
        data = ap_pstrcat(r->pool, data, env[i], "\n", NULL); 
    }
    data = ap_pstrcat(r->pool, data, r->args, NULL);
    len = strlen(data);
    write(fd, &len, sizeof(int));
    write(fd, data, len);
    write(fd, &r->server->server_uid, sizeof(uid_t));
    write(fd, &r->server->server_gid, sizeof(gid_t));
    write(fd, &core_module.module_index, sizeof(int));

#ifdef RLIMIT_CPU
    if (conf->limit_cpu) {
        len = 1;
        write(fd, &len, sizeof(int)); 
        write(fd, conf->limit_cpu, sizeof(struct rlimit));
    } 
    else {
        len = 0;
        write(fd, &len, sizeof(int));
    } 
#endif

#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 
    if (conf->limit_mem) {
        len = 1;
        write(fd, &len, sizeof(int)); 
        write(fd, conf->limit_mem, sizeof(struct rlimit));
    } 
    else {
        len = 0;
        write(fd, &len, sizeof(int));
    } 
#endif
 
#ifdef RLIMIT_NPROC
    if (conf->limit_nproc) {
        len = 1;
        write(fd, &len, sizeof(int)); 
        write(fd, conf->limit_nproc, sizeof(struct rlimit));
    } 
    else {
        len = 0;
        write(fd, &len, sizeof(int));
    } 
#endif
}

static int cgid_server_child(int index)
{
    int fd, fd2, fd3;
    char *argv0;
    char *filename;
    char **env;
    pool *p;
    request_rec *r;

    p = ap_make_sub_pool(pcgi);
    r = ap_pcalloc(p, sizeof(request_rec));
    r->pool = p;
    fd = open(tmpnames[index], O_RDONLY);
    dup2(fd, STDIN_FILENO);
    get_req(fd, r, &filename, &argv0, &env);
    fd2 = open(tmpnames[index + 1], O_WRONLY);
    dup2(fd2, STDOUT_FILENO); 
    fd3 = open(tmpnames[index + 2], O_WRONLY);
    dup2(fd3, STDERR_FILENO); 
    cgid_work_func(r, argv0, env);
}

static int cgid_server(void *data, child_info *pinfo)
{
    int index;
    int length;
    int pid;

    signal(SIGCHLD, SIG_IGN);

    while (1) {
        length = read(STDIN_FILENO, &index, sizeof(int));
        if (length > 0) {
            if ((pid = fork()) > 0) {
                continue;
            }
            else if (pid == 0) {
                close(STDIN_FILENO);
                cgid_server_child(index);
            }
            else {
                ap_log_error(APLOG_MARK, APLOG_ERR, (server_rec *)data,
                             "Couldn't create named pipe, cgid's may not work.");
            }  
        }
    }
    return -1;
}

static void cleanup_tmp_dir(void *d)
{
    int i, rc;
   
 
    for (i = 0; i < ap_threads_per_child * HARD_SERVER_LIMIT * 3; i++) {
        rc = unlink(tmpnames[i]);
    }
    rmdir((char *)d);
}

static void cgid_init(server_rec *main_server, pool *p)
{
    int i;
    static char *tmp;
    int pid;
    char *dirname;
 
    cgid_server_conf *sconf = (cgid_server_conf *)ap_get_module_config(
                       main_server->module_config, &cgid_module);

    if (once_through) {
        dirname = getenv("TMPDIR");
        if (dirname) {
            dirname = ap_pstrcat(p, dirname, "/apache", NULL);
        }
        else {
            dirname = ap_pstrcat(p, "/tmp/apache", NULL);
        }
        mkdir(dirname, S_IREAD | S_IWRITE | S_IEXEC);

        ap_register_cleanup(p, dirname, cleanup_tmp_dir, NULL);

        for (i = 0; i < ap_threads_per_child * HARD_SERVER_LIMIT * 3; i++) {
            tmp = tempnam(dirname, NULL);
            if (mkfifo(tmp, S_IREAD | S_IWRITE) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ERR, main_server,
                             "Couldn't create named pipe, cgid's may not work.");
            }
            strcpy(tmpnames[i], tmp); 
            free(tmp);
        }
        pcgi = ap_make_sub_pool(p);
 
        if (!(pid = ap_bspawn_child(p, cgid_server, main_server, kill_always, 
                             &sconf->bin, &sconf->bout, NULL))) {
            ap_log_error(APLOG_MARK, APLOG_ERR, main_server,
                         "Couldn't spawn cgid daemon process");
        }
    }
    else once_through++;
}

static void *create_cgid_config(pool *p, server_rec *s)
{
    cgid_server_conf *c =
    (cgid_server_conf *) ap_pcalloc(p, sizeof(cgid_server_conf));

    c->logname = NULL;
    c->logbytes = DEFAULT_LOGBYTES;
    c->bufbytes = DEFAULT_BUFBYTES;
    c->bin = c->bout = c->berror = NULL;
    return c;
}

static void *merge_cgid_config(pool *p, void *basev, void *overridesv)
{
    cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *)
overridesv;

    return overrides->logname ? overrides : base;
}

static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
{
    server_rec *s = cmd->server;
    cgid_server_conf *conf =
    (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module);

    conf->logname = arg;
    return NULL;
}

static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg)
{
    server_rec *s = cmd->server;
    cgid_server_conf *conf =
    (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module);

    conf->logbytes = atol(arg);
    return NULL;
}

static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, char *arg)
{
    server_rec *s = cmd->server;
    cgid_server_conf *conf =
    (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module);

    conf->bufbytes = atoi(arg);
    return NULL;
}

static const command_rec cgid_cmds[] =
{
    {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
     "the name of a log for script debugging info"},
    {"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1,
     "the maximum length (in bytes) of the script debug log"},
    {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
     "the maximum size (in bytes) to record of a POST request"},
    {NULL}
};

static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret,
			   int show_errno, char *error)
{
    FILE *f;
    struct stat finfo;

    ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r, 
		"%s: %s", error, r->filename);

    if (!conf->logname ||
	((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
	 &&   (finfo.st_size > conf->logbytes)) ||
         ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
		      "a")) == NULL)) {
	return ret;
    }

    /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */
    fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
	    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
    /* "%% 500 /usr/local/apache/cgid-bin */
    fprintf(f, "%%%% %d %s\n", ret, r->filename);

    fprintf(f, "%%error\n%s\n", error);

    ap_pfclose(r->pool, f);
    return ret;
}

static int log_script(request_rec *r, cgid_server_conf * conf, int ret,
		  char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err)
{
    array_header *hdrs_arr = ap_table_elts(r->headers_in);
    table_entry *hdrs = (table_entry *) hdrs_arr->elts;
    char argsbuffer[HUGE_STRING_LEN];
    FILE *f;
    int i;
    struct stat finfo;

    if (!conf->logname ||
	((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
	 &&   (finfo.st_size > conf->logbytes)) ||
         ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
		      "a")) == NULL)) {
	/* Soak up script output */
	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in, 0) > 0)
	    continue;
	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err, 0) > 0)
	    continue;
	return ret;
    }

    /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */
    fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
	    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
    /* "%% 500 /usr/local/apache/cgid-bin" */
    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 = ap_table_elts(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);

    if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in, 0) > 0) {
	fputs("%stdout\n", f);
	fputs(argsbuffer, f);
	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in, 0) > 0)
	    fputs(argsbuffer, f);
	fputs("\n", f);
    }

    if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err, 0) > 0) {
	fputs("%stderr\n", f);
	fputs(argsbuffer, f);
	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err, 0) > 0)
	    fputs(argsbuffer, f);
	fputs("\n", f);
    }

    ap_bclose(script_in);
    ap_bclose(script_err);

    ap_pfclose(r->pool, f);
    return ret;
}

/****************************************************************
 *
 * Actual cgid handling...
 */


struct cgid_child_stuff {
    request_rec *r;
    int nph;
    int debug;
    char *argv0;
};

static int cgid_child(struct cgid_child_stuff *child_stuff, child_info *pinfo, char **env)
{
    struct cgid_child_stuff *cld = (struct cgid_child_stuff *) child_stuff;
    request_rec *r = cld->r;
    char *filename = cld->r->filename;
    int child_pid;
    char *argv0 = cld->argv0;
    int res;

    ap_call_exec(r, pinfo, argv0, env, 0);
}

static int cgid_work_func(request_rec *r, char *argv0, char **env)
{
    int nph;
    char *filename;

    struct cgid_child_stuff cld;

    nph = !(strncmp(argv0, "nph-", 4));

    cld.argv0 = strdup(argv0);
    cld.nph = nph;
    cld.r = r;

    cgid_child(&cld, NULL, env);
    return -1; /* should never get here */
}

static int cgid_handler(request_rec *r)
{
    int retval, nph, dbpos = 0;
    char *argv0, *dbuf = NULL;
    BUFF *script_out, *script_in, *script_err;
    char argsbuffer[HUGE_STRING_LEN];
    int fd, fd2, fd3;
    void *sconf = r->server->module_config;
    cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module);
    int index = ((r->connection->child_num * ap_threads_per_child) + r->connection->thread_num)
* 3;
    int is_included = 0;
    char **env;


    script_in = ap_bcreate(r->pool, B_RD);
    script_out = ap_bcreate(r->pool, B_WR);
    script_err = ap_bcreate(r->pool, B_RD);

    if (r->method_number == M_OPTIONS) {
	/* 99 out of 100 cgid scripts, this is all they support */
	r->allowed |= (1 << M_GET);
	r->allowed |= (1 << M_POST);
	return DECLINED;
    }

    if ((argv0 = strrchr(r->filename, '/')) != NULL)
	argv0++;
    else
	argv0 = r->filename;

    if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
			       "Options ExecCGI is off in this directory");
    if (nph && is_included)
	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
			       "attempt to include NPH CGI script");

#if defined(OS2) || defined(WIN32)
    /* Allow for cgid files without the .EXE extension on them under OS/2 */
    if (r->finfo.st_mode == 0) {
	struct stat statbuf;
	char *newfile;

	newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL);

	if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) {
	    return log_scripterror(r, conf, NOT_FOUND, 0,
				   "script not found or unable to stat");
	} else {
	    r->filename = newfile;
	}
    }
#else
    if (r->finfo.st_mode == 0)
	return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO,
			       "script not found or unable to stat");
#endif
    if (S_ISDIR(r->finfo.st_mode))
	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
			       "attempt to invoke directory as script");
    if (!ap_suexec_enabled) {
	if (!ap_can_exec(&r->finfo))
	    return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
				   "file permissions deny server execution");
    }

    nph = !(strncmp(argv0, "nph-", 4));

    ap_add_common_vars(r);
    ap_add_cgi_vars(r);
    env = ap_create_environment(r->pool, r->subprocess_env);

    write(conf->bin->fd, &index, sizeof(int));

    fd = open(tmpnames[index], O_WRONLY);
    send_req(fd, r, argv0, env);
    ap_bpushfd(script_out, fd, fd);

    fd2 = open(tmpnames[index + 1], O_RDONLY);
    ap_bpushfd(script_in, fd2, fd2);

    fd3 = open(tmpnames[index + 2], O_RDONLY);
    ap_bpushfd(script_err, fd3, fd3);

    if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
	return retval;
    
    if ((argv0 = strrchr(r->filename, '/')) != NULL)
	argv0++;
    else
	argv0 = r->filename;

    /* Transfer any put/post args, CERN style...
     * Note that we already ignore SIGPIPE in the core server.
     */

    if (ap_should_client_block(r)) {
	int dbsize, len_read;

	if (conf->logname) {
	    dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1);
	    dbpos = 0;
	}

	while ((len_read =
		ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
	    if (conf->logname) {
		if ((dbpos + len_read) > conf->bufbytes) {
		    dbsize = conf->bufbytes - dbpos;
		}
		else {
		    dbsize = len_read;
		}
		memcpy(dbuf + dbpos, argsbuffer, dbsize);
		dbpos += dbsize;
	    }
	    if (ap_bwrite(script_out, argsbuffer, len_read, 0) < len_read) {
		/* silly script stopped reading, soak up remaining message */
		while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) {
		    /* dump it */
		}
		break;
	    }
	}

	ap_bflush(script_out);

    }

    ap_bclose(script_out);

    /* Handle script return... */
    if (script_in && !nph) {
	const char *location;
	char sbuf[MAX_STRING_LEN];
	int ret;

	if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) {
	    return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
	}

#ifdef CHARSET_EBCDIC
        /* Now check the Content-Type to decide if conversion is needed */
        ap_checkconv(r);
#endif /*CHARSET_EBCDIC*/

	location = ap_table_get(r->headers_out, "Location");

	if (location && location[0] == '/' && r->status == 200) {

	    /* Soak up all the script output */
	    while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in, 0) > 0) {
		continue;
	    }
	    while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err, 0) > 0) {
		continue;
	    }
	    /* This redirect needs to be a GET no matter what the original
	     * method was.
	     */
	    r->method = ap_pstrdup(r->pool, "GET");
	    r->method_number = M_GET;

	    /* We already read the message body (if any), so don't allow
	     * the redirected request to think it has one.  We can ignore 
	     * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
	     */
	    ap_table_unset(r->headers_in, "Content-Length");

	    ap_internal_redirect_handler(location, r);
	    return OK;
	}
	else if (location && r->status == 200) {
	    /* XX Note that if a script wants to produce its own Redirect
	     * body, it now has to explicitly *say* "Status: 302"
	     */
	    return REDIRECT;
	}

	ap_send_http_header(r);
	if (!r->header_only) {
	    ap_send_fb(script_in, r);
	}
	ap_bclose(script_in);

	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err, 0) > 0) {
	    continue;
	}
	ap_bclose(script_err);
    }

    if (script_in && nph) {
	ap_send_fb(script_in, r);
    }

    return OK;			/* NOT r->status, even if it has changed. */
}

static const handler_rec cgid_handlers[] =
{
    {CGI_MAGIC_TYPE, cgid_handler},
    {"cgi-script", cgid_handler},
    {NULL}
};

module MODULE_VAR_EXPORT cgid_module = {
    STANDARD_MODULE_STUFF,
    cgid_init, 			/* initializer */
    NULL, 			/* dir config creater */ 
    NULL, 			/* dir merger --- default is to override */
    create_cgid_config, 	/* server config */
    merge_cgid_config, 		/* merge server config */
    cgid_cmds, 			/* command table */
    cgid_handlers, 		/* handlers */
    NULL, 			/* filename translation */
    NULL, 			/* check_user_id */
    NULL, 			/* check auth */
    NULL, 			/* check access */
    NULL, 			/* type_checker */
    NULL, 			/* fixups */
    NULL, 			/* logger */
    NULL, 			/* header parser */
    NULL, 			/* child_init */
    NULL, 			/* child_exit */
    NULL			/* post read_request */
};



_______________________________________________________________________
Ryan Bloom		rbb@raleigh.ibm.com
4205 S Miami Blvd	
RTP, NC 27709		It's a beautiful sight to see good dancers 
			doing simple steps.  It's a painful sight to
			see beginners doing complicated patterns.	


Mime
View raw message