]> granicus.if.org Git - php/commitdiff
make fastcgi usage threadsafe, ready for future multithreaded fastcgi implementation
authorShane Caraveo <shane@php.net>
Sun, 13 Oct 2002 09:40:44 +0000 (09:40 +0000)
committerShane Caraveo <shane@php.net>
Sun, 13 Oct 2002 09:40:44 +0000 (09:40 +0000)
get rid of environment overwriting but hooking into php's environment function
set $_ENV correctly for mod_fastcgi
add -b to specify binding for fastcgi
new readme file with information for running under apache2.0 and iis

sapi/cgi/CREDITS
sapi/cgi/README.FastCGI [new file with mode: 0644]
sapi/cgi/cgi_main.c

index 734f5c5a78572371817d60f813146e30f3c6ed49..a1c6a0be59bcf721eb87fd9081635792bd3b96f0 100644 (file)
@@ -1,2 +1,2 @@
-CGI
-Rasmus Lerdorf, Stig Bakken
+CGI / FastCGI
+Rasmus Lerdorf, Stig Bakken, Shane Caraveo
diff --git a/sapi/cgi/README.FastCGI b/sapi/cgi/README.FastCGI
new file mode 100644 (file)
index 0000000..6c865c4
--- /dev/null
@@ -0,0 +1,126 @@
+Credits:
+Ben Mansell, Stephen Landamore, Daniel Silverstone, Shane Caraveo
+
+Running the FastCGI PHP module
+------------------------------
+
+There are two ways to run the resulting 'php' binary after the fastcgi
+version has been built:
+
+1) Configure your web server to run the PHP binary itself.
+
+This is the simplest method, obviously you will have to configure your
+web server appropriately. Some web servers may also not support this method,
+or may not be as efficient.
+
+2) Run PHP separately from the web server.
+
+In this setup, PHP is started as a separate process entirely from the web
+server. It will listen on a socket for new FastCGI requests, and deliver
+PHP pages as appropriate. This is the recommended way of running PHP-FastCGI.
+To run this way, you must start the PHP binary running by giving it a port
+number to listen to on the command line, e.g.:
+
+./php -b 8002
+
+(you can also specify a bind address, e.g. ./php -b localhost:8002. However, this
+ relies on the FastCGI devkit and does not seem to work properly)
+
+You must also configure your web server to connect to the appropriate port
+in order to talk to the PHP FastCGI process.
+
+The advantage of running PHP in this way is that it entirely separates the
+web server and PHP process, so that one cannot disrupt the other. It also
+allows PHP to be on an entirely separate machine from the web server if need
+be, you could even have several web servers utilising the same running PHP
+process if required!
+
+
+Using FastCGI PHP with Apache
+=============================
+
+First of all, you may well ask 'Why?'. After all, Apache already has mod_php.
+However, there are advantages to running PHP with FastCGI. Separating the
+PHP code from the web server removes 'bloat' from the main server, and should
+improve the performance of non-PHP requests. Secondly, having one permanent
+PHP process as opposed to one per apache process means that shared resources
+like persistent database connections are used more efficiently.
+
+First of all, make sure that the FastCGI module is enabled. You should have
+a line in your config like:
+
+    LoadModule fastcgi_module /usr/lib/apache/2.0/mod_fastcgi.so
+
+Don't load mod_php, by the way. Make sure it is commented out!
+
+    #LoadModule php4_module /usr/lib/apache/2.0/libphp4.so
+
+Now, we'll create a fcgi-bin directory, just like you would do with normal
+CGI scripts. You'll need to create a directory somewhere to store your
+FastCGI binaries. We'll use /space/fcgi-bin/ for this example. Remember to
+copy the FastCGI-PHP binary in there. (named just 'php')
+
+    ScriptAlias /fcgi-bin/ /space/fcgi-bin/
+    <Location /fcgi-bin/>
+        Options ExecCGI
+        SetHandler fastcgi-script
+    </Location>
+
+To have mod_fastcgi manage your php fastcgi processes for you, use the 
+configuration directive FCGIServer (see mod_fastcgi docs for more
+configuration information):
+
+    FastCgiServer /fcgi-bin/php-cgi -processes 5
+
+Next, we need to tell Apache to use the FastCGI binary /fcgi-bin/php to
+deliver PHP pages. All that is needed is:
+
+    AddType application/x-httpd-fastphp .php
+    Action application/x-httpd-fastphp /fcgi-bin/php-cgi
+
+Now, if you restart Apache, php pages should now be delivered!
+
+Using FastCGI PHP with IIS or iPlanet
+=====================================
+
+FastCGI server plugins are available at www.caraveo.com/fastcgi/
+Documentation on these are sparse.  iPlanet is not very tested,
+and no makefile exists yet for unix based iPlanet servers.
+
+
+Security
+--------
+
+Be sure to run the php binary as an appropriate userid. Also, firewall out
+the port that PHP is listening on. In addition, you can set the environment
+variable FCGI_WEB_SERVER_ADDRS to control who can connect to the FastCGI.
+Set it to a comma separated list of IP addresses, e.g.:
+
+export FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71
+
+
+Tuning
+------
+
+There are a few tuning parameters that can be tweaked to control the
+performance of FastCGI PHP. The following are environment variables that can
+be set before running the PHP binary:
+
+PHP_FCGI_CHILDREN  (default value: 8)
+
+This controls how many child processes the PHP process spawns. When the
+fastcgi starts, it creates a number of child processes which handle one
+page request at a time. So by default, you will be able to handle 8
+concurrent PHP page requests. Further requests will be queued.
+Increasing this number will allow for better concurrency, especially if you
+have pages that take a significant time to create, or supply a lot of data
+(e.g. downloading huge files via PHP). On the other hand, having more
+processes running will use more RAM, and letting too many PHP pages be
+generated concurrently will mean that each request will be slow.
+
+PHP_FCGI_MAX_REQUESTS (default value: 500)
+
+This controls how many requests each child process will handle before
+exitting. When one process exits, another will be created. This tuning is
+necessary because several PHP functions are known to have memory leaks. If the
+PHP processes were left around forever, they would be become very inefficient.
index 7b276df55723f0e43e71956597fbb30add67f06f..88305f861341a939add5b654d4758d78cb8d4528 100644 (file)
 #include "fcgi_config.h"
 #include "fcgiapp.h"
 /* don't want to include fcgios.h, causes conflicts */
+#ifdef PHP_WIN32
 extern int OS_SetImpersonate(void);
+#endif
 
-FCGX_Stream *in, *out, *err;
-FCGX_ParamArray envp;
-
-/* Our original environment from when the FastCGI first started */
-char **orig_env;
-
-/* The environment given by the FastCGI */
-char **cgi_env;
-
-/* The manufactured environment, from merging the base environ with
- * the parameters set by the per-connection environment
- */
-char **merge_env;
+static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
 
+#ifndef PHP_WIN32
+/* these globals used for forking children on unix systems */
 /**
  * Number of child processes that will get created to service requests
  */
@@ -112,7 +104,7 @@ static int parent = 1;
  * Process group
  */
 static pid_t pgroup;
-
+#endif
 
 #endif
 
@@ -143,7 +135,7 @@ static int _print_extension_info(zend_extension *module, void *arg TSRMLS_DC)
 #define STDOUT_FILENO 1
 #endif
 
-static inline size_t sapi_cgibin_single_write(const char *str, uint str_length)
+static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
 {
 #ifdef PHP_WRITE_STDOUT
        long ret;
@@ -153,7 +145,12 @@ static inline size_t sapi_cgibin_single_write(const char *str, uint str_length)
 
 #ifdef PHP_FASTCGI
        if (!FCGX_IsCGI()) {
-               return FCGX_PutStr( str, str_length, out );
+               FCGX_Request *request = (FCGX_Request *)SG(server_context);
+               long ret = FCGX_PutStr( str, str_length, request->out );
+               if (ret <= 0) {
+                       return 0;
+               }
+               return ret;
        }
 #endif
 #ifdef PHP_WRITE_STDOUT
@@ -174,7 +171,7 @@ static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
 
        while (remaining > 0)
        {
-               ret = sapi_cgibin_single_write(ptr, remaining);
+               ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
                if (!ret) {
                        php_handle_aborted_connection();
                }
@@ -190,7 +187,8 @@ static void sapi_cgibin_flush(void *server_context)
 {
 #ifdef PHP_FASTCGI
        if (!FCGX_IsCGI()) {
-               if( FCGX_FFlush( out ) == -1 ) {
+               FCGX_Request *request = (FCGX_Request *)server_context;
+               if(!request || FCGX_FFlush( request->out ) == -1 ) {
                        php_handle_aborted_connection();
                }
        } else
@@ -244,7 +242,8 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
        while (read_bytes < count_bytes) {
 #ifdef PHP_FASTCGI
                if (!FCGX_IsCGI()) {
-                       tmp_read_bytes = FCGX_GetStr( pos, count_bytes-read_bytes, in );
+                       FCGX_Request *request = (FCGX_Request *)SG(server_context);
+                       tmp_read_bytes = FCGX_GetStr( pos, count_bytes-read_bytes, request->in );
                        pos += tmp_read_bytes;
                } else
 #endif
@@ -258,12 +257,55 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
        return read_bytes;
 }
 
+static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+#ifdef PHP_FASTCGI
+       /* when php is started by mod_fastcgi, no regular environment
+          is provided to PHP.  It is always sent to PHP at the start
+          of a request.  So we have to do our own lookup to get env
+          vars.  This could probably be faster somehow.  */
+       if (!FCGX_IsCGI()) {
+               int cgi_env_size = 0;
+               FCGX_Request *request = (FCGX_Request *)SG(server_context);
+               while( request->envp[ cgi_env_size ] ) { 
+                       if (strnicmp(name,request->envp[cgi_env_size],name_len) == 0) {
+                               return (request->envp[cgi_env_size])+name_len+1;
+                       }
+                       cgi_env_size++; 
+               }
+       }
+#endif
+       /*  if cgi, or fastcgi and not found in fcgi env
+               check the regular environment */
+       return getenv(name);
+}
 
 static char *sapi_cgi_read_cookies(TSRMLS_D)
 {
-       return getenv("HTTP_COOKIE");
+       return sapi_cgibin_getenv((char *)"HTTP_COOKIE",strlen("HTTP_COOKIE") TSRMLS_CC);
 }
 
+#ifdef PHP_FASTCGI
+void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
+{
+       if (!FCGX_IsCGI()) {
+               FCGX_Request *request = (FCGX_Request *)SG(server_context);
+               char **env, *p, *t;
+
+               for (env = request->envp; env != NULL && *env != NULL; env++) {
+                       p = strchr(*env, '=');
+                       if (!p) {                               /* malformed entry? */
+                               continue;
+                       }
+                       t = estrndup(*env, p - *env);
+                       php_register_variable(t, p+1, array_ptr TSRMLS_CC);
+                       efree(t);
+               }
+       }
+       /* call php's original import as a catch-all */
+       php_php_import_environment_variables(array_ptr TSRMLS_CC);
+}
+#endif
 
 static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
 {
@@ -271,7 +313,6 @@ static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
         * variables
         */
        php_import_environment_variables(track_vars_array TSRMLS_CC);
-
        /* Build the special-case PHP_SELF variable for the CGI version */
        php_register_variable("PHP_SELF", (SG(request_info).request_uri ? SG(request_info).request_uri:""), track_vars_array TSRMLS_CC);
 }
@@ -308,10 +349,11 @@ static int php_cgi_startup(sapi_module_struct *sapi_module)
 /* {{{ sapi_module_struct cgi_sapi_module
  */
 static sapi_module_struct cgi_sapi_module = {
-       "cgi",                                                  /* name */
 #ifdef PHP_FASTCGI
+       "cgi-fcgi",                                             /* name */
        "CGI/FastCGI",                                  /* pretty name */
 #else
+       "cgi",                                                  /* name */
        "CGI",                                                  /* pretty name */
 #endif
        
@@ -324,7 +366,7 @@ static sapi_module_struct cgi_sapi_module = {
        sapi_cgibin_ub_write,                   /* unbuffered write */
        sapi_cgibin_flush,                              /* flush */
        NULL,                                                   /* get uid */
-       NULL,                                                   /* getenv */
+       sapi_cgibin_getenv,                             /* getenv */
 
        php_error,                                              /* error handler */
 
@@ -366,6 +408,9 @@ static void php_cgi_usage(char *argv0)
                           "  -v               Version number\n"
                           "  -C               Do not chdir to the script's directory\n"
                           "  -c <path>|<file> Look for php.ini file in this directory\n"
+#ifdef PHP_FASTCGI
+                          "  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
+#endif
                           "  -a               Run interactively\n"
                           "  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
                           "  -e               Generate extended information for debugger/profiler\n"
@@ -381,8 +426,8 @@ static void php_cgi_usage(char *argv0)
  */
 static void init_request_info(TSRMLS_D)
 {
-       char *content_length = getenv("CONTENT_LENGTH");
-       char *content_type = getenv("CONTENT_TYPE");
+       char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH",strlen("CONTENT_LENGTH") TSRMLS_CC);
+       char *content_type = sapi_cgibin_getenv("CONTENT_TYPE",strlen("CONTENT_TYPE") TSRMLS_CC);
        const char *auth;
 
 #if 0
@@ -391,7 +436,7 @@ static void init_request_info(TSRMLS_D)
        char *script_filename;
 
 
-       script_filename = getenv("SCRIPT_FILENAME");
+       script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME",strlen("SCRIPT_FILENAME") TSRMLS_CC);
        /* Hack for annoying servers that do not set SCRIPT_FILENAME for us */
        if (!script_filename) {
                script_filename = SG(request_info).argv0;
@@ -403,7 +448,7 @@ static void init_request_info(TSRMLS_D)
           requires we get the info from path translated.  This can be removed at
           such a time that apache nt is fixed */
        if (!script_filename) {
-               script_filename = getenv("PATH_TRANSLATED");
+               script_filename = sapi_cgibin_getenv("PATH_TRANSLATED",strlen("PATH_TRANSLATED") TSRMLS_CC);
        }
 #endif
 
@@ -424,11 +469,11 @@ static void init_request_info(TSRMLS_D)
 
 #endif /* 0 */
 
-       SG(request_info).request_method = getenv("REQUEST_METHOD");
-       SG(request_info).query_string = getenv("QUERY_STRING");
-       SG(request_info).request_uri = getenv("PATH_INFO");
+       SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD",strlen("REQUEST_METHOD") TSRMLS_CC);
+       SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING",strlen("QUERY_STRING") TSRMLS_CC);
+       SG(request_info).request_uri = sapi_cgibin_getenv("PATH_INFO",strlen("PATH_INFO") TSRMLS_CC);
        if (!SG(request_info).request_uri) {
-               SG(request_info).request_uri = getenv("SCRIPT_NAME");
+               SG(request_info).request_uri = sapi_cgibin_getenv("SCRIPT_NAME",strlen("SCRIPT_NAME") TSRMLS_CC);
        }
        SG(request_info).path_translated = NULL; /* we have to update it later, when we have that information */
        SG(request_info).content_type = (content_type ? content_type : "" );
@@ -436,7 +481,7 @@ static void init_request_info(TSRMLS_D)
        SG(sapi_headers).http_response_code = 200;
        
        /* The CGI RFC allows servers to pass on unvalidated Authorization data */
-       auth = getenv("HTTP_AUTHORIZATION");
+       auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION",strlen("HTTP_AUTHORIZATION") TSRMLS_CC);
        php_handle_auth_data(auth TSRMLS_CC);
 }
 /* }}} */
@@ -506,30 +551,16 @@ int main(int argc, char *argv[])
 #endif
 
 #ifdef PHP_FASTCGI
-       int env_size, cgi_env_size;
        int max_requests = 500;
        int requests = 0;
        int fastcgi = !FCGX_IsCGI();
+       char *bindpath = NULL;
+       int fcgi_fd = 0;
+       FCGX_Request request;
 #ifdef PHP_WIN32
        int impersonate = 0;
 #endif
-
-       if (fastcgi) { 
-               /* Calculate environment size */
-               env_size = 0;
-               while( environ[ env_size ] ) { env_size++; }
-               /* Also include the final NULL pointer */
-               env_size++;
-
-               /* Allocate for our environment */
-               orig_env = malloc( env_size * sizeof( char *));
-               if( !orig_env ) {
-                       perror( "Can't malloc environment" );
-                       exit( 1 );
-               }
-               memcpy( orig_env, environ, env_size * sizeof( char *));
-       }
-#endif
+#endif /* PHP_FASTCGI */
 
 #ifdef HAVE_SIGNAL_H
 #if defined(SIGPIPE) && defined(SIG_IGN)
@@ -556,7 +587,9 @@ int main(int argc, char *argv[])
        setmode(_fileno(stderr), O_BINARY);             /* make the stdio mode be binary */
 #endif
 
-
+#ifdef PHP_FASTCGI
+       if (!fastcgi) {
+#endif
        /* Make sure we detect we are a cgi - a bit redundancy here,
           but the default case is that we have to check only the first one. */
        if (getenv("SERVER_SOFTWARE")
@@ -570,6 +603,9 @@ int main(int argc, char *argv[])
                        argv0 = NULL;
                }
        }
+#ifdef PHP_FASTCGI
+       }
+#endif
 
        if (!cgi
 #ifdef PHP_FASTCGI
@@ -588,6 +624,24 @@ int main(int argc, char *argv[])
                ap_php_optarg = orig_optarg;
        }
 
+#ifdef PHP_FASTCGI
+       if (!cgi && !fastcgi) {
+               /* if we're started on command line, check to see if
+                  we are being started as an 'external' fastcgi
+                  server by accepting a bindpath parameter. */
+               while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
+                       switch (c) {
+                               case 'b':
+                                       bindpath= strdup(ap_php_optarg);
+                               break;
+                       }
+
+               }
+               ap_php_optind = orig_optind;
+               ap_php_optarg = orig_optarg;
+       }
+#endif
+
 
 #ifdef ZTS
        compiler_globals = ts_resource(compiler_globals_id);
@@ -648,15 +702,46 @@ consult the installation file that came with this distribution, or visit \n\
 #endif                                                 /* FORCE_CGI_REDIRECT */
 
 #ifdef PHP_FASTCGI
-       /* How many times to run PHP scripts before dying */
-       if( getenv( "PHP_FCGI_MAX_REQUESTS" )) {
-               max_requests = atoi( getenv( "PHP_FCGI_MAX_REQUESTS" ));
-               if( !max_requests ) {
-                       fprintf( stderr,
-                                "PHP_FCGI_MAX_REQUESTS is not valid\n" );
-                       exit( 1 );
+       if (bindpath) {
+               /* Pass on the arg to the FastCGI library, with one exception.
+                * If just a port is specified, then we prepend a ':' onto the
+                * path (it's what the fastcgi library expects)
+                */
+               int port = atoi( bindpath );
+               if( port ) {
+                       char bindport[ 32 ];
+                       snprintf( bindport, 32, ":%s", bindpath );
+                       fcgi_fd = FCGX_OpenSocket( bindport, 128 );
+               } else {
+                       fcgi_fd = FCGX_OpenSocket( bindpath, 128 );
+               }
+               if( fcgi_fd < 0 ) {
+                       fprintf( stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
+#ifdef ZTS
+               tsrm_shutdown();
+#endif
+                       return FAILURE;
                }
+               fastcgi = !FCGX_IsCGI();
        }
+       if (fastcgi) {
+               /* How many times to run PHP scripts before dying */
+               if( getenv( "PHP_FCGI_MAX_REQUESTS" )) {
+                       max_requests = atoi( getenv( "PHP_FCGI_MAX_REQUESTS" ));
+                       if( !max_requests ) {
+                               fprintf( stderr,
+                                        "PHP_FCGI_MAX_REQUESTS is not valid\n" );
+                               return FAILURE;
+                       }
+               }
+
+               /* make php call us to get _ENV vars */
+               php_php_import_environment_variables = php_import_environment_variables;
+               php_import_environment_variables = cgi_php_import_environment_variables;
+
+               /* library is already initialized, now init our request */
+               FCGX_Init();
+               FCGX_InitRequest( &request, fcgi_fd, 0 );
 
 #ifndef PHP_WIN32
        /* Pre-fork, if required */
@@ -665,7 +750,7 @@ consult the installation file that came with this distribution, or visit \n\
                if( !children ) {
                        fprintf( stderr,
                                 "PHP_FCGI_CHILDREN is not valid\n" );
-                       exit( 1 );
+                       return FAILURE;
                }
        }
 
@@ -734,8 +819,8 @@ consult the installation file that came with this distribution, or visit \n\
        }
 
 #endif /* WIN32 */
-
-#endif
+       }
+#endif /* FASTCGI */
 
        zend_first_try {
                if (!cgi
@@ -762,6 +847,7 @@ consult the installation file that came with this distribution, or visit \n\
 
 #ifdef PHP_FASTCGI
                /* start of FAST CGI loop */
+               /* Initialise FastCGI request structure */
 
 #ifdef PHP_WIN32
                /* attempt to set security impersonation for fastcgi
@@ -775,26 +861,15 @@ consult the installation file that came with this distribution, or visit \n\
 #endif
 
                while (!fastcgi
-                       || FCGX_Accept( &in, &out, &err, &cgi_env ) >= 0) {
-
-                       if (fastcgi) {
-                               /* set up environment */
-                               cgi_env_size = 0;
-                               while( cgi_env[ cgi_env_size ] ) { cgi_env_size++; }
-                               merge_env = malloc( (env_size+cgi_env_size)*sizeof(char*) );
-                               if( !merge_env ) {
-                                  perror( "Can't malloc environment" );
-                                  exit( 1 );
-                               }
-                               memcpy( merge_env, orig_env, (env_size-1)*sizeof(char *) );
-                               memcpy( merge_env + env_size - 1,
-                                       cgi_env, (cgi_env_size+1)*sizeof(char *) );
-                               environ = merge_env;
-                       }
+                       || FCGX_Accept_r( &request ) >= 0) {
 #endif
 
-               init_request_info(TSRMLS_C);
+#ifdef PHP_FASTCGI
+               SG(server_context) = (void *) &request;
+#else
                SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */
+#endif
+               init_request_info(TSRMLS_C);
 
                SG(request_info).argv0 = argv0;
 
@@ -1009,9 +1084,9 @@ consult the installation file that came with this distribution, or visit \n\
                 */
                        char *env_path_translated=NULL;
 #if DISCARD_PATH
-                       env_path_translated = getenv("SCRIPT_FILENAME");
+                       env_path_translated = sapi_cgibin_getenv("SCRIPT_FILENAME",strlen("SCRIPT_FILENAME") TSRMLS_CC);
 #else
-                       env_path_translated = getenv("PATH_TRANSLATED");
+                       env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED",strlen("PATH_TRANSLATED") TSRMLS_CC);
 #endif
                        if(env_path_translated) {
 #ifdef __riscos__
@@ -1116,19 +1191,13 @@ consult the installation file that came with this distribution, or visit \n\
 #ifdef PHP_FASTCGI
                        if (!fastcgi) break;
                        /* only fastcgi will get here */
-
-                       /* TODO: We should free our environment here, but
-                        * some platforms are unhappy if they've altered our
-                        * existing environment and we then free() the new
-                        * environ pointer
-                        */
-
                        requests++;
                        if( max_requests && ( requests == max_requests )) {
-                               FCGX_Finish();
+                               FCGX_Finish_r(&request);
+                               if (bindpath) free (bindpath);
                                break;
                        }
-               /* end of fastcgi loop */
+                       /* end of fastcgi loop */
                }
 #endif