diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /sapi/litespeed/lsapilib.c | |
download | php2-master.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'sapi/litespeed/lsapilib.c')
-rw-r--r-- | sapi/litespeed/lsapilib.c | 2227 |
1 files changed, 2227 insertions, 0 deletions
diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c new file mode 100644 index 0000000..10ea749 --- /dev/null +++ b/sapi/litespeed/lsapilib.c @@ -0,0 +1,2227 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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: George Wang <gwang@litespeedtech.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* +Copyright (c) 2007, Lite Speed Technologies Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Lite Speed Technologies Inc nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <lsapilib.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> + +#define LSAPI_ST_REQ_HEADER 1 +#define LSAPI_ST_REQ_BODY 2 +#define LSAPI_ST_RESP_HEADER 4 +#define LSAPI_ST_RESP_BODY 8 + +#define LSAPI_RESP_BUF_SIZE 8192 +#define LSAPI_INIT_RESP_HEADER_LEN 4096 + + +static int g_inited = 0; +static int g_running = 1; +static int s_ppid; +static int s_slow_req_msecs = 0; +LSAPI_Request g_req = { -1, -1 }; + +void Flush_RespBuf_r( LSAPI_Request * pReq ); + +static const char *CGI_HEADERS[H_TRANSFER_ENCODING+1] = +{ + "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", + "HTTP_ACCEPT_ENCODING", + "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_CONTROL", + "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_MATCH", + "HTTP_IF_NONE_MATCH", + "HTTP_IF_RANGE", + "HTTP_IF_UNMODIFIED_SINCE", + "HTTP_KEEP_ALIVE", + "HTTP_RANGE", + "HTTP_X_FORWARDED_FOR", + "HTTP_VIA", + "HTTP_TRANSFER_ENCODING" +}; + +static int CGI_HEADER_LEN[H_TRANSFER_ENCODING+1] = { + 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 ) +{ +} +static void lsapi_siguser1( int sig ) +{ + g_running = 0; +} + +#ifndef sighandler_t +typedef void (*sighandler_t)(int); +#endif + +static void lsapi_signal(int signo, sighandler_t handler) +{ + struct sigaction sa; + + sigaction(signo, NULL, &sa); + + if (sa.sa_handler == SIG_DFL) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = handler; + sigaction(signo, &sa, NULL); + } +} + + +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_versionB1 = LSAPI_VERSION_B1; + pHeader->m_type = type; + pHeader->m_flag = LSAPI_ENDIAN; + pHeader->m_packetLen.m_iLen = len; +} + +static int lsapi_set_nblock( int fd, int nonblock ) +{ + int val = fcntl( fd, F_GETFL, 0 ); + if ( nonblock ) + { + if (!( val & O_NONBLOCK )) + { + return fcntl( fd, F_SETFL, val | O_NONBLOCK ); + } + } + else + { + if ( val & O_NONBLOCK ) + { + return fcntl( fd, F_SETFL, val &(~O_NONBLOCK) ); + } + } + return 0; +} + + +static int lsapi_close( int fd ) +{ + int ret; + while( 1 ) { + ret = close( fd ); + if (( ret == -1 )&&( errno == EINTR )&&(g_running)) { + continue; + } + return ret; + } +} + +static inline int lsapi_read( int fd, void * pBuf, int len ) +{ + int ret; + while( 1 ) { + ret = read( fd, (char *)pBuf, len ); + if (( ret == -1 )&&( errno == EINTR )&&(g_running)) { + continue; + } + return ret; + } +} + + +static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen ) +{ + int ret; + int left = totalLen; + int n = count; + while(( left > 0 )&&g_running ) { + ret = writev( fd, *pVec, n ); + if ( ret > 0 ) { + left -= ret; + if (( left <= 0)||( !g_running )) { + return totalLen - left; + } + while( ret > 0 ) { + if ( (*pVec)->iov_len <= ret ) { + ret -= (*pVec)->iov_len; + ++(*pVec); + } else { + (*pVec)->iov_base = (char *)(*pVec)->iov_base + ret; + (*pVec)->iov_len -= ret; + break; + } + } + } 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 inline int allocateBuf( LSAPI_Request * pReq, int size ) +{ + char * pBuf = (char *)realloc( pReq->m_pReqBuf, size ); + if ( pBuf ) { + pReq->m_pReqBuf = pBuf; + pReq->m_reqBufSize = size; + pReq->m_pHeader = (struct lsapi_req_header *)pReq->m_pReqBuf; + return 0; + } + return -1; +} + + +static int allocateIovec( LSAPI_Request * pReq, int n ) +{ + struct iovec * p = (struct iovec *)realloc( + pReq->m_pIovec, sizeof(struct iovec) * n ); + if ( !p ) { + return -1; + } + pReq->m_pIovecToWrite = p + ( pReq->m_pIovecToWrite - pReq->m_pIovec ); + pReq->m_pIovecCur = p + ( pReq->m_pIovecCur - pReq->m_pIovec ); + pReq->m_pIovec = p; + pReq->m_pIovecEnd = p + n; + return 0; +} + +static int allocateRespHeaderBuf( LSAPI_Request * pReq, int size ) +{ + char * p = (char *)realloc( pReq->m_pRespHeaderBuf, size ); + if ( !p ) { + return -1; + } + pReq->m_pRespHeaderBufPos = p + ( pReq->m_pRespHeaderBufPos - pReq->m_pRespHeaderBuf ); + pReq->m_pRespHeaderBuf = p; + pReq->m_pRespHeaderBufEnd = p + size; + return 0; +} + + +static inline int verifyHeader( struct lsapi_packet_header * pHeader, char pktType ) +{ + if (( LSAPI_VERSION_B0 != pHeader->m_versionB0 )|| + ( LSAPI_VERSION_B1 != pHeader->m_versionB1 )|| + ( pktType != pHeader->m_type )) { + return -1; + } + if ( LSAPI_ENDIAN != (pHeader->m_flag & LSAPI_ENDIAN_BIT )) { + register char b; + b = pHeader->m_packetLen.m_bytes[0]; + pHeader->m_packetLen.m_bytes[0] = pHeader->m_packetLen.m_bytes[3]; + pHeader->m_packetLen.m_bytes[3] = b; + b = pHeader->m_packetLen.m_bytes[1]; + pHeader->m_packetLen.m_bytes[1] = pHeader->m_packetLen.m_bytes[2]; + pHeader->m_packetLen.m_bytes[2] = b; + } + return pHeader->m_packetLen.m_iLen; +} + +static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList, + int *curSize, int newSize ) +{ + struct LSAPI_key_value_pair * pBuf; + if ( *curSize >= newSize ) { + return 0; + } + if ( newSize > 8192 ) { + return -1; + } + pBuf = (struct LSAPI_key_value_pair *)realloc( *pEnvList, newSize * + sizeof(struct LSAPI_key_value_pair) ); + if ( pBuf ) { + *pEnvList = pBuf; + *curSize = newSize; + return 0; + } else { + return -1; + } + +} + +static inline int isPipe( int fd ) +{ + char achPeer[128]; + socklen_t len = 128; + if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&& + ( errno == ENOTCONN )) { + return 0; + } else { + return 1; + } +} + +static int parseEnv( struct LSAPI_key_value_pair * pEnvList, int count, + char **pBegin, char * pEnd ) +{ + struct LSAPI_key_value_pair * pEnvEnd; + int keyLen = 0, valLen = 0; + if ( count > 8192 ) { + return -1; + } + pEnvEnd = pEnvList + count; + while( pEnvList != pEnvEnd ) { + if ( pEnd - *pBegin < 4 ) { + return -1; + } + keyLen = *((unsigned char *)((*pBegin)++)); + keyLen = (keyLen << 8) + *((unsigned char *)((*pBegin)++)); + valLen = *((unsigned char *)((*pBegin)++)); + valLen = (valLen << 8) + *((unsigned char *)((*pBegin)++)); + if ( *pBegin + keyLen + valLen > pEnd ) { + return -1; + } + if (( !keyLen )||( !valLen )) { + return -1; + } + + pEnvList->pKey = *pBegin; + *pBegin += keyLen; + pEnvList->pValue = *pBegin; + *pBegin += valLen; + + pEnvList->keyLen = keyLen - 1; + pEnvList->valLen = valLen - 1; + ++pEnvList; + } + if ( memcmp( *pBegin, "\0\0\0\0", 4 ) != 0 ) { + return -1; + } + *pBegin += 4; + return 0; +} + +static inline void swapIntEndian( int * pInteger ) +{ + char * p = (char *)pInteger; + register char b; + b = p[0]; + p[0] = p[3]; + p[3] = b; + b = p[1]; + p[1] = p[2]; + p[2] = b; + +} + +static inline void fixEndian( LSAPI_Request * pReq ) +{ + struct lsapi_req_header *p= pReq->m_pHeader; + swapIntEndian( &p->m_httpHeaderLen ); + swapIntEndian( &p->m_reqBodyLen ); + swapIntEndian( &p->m_scriptFileOff ); + swapIntEndian( &p->m_scriptNameOff ); + swapIntEndian( &p->m_queryStringOff ); + swapIntEndian( &p->m_requestMethodOff ); + swapIntEndian( &p->m_cntUnknownHeaders ); + swapIntEndian( &p->m_cntEnv ); + swapIntEndian( &p->m_cntSpecialEnv ); +} + +static void fixHeaderIndexEndian( LSAPI_Request * pReq ) +{ + int i; + for( i = 0; i < H_TRANSFER_ENCODING; ++i ) { + if ( pReq->m_pHeaderIndex->m_headerOff[i] ) { + register char b; + char * p = (char *)(&pReq->m_pHeaderIndex->m_headerLen[i]); + b = p[0]; + p[0] = p[1]; + p[1] = b; + swapIntEndian( &pReq->m_pHeaderIndex->m_headerOff[i] ); + } + } + if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) { + struct lsapi_header_offset * pCur, *pEnd; + pCur = pReq->m_pUnknownHeader; + pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders; + while( pCur < pEnd ) { + swapIntEndian( &pCur->nameOff ); + swapIntEndian( &pCur->nameLen ); + swapIntEndian( &pCur->valueOff ); + swapIntEndian( &pCur->valueLen ); + ++pCur; + } + } +} + +static int parseRequest( LSAPI_Request * pReq, int totalLen ) +{ + int shouldFixEndian; + char * pBegin = pReq->m_pReqBuf + sizeof( struct lsapi_req_header ); + char * pEnd = pReq->m_pReqBuf + totalLen; + shouldFixEndian = ( LSAPI_ENDIAN != ( + pReq->m_pHeader->m_pktHeader.m_flag & LSAPI_ENDIAN_BIT ) ); + if ( shouldFixEndian ) { + fixEndian( pReq ); + } + if ( (pReq->m_specialEnvListSize < pReq->m_pHeader->m_cntSpecialEnv )&& + allocateEnvList( &pReq->m_pSpecialEnvList, + &pReq->m_specialEnvListSize, + pReq->m_pHeader->m_cntSpecialEnv ) == -1 ) { + return -1; + } + if ( (pReq->m_envListSize < pReq->m_pHeader->m_cntEnv )&& + allocateEnvList( &pReq->m_pEnvList, &pReq->m_envListSize, + pReq->m_pHeader->m_cntEnv ) == -1 ) { + return -1; + } + if ( parseEnv( pReq->m_pSpecialEnvList, + pReq->m_pHeader->m_cntSpecialEnv, + &pBegin, pEnd ) == -1 ) { + return -1; + } + if ( parseEnv( pReq->m_pEnvList, pReq->m_pHeader->m_cntEnv, + &pBegin, pEnd ) == -1 ) { + return -1; + } + pReq->m_pScriptFile = pReq->m_pReqBuf + pReq->m_pHeader->m_scriptFileOff; + pReq->m_pScriptName = pReq->m_pReqBuf + pReq->m_pHeader->m_scriptNameOff; + pReq->m_pQueryString = pReq->m_pReqBuf + pReq->m_pHeader->m_queryStringOff; + pReq->m_pRequestMethod = pReq->m_pReqBuf + pReq->m_pHeader->m_requestMethodOff; + + pBegin = pReq->m_pReqBuf + (( pBegin - pReq->m_pReqBuf + 7 ) & (~0x7)); + pReq->m_pHeaderIndex = ( struct lsapi_http_header_index * )pBegin; + pBegin += sizeof( struct lsapi_http_header_index ); + + pReq->m_pUnknownHeader = (struct lsapi_header_offset *)pBegin; + pBegin += sizeof( struct lsapi_header_offset) * + pReq->m_pHeader->m_cntUnknownHeaders; + + pReq->m_pHttpHeader = pBegin; + pBegin += pReq->m_pHeader->m_httpHeaderLen; + if ( pBegin != pEnd ) { + return -1; + } + + if ( shouldFixEndian ) { + fixHeaderIndexEndian( pReq ); + } + + return 0; +} + +static int s_accept_notify = 0; + +static struct lsapi_packet_header ack = {'L', 'S', + LSAPI_REQ_RECEIVED, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} }; +static inline int notify_req_received( int fd ) +{ + if ( write( fd, &ack, LSAPI_PACKET_HEADER_LEN ) + < LSAPI_PACKET_HEADER_LEN ) { + return -1; + } + return 0; +} + + +static int readReq( LSAPI_Request * pReq ) +{ + int len; + int packetLen; + if ( !pReq ) { + return -1; + } + if ( pReq->m_reqBufSize < 8192 ) { + if ( allocateBuf( pReq, 8192 ) == -1 ) { + return -1; + } + } + + while ( pReq->m_bufRead < LSAPI_PACKET_HEADER_LEN ) { + len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf, pReq->m_reqBufSize ); + if ( len <= 0 ) { + return -1; + } + pReq->m_bufRead += len; + } + pReq->m_reqState = LSAPI_ST_REQ_HEADER; + + packetLen = verifyHeader( &pReq->m_pHeader->m_pktHeader, LSAPI_BEGIN_REQUEST ); + if ( packetLen < 0 ) { + return -1; + } + if ( packetLen > LSAPI_MAX_HEADER_LEN ) { + return -1; + } + + if ( packetLen + 1024 > pReq->m_reqBufSize ) { + if ( allocateBuf( pReq, packetLen + 1024 ) == -1 ) { + return -1; + } + } + while( packetLen > pReq->m_bufRead ) { + len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf + pReq->m_bufRead, packetLen - pReq->m_bufRead ); + if ( len <= 0 ) { + return -1; + } + pReq->m_bufRead += len; + } + if ( parseRequest( pReq, packetLen ) < 0 ) { + return -1; + } + pReq->m_bufProcessed = packetLen; + pReq->m_reqState = LSAPI_ST_REQ_BODY | LSAPI_ST_RESP_HEADER; + + if ( !s_accept_notify ) + return notify_req_received( pReq->m_fd ); + else + return 0; +} + + + +int LSAPI_Init(void) +{ + if ( !g_inited ) { + lsapi_signal(SIGPIPE, lsapi_sigpipe); + lsapi_signal(SIGUSR1, lsapi_siguser1); + +#if defined(SIGXFSZ) && defined(SIG_IGN) + signal(SIGXFSZ, SIG_IGN); +#endif + /* let STDOUT function as STDERR, + just in case writing to STDOUT directly */ + dup2( 2, 1 ); + + if ( LSAPI_InitRequest( &g_req, LSAPI_SOCK_FILENO ) == -1 ) { + return -1; + } + g_inited = 1; + s_ppid = getppid(); + } + return 0; +} + +void LSAPI_Stop(void) +{ + g_running = 0; +} + +int LSAPI_IsRunning(void) +{ + return g_running; +} + +int LSAPI_InitRequest( LSAPI_Request * pReq, int fd ) +{ + if ( !pReq ) { + return -1; + } + memset( pReq, 0, sizeof( LSAPI_Request ) ); + if ( allocateIovec( pReq, 16 ) == -1 ) { + return -1; + } + pReq->m_pRespBuf = pReq->m_pRespBufPos = (char *)malloc( LSAPI_RESP_BUF_SIZE ); + if ( !pReq->m_pRespBuf ) { + return -1; + } + pReq->m_pRespBufEnd = pReq->m_pRespBuf + LSAPI_RESP_BUF_SIZE; + pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec + 1; + pReq->m_respPktHeaderEnd = &pReq->m_respPktHeader[5]; + if ( allocateRespHeaderBuf( pReq, LSAPI_INIT_RESP_HEADER_LEN ) == -1 ) { + return -1; + } + + if ( isPipe( fd ) ) { + pReq->m_fdListen = -1; + pReq->m_fd = fd; + } else { + pReq->m_fdListen = fd; + pReq->m_fd = -1; + lsapi_set_nblock( fd, 1 ); + } + return 0; +} + +int LSAPI_Is_Listen( void ) +{ + return LSAPI_Is_Listen_r( &g_req ); +} + +int LSAPI_Is_Listen_r( LSAPI_Request * pReq) +{ + return pReq->m_fdListen != -1; +} + + + +int LSAPI_Accept_r( LSAPI_Request * pReq ) +{ + char achPeer[128]; + socklen_t len; + int nodelay = 1; + + if ( !pReq ) { + return -1; + } + if ( LSAPI_Finish_r( pReq ) == -1 ) { + return -1; + } + while( g_running ) { + if ( pReq->m_fd == -1 ) { + if ( pReq->m_fdListen != -1) { + len = sizeof( achPeer ); + pReq->m_fd = accept( pReq->m_fdListen, + (struct sockaddr *)&achPeer, &len ); + if ( pReq->m_fd == -1 ) { + if (( errno == EINTR )||( errno == EAGAIN)) { + continue; + } else { + return -1; + } + } else { + lsapi_set_nblock( pReq->m_fd , 0 ); + if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) { + setsockopt(pReq->m_fd, IPPROTO_TCP, TCP_NODELAY, + (char *)&nodelay, sizeof(nodelay)); + } + + if ( s_accept_notify ) + return notify_req_received( pReq->m_fd ); + + } + } else { + return -1; + } + } + if ( !readReq( pReq ) ) { + break; + } + lsapi_close( pReq->m_fd ); + pReq->m_fd = -1; + LSAPI_Reset_r( pReq ); + } + return 0; +} + +static struct lsapi_packet_header finish = {'L', 'S', + LSAPI_RESP_END, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} }; + +int LSAPI_Finish_r( LSAPI_Request * pReq ) +{ + /* finish req body */ + if ( !pReq ) { + return -1; + } + if (pReq->m_reqState) { + if ( pReq->m_fd != -1 ) { + if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) { + LSAPI_FinalizeRespHeaders_r( pReq ); + } + if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) { + Flush_RespBuf_r( pReq ); + } + + pReq->m_pIovecCur->iov_base = (void *)&finish; + pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN; + pReq->m_totalLen += LSAPI_PACKET_HEADER_LEN; + ++pReq->m_pIovecCur; + LSAPI_Flush_r( pReq ); + } + LSAPI_Reset_r( pReq ); + } + return 0; +} + + +void LSAPI_Reset_r( LSAPI_Request * pReq ) +{ + pReq->m_pRespBufPos = pReq->m_pRespBuf; + pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec + 1; + pReq->m_pRespHeaderBufPos = pReq->m_pRespHeaderBuf; + + memset( &pReq->m_pHeaderIndex, 0, + (char *)(pReq->m_respHeaderLen) - (char *)&pReq->m_pHeaderIndex ); +} + + +int LSAPI_Release_r( LSAPI_Request * pReq ) +{ + if ( pReq->m_pReqBuf ) { + free( pReq->m_pReqBuf ); + } + if ( pReq->m_pSpecialEnvList ) { + free( pReq->m_pSpecialEnvList ); + } + if ( pReq->m_pEnvList ) { + free( pReq->m_pEnvList ); + } + if ( pReq->m_pRespHeaderBuf ) { + free( pReq->m_pRespHeaderBuf ); + } + return 0; +} + + +char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex ) +{ + int off; + if ( !pReq || ((unsigned int)headerIndex > H_TRANSFER_ENCODING) ) { + return NULL; + } + off = pReq->m_pHeaderIndex->m_headerOff[ headerIndex ]; + if ( !off ) { + return NULL; + } + if ( *(pReq->m_pHttpHeader + off + + pReq->m_pHeaderIndex->m_headerLen[ headerIndex ]) ) { + *( pReq->m_pHttpHeader + off + + pReq->m_pHeaderIndex->m_headerLen[ headerIndex ]) = 0; + } + 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 || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 )) { + return -1; + } + total = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead; + + if ( total <= 0 ) { + return 0; + } + if ( total < bufLen ) { + bufLen = total; + } + + total = 0; + len = pReq->m_bufRead - pReq->m_bufProcessed; + if ( len > 0 ) { + if ( len > bufLen ) { + len = bufLen; + } + memmove( pBuf, pReq->m_pReqBuf + pReq->m_bufProcessed, len ); + pReq->m_bufProcessed += len; + total += len; + pBuf += len; + bufLen -= len; + } + while( bufLen > 0 ) { + len = lsapi_read( pReq->m_fd, pBuf, bufLen ); + if ( len > 0 ) { + total += len; + pBuf += len; + bufLen -= len; + } else { + if ( len <= 0 ) { + if ( !total) { + return -1; + } + break; + } + } + } + pReq->m_reqBodyRead += total; + return total; + +} + + +int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len ) +{ + struct lsapi_packet_header * pHeader; + const char * pEnd; + const char * p; + int bufLen; + int toWrite; + int packetLen; + + if ( !pReq || !pBuf || (pReq->m_fd == -1) ) { + return -1; + } + if ( len < pReq->m_pRespBufEnd - pReq->m_pRespBufPos ) { + memmove( pReq->m_pRespBufPos, pBuf, len ); + pReq->m_pRespBufPos += len; + return len; + } + + if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) { + LSAPI_FinalizeRespHeaders_r( pReq ); + } + pReq->m_reqState |= LSAPI_ST_RESP_BODY; + + pHeader = pReq->m_respPktHeader; + p = pBuf; + pEnd = pBuf + len; + bufLen = pReq->m_pRespBufPos - pReq->m_pRespBuf; + + while( ( toWrite = pEnd - p ) > 0 ) { + packetLen = toWrite + bufLen; + if ( LSAPI_MAX_DATA_PACKET_LEN < packetLen) { + packetLen = LSAPI_MAX_DATA_PACKET_LEN; + toWrite = packetLen - bufLen; + } + + lsapi_buildPacketHeader( pHeader, LSAPI_RESP_STREAM, + packetLen + LSAPI_PACKET_HEADER_LEN ); + pReq->m_totalLen += packetLen + LSAPI_PACKET_HEADER_LEN; + + pReq->m_pIovecCur->iov_base = (void *)pHeader; + pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN; + ++pReq->m_pIovecCur; + ++pHeader; + if ( bufLen > 0 ) { + pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespBuf; + pReq->m_pIovecCur->iov_len = bufLen; + pReq->m_pRespBufPos = pReq->m_pRespBuf; + ++pReq->m_pIovecCur; + bufLen = 0; + } + + pReq->m_pIovecCur->iov_base = (void *)p; + pReq->m_pIovecCur->iov_len = toWrite; + ++pReq->m_pIovecCur; + p += toWrite; + + if ( pHeader >= pReq->m_respPktHeaderEnd - 1) { + if ( LSAPI_Flush_r( pReq ) == -1 ) { + return -1; + } + pHeader = pReq->m_respPktHeader; + } + } + if ( pHeader != pReq->m_respPktHeader ) { + if ( LSAPI_Flush_r( pReq ) == -1 ) { + return -1; + } + } + return p - pBuf; +} + +void Flush_RespBuf_r( LSAPI_Request * pReq ) +{ + struct lsapi_packet_header * pHeader = pReq->m_respPktHeader; + int bufLen = pReq->m_pRespBufPos - pReq->m_pRespBuf; + pReq->m_reqState |= LSAPI_ST_RESP_BODY; + lsapi_buildPacketHeader( pHeader, LSAPI_RESP_STREAM, + bufLen + LSAPI_PACKET_HEADER_LEN ); + pReq->m_totalLen += bufLen + LSAPI_PACKET_HEADER_LEN; + + pReq->m_pIovecCur->iov_base = (void *)pHeader; + pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN; + ++pReq->m_pIovecCur; + ++pHeader; + if ( bufLen > 0 ) { + pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespBuf; + pReq->m_pIovecCur->iov_len = bufLen; + pReq->m_pRespBufPos = pReq->m_pRespBuf; + ++pReq->m_pIovecCur; + bufLen = 0; + } +} + + + + +int LSAPI_Flush_r( LSAPI_Request * pReq ) +{ + int ret = 0; + 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 ); + } + if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) { + Flush_RespBuf_r( pReq ); + } + + n = pReq->m_pIovecCur - pReq->m_pIovecToWrite; + if ( n > 0 ) { + + ret = lsapi_writev( pReq->m_fd, &pReq->m_pIovecToWrite, + n, pReq->m_totalLen ); + if ( ret < pReq->m_totalLen ) { + lsapi_close( pReq->m_fd ); + pReq->m_fd = -1; + ret = -1; + } + pReq->m_totalLen = 0; + pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec; + } + return ret; +} + + +int LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, int len ) +{ + struct lsapi_packet_header header; + const char * pEnd; + const char * p; + int packetLen; + int totalLen; + int ret; + 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 ); + } + + p = pBuf; + pEnd = pBuf + len; + + while( ( packetLen = pEnd - p ) > 0 ) { + if ( LSAPI_MAX_DATA_PACKET_LEN < packetLen) { + packetLen = LSAPI_MAX_DATA_PACKET_LEN; + } + + lsapi_buildPacketHeader( &header, LSAPI_STDERR_STREAM, + packetLen + LSAPI_PACKET_HEADER_LEN ); + totalLen = packetLen + LSAPI_PACKET_HEADER_LEN; + + iov[0].iov_base = (void *)&header; + iov[0].iov_len = LSAPI_PACKET_HEADER_LEN; + + iov[1].iov_base = (void *)p; + iov[1].iov_len = packetLen; + p += packetLen; + pIov = iov; + ret = lsapi_writev( pReq->m_fd, &pIov, + 2, totalLen ); + if ( ret < totalLen ) { + lsapi_close( pReq->m_fd ); + pReq->m_fd = -1; + ret = -1; + } + } + 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 ) +{ + struct LSAPI_key_value_pair * pBegin = pReq->m_pEnvList; + 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 ) { + return pBegin->pValue; + } + ++pBegin; + } + 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 ) +{ + 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)( CGI_HEADERS[i], CGI_HEADER_LEN[i], + pValue, len, arg ); + ++count; + if ( ret <= 0 ) { + return ret; + } + } + } + if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) { + char achHeaderName[256]; + 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; + if ( keyLen > 250 ) { + keyLen = 250; + } + + pKeyEnd = pKey + keyLen; + memcpy( achHeaderName, "HTTP_", 5 ); + p = &achHeaderName[5]; + + while( pKey < pKeyEnd ) { + char ch = *pKey++; + if ( ch == '-' ) { + *p++ = '_'; + } else { + *p++ = toupper( ch ); + } + } + *p = 0; + keyLen += 5; + + pValue = pReq->m_pHttpHeader + pCur->valueOff; + *(pValue + pCur->valueLen ) = 0; + ret = (*fn)( achHeaderName, keyLen, + pValue, pCur->valueLen, arg ); + if ( ret <= 0 ) { + return ret; + } + ++pCur; + } + } + return count + pReq->m_pHeader->m_cntUnknownHeaders; + +} + +static int EnvForeach( struct LSAPI_key_value_pair * pEnv, + int n, LSAPI_CB_EnvHandler fn, void * arg ) +{ + struct LSAPI_key_value_pair * pEnd = pEnv + n; + int ret; + if ( !pEnv || !fn ) { + return -1; + } + while( pEnv < pEnd ) { + ret = (*fn)( pEnv->pKey, pEnv->keyLen, + pEnv->pValue, pEnv->valLen, arg ); + if ( ret <= 0 ) { + return ret; + } + ++pEnv; + } + return n; +} + + + +int LSAPI_ForeachEnv_r( LSAPI_Request * pReq, + LSAPI_CB_EnvHandler fn, void * arg ) +{ + if ( !pReq || !fn ) { + return -1; + } + if ( pReq->m_pHeader->m_cntEnv > 0 ) { + return EnvForeach( pReq->m_pEnvList, pReq->m_pHeader->m_cntEnv, + fn, arg ); + } + return 0; +} + + + +int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq, + LSAPI_CB_EnvHandler fn, void * arg ) +{ + if ( !pReq || !fn ) { + return -1; + } + if ( pReq->m_pHeader->m_cntSpecialEnv > 0 ) { + return EnvForeach( pReq->m_pSpecialEnvList, + pReq->m_pHeader->m_cntSpecialEnv, + fn, arg ); + } + return 0; + +} + + + +int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq ) +{ + if ( !pReq || !pReq->m_pIovec ) { + return -1; + } + if ( !( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) ) { + return 0; + } + pReq->m_reqState &= ~LSAPI_ST_RESP_HEADER; + if ( pReq->m_pRespHeaderBufPos > pReq->m_pRespHeaderBuf ) { + pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespHeaderBuf; + pReq->m_pIovecCur->iov_len = pReq->m_pRespHeaderBufPos - pReq->m_pRespHeaderBuf; + pReq->m_totalLen += pReq->m_pIovecCur->iov_len; + ++pReq->m_pIovecCur; + } + + pReq->m_pIovec->iov_len = sizeof( struct lsapi_resp_header) + + pReq->m_respHeader.m_respInfo.m_cntHeaders * sizeof( short ); + pReq->m_totalLen += pReq->m_pIovec->iov_len; + + lsapi_buildPacketHeader( &pReq->m_respHeader.m_pktHeader, + LSAPI_RESP_HEADER, pReq->m_totalLen ); + pReq->m_pIovec->iov_base = (void *)&pReq->m_respHeader; + pReq->m_pIovecToWrite = pReq->m_pIovec; + return 0; +} + + + + +int LSAPI_AppendRespHeader_r( LSAPI_Request * pReq, char * pBuf, int len ) +{ + if ( !pReq || !pBuf || len <= 0 || len > LSAPI_RESP_HTTP_HEADER_MAX ) { + return -1; + } + if ( pReq->m_reqState & LSAPI_ST_RESP_BODY ) { + return -1; + } + if ( pReq->m_respHeader.m_respInfo.m_cntHeaders >= LSAPI_MAX_RESP_HEADERS ) { + return -1; + } + if ( pReq->m_pRespHeaderBufPos + len + 1 > pReq->m_pRespHeaderBufEnd ) { + int newlen = pReq->m_pRespHeaderBufPos + len + 4096 - pReq->m_pRespHeaderBuf; + newlen -= newlen % 4096; + if ( allocateRespHeaderBuf( pReq, newlen ) == -1 ) { + return -1; + } + } + memmove( pReq->m_pRespHeaderBufPos, pBuf, len ); + pReq->m_pRespHeaderBufPos += len; + *pReq->m_pRespHeaderBufPos++ = 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( *pBind ) ) { + ++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 > 65535 )) { + 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)); + } + + if ( s_accept_notify ) + notify_req_received( fd ); + } + 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; + lsapi_set_nblock( pReq->m_fd, 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; + lsapi_set_nblock( fd, 0 ); + } else { + if (( errno == EINTR )||( errno == EAGAIN)) { + continue; + } + 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_Set_Slow_Req_Msecs( int msecs ) +{ + s_slow_req_msecs = msecs; +} + +int LSAPI_Get_Slow_Req_Msecs() +{ + return s_slow_req_msecs; +} + +void LSAPI_No_Check_ppid() +{ + s_ppid = 0; +} + +#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +#include <crt_externs.h> +#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_ACCEPT_NOTIFY" ); + if ( p ) { + s_accept_notify = atoi( p ); + } + + p = getenv( "LSAPI_SLOW_REQ_MSECS" ); + if ( p ) { + n = atoi( p ); + LSAPI_Set_Slow_Req_Msecs( n ); + } + + +#if defined( RLIMIT_CORE ) + p = getenv( "LSAPI_ALLOW_CORE_DUMP" ); + if ( !p ) { + struct rlimit limit = { 0, 0 }; + setrlimit( RLIMIT_CORE, &limit ); + } +#endif + + 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(); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + + |