@ New FastCGI server module

New FastCGI server module. Based on the thttpd server module with
large chunks of code ripped from the cgi module. Also modifies
Makefile.in and configure.in in order to make it build properly.
# This initial checkin has been tested with the Zeus web server,
# but should work with any server that has a correct FastCGI
# implementation.
This commit is contained in:
Ben Mansell 2001-03-19 11:03:29 +00:00
parent d8f3ea791a
commit 08df6f9b67
9 changed files with 539 additions and 1 deletions

View File

@ -18,7 +18,7 @@ LTLIBRARY_LIBADD = $(LTLIBRARY_DEPENDENCIES) $(EXTRA_LIBS)
PROGRAM_NAME = php PROGRAM_NAME = php
PROGRAM_SOURCES = stub.c PROGRAM_SOURCES = stub.c
PROGRAM_LDADD = libphp4.la PROGRAM_LDADD = libphp4.la $(EXT_PROGRAM_LDADD)
PROGRAM_LDFLAGS = -export-dynamic PROGRAM_LDFLAGS = -export-dynamic
PROGRAM_DEPENDENCIES = $(PROGRAM_LDADD) PROGRAM_DEPENDENCIES = $(PROGRAM_LDADD)

View File

@ -645,6 +645,11 @@ if test "$PHP_SAPI" = "cgi"; then
PHP_PROGRAM=php PHP_PROGRAM=php
fi fi
if test "$PHP_SAPI" = "fastcgi"; then
PHP_PROGRAM=php
fi
PHP_REGEX PHP_REGEX
PHP_CONFIGURE_PART(Configuring Zend) PHP_CONFIGURE_PART(Configuring Zend)

4
sapi/fastcgi/CREDITS Normal file
View File

@ -0,0 +1,4 @@
fastcgi
Ben Mansell
Stephen Landamore
Daniel Silverstone

5
sapi/fastcgi/Makefile.in Normal file
View File

@ -0,0 +1,5 @@
LTLIBRARY_NAME = libsapi.la
LTLIBRARY_SOURCES = fastcgi.c
include $(top_srcdir)/build/ltlib.mk

View File

@ -0,0 +1,17 @@
FastCGI module
--------------
This module requires the FastCGI development kit, available from
http://www.fastcgi.com/
Before building PHP, please enter the dev kit, and run:
./configure
make
make export
This will compile the library code required for the FastCGI module. All
that is then required is to configure PHP with the '--with-fastcgi' option.
After making the code, you will end up with a binary file called 'php'.
Installation of this file will depend on the web server being used, please
see their documentation for details.

22
sapi/fastcgi/config.m4 Normal file
View File

@ -0,0 +1,22 @@
AC_MSG_CHECKING(for FastCGI support)
AC_ARG_WITH(fastcgi,
[ --with-fastcgi=SRCDIR Build PHP as FastCGI application],[
if test "$withval" = "yes"; then
FASTCGIPATH=/usr/local
else
FASTCGIPATH=$withval
fi
test -f "$FASTCGIPATH/lib/libfcgi.a" || AC_MSG_ERROR(Unable to find libfcgi.a in $FASTCGIPATH/lib)
test -f "$FASTCGIPATH/include/fastcgi.h" || AC_MSG_ERROR(Unable to find fastcgi.h in $FASTCGIPATH/include)
PHP_SAPI=fastcgi
PHP_LIBS=$FASTCGIPATH/lib/libfcgi.a
AC_ADD_INCLUDE($FASTCGIPATH/include)
EXT_PROGRAM_LDADD="$EXT_PROGRAM_LDADD $FASTCGIPATH/lib/libfcgi.a"
INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_PROGRAM \$(bindir)/$SAPI_FASTCGI"
RESULT="yes"
PHP_SUBST(FASTCGI_LIBADD)
PHP_SUBST(EXT_PROGRAM_LDADD)
],[
RESULT="no"
])
AC_MSG_RESULT($RESULT)

457
sapi/fastcgi/fastcgi.c Normal file
View File

@ -0,0 +1,457 @@
/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2001 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Ben Mansell <php@slimyhorror.com> |
+----------------------------------------------------------------------+
*/
/* Debugging */
/* #define DEBUG_FASTCGI 1 */
/* Two configurables for the FastCGI runner.
*
* PHP_FCGI_CHILDREN - if set, the FastCGI will pre-fork this many processes
* which will accept requests.
*
* PHP_FCGI_MAX_REQUESTS - if set, the runner will kill itself after handling
* the given number of requests. This is to curb any
* memory leaks in PHP.
*/
/* The following code is based mainly on the thttpd sapi and the original
* CGI code, no doubt with many new and interesting bugs created... :)
*/
#include "php.h"
#include "SAPI.h"
#include "php_main.h"
#include "php_fastcgi.h"
#include "php_variables.h"
#include "fcgi_config.h"
#include "fcgiapp.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#define TLS_D
#define TLS_DC
#define TLS_C
#define TLS_CC
#define TLS_FETCH()
FCGX_Stream *in, *out, *err;
FCGX_ParamArray envp;
char *path_info = NULL;
/* 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 int sapi_fastcgi_ub_write(const char *str, uint str_length)
{
uint sent = FCGX_PutStr( str, str_length, out );
return sent;
}
static void sapi_fastcgi_flush( void *server_context )
{
if( FCGX_FFlush( out ) == -1 ) {
php_handle_aborted_connection();
}
}
static int sapi_fastcgi_send_headers(sapi_headers_struct *sapi_headers SLS_DC)
{
char buf[1024];
int n = 0;
zend_llist_position pos;
sapi_header_struct *h;
switch( sapi_headers->http_response_code ) {
case 200:
/* Default, assumed by FastCGI */
break;
case 302:
FCGX_PutS( "Status: 302 Moved Temporarily\r\n", out );
break;
case 401:
FCGX_PutS( "Status: 401 Authorization Required\r\n", out );
break;
default:
FCGX_FPrintF( out, "Status: %d Undescribed\r\\n",
sapi_headers->http_response_code );
}
h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
while (h) {
/* TODO: Buffer headers together into one big Put? */
#ifdef DEBUG_FASTCGI
fprintf( stderr, "Printing header %s\n", h->header );
#endif
FCGX_PutStr( h->header, h->header_len, out );
FCGX_PutStr( "\r\n", 2, out );
h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
}
FCGX_PutStr( "\r\n", 2, out );
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int sapi_fastcgi_read_post(char *buffer, uint count_bytes SLS_DC)
{
size_t read_bytes = 0, tmp;
int c;
char *pos = buffer;
TLS_FETCH();
while( count_bytes ) {
c = FCGX_GetStr( pos, count_bytes, in );
read_bytes += c;
count_bytes -= c;
pos += c;
if( !c ) break;
}
return read_bytes;
}
static char *sapi_fastcgi_read_cookies(SLS_D)
{
return getenv( "HTTP_COOKIE" );
}
static void sapi_fastcgi_register_variables(zval *track_vars_array ELS_DC SLS_DC PLS_DC)
{
char *self = getenv("REQUEST_URI");
char *ptr = strchr( self, '?' );
/* In CGI mode, we consider the environment to be a part of the server
* variables
*/
php_import_environment_variables(track_vars_array ELS_CC PLS_CC);
/* strip query string off this */
if ( ptr ) *ptr = 0;
php_register_variable( "PHP_SELF", getenv("REQUEST_URI"), track_vars_array ELS_CC PLS_CC);
if ( ptr ) *ptr = '?';
}
static sapi_module_struct fastcgi_sapi_module = {
"fastcgi",
"FastCGI",
php_module_startup,
php_module_shutdown_wrapper,
NULL, /* activate */
NULL, /* deactivate */
sapi_fastcgi_ub_write,
sapi_fastcgi_flush,
NULL, /* get uid */
NULL, /* getenv */
php_error,
NULL,
sapi_fastcgi_send_headers,
NULL,
sapi_fastcgi_read_post,
sapi_fastcgi_read_cookies,
sapi_fastcgi_register_variables,
NULL, /* Log message */
NULL, /* Block interruptions */
NULL, /* Unblock interruptions */
STANDARD_SAPI_MODULE_PROPERTIES
};
static void fastcgi_module_main(TLS_D SLS_DC)
{
zend_file_handle file_handle;
CLS_FETCH();
ELS_FETCH();
PLS_FETCH();
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = SG(request_info).path_translated;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
if (php_request_startup(CLS_C ELS_CC PLS_CC SLS_CC) == SUCCESS) {
php_execute_script(&file_handle CLS_CC ELS_CC PLS_CC);
}
php_request_shutdown(NULL);
}
static void init_request_info( SLS_D )
{
char *content_length = getenv("CONTENT_LENGTH");
const char *auth;
struct stat st;
char *pi = getenv( "PATH_INFO" );
char *pt = getenv( "PATH_TRANSLATED" );
path_info = strdup( pi );
SG(request_info).request_method = getenv("REQUEST_METHOD");
SG(request_info).query_string = getenv("QUERY_STRING");
SG(request_info).request_uri = path_info;
SG(request_info).content_type = getenv("CONTENT_TYPE");
SG(request_info).content_length = (content_length?atoi(content_length):0);
SG(sapi_headers).http_response_code = 200;
SG(request_info).path_translated = pt;
/*
* if the file doesn't exist, try to extract PATH_INFO out
* of it by stat'ing back through the '/'
*/
if ( stat( pt, &st ) == -1 ) {
int len = strlen(pt);
char *ptr;
while( ptr = strrchr(pt,'/') ) {
*ptr = 0;
if ( stat(pt,&st) == 0 && S_ISREG(st.st_mode) ) {
/*
* okay, we found the base script!
* work out how many chars we had to strip off;
* then we can modify PATH_INFO
* accordingly
*/
int slen = len - strlen(pt);
if ( pi ) {
int pilen = strlen( pi );
strcpy( pi, pi + pilen - slen );
}
break;
}
}
/*
* if we stripped out all the '/' and still didn't find
* a valid path... we will fail, badly. of course we would
* have failed anyway... is there a nice way to error?
*/
} else {
/* the first stat succeeded... */
if ( pi ) *pi = 0;
}
/* The CGI RFC allows servers to pass on unvalidated Authorization data */
auth = getenv("HTTP_AUTHORIZATION");
#ifdef DEBUG_FASTCGI
fprintf( stderr, "Authorization: %s\n", auth );
#endif
php_handle_auth_data(auth SLS_CC);
}
void fastcgi_php_init(void)
{
sapi_startup(&fastcgi_sapi_module);
fastcgi_sapi_module.startup(&fastcgi_sapi_module);
SG(server_context) = (void *) 1;
}
void fastcgi_php_shutdown(void)
{
if (SG(server_context) != NULL) {
fastcgi_sapi_module.shutdown(&fastcgi_sapi_module);
sapi_shutdown();
}
}
int main(int argc, char *argv[])
{
int exit_status = SUCCESS;
int c, i, len;
zend_file_handle file_handle;
char *s;
char *argv0=NULL;
char *script_file=NULL;
zend_llist global_vars;
int children = 8;
int max_requests = 500;
int requests = 0;
int status;
int env_size;
#ifdef FASTCGI_DEBUG
fprintf( stderr, "Initialising now!\n" );
#endif
/* 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 *));
#ifdef HAVE_SIGNAL_H
#if defined(SIGPIPE) && defined(SIG_IGN)
signal(SIGPIPE,SIG_IGN); /* ignore SIGPIPE in standalone mode so
that sockets created via fsockopen()
don't kill PHP if the remote site
closes it. in apache|apxs mode apache
does that for us! thies@thieso.net
20000419 */
#endif
#endif
sapi_startup(&fastcgi_sapi_module);
if (php_module_startup(&fastcgi_sapi_module)==FAILURE) {
return FAILURE;
}
/* 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 );
}
}
/* Pre-fork, if required */
if( getenv( "PHP_FCGI_CHILDREN" )) {
children = atoi( getenv( "PHP_FCGI_CHILDREN" ));
if( !children ) {
fprintf( stderr,
"PHP_FCGI_CHILDREN is not valid\n" );
exit( 1 );
}
}
if( children ) {
int parent = 1;
int running = 0;
while( parent ) {
do {
#ifdef FASTCGI_DEBUG
fprintf( stderr, "Forking, %d running\n",
running );
#endif
switch( fork() ) {
case 0:
/* One of the children.
* Make sure we don't go round the
* fork loop any more
*/
parent = 0;
break;
case -1:
perror( "php (pre-forking)" );
exit( 1 );
break;
default:
/* Fine */
running++;
break;
}
} while( parent && ( running < children ));
if( parent ) {
wait( &status );
running--;
}
}
}
/* Main FastCGI loop */
#ifdef FASTCGI_DEBUG
fprintf( stderr, "Going into accept loop\n" );
#endif
while( FCGX_Accept( &in, &out, &err, &cgi_env ) >= 0 ) {
#ifdef FASTCGI_DEBUG
fprintf( stderr, "Got accept\n" );
#endif
/* Allocate for our environment */
merge_env = malloc( env_size * sizeof( char *));
if( !merge_env ) {
perror( "Can't malloc environment" );
exit( 1 );
}
memcpy( merge_env, orig_env, env_size * sizeof( char *));
/* Use the new environment */
environ = merge_env;
/* Populate our environment with the CGI's */
for( i = 0; cgi_env[ i ]; i++ ) {
putenv( cgi_env[ i ] );
}
init_request_info( TLS_C SLS_CC );
SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */
CG(extended_info) = 0;
SG(request_info).argv0 = argv0;
zend_llist_init(&global_vars, sizeof(char *), NULL, 0);
fastcgi_module_main( TLS_C SLS_CC );
if( path_info ) {
free( path_info );
path_info = NULL;
}
/* 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();
break;
}
}
#ifdef FASTCGI_DEBUG
fprintf( stderr, "Exiting...\n" );
#endif
return 0;
}

0
sapi/fastcgi/php.sym Normal file
View File

View File

@ -0,0 +1,28 @@
/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2001 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Sascha Schumann <sascha@schumann.cx> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_FASTCGI_H
#define PHP_FASTCGI_H
#include <sys/types.h>
#include <sys/stat.h>
void fastcgi_php_shutdown(void);
void fastcgi_php_init(void);
#endif