root/sapi/litespeed/lsapilib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lsapi_sigpipe
  2. lsapi_siguser1
  3. lsapi_signal
  4. lsapi_buildPacketHeader
  5. lsapi_set_nblock
  6. lsapi_close
  7. lsapi_read
  8. lsapi_writev
  9. allocateBuf
  10. allocateIovec
  11. allocateRespHeaderBuf
  12. verifyHeader
  13. allocateEnvList
  14. isPipe
  15. parseEnv
  16. swapIntEndian
  17. fixEndian
  18. fixHeaderIndexEndian
  19. parseRequest
  20. notify_req_received
  21. readReq
  22. LSAPI_Init
  23. LSAPI_Stop
  24. LSAPI_IsRunning
  25. LSAPI_InitRequest
  26. LSAPI_Is_Listen
  27. LSAPI_Is_Listen_r
  28. LSAPI_Accept_r
  29. LSAPI_Finish_r
  30. LSAPI_Reset_r
  31. LSAPI_Release_r
  32. LSAPI_GetHeader_r
  33. readBodyToReqBuf
  34. LSAPI_ReqBodyGetChar_r
  35. LSAPI_ReqBodyGetLine_r
  36. LSAPI_ReadReqBody_r
  37. LSAPI_Write_r
  38. Flush_RespBuf_r
  39. LSAPI_Flush_r
  40. LSAPI_Write_Stderr_r
  41. GetHeaderVar
  42. LSAPI_GetEnv_r
  43. LSAPI_ForeachOrgHeader_r
  44. LSAPI_ForeachHeader_r
  45. EnvForeach
  46. LSAPI_ForeachEnv_r
  47. LSAPI_ForeachSpecialEnv_r
  48. LSAPI_FinalizeRespHeaders_r
  49. LSAPI_AppendRespHeader_r
  50. LSAPI_CreateListenSock2
  51. LSAPI_ParseSockAddr
  52. LSAPI_CreateListenSock
  53. LSAPI_Init_Prefork_Server
  54. LSAPI_Set_Server_fd
  55. lsapi_accept
  56. lsapi_cleanup
  57. find_child_status
  58. lsapi_sigchild
  59. lsapi_init_children_status
  60. lsapi_check_child_status
  61. lsapi_all_children_must_die
  62. lsapi_prefork_server_accept
  63. LSAPI_Prefork_Accept_r
  64. LSAPI_Set_Max_Reqs
  65. LSAPI_Set_Max_Idle
  66. LSAPI_Set_Max_Children
  67. LSAPI_Set_Extra_Children
  68. LSAPI_Set_Max_Process_Time
  69. LSAPI_Set_Max_Idle_Children
  70. LSAPI_Set_Server_Max_Idle_Secs
  71. LSAPI_No_Check_ppid
  72. unset_lsapi_envs
  73. LSAPI_Init_Env_Parameters

/*
   +----------------------------------------------------------------------+
   | 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;
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 struct lsapi_packet_header ack = {'L', 'S',
                LSAPI_REQ_RECEIVED, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} };
static inline int notify_req_received( LSAPI_Request * pReq )
{
    if ( write( pReq->m_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;

    return notify_req_received( pReq );
}



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));
                    }
                }
            } 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;
            pKeyEnd = pKey + keyLen;
            memcpy( achHeaderName, "HTTP_", 5 );
            p = &achHeaderName[5];
            if ( keyLen > 250 ) {
                keyLen = 250;
            }

            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));
        }
    }
    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_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 );
    }    

#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
 */



/* [<][>][^][v][top][bottom][index][help] */