From: George Wang Date: Wed, 19 Mar 2008 22:43:25 +0000 (+0000) Subject: catch up with the latest release X-Git-Tag: RELEASE_2_0_0a1~84 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a4213c5e63f8f7acbae3c390a4623afcd14575d8;p=php catch up with the latest release --- diff --git a/sapi/litespeed/README b/sapi/litespeed/README index bd57f1f315..3f138c2163 100644 --- a/sapi/litespeed/README +++ b/sapi/litespeed/README @@ -3,27 +3,27 @@ Introduction LiteSpeed SAPI module is a dedicated interface for PHP integration with LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the -FastCGI SAPI with two major enhancements: better performance and -support for dynamic PHP configuration changes through web server +FastCGI SAPI with there major enhancements: better performance, dynamic +spawning and PHP configuration modification through web server configuration and .htaccess files. Our simple benchmark test ("hello world") shows that PHP with LiteSpeed SAPI has 30% better performance over PHP with FastCGI SAPI, which is nearly twice the performance that Apache mod_php can deliver. -A major drawback of FastCGI PHP comparing to mod_php is that "php.ini" -is the only way to set PHP configuration, and cannot be changed at -runtime via configuration files like .htaccess files or web server's -virtual host configuration. As FastCGI PHP is usually shared at server -level by all virtual hosts, it is big security concern to use it in a -shared hosting environment. LiteSpeed SAPI is carefully designed to -address this issue. It accepts same flexible configuration overridden -methods as those supported in mod_php. LiteSpeed SAPI also uses the -same configuration directives as Apache mod_php. +A major drawback of FastCGI PHP comparing to Apache mod_php is lacking +the flexibilities in PHP configurations. PHP configurations cannot be +changed at runtime via configuration files like .htaccess files or web +server's virtual host configuration. In shared hosting environment, +each hosting account will has its own "open_basedir" overridden in +server configuration to enhance server security when mod_php is used. +usually, FastCGI PHP is not an option in shared hosting environment +due to lacking of this flexibility. LiteSpeed SAPI is carefully designed +to address this issue. PHP configurations can be modified the same way +as that in mod_php with the the same configuration directives. -Therefore, with above enhancements, LiteSpeed SAPI is highly -recommended over FastCGI SAPI for using PHP with LiteSpeed web -server. +PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for +PHP scripting with LiteSpeed web server. Building PHP with LiteSpeed SAPI @@ -44,6 +44,33 @@ has been configured to run PHP with LiteSpeed SAPI already, you just need to overwrite the old executable with this one and you are all set. +Start PHP from command line +=========================== + +Usually, lsphp is managed by LiteSpeed web server in a single server +installation. lsphp can be used in clustered environment with one +LiteSpeed web server at the front, load balancing lsphp processes +running on multiple backend servers. In such environment, lsphp can be +start manually from command with option "-b ", socket +address can be IPv4, IPv6 or Unix Domain Socket address. +for example: + + ./lsphp -b [::]:3000 + +have lsphp bind to port 3000 on all IPv4 and IPv6 address, + + ./lsphp -b *:3000 + +have lsphp bind to port 300 on all IPv4 address. + + ./lsphp -b 192.168.0.2:3000 + +have lsphp bind to address 192.168.0.2:3000. + + ./lsphp -b /tmp/lsphp_manual.sock + +have lsphp accept request on Unix domain socket "/tmp/lsphp_manual.sock" + Using LiteSpeed PHP with LiteSpeed Web Server ============================================= @@ -59,11 +86,12 @@ LiteSpeed SAPI or vice versa. Brief instructions are as follow: -1) Login to web admin interface, go to 'Server'->'Ext App' tab, add an - external application of type "LSAPI app", "Command" should be set - to a shell command that executes the PHP binary you just - built. "Instances" should be set to match the value of "Max - Connections". +1) Login to web administration interface, go to 'Server'->'Ext App' tab, + add an external application of type "LSAPI app", "Command" should be + set to a shell command that executes the PHP binary you just built. + "Instances" should be set to "1". Add "LSAPI_CHILDREN" environment + variable to match the value of "Max Connections". More tunable + environment variable described below can be added. 2) Go to 'Server'->'Script Handler' tab, add a script handler configuration: set 'suffix' to 'php', 'Handler Type' to 'LiteSpeed @@ -74,34 +102,113 @@ Brief instructions are as follow: 3) Click 'Apply Changes' link on the top left of the page, then click 'graceful restart'. Now PHP is running with LiteSpeed SAPI. -Tuning ------- +Tunings +------- -There are two environment variables that can be tweaked to control the -behavior of LiteSpeed PHP. +There are a few environment variables that can be tweaked to control the +behavior of LSAPI application. -PHP_LSAPI_CHILDREN (no default) +* LSAPI_CHILDREN or PHP_LSAPI_CHILDREN (default: 0) -In order to handle multiple requests concurrently, LiteSpeed web -server can either spawn multiple PHP processes; or spawn one process, -and this process will create a number of child processes to handle -multiple requests simultaneously. +There are two ways to let PHP handle multiple requests concurrently, +Server Managed Mode and Self Managed Mode. In Server Managed Mode, +LiteSpeed web server dynamically spawn/stop PHP processes, in this mode +"Instances" should match "Max Connections" configuration for PHP +external application. To start PHP in Self Managed Mode, "Instances" +should be set to "1", while "LSAPI_CHILDREN" environment variable should +be set to match the value of "Max Connections" and >1. Web Server will +start one PHP process, this process will start/stop children PHP processes +dynamically based on on demand. If "LSAPI_CHILDREN" <=1, PHP will be +started in server managed mode. -The web server will create PHP processes specified by "Instance" in -LSAPI application configuration. For one PHP process launched by the -server, if PHP_LSAPI_CHILDREN is not set, it will not create any child -process; if PHP_LSAPI_CHILDREN is set, it will spawn a number of child -processes specified by PHP_LSAPI_CHILDREN. Usually, it should match -"Max Connections" configured for the LSAPI application, and both -values should not be set over 100 in most cases. +Self Managed Mode is preferred because all PHP processes can share one +shared memory block for the opcode cache. -PHP_LSAPI_MAX_REQUESTS (default value: 500) +Usually, there is no need to set value of LSAPI_CHILDREN over 100 in +most server environment. + + +* LSAPI_AVOID_FORK (default: 0) + +LSAPI_AVOID_FORK specifies the policy of the internal process manager in +"Self Managed Mode". When set to 0, the internal process manager will stop +and start children process on demand to save system resource. This is +preferred in a shared hosting environment. When set to 1, the internal +process manager will try to avoid freqently stopping and starting children +process. This might be preferred in a dedicate hosting environment. + + +* LSAPI_EXTRA_CHILDREN (default: 1/3 of LSAPI_CHILDREN or 0) + +LSAPI_EXTRA_CHILDREN controls the maximum number of extra children processes +can be started when some or all existing children processes are in +malfunctioning state. Total number of children processes will be reduced to +LSAPI_CHILDREN level as soon as service is back to normal. +When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of +LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value is 0. + + +* LSAPI_MAX_REQS or PHP_LSAPI_MAX_REQUESTS (default value: 10000) This controls how many requests each child process will handle before -exit. When one process exits, another will be created. This tuning is -necessary because several PHP functions have been identified having -memory leaks. If the PHP processes were left around forever, they -could become very inefficient. +it exits automatically. Several PHP functions have been identified +having memory leaks. This parameter can help reducing memory usage +of leaky PHP functions. + + +* LSAPI_MAX_IDLE (default value: 300 seconds) + +In Self Managed Mode, LSAPI_MAX_IDLE controls how long a idle child +process will wait for a new request before it exits. This option help +releasing system resources taken by idle processes. + + +* LSAPI_MAX_IDLE_CHILDREN + (default value: 1/3 of LSAPI_CHILDREN or LSAPI_CHILDREN) + +In Self Managed Mode, LSAI_MAX_IDLE_CHILDREN controls how many idle +children processes are allowed. Excessive idle children processes +will be killed by the parent process immediately. +When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of +LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value +is LSAPI_CHILDREN. + + +* LSAPI_MAX_PROCESS_TIME (default value: 300 seconds) + +In Self Managed Mode, LSAPI_MAX_PROCESS_TIME controls the maximum +processing time allowed when processing a request. If a child process +can not finish processing of a request in the given time period, it +will be killed by the parent process. This option can help getting rid +of dead or runaway child process. + + +* LSAPI_PGRP_MAX_IDLE (default value: FOREVER ) + +In Self Managed Mode, LSAPI_PGRP_MAX_IDLE controls how long the parent +process will wait before exiting when there is no child process. +This option help releasing system resources taken by an idle parent +process. + + +* LSAPI_PPID_NO_CHECK + +By default a LSAPI application check the existence of its parent process +and exits automatically if the parent process died. This is to reduce +orphan process when web server is restarted. However, it is desireable +to disable this feature, such as when a LSAPI process was started +manually from command line. LSAPI_PPID_NO_CHECK should be set when +you want to disable the checking of existence of parent process. +When PHP started by "-b" option, it is disabled automatically. + + +Compatibility with Apache mod_php +================================= + +LSAPI PHP supports PHP configuration overridden via web server configuration +as well as .htaccess. +Since 4.0 release "apache_response_headers" function is supported. + Contact diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c index c8c84201c5..3dc00caa2d 100644 --- a/sapi/litespeed/lsapi_main.c +++ b/sapi/litespeed/lsapi_main.c @@ -23,6 +23,7 @@ #include "php_ini.h" #include "php_variables.h" #include "zend_highlight.h" +#include "zend.h" #include "lsapilib.h" @@ -55,11 +56,11 @@ #define SAPI_LSAPI_MAX_HEADER_LENGTH 2048 -static char s_headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH]; static int lsapi_mode = 1; static char *php_self = ""; static char *script_filename = ""; +static int source_highlight = 0; #ifdef ZTS zend_compiler_globals *compiler_globals; @@ -69,6 +70,7 @@ sapi_globals_struct *sapi_globals; void ***tsrm_ls; #endif +zend_module_entry litespeed_module_entry; /* {{{ php_lsapi_startup */ @@ -233,20 +235,22 @@ static int sapi_lsapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) h = zend_llist_get_first_ex(&sapi_headers->headers, &pos); while (h) { - LSAPI_AppendRespHeader(h->header, h->header_len); + if ( h->header_len > 0 ) + LSAPI_AppendRespHeader(h->header, h->header_len); h = zend_llist_get_next_ex(&sapi_headers->headers, &pos); } if (SG(sapi_headers).send_default_content_type) { char *hd; int len; + char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH]; hd = sapi_get_default_content_type(TSRMLS_C); - len = snprintf( s_headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1, + len = snprintf( headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1, "Content-type: %s", hd ); efree(hd); - LSAPI_AppendRespHeader( s_headerBuf, len ); + LSAPI_AppendRespHeader( headerBuf, len ); } } LSAPI_FinalizeRespHeaders(); @@ -322,7 +326,9 @@ static int init_request_info( TSRMLS_D ) SG(request_info).request_uri = LSAPI_GetScriptName(); SG(request_info).content_length = LSAPI_GetReqBodyLen(); SG(request_info).path_translated = LSAPI_GetScriptFileName(); - SG(sapi_headers).http_response_code = 0; //It is not reset by zend engine, set it to 0. + + /* It is not reset by zend engine, set it to 0. */ + SG(sapi_headers).http_response_code = 0; pAuth = LSAPI_GetHeader( H_AUTHORIZATION ); php_handle_auth_data(pAuth TSRMLS_CC); @@ -397,7 +403,7 @@ static int processReq( TSRMLS_D ) override_ini(); - if ( lsapi_module_main( 0 TSRMLS_CC ) == -1 ) + if ( lsapi_module_main( source_highlight TSRMLS_CC ) == -1 ) ret = -1; } zend_end_try(); @@ -408,27 +414,28 @@ static void cli_usage( TSRMLS_D ) { static const char * usage = "Usage: php\n" - " php -[h|i|q|v|?] [] [args...]\n" - " Run in LSAPI mode when no parameter or only '-c' is specified\n" - " Run in Command Line Interpreter mode when parameters are specified" + " php -[b|c|h|i|q|s|v|?] [] [args...]\n" + " Run in LSAPI mode, only '-b', '-s' and '-c' are effective\n" + " Run in Command Line Interpreter mode when parameters are specified\n" "\n" + " -b | Bind Path for external LSAPI Server mode\n" " -c | Look for php.ini file in this directory\n" " -h This help\n" " -i PHP information\n" " -q Quiet-mode. Suppress HTTP Header output.\n" + " -s Display colour syntax highlighted source.\n" " -v Version number\n" " -? This help\n" "\n" " args... Arguments passed to script.\n"; php_output_startup(); php_output_activate(TSRMLS_C); -// SG(headers_sent) = 1; php_printf( usage ); php_end_ob_buffers(1 TSRMLS_CC); } static int parse_opt( int argc, char * argv[], int *climode, - char **php_ini_path ) + char **php_ini_path, char ** php_bind ) { char ** p = &argv[1]; char ** argend= &argv[argc]; @@ -439,6 +446,15 @@ static int parse_opt( int argc, char * argv[], int *climode, ++p; switch( c ) { + case 'b': + if ( p >= argend ) + { + fprintf( stderr, "TCP or socket address must be specified following '-b' option.\n"); + return -1; + } + *php_bind = *p++; + break; + case 'c': if ( p >= argend ) { @@ -448,6 +464,9 @@ static int parse_opt( int argc, char * argv[], int *climode, } *php_ini_path = *p++; break; + case 's': + source_highlight = 1; + break; case 'h': case 'i': case 'q': @@ -514,14 +533,10 @@ static int cli_main( int argc, char * argv[] ) switch( c ) { case 'q': -// SG(headers_sent) = 1; -// SG(request_info).no_headers = 1; break; case 'i': if (php_request_startup(TSRMLS_C) != FAILURE) { -// SG(headers_sent) = 1; -// SG(request_info).no_headers = 1; php_print_info(0xFFFFFFFF TSRMLS_CC); php_end_ob_buffers(1 TSRMLS_CC); php_request_shutdown( NULL ); @@ -531,8 +546,6 @@ static int cli_main( int argc, char * argv[] ) case 'v': if (php_request_startup(TSRMLS_C) != FAILURE) { -// SG(headers_sent) = 1; -// SG(request_info).no_headers = 1; #if ZEND_DEBUG php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); #else @@ -545,7 +558,10 @@ static int cli_main( int argc, char * argv[] ) break; case 'c': ++p; + /* fall through */ + case 's': break; + case 'h': case '?': default: @@ -580,12 +596,22 @@ static int cli_main( int argc, char * argv[] ) } else { + if (source_highlight) + { + zend_syntax_highlighter_ini syntax_highlighter_ini; + + php_get_highlight_struct(&syntax_highlighter_ini); + highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC); + } + else + { + file_handle.filename = *p; + file_handle.free_filename = 0; + file_handle.opened_path = NULL; + + php_execute_script(&file_handle TSRMLS_CC); + } - file_handle.filename = *p; - file_handle.free_filename = 0; - file_handle.opened_path = NULL; - - php_execute_script(&file_handle TSRMLS_CC); php_request_shutdown( NULL ); } } @@ -667,7 +693,7 @@ void start_children( int children ) pid = wait( &status ); running--; } - kill( -getpgrp(), s_stop ); + kill( -getpgrp(), SIGUSR1 ); exit( 0 ); } @@ -677,12 +703,14 @@ void start_children( int children ) int main( int argc, char * argv[] ) { int ret; - int max_requests = 500; - int requests = 0; + int bindFd; char * php_ini_path = NULL; + char * php_bind = NULL; + char * p; + int n; int climode = 0; - + #ifdef HAVE_SIGNAL_H #if defined(SIGPIPE) && defined(SIG_IGN) signal(SIGPIPE, SIG_IGN); @@ -695,7 +723,7 @@ int main( int argc, char * argv[] ) if (argc > 1 ) { - if ( parse_opt( argc, argv, &climode, &php_ini_path ) == -1 ) + if ( parse_opt( argc, argv, &climode, &php_ini_path, &php_bind ) == -1 ) return 1; } if ( climode ) @@ -717,7 +745,7 @@ int main( int argc, char * argv[] ) if ( php_ini_path ) lsapi_sapi_module.php_ini_path_override = php_ini_path; - if (php_module_startup(&lsapi_sapi_module, NULL, 0) == FAILURE) { + if (php_module_startup(&lsapi_sapi_module, &litespeed_module_entry, 1) == FAILURE) { #ifdef ZTS tsrm_shutdown(); #endif @@ -729,37 +757,36 @@ int main( int argc, char * argv[] ) return cli_main(argc, argv); } - if( getenv( "PHP_LSAPI_MAX_REQUESTS" )) + + if ( php_bind ) { - max_requests = atoi( getenv( "PHP_LSAPI_MAX_REQUESTS" )); - if( !max_requests ) + bindFd = LSAPI_CreateListenSock( php_bind, 10 ); + if ( bindFd == -1 ) { fprintf( stderr, - "PHP_LSAPI_MAX_REQUESTS is not valid\n" ); - exit( 1 ); + "Failed to bind socket [%s]: %s\n", php_bind, strerror( errno ) ); + exit( 2 ); + } + if ( bindFd != 0 ) + { + dup2( bindFd, 0 ); + close( bindFd ); } } - + LSAPI_Init(); + + LSAPI_Init_Env_Parameters( NULL ); - if (( getenv( "PHP_LSAPI_CHILDREN" ) )&& LSAPI_Is_Listen() ) - { - int children = atoi( getenv( "PHP_LSAPI_CHILDREN" )); - if ( children > 0 ) - start_children( children ); - } + if ( php_bind ) + LSAPI_No_Check_ppid(); - while( LSAPI_Accept() >= 0 ) + while( LSAPI_Prefork_Accept_r( &g_req ) >= 0 ) { ret = processReq(TSRMLS_C); LSAPI_Finish(); if ( ret ) break; - requests++; - if( max_requests && ( requests == max_requests )) - { - break; - } } php_module_shutdown(TSRMLS_C); @@ -769,6 +796,122 @@ int main( int argc, char * argv[] ) return ret; } + +/* LiteSpeed PHP module starts here */ + + + +PHP_FUNCTION(litespeed_request_headers); +PHP_FUNCTION(litespeed_response_headers); + +PHP_MINFO_FUNCTION(litespeed); + +zend_function_entry litespeed_functions[] = { + PHP_FE(litespeed_request_headers, NULL) + PHP_FE(litespeed_response_headers, NULL) + PHP_FALIAS(getallheaders, litespeed_request_headers, NULL) + PHP_FALIAS(apache_request_headers, litespeed_request_headers, NULL) + PHP_FALIAS(apache_response_headers, litespeed_response_headers, NULL) + {NULL, NULL, NULL} +}; + +static PHP_MINIT_FUNCTION(litespeed) +{ + //REGISTER_INI_ENTRIES(); + return SUCCESS; +} + + +static PHP_MSHUTDOWN_FUNCTION(litespeed) +{ + //UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +zend_module_entry litespeed_module_entry = { + STANDARD_MODULE_HEADER, + "litespeed", + litespeed_functions, + PHP_MINIT(litespeed), + PHP_MSHUTDOWN(litespeed), + NULL, + NULL, + NULL, + NO_VERSION_YET, + STANDARD_MODULE_PROPERTIES +}; + +static int add_associate_array( const char * pKey, int keyLen, const char * pValue, int valLen, + void * arg ) +{ + add_assoc_string_ex( (zval *)arg, (char *)pKey, keyLen+1, (char *)pValue, 1 ); + return 1; +} + + +/* {{{ proto array litespeed_request_headers(void) + Fetch all HTTP request headers */ +PHP_FUNCTION(litespeed_request_headers) +{ + //TODO: + if (ZEND_NUM_ARGS() > 0) { + WRONG_PARAM_COUNT; + } + array_init(return_value); + + LSAPI_ForeachOrgHeader( add_associate_array, return_value ); + +} +/* }}} */ + + + +/* {{{ proto array litespeed_response_headers(void) + Fetch all HTTP response headers */ +PHP_FUNCTION(litespeed_response_headers) +{ + sapi_header_struct *h; + zend_llist_position pos; + char * p; + int len; + char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH]; + + if (ZEND_NUM_ARGS() > 0) { + WRONG_PARAM_COUNT; + } + + if (!&SG(sapi_headers).headers) { + RETURN_FALSE; + } + array_init(return_value); + + h = zend_llist_get_first_ex(&SG(sapi_headers).headers, &pos); + while (h) + { + if ( h->header_len > 0 ) + { + p = strchr( h->header, ':' ); + len = p - h->header; + if (( p )&&( len > 0 )) + { + memmove( headerBuf, h->header, len ); + while( len > 0 && (isspace( headerBuf[len-1])) ) + --len; + headerBuf[len] = 0; + if ( len ) + { + while( isspace(*++p)); + add_assoc_string_ex(return_value, headerBuf, len+1, p, 1 ); + } + } + } + h = zend_llist_get_next_ex(&SG(sapi_headers).headers, &pos); + } +} + +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/sapi/litespeed/lsapidef.h b/sapi/litespeed/lsapidef.h index 420f3ed3b4..c7f0d20c62 100644 --- a/sapi/litespeed/lsapidef.h +++ b/sapi/litespeed/lsapidef.h @@ -79,9 +79,9 @@ enum #define LSAPI_SOCK_FILENO 0 #define LSAPI_VERSION_B0 'L' -#define LSAPI_VERSION_B1 'S' +#define LSAPI_VERSION_B1 'S' -//Values for m_flag in lsapi_packet_header +/* Values for m_flag in lsapi_packet_header */ #define LSAPI_ENDIAN_LITTLE 0 #define LSAPI_ENDIAN_BIG 1 #define LSAPI_ENDIAN_BIT 1 @@ -92,7 +92,7 @@ enum #define LSAPI_ENDIAN LSAPI_ENDIAN_BIG #endif -//Values for m_type in lsapi_packet_header +/* Values for m_type in lsapi_packet_header */ #define LSAPI_BEGIN_REQUEST 1 #define LSAPI_ABORT_REQUEST 2 #define LSAPI_RESP_HEADER 3 @@ -111,24 +111,26 @@ enum struct lsapi_packet_header { - char m_versionB0; //LSAPI protocol version + char m_versionB0; /* LSAPI protocol version */ char m_versionB1; char m_type; char m_flag; union { - int32_t m_iLen; //include this header + int32_t m_iLen; /* include this header */ char m_bytes[4]; }m_packetLen; }; -// LSAPI request header packet -// -// 1. struct lsapi_req_header -// 2. struct lsapi_http_header_index -// 3. lsapi_header_offset * unknownHeaders -// 4. org http request header -// 5. request body if available +/* + LSAPI request header packet + + 1. struct lsapi_req_header + 2. struct lsapi_http_header_index + 3. lsapi_header_offset * unknownHeaders + 4. org http request header + 5. request body if available +*/ struct lsapi_req_header { @@ -136,9 +138,9 @@ struct lsapi_req_header int32_t m_httpHeaderLen; int32_t m_reqBodyLen; - int32_t m_scriptFileOff; //path to the script file. - int32_t m_scriptNameOff; //decrypted URI, without pathinfo, - int32_t m_queryStringOff; //Query string inside env + int32_t m_scriptFileOff; /* path to the script file. */ + int32_t m_scriptNameOff; /* decrypted URI, without pathinfo, */ + int32_t m_queryStringOff; /* Query string inside env */ int32_t m_requestMethodOff; int32_t m_cntUnknownHeaders; int32_t m_cntEnv; diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 53232694e7..8e85958200 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -45,16 +45,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -//#include -#include +#include +#include #include #include +#include #include #include #include #include +#include #include #include +#include +#include #include #define LSAPI_ST_REQ_HEADER 1 @@ -68,7 +72,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static int g_inited = 0; static int g_running = 1; -LSAPI_Request g_req; +static int s_ppid; +LSAPI_Request g_req = { -1, -1 }; void Flush_RespBuf_r( LSAPI_Request * pReq ); @@ -76,17 +81,17 @@ static const char *CGI_HEADERS[H_TRANSFER_ENCODING+1] = { "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", - "HTTP_ACCEPT_LANG", "HTTP_AUTHORIZATION", + "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHORIZATION", "HTTP_CONNECTION", "CONTENT_TYPE", "CONTENT_LENGTH", "HTTP_COOKIE", "HTTP_COOKIE2", "HTTP_HOST", "HTTP_PRAGMA", "HTTP_REFERER", "HTTP_USER_AGENT", - "HTTP_CACHE_CTRL", + "HTTP_CACHE_CONTROL", "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_MATCH", "HTTP_IF_NONE_MATCH", "HTTP_IF_RANGE", "HTTP_IF_UNMODIFIED_SINCE", - "HTTP_KEEPALIVE", + "HTTP_KEEP_ALIVE", "HTTP_RANGE", "HTTP_X_FORWARDED_FOR", "HTTP_VIA", @@ -94,9 +99,36 @@ static const char *CGI_HEADERS[H_TRANSFER_ENCODING+1] = }; static int CGI_HEADER_LEN[H_TRANSFER_ENCODING+1] = -{ 11, 19, 20, 16, 18, 15, 12, 14, 11, 12, 9, 11, 12, 15, 15, - 22, 13, 18, 13, 24, 14, 10, 20, 8, 22 }; +{ 11, 19, 20, 20, 18, 15, 12, 14, 11, 12, 9, 11, 12, 15, 18, + 22, 13, 18, 13, 24, 15, 10, 20, 8, 22 }; + +static const char *HTTP_HEADERS[H_TRANSFER_ENCODING+1] = +{ + "Accept", "Accept-Charset", + "Accept-Encoding", + "Accept-Language", "Authorization", + "Connection", "Content-Type", + "Content-Length", "Cookie", "Cookie2", + "Host", "Pragma", + "Referer", "User-Agent", + "Cache-Control", + "If-Modified-Since", "If-Match", + "If-None-Match", + "If-Range", + "If-Unmodified-Since", + "Keep-Alive", + "Range", + "X-Forwarded-For", + "Via", + "Transfer-Encoding" +}; + +static int HTTP_HEADER_LEN[H_TRANSFER_ENCODING+1] = +{ 6, 14, 15, 15, 13, 10, 12, 14, 6, 7, 4, 6, 7, 10, //user-agent + 13,17, 8, 13, 8, 19, 10, 5, 15, 3, 17 +}; + static void lsapi_sigpipe( int sig ) { } @@ -128,7 +160,7 @@ static void lsapi_signal(int signo, sighandler_t handler) static inline void lsapi_buildPacketHeader( struct lsapi_packet_header * pHeader, char type, int len ) { - pHeader->m_versionB0 = LSAPI_VERSION_B0; //LSAPI protocol version + pHeader->m_versionB0 = LSAPI_VERSION_B0; /* LSAPI protocol version */ pHeader->m_versionB1 = LSAPI_VERSION_B1; pHeader->m_type = type; pHeader->m_flag = LSAPI_ENDIAN; @@ -159,25 +191,27 @@ static inline int lsapi_read( int fd, void * pBuf, int len ) } } -//static int lsapi_write( int fd, const void * pBuf, int len ) -//{ -// int ret; -// const char * pCur; -// const char * pEnd; -// if ( len == 0 ) -// return 0; -// pCur = (const char *)pBuf; -// pEnd = pCur + len; -// while( g_running && (pCur < pEnd) ) -// { -// ret = write( fd, pCur, pEnd - pCur ); -// if ( ret >= 0) -// pCur += ret; -// else if (( ret == -1 )&&( errno != EINTR )) -// return ret; -// } -// return pCur - (const char *)pBuf; -//} +/* +static int lsapi_write( int fd, const void * pBuf, int len ) +{ + int ret; + const char * pCur; + const char * pEnd; + if ( len == 0 ) + return 0; + pCur = (const char *)pBuf; + pEnd = pCur + len; + while( g_running && (pCur < pEnd) ) + { + ret = write( fd, pCur, pEnd - pCur ); + if ( ret >= 0) + pCur += ret; + else if (( ret == -1 )&&( errno != EINTR )) + return ret; + } + return pCur - (const char *)pBuf; +} +*/ static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen ) { @@ -207,24 +241,35 @@ static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen ) } } } - else if (( ret == -1 )&&( errno != EINTR )) - return ret; + else if ( ret == -1 ) + { + if ( errno == EAGAIN ) + { + if ( totalLen - left > 0 ) + return totalLen - left; + else + return -1; + } + else if ( errno != EINTR ) + return ret; + } } return totalLen - left; } -//static int getTotalLen( struct iovec * pVec, int count ) -//{ -// struct iovec * pEnd = pVec + count; -// int total = 0; -// while( pVec < pEnd ) -// { -// total += pVec->iov_len; -// ++pVec; -// } -// return total; -//} - +/* +static int getTotalLen( struct iovec * pVec, int count ) +{ + struct iovec * pEnd = pVec + count; + int total = 0; + while( pVec < pEnd ) + { + total += pVec->iov_len; + ++pVec; + } + return total; +} +*/ static inline int allocateBuf( LSAPI_Request * pReq, int size ) { @@ -307,8 +352,8 @@ static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList, static inline int isPipe( int fd ) { - char achPeer[128]; - int len = 128; + char achPeer[128]; + socklen_t len = 128; if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&& ( errno == ENOTCONN )) return 0; @@ -503,9 +548,9 @@ static int readReq( LSAPI_Request * pReq ) if ( packetLen > LSAPI_MAX_HEADER_LEN ) return -1; - if ( packetLen > pReq->m_reqBufSize ) + if ( packetLen + 1024 > pReq->m_reqBufSize ) { - if ( allocateBuf( pReq, packetLen ) == -1 ) + if ( allocateBuf( pReq, packetLen + 1024 ) == -1 ) return -1; } while( packetLen > pReq->m_bufRead ) @@ -535,15 +580,21 @@ int LSAPI_Init(void) if ( LSAPI_InitRequest( &g_req, LSAPI_SOCK_FILENO ) == -1 ) return -1; g_inited = 1; + s_ppid = getppid(); } return 0; } -void LSAPI_stop(void) +void LSAPI_Stop(void) { g_running = 0; } +int LSAPI_IsRunning(void) +{ + return g_running; +} + int LSAPI_InitRequest( LSAPI_Request * pReq, int fd ) { if ( !pReq ) @@ -587,9 +638,10 @@ int LSAPI_Is_Listen_r( LSAPI_Request * pReq) int LSAPI_Accept_r( LSAPI_Request * pReq ) { - char achPeer[128]; - int len; - int nodelay = 1; + char achPeer[128]; + socklen_t len; + int nodelay = 1; + if ( !pReq ) return -1; if ( LSAPI_Finish_r( pReq ) == -1 ) @@ -603,10 +655,14 @@ int LSAPI_Accept_r( LSAPI_Request * pReq ) len = sizeof( achPeer ); pReq->m_fd = accept( pReq->m_fdListen, (struct sockaddr *)&achPeer, &len ); - if (( pReq->m_fd == -1 )&&( errno == EINTR )) - continue; - if (( pReq->m_fd != -1 )&& - (((struct sockaddr *)&achPeer)->sa_family == AF_INET )) + if ( pReq->m_fd == -1 ) + { + if (( errno == EINTR )||( errno == EAGAIN)) + continue; + else + return -1; + } + else if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) { setsockopt(pReq->m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); @@ -629,7 +685,7 @@ static struct lsapi_packet_header finish = {'L', 'S', int LSAPI_Finish_r( LSAPI_Request * pReq ) { - //finish req body + /* finish req body */ if ( !pReq ) return -1; if (pReq->m_reqState) @@ -697,14 +753,96 @@ char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex ) return pReq->m_pHttpHeader + off; } +static int readBodyToReqBuf( LSAPI_Request * pReq ) +{ + int bodyLeft; + int len = pReq->m_bufRead - pReq->m_bufProcessed; + if ( len > 0 ) + return len; + pReq->m_bufRead = pReq->m_bufProcessed = pReq->m_pHeader->m_pktHeader.m_packetLen.m_iLen; + + bodyLeft = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead; + len = pReq->m_reqBufSize - pReq->m_bufRead; + if ( len < 0 ) + return -1; + if ( len > bodyLeft ) + len = bodyLeft; + + len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf + pReq->m_bufRead, len ); + if ( len > 0 ) + pReq->m_bufRead += len; + return len; +} + + +int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq ) +{ + if (!pReq || (pReq->m_fd ==-1) ) + return EOF; + if ( pReq->m_bufProcessed >= pReq->m_bufRead ) + { + if ( readBodyToReqBuf( pReq ) <= 0 ) + return EOF; + } + ++pReq->m_reqBodyRead; + return (unsigned char)*(pReq->m_pReqBuf + pReq->m_bufProcessed++); +} + + + +int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, int bufLen, int *getLF ) +{ + int len; + int left; + char * pBufEnd = pBuf + bufLen - 1; + char * pBufCur = pBuf; + char * pCur; + char * p; + if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF ) + return -1; + *getLF = 0; + while( (left = pBufEnd - pBufCur ) > 0 ) + { + + len = pReq->m_bufRead - pReq->m_bufProcessed; + if ( len <= 0 ) + { + if ( (len = readBodyToReqBuf( pReq )) <= 0 ) + { + *getLF = 1; + break; + } + } + if ( len > left ) + len = left; + pCur = pReq->m_pReqBuf + pReq->m_bufProcessed; + p = memchr( pCur, '\n', len ); + if ( p ) + len = p - pCur + 1; + memmove( pBufCur, pCur, len ); + pBufCur += len; + pReq->m_bufProcessed += len; + + pReq->m_reqBodyRead += len; + + if ( p ) + { + *getLF = 1; + break; + } + } + *pBufCur = 0; + + return pBufCur - pBuf; +} int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int bufLen ) { int len; int total; - //char *pOldBuf = pBuf; - if (!pReq || ( !pBuf )||(bufLen < 0 )) + /* char *pOldBuf = pBuf; */ + if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 )) return -1; total = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead; @@ -735,8 +873,12 @@ int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int bufLen ) pBuf += len; bufLen -= len; } - else if ( len < 0 ) - return -1; + else if ( len <= 0 ) + { + if ( !total) + return -1; + break; + } } pReq->m_reqBodyRead += total; return total; @@ -744,11 +886,6 @@ int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int bufLen ) } -//int LSAPI_Write( const char * pBuf, int len ) -//{ -// return LSAPI_Write_r( &g_req, pBuf, len ); -//} - int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len ) { struct lsapi_packet_header * pHeader; @@ -758,7 +895,7 @@ int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len ) int toWrite; int packetLen; - if ( !pReq || !pBuf ) + if ( !pReq || !pBuf || (pReq->m_fd == -1) ) return -1; if ( len < pReq->m_pRespBufEnd - pReq->m_pRespBufPos ) { @@ -854,6 +991,16 @@ int LSAPI_Flush_r( LSAPI_Request * pReq ) int n; if ( !pReq ) return -1; + n = pReq->m_pIovecCur - pReq->m_pIovecToWrite; + if (( 0 == n )&&( pReq->m_pRespBufPos == pReq->m_pRespBuf )) + return 0; + if ( pReq->m_fd == -1 ) + { + pReq->m_pRespBufPos = pReq->m_pRespBuf; + pReq->m_totalLen = 0; + pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec; + return -1; + } if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) { LSAPI_FinalizeRespHeaders_r( pReq ); @@ -862,6 +1009,7 @@ int LSAPI_Flush_r( LSAPI_Request * pReq ) { Flush_RespBuf_r( pReq ); } + n = pReq->m_pIovecCur - pReq->m_pIovecToWrite; if ( n > 0 ) { @@ -892,6 +1040,10 @@ int LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, int len ) struct iovec iov[2]; struct iovec *pIov; + if ( !pReq ) + return -1; + if (( pReq->m_fd == -1 )||(pReq->m_fd == pReq->m_fdListen )) + return write( 2, pBuf, len ); if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) { LSAPI_Flush_r( pReq ); @@ -930,6 +1082,48 @@ int LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, int len ) return p - pBuf; } +static char * GetHeaderVar( LSAPI_Request * pReq, const char * name ) +{ + int i; + for( i = 0; i < H_TRANSFER_ENCODING; ++i ) + { + if ( pReq->m_pHeaderIndex->m_headerOff[i] ) + { + if ( strcmp( name, CGI_HEADERS[i] ) == 0 ) + return pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i]; + } + } + if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) + { + const char *p; + char *pKey; + char *pKeyEnd; + int keyLen; + struct lsapi_header_offset * pCur, *pEnd; + pCur = pReq->m_pUnknownHeader; + pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders; + while( pCur < pEnd ) + { + pKey = pReq->m_pHttpHeader + pCur->nameOff; + keyLen = pCur->nameLen; + pKeyEnd = pKey + keyLen; + p = &name[5]; + + while(( pKey < pKeyEnd )&&( *p )) + { + char ch = toupper( *pKey ); + if ((ch != *p )||(( *p == '_' )&&( ch != '-'))) + break; + ++p; ++pKey; + } + if (( pKey == pKeyEnd )&& (!*p )) + return pReq->m_pHttpHeader + pCur->valueOff; + ++pCur; + } + } + return NULL; +} + char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ) { @@ -937,6 +1131,10 @@ char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ) struct LSAPI_key_value_pair * pEnd = pBegin + pReq->m_pHeader->m_cntEnv; if ( !pReq || !name ) return NULL; + if ( strncmp( name, "HTTP_", 5 ) == 0 ) + { + return GetHeaderVar( pReq, name ); + } while( pBegin < pEnd ) { if ( strcmp( name, pBegin->pKey ) == 0 ) @@ -946,6 +1144,55 @@ char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ) return NULL; } +int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq, + LSAPI_CB_EnvHandler fn, void * arg ) +{ + int i; + int len = 0; + char * pValue; + int ret; + int count = 0; + if ( !pReq || !fn ) + return -1; + for( i = 0; i < H_TRANSFER_ENCODING; ++i ) + { + if ( pReq->m_pHeaderIndex->m_headerOff[i] ) + { + len = pReq->m_pHeaderIndex->m_headerLen[i]; + pValue = pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i]; + *(pValue + len ) = 0; + ret = (*fn)( HTTP_HEADERS[i], HTTP_HEADER_LEN[i], + pValue, len, arg ); + ++count; + if ( ret <= 0 ) + return ret; + } + } + if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) + { + char *pKey; + int keyLen; + struct lsapi_header_offset * pCur, *pEnd; + pCur = pReq->m_pUnknownHeader; + pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders; + while( pCur < pEnd ) + { + pKey = pReq->m_pHttpHeader + pCur->nameOff; + keyLen = pCur->nameLen; + + pValue = pReq->m_pHttpHeader + pCur->valueOff; + *(pValue + pCur->valueLen ) = 0; + ret = (*fn)( pKey, keyLen, + pValue, pCur->valueLen, arg ); + if ( ret <= 0 ) + return ret; + ++pCur; + } + } + return count + pReq->m_pHeader->m_cntUnknownHeaders; + +} + int LSAPI_ForeachHeader_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ) @@ -1004,8 +1251,8 @@ int LSAPI_ForeachHeader_r( LSAPI_Request * pReq, pValue = pReq->m_pHttpHeader + pCur->valueOff; *(pValue + pCur->valueLen ) = 0; - ret = (*fn)( achHeaderName, pCur->valueLen, - pValue, len, arg ); + ret = (*fn)( achHeaderName, keyLen, + pValue, pCur->valueLen, arg ); if ( ret <= 0 ) return ret; ++pCur; @@ -1114,14 +1361,805 @@ int LSAPI_AppendRespHeader_r( LSAPI_Request * pReq, char * pBuf, int len ) memmove( pReq->m_pRespHeaderBufPos, pBuf, len ); pReq->m_pRespHeaderBufPos += len; *pReq->m_pRespHeaderBufPos++ = 0; - ++len; //add one byte padding for \0 + ++len; /* add one byte padding for \0 */ pReq->m_respHeaderLen[pReq->m_respHeader.m_respInfo.m_cntHeaders] = len; ++pReq->m_respHeader.m_respInfo.m_cntHeaders; return 0; } +int LSAPI_CreateListenSock2( const struct sockaddr * pServerAddr, int backlog ) +{ + int ret; + int fd; + int flag = 1; + int addr_len; + + switch( pServerAddr->sa_family ) + { + case AF_INET: + addr_len = 16; + break; + case AF_INET6: + addr_len = sizeof( struct sockaddr_in6 ); + break; + case AF_UNIX: + addr_len = sizeof( struct sockaddr_un ); + unlink( ((struct sockaddr_un *)pServerAddr)->sun_path ); + break; + default: + return -1; + } + + fd = socket( pServerAddr->sa_family, SOCK_STREAM, 0 ); + if ( fd == -1 ) + return -1; + + fcntl( fd, F_SETFD, FD_CLOEXEC ); + + if(setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, + (char *)( &flag ), sizeof(flag)) == 0) + { + ret = bind( fd, pServerAddr, addr_len ); + if ( !ret ) + { + ret = listen( fd, backlog ); + if ( !ret ) + return fd; + } + } + + ret = errno; + close(fd); + errno = ret; + return -1; + +} + +int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr ) +{ + char achAddr[256]; + char * p = achAddr; + char * pEnd; + struct addrinfo *res, hints; + int doAddrInfo = 0; + int port; + + if ( !pBind ) + return -1; + while( isspace( *p ) ) + ++pBind; + + strncpy( achAddr, pBind, 256 ); + + switch( *p ) + { + case '/': + pAddr->sa_family = AF_UNIX; + strncpy( ((struct sockaddr_un *)pAddr)->sun_path, p, + sizeof(((struct sockaddr_un *)pAddr)->sun_path) ); + return 0; + + case '[': + pAddr->sa_family = AF_INET6; + ++p; + pEnd = strchr( p, ']' ); + if ( !pEnd ) + return -1; + *pEnd++ = 0; + + if ( *p == '*' ) + { + strcpy( achAddr, "::" ); + p = achAddr; + } + doAddrInfo = 1; + break; + + default: + pAddr->sa_family = AF_INET; + pEnd = strchr( p, ':' ); + if ( !pEnd ) + return -1; + *pEnd++ = 0; + + doAddrInfo = 0; + if ( *p == '*' ) + { + ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl(INADDR_ANY); + } + else if (!strcasecmp( p, "localhost" ) ) + ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + else + { + ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = inet_addr( p ); + if ( ((struct sockaddr_in *)pAddr)->sin_addr.s_addr == INADDR_BROADCAST) + { + doAddrInfo = 1; + } + } + break; + } + if ( *pEnd == ':' ) + ++pEnd; + + port = atoi( pEnd ); + if (( port <= 0 )||( port > 655535 )) + return -1; + if ( doAddrInfo ) + { + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = pAddr->sa_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if ( getaddrinfo(p, NULL, &hints, &res) ) + { + return -1; + } + + memcpy(pAddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } + + if ( pAddr->sa_family == AF_INET ) + ((struct sockaddr_in *)pAddr)->sin_port = htons( port ); + else + ((struct sockaddr_in6 *)pAddr)->sin6_port = htons( port ); + return 0; + +} + +int LSAPI_CreateListenSock( const char * pBind, int backlog ) +{ + char serverAddr[128]; + int ret; + int fd = -1; + ret = LSAPI_ParseSockAddr( pBind, (struct sockaddr *)serverAddr ); + if ( !ret ) + { + fd = LSAPI_CreateListenSock2( (struct sockaddr *)serverAddr, backlog ); + } + return fd; +} + +static fn_select_t g_fnSelect = select; + +typedef struct _lsapi_child_status +{ + int m_pid; + + volatile short m_iKillSent; + volatile short m_inProcess; + + volatile long m_tmWaitBegin; + volatile long m_tmReqBegin; + volatile long m_tmLastCheckPoint; +} +lsapi_child_status; + +static lsapi_child_status * s_pChildStatus = NULL; + +typedef struct _lsapi_prefork_server +{ + int m_fd; + int m_iMaxChildren; + int m_iExtraChildren; + int m_iCurChildren; + int m_iMaxIdleChildren; + int m_iServerMaxIdle; + int m_iChildrenMaxIdleTime; + int m_iMaxReqProcessTime; + int m_iAvoidFork; + + lsapi_child_status * m_pChildrenStatus; + +}lsapi_prefork_server; + +static lsapi_prefork_server * g_prefork_server = NULL; + +int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork ) +{ + if ( g_prefork_server ) + return 0; + if ( max_children <= 1 ) + return -1; + if ( max_children >= 10000) + max_children = 10000; + + + g_prefork_server = (lsapi_prefork_server *)malloc( sizeof( lsapi_prefork_server ) ); + if ( !g_prefork_server ) + return -1; + memset( g_prefork_server, 0, sizeof( lsapi_prefork_server ) ); + if ( fp != NULL ) + g_fnSelect = fp; + + s_ppid = getppid(); + g_prefork_server->m_iAvoidFork = avoidFork; + g_prefork_server->m_iMaxChildren = max_children; + + g_prefork_server->m_iExtraChildren = ( avoidFork ) ? 0 : (max_children / 3) ; + g_prefork_server->m_iMaxIdleChildren = ( avoidFork ) ? (max_children + 1) : (max_children / 3); + g_prefork_server->m_iChildrenMaxIdleTime = 300; + g_prefork_server->m_iMaxReqProcessTime = 300; + return 0; +} + +void LSAPI_Set_Server_fd( int fd ) +{ + if( g_prefork_server ) + g_prefork_server->m_fd = fd; +} + + +static int lsapi_accept( int fdListen ) +{ + int fd; + int nodelay = 1; + socklen_t len; + char achPeer[128]; + + len = sizeof( achPeer ); + fd = accept( fdListen, (struct sockaddr *)&achPeer, &len ); + if ( fd != -1 ) + { + if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) + { + setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, + (char *)&nodelay, sizeof(nodelay)); + } + } + return fd; + +} + + + + +static int s_req_processed = 0; +static int s_max_reqs = 10000; +static int s_max_idle_secs = 300; + +static int s_stop; + +static void lsapi_cleanup(int signal) +{ + s_stop = signal; +} + +static lsapi_child_status * find_child_status( int pid ) +{ + lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus; + lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2; + while( pStatus < pEnd ) + { + if ( pStatus->m_pid == pid ) + return pStatus; + ++pStatus; + } + return NULL; +} + + + +static void lsapi_sigchild( int signal ) +{ + int status, pid; + lsapi_child_status * child_status; + while( 1 ) + { + pid = waitpid( -1, &status, WNOHANG|WUNTRACED ); + if ( pid <= 0 ) + { + break; + } + child_status = find_child_status( pid ); + if ( child_status ) + child_status->m_pid = 0; + --g_prefork_server->m_iCurChildren; + } + +} + +static int lsapi_init_children_status() +{ + int size = 4096; + + char * pBuf; + size = g_prefork_server->m_iMaxChildren * sizeof( lsapi_child_status ) * 2; + size = (size + 4095 ) / 4096 * 4096; + pBuf =( char*) mmap( NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0 ); + if ( pBuf == MAP_FAILED ) + { + perror( "Anonymous mmap() failed" ); + return -1; + } + memset( pBuf, 0, size ); + g_prefork_server->m_pChildrenStatus = (lsapi_child_status *)pBuf; + return 0; +} + +static void lsapi_check_child_status( long tmCur ) +{ + int idle = 0; + int tobekilled; + int dying = 0; + lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus; + lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2; + while( pStatus < pEnd ) + { + tobekilled = pStatus->m_iKillSent; + if ( pStatus->m_pid != 0 ) + { + if ( !tobekilled ) + { + if ( !pStatus->m_inProcess ) + { + + if (( g_prefork_server->m_iCurChildren - dying > g_prefork_server->m_iMaxChildren)|| + ( idle >= g_prefork_server->m_iMaxIdleChildren )) + { + tobekilled = 1; + } + else + { + if (( s_max_idle_secs> 0)&&(tmCur - pStatus->m_tmWaitBegin > s_max_idle_secs + 5 )) + tobekilled = 1; + } + if ( !tobekilled ) + ++idle; + } + else + { + if ( tmCur - pStatus->m_tmReqBegin > + g_prefork_server->m_iMaxReqProcessTime ) + tobekilled = 1; + } + } + else + { + if ( pStatus->m_inProcess ) + tobekilled = pStatus->m_iKillSent = 0; + } + if ( tobekilled ) + { + tobekilled = 0; + if ( pStatus->m_iKillSent > 5 ) + tobekilled = SIGKILL; + else if ( pStatus->m_iKillSent == 3 ) + tobekilled = SIGTERM; + else if ( pStatus->m_iKillSent == 1 ) + { + tobekilled = SIGUSR1; + } + if ( tobekilled ) + kill( pStatus->m_pid, tobekilled ); + ++pStatus->m_iKillSent; + ++dying; + } + + } + else + ++dying; + ++pStatus; + } +} + +static int lsapi_all_children_must_die() +{ + int maxWait; + int sec =0; + g_prefork_server->m_iMaxReqProcessTime = 10; + g_prefork_server->m_iMaxIdleChildren = -1; + maxWait = 15; + + while( g_prefork_server->m_iCurChildren && (sec < maxWait) ) + { + lsapi_check_child_status(time(NULL)); + sleep( 1 ); + sec++; + } + if ( g_prefork_server->m_iCurChildren != 0 ) + kill( -getpgrp(), SIGKILL ); + return 0; +} + + + +static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Request * pReq ) +{ + struct sigaction act, old_term, old_quit, old_int, + old_usr1, old_child; + lsapi_child_status * child_status; + int wait_secs = 0; + int ret = 0; + int pid; + time_t lastTime = 0; + time_t curTime = 0; + fd_set readfds; + struct timeval timeout; + + lsapi_init_children_status(); + + setsid(); + + act.sa_flags = 0; + act.sa_handler = lsapi_sigchild; + if( sigaction( SIGCHLD, &act, &old_child ) ) + { + perror( "Can't set signal handler for SIGCHILD" ); + return -1; + } + + /* Set up handler to kill children upon exit */ + act.sa_flags = 0; + act.sa_handler = lsapi_cleanup; + if( sigaction( SIGTERM, &act, &old_term ) || + sigaction( SIGINT, &act, &old_int ) || + sigaction( SIGUSR1, &act, &old_usr1 ) || + sigaction( SIGQUIT, &act, &old_quit )) + { + perror( "Can't set signals" ); + return -1; + } + s_stop = 0; + while( !s_stop ) + { + if ( ret ) + curTime = time( NULL ); + else + ++curTime; + if (curTime != lastTime ) + { + lastTime = curTime; + if (s_ppid && (getppid() != s_ppid )) + break; + lsapi_check_child_status(curTime ); + if (pServer->m_iServerMaxIdle) + { + if ( pServer->m_iCurChildren <= 0 ) + { + ++wait_secs; + if ( wait_secs > pServer->m_iServerMaxIdle ) + return -1; + } + else + wait_secs = 0; + } + } + + if ( pServer->m_iCurChildren >= (pServer->m_iMaxChildren + pServer->m_iExtraChildren ) ) + { + usleep( 100000 ); + continue; + } + + FD_ZERO( &readfds ); + FD_SET( pServer->m_fd, &readfds ); + timeout.tv_sec = 1; timeout.tv_usec = 0; + if ((ret = (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout)) == 1 ) + { + if ( pServer->m_iCurChildren >= 0 ) + { + usleep( 10 ); + FD_ZERO( &readfds ); + FD_SET( pServer->m_fd, &readfds ); + timeout.tv_sec = 0; timeout.tv_usec = 0; + if ( (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout) == 0 ) + continue; + } + } + else if ( ret == -1 ) + { + if ( errno == EINTR ) + continue; + /* perror( "select()" ); */ + break; + } + else + { + continue; + } + + pReq->m_fd = lsapi_accept( pServer->m_fd ); + if ( pReq->m_fd != -1 ) + { + child_status = find_child_status( 0 ); + pid = fork(); + if ( !pid ) + { + g_prefork_server = NULL; + s_ppid = getppid(); + s_req_processed = 0; + s_pChildStatus = child_status; + child_status->m_iKillSent = 0; + + /* don't catch our signals */ + sigaction( SIGCHLD, &old_child, 0 ); + sigaction( SIGTERM, &old_term, 0 ); + sigaction( SIGQUIT, &old_quit, 0 ); + sigaction( SIGINT, &old_int, 0 ); + sigaction( SIGUSR1, &old_usr1, 0 ); + return 0; + } + else if ( pid == -1 ) + { + perror( "fork() failed, please increase process limit" ); + } + else + { + ++pServer->m_iCurChildren; + if ( child_status ) + { + child_status->m_pid = pid; + child_status->m_iKillSent = 0; + child_status->m_tmWaitBegin = time(NULL); + } + } + close( pReq->m_fd ); + pReq->m_fd = -1; + + } + else + { + if (( errno == EINTR )||( errno == EAGAIN)) + continue; + perror( "accept() failed" ); + return -1; + } + } + sigaction( SIGUSR1, &old_usr1, 0 ); + kill( -getpgrp(), SIGUSR1 ); + lsapi_all_children_must_die(); /* Sorry, children ;-) */ + return -1; + +} + +int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) +{ + int fd; + int ret; + int wait_secs; + fd_set readfds; + struct timeval timeout; + + LSAPI_Finish_r( pReq ); + + + if ( g_prefork_server ) + { + if ( g_prefork_server->m_fd != -1 ) + if ( lsapi_prefork_server_accept( g_prefork_server, pReq ) == -1 ) + return -1; + } + if ( s_req_processed >= s_max_reqs ) + return -1; + + if ( s_pChildStatus ) + { + s_pChildStatus->m_tmWaitBegin = time( NULL ); + } + + while( g_running ) + { + if ( pReq->m_fd != -1 ) + { + fd = pReq->m_fd; + } + else if ( pReq->m_fdListen != -1 ) + fd = pReq->m_fdListen; + else + return -1; + wait_secs = 0; + while( 1 ) + { + if ( !g_running ) + return -1; + if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) + return -1; + FD_ZERO( &readfds ); + FD_SET( fd, &readfds ); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + ret = (*g_fnSelect)(fd+1, &readfds, NULL, NULL, &timeout); + if ( ret == 0 ) + { + if ( s_pChildStatus ) + { + s_pChildStatus->m_inProcess = 0; + } + ++wait_secs; + if (( s_max_idle_secs > 0 )&&(wait_secs >= s_max_idle_secs )) + return -1; + if ( s_ppid &&( getppid() != s_ppid)) + return -1; + } + else if ( ret == -1 ) + { + if ( errno == EINTR ) + continue; + else + return -1; + } + else if ( ret >= 1 ) + { + if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) + return -1; + if ( fd == pReq->m_fdListen ) + { + pReq->m_fd = lsapi_accept( pReq->m_fdListen ); + if ( pReq->m_fd != -1 ) + { + fd = pReq->m_fd; + } + else + return -1; + } + else + break; + } + } + + if ( !readReq( pReq ) ) + { + if ( s_pChildStatus ) + { + s_pChildStatus->m_inProcess = 1; + s_pChildStatus->m_tmReqBegin = s_pChildStatus->m_tmLastCheckPoint = time(NULL); + } + ++s_req_processed; + return 0; + } + lsapi_close( pReq->m_fd ); + pReq->m_fd = -1; + LSAPI_Reset_r( pReq ); + } + return -1; + +} + +void LSAPI_Set_Max_Reqs( int reqs ) +{ s_max_reqs = reqs; } + +void LSAPI_Set_Max_Idle( int secs ) +{ s_max_idle_secs = secs; } + +void LSAPI_Set_Max_Children( int maxChildren ) +{ + if ( g_prefork_server ) + g_prefork_server->m_iMaxChildren = maxChildren; +} + +void LSAPI_Set_Extra_Children( int extraChildren ) +{ + if (( g_prefork_server )&&( extraChildren >= 0 )) + g_prefork_server->m_iExtraChildren = extraChildren; +} + +void LSAPI_Set_Max_Process_Time( int secs ) +{ + if (( g_prefork_server )&&( secs > 0 )) + g_prefork_server->m_iMaxReqProcessTime = secs; +} + + +void LSAPI_Set_Max_Idle_Children( int maxIdleChld ) +{ + if (( g_prefork_server )&&( maxIdleChld > 0 )) + g_prefork_server->m_iMaxIdleChildren = maxIdleChld; +} + +void LSAPI_Set_Server_Max_Idle_Secs( int serverMaxIdle ) +{ + if ( g_prefork_server ) + g_prefork_server->m_iServerMaxIdle = serverMaxIdle; +} + + +void LSAPI_No_Check_ppid() +{ + s_ppid = 0; +} + +#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +#include +#else +extern char ** environ; +#endif +static void unset_lsapi_envs() +{ + char **env; +#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + env = *_NSGetEnviron(); +#else + env = environ; +#endif + while( env != NULL && *env != NULL ) + { + if (!strncmp(*env, "LSAPI_", 6) || !strncmp( *env, "PHP_LSAPI_", 10 ) ) + { + char ** del = env; + do + *del = del[1]; + while( *del++ ); + } + else + ++env; + } +} + +void LSAPI_Init_Env_Parameters( fn_select_t fp ) +{ + const char *p; + int n; + int avoidFork = 0; + p = getenv( "PHP_LSAPI_MAX_REQUESTS" ); + if ( !p ) + p = getenv( "LSAPI_MAX_REQS" ); + if ( p ) + { + n = atoi( p ); + if ( n > 0 ) + LSAPI_Set_Max_Reqs( n ); + } + + p = getenv( "LSAPI_AVOID_FORK" ); + if ( p ) + { + avoidFork = atoi( p ); + } + + p = getenv( "LSAPI_MAX_IDLE" ); + if ( p ) + { + n = atoi( p ); + LSAPI_Set_Max_Idle( n ); + } + + if ( LSAPI_Is_Listen() ) + { + n = 0; + p = getenv( "PHP_LSAPI_CHILDREN" ); + if ( !p ) + p = getenv( "LSAPI_CHILDREN" ); + if ( p ) + n = atoi( p ); + if ( n > 1 ) + { + LSAPI_Init_Prefork_Server( n, fp, avoidFork ); + LSAPI_Set_Server_fd( g_req.m_fdListen ); + } + + p = getenv( "LSAPI_EXTRA_CHILDREN" ); + if ( p ) + LSAPI_Set_Extra_Children( atoi( p ) ); + + p = getenv( "LSAPI_MAX_IDLE_CHILDREN" ); + if ( p ) + LSAPI_Set_Max_Idle_Children( atoi( p ) ); + + p = getenv( "LSAPI_PGRP_MAX_IDLE" ); + if ( p ) + { + LSAPI_Set_Server_Max_Idle_Secs( atoi( p ) ); + } + + p = getenv( "LSAPI_MAX_PROCESS_TIME" ); + if ( p ) + LSAPI_Set_Max_Process_Time( atoi( p ) ); + + if ( getenv( "LSAPI_PPID_NO_CHECK" ) ) + { + LSAPI_No_Check_ppid(); + } + } + unset_lsapi_envs(); +} diff --git a/sapi/litespeed/lsapilib.h b/sapi/litespeed/lsapilib.h index e1c156fa51..10d3638b48 100644 --- a/sapi/litespeed/lsapilib.h +++ b/sapi/litespeed/lsapilib.h @@ -49,6 +49,9 @@ extern "C" { #include #include +#include +#include + struct LSAPI_key_value_pair { char * pKey; @@ -64,7 +67,10 @@ typedef struct lsapi_request { int m_fdListen; int m_fd; - + + long m_lLastActive; + long m_lReqBegin; + char * m_pReqBuf; int m_reqBufSize; @@ -114,14 +120,14 @@ typedef struct lsapi_request extern LSAPI_Request g_req; -//return: >0 continue, ==0 stop, -1 failed +/* return: >0 continue, ==0 stop, -1 failed */ typedef int (*LSAPI_CB_EnvHandler )( const char * pKey, int keyLen, const char * pValue, int valLen, void * arg ); int LSAPI_Init(void); -void LSAPI_stop(void); +void LSAPI_Stop(void); int LSAPI_Is_Listen_r( LSAPI_Request * pReq); @@ -140,6 +146,9 @@ char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex ); int LSAPI_ForeachHeader_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ); +int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq, + LSAPI_CB_EnvHandler fn, void * arg ); + int LSAPI_ForeachEnv_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ); @@ -149,10 +158,13 @@ int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq, char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ); -int LSAPI_GetContentLen_r( LSAPI_Request * pReq ); - int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int len ); +int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq ); + +int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, int bufLen, int *getLF ); + + int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq ); int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len ); @@ -211,6 +223,13 @@ static inline int LSAPI_GetReqBodyLen_r( LSAPI_Request * pReq ) return -1; } +static inline int LSAPI_GetReqBodyRemain_r( LSAPI_Request * pReq ) +{ + if ( pReq ) + return pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead; + return -1; +} + int LSAPI_Is_Listen(void); @@ -226,6 +245,10 @@ static inline char * LSAPI_GetHeader( int headerIndex ) static inline int LSAPI_ForeachHeader( LSAPI_CB_EnvHandler fn, void * arg ) { return LSAPI_ForeachHeader_r( &g_req, fn, arg ); } +static inline int LSAPI_ForeachOrgHeader( + LSAPI_CB_EnvHandler fn, void * arg ) +{ return LSAPI_ForeachOrgHeader_r( &g_req, fn, arg ); } + static inline int LSAPI_ForeachEnv( LSAPI_CB_EnvHandler fn, void * arg ) { return LSAPI_ForeachEnv_r( &g_req, fn, arg ); } @@ -250,9 +273,20 @@ static inline char * LSAPI_GetRequestMethod() static inline int LSAPI_GetReqBodyLen() { return LSAPI_GetReqBodyLen_r( &g_req ); } +static inline int LSAPI_GetReqBodyRemain() +{ return LSAPI_GetReqBodyRemain_r( &g_req ); } + static inline int LSAPI_ReadReqBody( char * pBuf, int len ) { return LSAPI_ReadReqBody_r( &g_req, pBuf, len ); } +static inline int LSAPI_ReqBodyGetChar() +{ return LSAPI_ReqBodyGetChar_r( &g_req ); } + +static inline int LSAPI_ReqBodyGetLine( char * pBuf, int len, int *getLF ) +{ return LSAPI_ReqBodyGetLine_r( &g_req, pBuf, len, getLF ); } + + + static inline int LSAPI_FinalizeRespHeaders(void) { return LSAPI_FinalizeRespHeaders_r( &g_req ); } @@ -271,6 +305,31 @@ static inline int LSAPI_AppendRespHeader( char * pBuf, int len ) static inline int LSAPI_SetRespStatus( int code ) { return LSAPI_SetRespStatus_r( &g_req, code ); } +int LSAPI_IsRunning(void); + +int LSAPI_CreateListenSock( const char * pBind, int backlog ); + +typedef int (*fn_select_t)( int, fd_set *, fd_set *, fd_set *, struct timeval * ); + +int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork ); + +void LSAPI_Set_Server_fd( int fd ); + +int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ); + +void LSAPI_Set_Max_Reqs( int reqs ); + +void LSAPI_Set_Max_Idle( int secs ); + +void LSAPI_Set_Max_Children( int maxChildren ); + +void LSAPI_Set_Max_Idle_Children( int maxIdleChld ); + +void LSAPI_Set_Server_Max_Idle_Secs( int serverMaxIdle ); + +void LSAPI_Set_Max_Process_Time( int secs ); + +void LSAPI_Init_Env_Parameters( fn_select_t fp ); #if defined (c_plusplus) || defined (__cplusplus) }