pax_global_header00006660000000000000000000000064147772435770014541gustar00rootroot0000000000000052 comment=12ae40e58f6b1d8325408ff6765894f09994f5a9 fcgi2-2.4.5/000077500000000000000000000000001477724357700125435ustar00rootroot00000000000000fcgi2-2.4.5/.github/000077500000000000000000000000001477724357700141035ustar00rootroot00000000000000fcgi2-2.4.5/.github/workflows/000077500000000000000000000000001477724357700161405ustar00rootroot00000000000000fcgi2-2.4.5/.github/workflows/build.yml000066400000000000000000000020431477724357700177610ustar00rootroot00000000000000name: Build CI on: push: {} pull_request: {} jobs: build-linux: runs-on: ubuntu-latest container: ubuntu:24.04 steps: - uses: actions/checkout@v4 - name: setup os run: apt-get update && apt-get -y install libfcgi-client-perl autoconf automake libtool make gcc make m4 g++ - name: libfcgi - autogen.sh run: ./autogen.sh - name: libfcgi-configure run: ./configure - name: libfcgi - make run: make - name: perl module - distrib and perl Makefile.PL working-directory: perl run: ./distrib && perl Makefile.PL - name: perl module - make working-directory: perl run: make - name: perl module - make test working-directory: perl run: make test build-windows: runs-on: windows-2022 strategy: matrix: arch: - x86 - amd64 steps: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 with: arch: ${{ matrix.arch }} - name: build with nmake run: nmake -f Makefile.nt fcgi2-2.4.5/.gitignore000066400000000000000000000006051477724357700145340ustar00rootroot00000000000000*~ bak .bak *.bak *.exe *.o *.obj *.old *.fpl *.la *.lo *.CS_ *.plg *.opt *.ncb *.positions Debug Release .deps .libs config.cache config.status config.log FcgiBin Makefile Makefile.in aclocal.m4 configure fcgi_config.h fcgi_config.h.in libtool stamp-h stamp-h.in stamp-h1 *.tar.gz autom4te.cache/ config.guess config.sub depcomp compile fcgi.pc fcgi++.pc install-sh missing ltmain.sh fcgi2-2.4.5/LICENSE000077500000000000000000000032371477724357700135600ustar00rootroot00000000000000This FastCGI application library source and object code (the "Software") and its documentation (the "Documentation") are copyrighted by Open Market, Inc ("Open Market"). The following terms apply to all files associated with the Software and Documentation unless explicitly disclaimed in individual files. Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this Software and Documentation may be copyrighted by their authors and need not follow the licensing terms described here. If modifications to this Software and Documentation have new licensing terms, the new terms must be clearly indicated on the first page of each file where they apply. OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. fcgi2-2.4.5/Makefile.am000077500000000000000000000100201477724357700145730ustar00rootroot00000000000000# # Makefile for FastCGI development kit # # $Id: Makefile.am,v 1.8 2003/11/02 21:42:47 robs Exp $ # ACLOCAL_AMFLAGS = -I m4 DISTCLEANFILES= compile config.guess config.sub configure configure~ depcomp fcgi_config.h.in fcgi_config.h.in~ \ aclocal.m4 m4/libtool.m4 m4/lt*.m4 Makefile.in install-sh missing ltmain.sh \ COPYING INSTALL fcgi-*.tar.gz SUBDIRS = libfcgi cgi-fcgi include if HAVE_EXAMPLES SUBDIRS += examples endif include_HEADERS = fcgi_config.h man_MANS= doc/cgi-fcgi.1 \ doc/FCGI_Accept.3 \ doc/FCGI_Finish.3 \ doc/FCGI_SetExitStatus.3 \ doc/FCGI_StartFilterData.3 pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = fcgi.pc \ fcgi++.pc EXTRA_DIST = LICENSE \ Makefile.nt \ cgi-fcgi/cgi-fcgi.mak \ examples/authorizer.mak \ examples/echo.mak \ examples/echox.mak \ examples/size.mak \ examples/echo-cpp.mak \ libfcgi/libfcgi.mak \ images/aplib-hd.gif \ images/divider.gif \ images/fcgi-hd.gif \ images/mail-hd.gif \ images/navbar.gif \ images/serv-hd.gif \ images/words-hd.gif \ include/fcgi_config_x86.h \ java/FCGIGlobalDefs.java \ java/FCGIInputStream.java \ java/FCGIInterface.java \ java/FCGIMessage.java \ java/FCGIOutputStream.java \ java/FCGIRequest.java \ libfcgi/os_unix.c \ libfcgi/os_win32.c \ perl/eg/echo.pl \ perl/eg/remote.pl \ perl/eg/threaded.pl \ perl/README \ perl/ChangeLog \ perl/t/02-unix_domain_socket.t \ perl/t/01-load.t \ perl/FCGI.pm \ perl/Makefile.PL \ perl/MANIFEST \ perl/MANIFEST.SKIP \ perl/configure.in \ perl/distrib \ perl/configure.readme \ perl/typemap \ perl/FCGI.xs \ Win32/FastCGI.dsw \ Win32/authorizer.dsp \ Win32/cgifcgi.dsp \ Win32/config_h.dsp \ Win32/echo-cpp.dsp \ Win32/echo.dsp \ Win32/echox.dsp \ Win32/libfcgi.dsp \ Win32/logdump.dsp \ Win32/size.dsp \ Win32/threaded.dsp \ doc/FCGI_Accept.3 \ doc/FCGI_Finish.3 \ doc/FCGI_SetExitStatus.3 \ doc/FCGI_StartFilterData.3 \ doc/cgi-fcgi.1 \ doc/fcgi-devel-kit.htm \ doc/fcgi-java.htm \ doc/fcgi-perf.htm \ doc/fcgi-perl.htm \ doc/fcgi-spec.html \ doc/fcgi-tcl.htm \ doc/omi-logo.gif \ doc/overview.html \ doc/www5-api-workshop.html \ doc/fastcgi-prog-guide/ap_guida.htm \ doc/fastcgi-prog-guide/ap_guide.htm \ doc/fastcgi-prog-guide/apaman.htm \ doc/fastcgi-prog-guide/ch1inta1.gif \ doc/fastcgi-prog-guide/ch1intra.gif \ doc/fastcgi-prog-guide/ch1intro.htm \ doc/fastcgi-prog-guide/ch2c.htm \ doc/fastcgi-prog-guide/ch3perl.htm \ doc/fastcgi-prog-guide/ch4tcl.htm \ doc/fastcgi-prog-guide/cover.htm \ doc/fastcgi-prog-guide/covera.gif \ doc/fastcgi-whitepaper/fastcgi.htm \ doc/fastcgi-whitepaper/img00001.gif \ doc/fastcgi-whitepaper/img00002.gif \ doc/fastcgi-whitepaper/img00003.gif distclean-local: -rm -rf autom4te.cache fcgi2-2.4.5/Makefile.nt000066400000000000000000000026431477724357700146300ustar00rootroot00000000000000# # Windows Makefile for FastCGI development kit # # $Id: Makefile.nt,v 1.4 2001/11/27 14:02:54 robs Exp $ # !IF "$(CFG)" == "" CFG=release !ENDIF all: "include\fcgi_config.h" cd ".\libfcgi" $(MAKE) $(MAKEFLAGS) /NOLOGO /F libfcgi.mak CFG=$(CFG) $@ cd ".\..\cgi-fcgi" $(MAKE) $(MAKEFLAGS) /NOLOGO /F cgi-fcgi.mak CFG=$(CFG) $@ cd ".\..\examples" $(MAKE) $(MAKEFLAGS) /NOLOGO /F authorizer.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echo.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echox.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F size.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echo-cpp.mak CFG=$(CFG) $@ cd ".\.." "include\fcgi_config.h": "include\fcgi_config_x86.h" copy "include\fcgi_config_x86.h" "include\fcgi_config.h" clean: cd ".\libfcgi" $(MAKE) $(MAKEFLAGS) /NOLOGO /F libfcgi.mak CFG=$(CFG) $@ cd ".\..\cgi-fcgi" $(MAKE) $(MAKEFLAGS) /NOLOGO /F cgi-fcgi.mak CFG=$(CFG) $@ cd ".\..\examples" $(MAKE) $(MAKEFLAGS) /NOLOGO /F authorizer.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echo.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echox.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F size.mak CFG=$(CFG) $@ $(MAKE) $(MAKEFLAGS) /NOLOGO /F echo-cpp.mak CFG=$(CFG) $@ cd ".\.." install: @echo "Sorry, the install target hasn't been written yet" fcgi2-2.4.5/README.md000077500000000000000000000023271477724357700140310ustar00rootroot00000000000000![Github Actions Build](https://github.com/FastCGI-Archives/fcgi2/actions/workflows/build.yml/badge.svg) FastCGI Developer's Kit ----------------------- Copyright (c) 1996 Open Market, Inc. See the file "[LICENSE](LICENSE)" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. Documentations -------------- - [FastCGI Developer's Kit Documentations](http://fastcgi-archives.github.io/fcgi2/doc/overview.html) Basic Directions ---------------- #### *Build Requires on unix:* You need to install gcc, gnu make, m4, aclocal, autoconf, automake and libtool packages. *Example on ubuntu :* ``` # apt install gcc make m4 autoconf automake libtool ``` #### *Unix:* ./autogen.sh ./configure make make install #### *Win32:* nmake -f Makefile.nt (or use the MSVC++ project files in the Win32 directory) CHANGES ------- This repository are a fork from the original fastcgi sdk from [FastCGI.com](https://fastcgi-archives.github.io/) that are now down, the new place of FastCGI.com are at https://fastcgi-archives.github.io/. For more detail regarding changes, please consult the [git log available](https://github.com/FastCGI-Archives/fcgi2/commits/master). fcgi2-2.4.5/README.supervise000066400000000000000000000024261477724357700154530ustar00rootroot00000000000000To use, call your cgi-fcgi -start -connect $host:$port script with the -supervise option, like: cgi-fcgi -start -supervise -connect 127.0.0.1:1791 /path/to/dispatch.fcgi Full Supervise run script becomes #!/bin/sh RAIL_NUMBER=$(basename $PWD|awk -F'-' '{print $2}') RAILS_HOST=$( contains one line that is the full path to your rails root directory. RAILS_ENV => contains one word, either ‘production’ or ‘development’ RAILS_HOST => contains one IP address or FQDN You can set any other environment variables in this way by simply creating a file with the variable name and its contents will become the value of that environment variable. Because of the envdir ./env call before the cgi-fcgi call, your rails application has access to any variables set in this way. fcgi2-2.4.5/Win32/000077500000000000000000000000001477724357700134455ustar00rootroot00000000000000fcgi2-2.4.5/Win32/.gitignore000066400000000000000000000000141477724357700154300ustar00rootroot00000000000000libfcgi.sbl fcgi2-2.4.5/Win32/FastCGI.dsw000066400000000000000000000057471477724357700154210ustar00rootroot00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "authorizer"=".\authorizer.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "cgifcgi"=".\cgifcgi.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "config_h"=".\config_h.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "echo"=".\echo.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "echo_cpp"=".\echo-cpp.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "echox"=".\echox.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "libfcgi"=".\libfcgi.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name config_h End Project Dependency }}} ############################################################################### Project: "logdump"=".\logdump.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "size"=".\size.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Project: "threaded"=".\threaded.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libfcgi End Project Dependency }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### fcgi2-2.4.5/Win32/authorizer.dsp000066400000000000000000000103611477724357700163520ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="authorizer" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=authorizer - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "authorizer.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "authorizer.mak" CFG="authorizer - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "authorizer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "authorizer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "authorizer - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\authorizer\Release" # PROP Intermediate_Dir "..\examples\authorizer\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "authorizer - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "..\examples/authorizer/Debug" # PROP Intermediate_Dir "..\examples/authorizer/Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "authorizer - Win32 Release" # Name "authorizer - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\examples\authorizer.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/cgifcgi.dsp000066400000000000000000000102061477724357700155470ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="cgifcgi" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=cgifcgi - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "cgifcgi.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "cgifcgi.mak" CFG="cgifcgi - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "cgifcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "cgifcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "cgifcgi - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\cgi-fcgi\Release" # PROP Intermediate_Dir "..\cgi-fcgi\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /out:"..\cgi-fcgi\Release\cgi-fcgi.exe" /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "cgifcgi - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../cgi-fcgi/Debug" # PROP Intermediate_Dir "../cgi-fcgi/Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /out:"..\cgi-fcgi\Debug\cgi-fcgi.exe" /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "cgifcgi - Win32 Release" # Name "cgifcgi - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE="..\cgi-fcgi\cgi-fcgi.c" # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/config_h.dsp000066400000000000000000000110051477724357700157260ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="config_h" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=config_h - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "config_h.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "config_h.mak" CFG="config_h - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "config_h - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "config_h - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "config_h" # PROP Scc_LocalPath ".." CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "config_h - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "config_h - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /GX /ZI /Od /I "../libfastcgi" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 Ws2_32.lib /nologo /subsystem:console /profile /debug /machine:I386 !ENDIF # Begin Target # Name "config_h - Win32 Release" # Name "config_h - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\include\fcgi_config_x86.h !IF "$(CFG)" == "config_h - Win32 Release" # Begin Custom Build InputPath=..\include\fcgi_config_x86.h "..\include\fcgi_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" copy $(InputPath) ..\include\fcgi_config.h # End Custom Build !ELSEIF "$(CFG)" == "config_h - Win32 Debug" # Begin Custom Build InputPath=..\include\fcgi_config_x86.h "..\include\fcgi_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" copy $(InputPath) ..\include\fcgi_config.h # End Custom Build !ENDIF # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/echo-cpp.dsp000066400000000000000000000103571477724357700156610ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="echo_cpp" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=echo_cpp - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "echo-cpp.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echo-cpp.mak" CFG="echo_cpp - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "echo_cpp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "echo_cpp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "echo_cpp - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "echo-cpp\Release" # PROP BASE Intermediate_Dir "echo-cpp\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\echo-cpp\Release" # PROP Intermediate_Dir "..\examples\echo-cpp\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /GX /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "echo_cpp - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../examples/echo-cpp\Debug" # PROP Intermediate_Dir "../examples/echo-cpp\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "echo_cpp - Win32 Release" # Name "echo_cpp - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE="..\examples\echo-cpp.cpp" # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/echo.dsp000066400000000000000000000101251477724357700150720ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="echo" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=echo - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "echo.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echo.mak" CFG="echo - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "echo - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "echo - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "echo" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "echo - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "echo\Release" # PROP BASE Intermediate_Dir "echo\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\echo\Release" # PROP Intermediate_Dir "..\examples\echo\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "echo - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../examples/echo\Debug" # PROP Intermediate_Dir "../examples/echo\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /map /debug /machine:IX86 /libpath:"..\libfcgi\Debug" # SUBTRACT LINK32 /verbose !ENDIF # Begin Target # Name "echo - Win32 Release" # Name "echo - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\examples\echo.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/echox.dsp000066400000000000000000000102551477724357700152660ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="echox" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=echox - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "echox.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echox.mak" CFG="echox - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "echox - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "echox - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "echox" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "echox - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "echo-x\Release" # PROP BASE Intermediate_Dir "echo-x\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\echo-x\Release" # PROP Intermediate_Dir "..\examples\echo-x\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /out:"..\examples\echo-x\Release\echo-x.exe" /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "echox - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../examples/echo-x\Debug" # PROP Intermediate_Dir "../examples/echo-x\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /out:"..\examples\echo-x\Debug\echo-x.exe" /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "echox - Win32 Release" # Name "echox - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE="..\examples\echo-x.c" # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/libfcgi.dsp000066400000000000000000000120721477724357700155560ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="libfcgi" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=libfcgi - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "libfcgi.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "libfcgi.mak" CFG="libfcgi - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "libfcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "libfcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "libfcgi" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "libfcgi - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\libfcgi\Release" # PROP Intermediate_Dir "..\libfcgi\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /Ob2 /I "..\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /Fr /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 Ws2_32.lib /nologo /dll /pdb:none /machine:I386 # SUBTRACT LINK32 /verbose /nodefaultlib !ELSEIF "$(CFG)" == "libfcgi - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "..\libfcgi\Debug" # PROP Intermediate_Dir "..\libfcgi\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /GX /ZI /Od /I "..\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 Ws2_32.lib /nologo /dll /profile /map /debug /machine:I386 # SUBTRACT LINK32 /verbose /nodefaultlib !ENDIF # Begin Target # Name "libfcgi - Win32 Release" # Name "libfcgi - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\libfcgi\fcgi_stdio.c # End Source File # Begin Source File SOURCE=..\libfcgi\fcgiapp.c # End Source File # Begin Source File SOURCE=..\libfcgi\fcgio.cpp !IF "$(CFG)" == "libfcgi - Win32 Release" # ADD CPP /GX !ELSEIF "$(CFG)" == "libfcgi - Win32 Debug" # ADD CPP /W3 /GX !ENDIF # End Source File # Begin Source File SOURCE=..\libfcgi\os_unix.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=..\libfcgi\os_win32.c # End Source File # Begin Source File SOURCE=..\libfcgi\strerror.c # PROP Exclude_From_Build 1 # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\include\fastcgi.h # End Source File # Begin Source File SOURCE=..\include\fcgi_config.h # End Source File # Begin Source File SOURCE=..\include\fcgi_config_x86.h # End Source File # Begin Source File SOURCE=..\include\fcgi_stdio.h # End Source File # Begin Source File SOURCE=..\include\fcgiapp.h # End Source File # Begin Source File SOURCE=..\include\fcgimisc.h # End Source File # Begin Source File SOURCE=..\include\fcgio.h # End Source File # Begin Source File SOURCE=..\include\fcgios.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # Begin Source File SOURCE=..\README # End Source File # End Target # End Project fcgi2-2.4.5/Win32/logdump.dsp000066400000000000000000000103631477724357700156270ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="logdump" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=logdump - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "logdump.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "logdump.mak" CFG="logdump - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "logdump - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "logdump - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "logdump" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "logdump - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "log-dump\Release" # PROP BASE Intermediate_Dir "log-dump\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\log-dump\Release" # PROP Intermediate_Dir "..\examples\log-dump\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /out:"..\examples\log-dump\Release\log-dump.exe" /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "logdump - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../examples/log-dump\Debug" # PROP Intermediate_Dir "../examples/log-dump\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /out:"..\examples\log-dump\Debug\log-dump.exe" /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "logdump - Win32 Release" # Name "logdump - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE="..\examples\log-dump.c" # PROP Exclude_From_Build 1 # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/size.dsp000066400000000000000000000100651477724357700151310ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="size" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=size - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "size.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "size.mak" CFG="size - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "size - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "size - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "size" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "size - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "size\Release" # PROP BASE Intermediate_Dir "size\Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\size\Release" # PROP Intermediate_Dir "..\examples\size\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "size - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../examples/size\Debug" # PROP Intermediate_Dir "../examples/size\Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "size - Win32 Release" # Name "size - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\examples\size.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/Win32/threaded.dsp000066400000000000000000000102111477724357700157300ustar00rootroot00000000000000# Microsoft Developer Studio Project File - Name="threaded" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=threaded - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "threaded.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "threaded.mak" CFG="threaded - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "threaded - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "threaded - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "threaded - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "..\examples\threaded\Release" # PROP Intermediate_Dir "..\examples\threaded\Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /Gi /GX /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # SUBTRACT CPP /Fr # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 libfcgi.lib /nologo /pdb:none /machine:IX86 /libpath:"..\libfcgi\Release" !ELSEIF "$(CFG)" == "threaded - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "..\examples/threaded/Debug" # PROP Intermediate_Dir "..\examples/threaded/Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIB_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W4 /Gm /Gi /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 libfcgi.lib /nologo /profile /debug /machine:IX86 /libpath:"..\libfcgi\Debug" !ENDIF # Begin Target # Name "threaded - Win32 Release" # Name "threaded - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\examples\threaded.c # PROP Exclude_From_Build 1 # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project fcgi2-2.4.5/acinclude.m4000066400000000000000000000341151477724357700147400ustar00rootroot00000000000000dnl $Id: acinclude.m4,v 1.3 2009/10/06 02:04:10 robs Exp $ AC_DEFUN([FCGI_COMMON_CHECKS], [ AC_CHECK_TYPE([ssize_t], [int]) AC_MSG_CHECKING([for sun_len in sys/un.h]) AC_EGREP_HEADER([sun_len], [sys/un.h], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SOCKADDR_UN_SUN_LEN], [1], [Define if sockaddr_un in sys/un.h contains a sun_len component])], AC_MSG_RESULT([no])) AC_MSG_CHECKING([for fpos_t in stdio.h]) AC_EGREP_HEADER([fpos_t], [stdio.h], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_FPOS], [1], [Define if the fpos_t typedef is in stdio.h])], AC_MSG_RESULT([no])) AC_CHECK_HEADERS([sys/socket.h netdb.h netinet/in.h arpa/inet.h]) AC_CHECK_HEADERS([sys/time.h limits.h sys/param.h unistd.h]) AC_MSG_CHECKING([for a fileno() prototype in stdio.h]) AC_EGREP_HEADER([fileno], [stdio.h], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_FILENO_PROTO], [1], [Define if there's a fileno() prototype in stdio.h])], AC_MSG_RESULT([no])) AC_MSG_CHECKING([for socklen_t in sys/socket.h]) AC_EGREP_HEADER([socklen_t], [sys/socket.h], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SOCKLEN], [1], [Define if the socklen_t typedef is in sys/socket.h])], AC_MSG_RESULT([no])) #-------------------------------------------------------------------- # Do we need cross-process locking on this platform? #-------------------------------------------------------------------- AC_MSG_CHECKING([whether cross-process locking is required by accept()]) case "`uname -sr`" in IRIX\ 5.* | SunOS\ 5.* | UNIX_System_V\ 4.0) AC_MSG_RESULT([yes]) AC_DEFINE([USE_LOCKING], [1], [Define if cross-process locking is required by accept()]) ;; *) AC_MSG_RESULT([no]) ;; esac #-------------------------------------------------------------------- # Does va_arg(arg, long double) crash the compiler? # hpux 9.04 compiler does and so does Stratus FTX (uses HP's compiler) #-------------------------------------------------------------------- AC_MSG_CHECKING([whether va_arg(arg, long double) crashes the compiler]) AC_TRY_COMPILE([#include ], [long double lDblArg; va_list arg; lDblArg = va_arg(arg, long double);], AC_MSG_RESULT([no]), [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_VA_ARG_LONG_DOUBLE_BUG], [1], [Define if va_arg(arg, long double) crashes the compiler])]) AC_C_CONST ]) dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) dnl dnl This macro figures out how to build C programs using POSIX dnl threads. It sets the PTHREAD_LIBS output variable to the threads dnl library and linker flags, and the PTHREAD_CFLAGS output variable dnl to any special C compiler flags that are needed. (The user can also dnl force certain compiler flags/libs to be tested by setting these dnl environment variables.) dnl dnl Also sets PTHREAD_CC to any special C compiler that is needed for dnl multi-threaded programs (defaults to the value of CC otherwise). dnl (This is necessary on AIX to use the special cc_r compiler alias.) dnl dnl If you are only building threads programs, you may wish to dnl use these variables in your default LIBS, CFLAGS, and CC: dnl dnl LIBS="$PTHREAD_LIBS $LIBS" dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" dnl CC="$PTHREAD_CC" dnl dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE dnl to that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). dnl dnl ACTION-IF-FOUND is a list of shell commands to run if a threads dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands dnl to run it if it is not found. If ACTION-IF-FOUND is not specified, dnl the default action will define HAVE_PTHREAD. dnl dnl Please let the authors know if this macro fails on any platform, dnl or if you have any other suggestions or comments. This macro was dnl based on work by SGJ on autoconf scripts for FFTW (www.fftw.org) dnl (with help from M. Frigo), as well as ac_pthread and hb_pthread dnl macros posted by AFC to the autoconf macro repository. We are also dnl grateful for the helpful feedback of numerous users. dnl dnl @version $Id: acinclude.m4,v 1.3 2009/10/06 02:04:10 robs Exp $ dnl @author Steven G. Johnson and Alejandro Forero Cuervo AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) acx_pthread_ok=no # First, check if the POSIX threads header, pthread.h, is available. # If it isn't, don't bother looking for the threads libraries. AC_CHECK_HEADER(pthread.h, , acx_pthread_ok=noheader) # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # pthread: Linux, etcetera # --thread-safe: KAI C++ case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthread or # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: threads are created detached by default # and the JOINABLE attribute has a nonstandard name (UNDETACHED). AC_MSG_CHECKING([for joinable pthread attribute]) AC_TRY_LINK([#include ], [int attr=PTHREAD_CREATE_JOINABLE;], ok=PTHREAD_CREATE_JOINABLE, ok=unknown) if test x"$ok" = xunknown; then AC_TRY_LINK([#include ], [int attr=PTHREAD_CREATE_UNDETACHED;], ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) fi if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, [Define to the necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_RESULT(${ok}) if test x"$ok" = xunknown; then AC_MSG_WARN([we do not know how to create joinable pthreads]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; *solaris* | alpha*-osf*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi ])dnl ACX_PTHREAD dnl @synopsis AC_PROG_CC_WARNINGS([ANSI]) dnl dnl Enables a reasonable set of warnings for the C compiler. Optionally, dnl if the first argument is nonempty, turns on flags which enforce and/or dnl enable proper ANSI C if such flags are known to the compiler used. dnl dnl Currently this macro knows about GCC, Solaris C compiler, dnl Digital Unix C compiler, C for AIX Compiler, HP-UX C compiler, dnl and IRIX C compiler. dnl dnl @version $Id: acinclude.m4,v 1.3 2009/10/06 02:04:10 robs Exp $ dnl @author Ville Laurikari dnl AC_DEFUN([AC_PROG_CC_WARNINGS], [ ansi=$1 if test -z "$ansi"; then msg="for C compiler warning flags" else msg="for C compiler warning and ANSI conformance flags" fi AC_CACHE_CHECK($msg, ac_cv_prog_cc_warnings, [ if test -n "$CC"; then cat > conftest.c <&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 && $CC -c -v -Xc conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then ac_cv_prog_cc_warnings="-v" else ac_cv_prog_cc_warnings="-v -Xc" fi dnl HP-UX C compiler elif $CC > /dev/null 2>&1 && $CC -c -Aa +w1 conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then ac_cv_prog_cc_warnings="+w1" else ac_cv_prog_cc_warnings="+w1 -Aa" fi dnl Digital Unix C compiler elif ! $CC > /dev/null 2>&1 && $CC -c -verbose -w0 -warnprotos -std1 conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then ac_cv_prog_cc_warnings="-verbose -w0 -warnprotos" else ac_cv_prog_cc_warnings="-verbose -w0 -warnprotos -std1" fi dnl C for AIX Compiler elif $CC > /dev/null 2>&1 | grep AIX > /dev/null 2>&1 && $CC -c -qlanglvl=ansi -qinfo=all conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then ac_cv_prog_cc_warnings="-qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" else ac_cv_prog_cc_warnings="-qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd -qlanglvl=ansi" fi dnl IRIX C compiler elif $CC -fullwarn -ansi -ansiE > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then ac_cv_prog_cc_warnings="-fullwarn" else ac_cv_prog_cc_warnings="-fullwarn -ansi -ansiE" fi fi rm -f conftest.* fi if test -n "$ac_cv_prog_cc_warnings"; then CFLAGS="$CFLAGS $ac_cv_prog_cc_warnings" else ac_cv_prog_cc_warnings="unknown" fi ]) ]) fcgi2-2.4.5/autogen.sh000077500000000000000000000000471477724357700145450ustar00rootroot00000000000000#!/bin/sh autoreconf --force --install fcgi2-2.4.5/cgi-fcgi/000077500000000000000000000000001477724357700142135ustar00rootroot00000000000000fcgi2-2.4.5/cgi-fcgi/.gitignore000066400000000000000000000000121477724357700161740ustar00rootroot00000000000000cgi-fcgi fcgi2-2.4.5/cgi-fcgi/Makefile.am000066400000000000000000000006451477724357700162540ustar00rootroot00000000000000# $Id: Makefile.am,v 1.4 2001/12/22 03:16:22 robs Exp $ bin_PROGRAMS = cgi-fcgi DISTCLEANFILES= Makefile.in INCLUDEDIR = ../include AM_CPPFLAGS = -I$(top_srcdir)/include INCLUDE_FILES = $(INCLUDEDIR)/fastcgi.h \ $(INCLUDEDIR)/fcgiapp.h \ $(INCLUDEDIR)/fcgimisc.h LIBDIR = ../libfcgi LIBFCGI = $(LIBDIR)/libfcgi.la cgi_fcgi_LDADD = $(LIBFCGI) cgi_fcgi_SOURCES = $(INCLUDE_FILES) cgi-fcgi.c fcgi2-2.4.5/cgi-fcgi/cgi-fcgi.c000066400000000000000000000631271477724357700160400ustar00rootroot00000000000000/* * cgifcgi.c -- * * CGI to FastCGI bridge * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif #include #include "fcgi_config.h" #ifdef HAVE_NETDB_H #include #endif #ifdef _WIN32 #include #include #elif defined(__APPLE__) #include #define environ (*_NSGetEnviron()) #else extern char **environ; #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "fcgimisc.h" #include "fcgiapp.h" #include "fastcgi.h" #include "fcgios.h" static int wsReadPending = 0; static int fcgiReadPending = 0; static int fcgiWritePending = 0; static void ScheduleIo(void); /* * Simple buffer (not ring buffer) type, used by all event handlers. */ #define BUFFLEN 8192 typedef struct { char *next; char *stop; char buff[BUFFLEN]; } Buffer; /* *---------------------------------------------------------------------- * * GetPtr -- * * Returns a count of the number of characters available * in the buffer (at most n) and advances past these * characters. Stores a pointer to the first of these * characters in *ptr. * *---------------------------------------------------------------------- */ static int GetPtr(char **ptr, int n, Buffer *pBuf) { int result; *ptr = pBuf->next; result = min(n, pBuf->stop - pBuf->next); pBuf->next += result; return result; } /* *---------------------------------------------------------------------- * * MakeHeader -- * * Constructs an FCGI_Header struct. * *---------------------------------------------------------------------- */ static FCGI_Header MakeHeader( int type, int requestId, int contentLength, int paddingLength) { FCGI_Header header; ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH); ASSERT(paddingLength >= 0 && paddingLength <= 0xff); header.version = FCGI_VERSION_1; header.type = (unsigned char) type; header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); header.requestIdB0 = (unsigned char) ((requestId ) & 0xff); header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); header.contentLengthB0 = (unsigned char) ((contentLength ) & 0xff); header.paddingLength = (unsigned char) paddingLength; header.reserved = 0; return header; } /* *---------------------------------------------------------------------- * * MakeBeginRequestBody -- * * Constructs an FCGI_BeginRequestBody record. * *---------------------------------------------------------------------- */ static FCGI_BeginRequestBody MakeBeginRequestBody( int role, int keepConnection) { FCGI_BeginRequestBody body; ASSERT((role >> 16) == 0); body.roleB1 = (unsigned char) ((role >> 8) & 0xff); body.roleB0 = (unsigned char) (role & 0xff); body.flags = (unsigned char) ((keepConnection) ? FCGI_KEEP_CONN : 0); memset(body.reserved, 0, sizeof(body.reserved)); return body; } static int bytesToRead; /* number of bytes to read from Web Server */ static int appServerSock = -1; /* Socket connected to FastCGI application, * used by AppServerReadHandler and * AppServerWriteHandler. */ static Buffer fromAS; /* Bytes read from the FCGI application server. */ static FCGI_Header header; /* Header of the current record. Is global * since read may return a partial header. */ static int headerLen = 0; /* Number of valid bytes contained in header. * If headerLen < sizeof(header), * AppServerReadHandler is reading a record header; * otherwise it is reading bytes of record content * or padding. */ static int contentLen; /* If headerLen == sizeof(header), contentLen * is the number of content bytes still to be * read. */ static int paddingLen; /* If headerLen == sizeof(header), paddingLen * is the number of padding bytes still * to be read. */ static int requestId; /* RequestId of the current request. * Set by main. */ static FCGI_EndRequestBody erBody; static int readingEndRequestBody = FALSE; /* If readingEndRequestBody, erBody contains * partial content: contentLen more bytes need * to be read. */ static int exitStatus = 0; static int exitStatusSet = FALSE; static int stdinFds[3]; /* *---------------------------------------------------------------------- * * FCGIexit -- * * FCGIexit provides a single point of exit. It's main use is for * application debug when porting to other operating systems. * *---------------------------------------------------------------------- */ static void FCGIexit(int exitCode) { if(appServerSock != -1) { OS_Close(appServerSock, TRUE); appServerSock = -1; } OS_LibShutdown(); exit(exitCode); } #undef exit #define exit FCGIexit /* *---------------------------------------------------------------------- * * AppServerReadHandler -- * * Reads data from the FCGI application server and (blocking) * writes all of it to the Web server. Exits the program upon * reading EOF from the FCGI application server. Called only when * there's data ready to read from the application server. * *---------------------------------------------------------------------- */ static void AppServerReadHandler(ClientData dc, int bytesRead) { int count, outFD; char *ptr; /* Touch unused parameters to avoid warnings */ dc = NULL; assert(fcgiReadPending == TRUE); fcgiReadPending = FALSE; count = bytesRead; if(count <= 0) { if(count < 0) { exit(OS_Errno); } if(headerLen > 0 || paddingLen > 0) { exit(FCGX_PROTOCOL_ERROR); } if(appServerSock != -1) { OS_Close(appServerSock, TRUE); appServerSock = -1; } /* * XXX: Shouldn't be here if exitStatusSet. */ exit((exitStatusSet) ? exitStatus : FCGX_PROTOCOL_ERROR); } fromAS.stop = fromAS.next + count; while(fromAS.next != fromAS.stop) { /* * fromAS is not empty. What to do with the contents? */ if(headerLen < sizeof(header)) { /* * First priority is to complete the header. */ count = GetPtr(&ptr, sizeof(header) - headerLen, &fromAS); assert(count > 0); memcpy(&header + headerLen, ptr, count); headerLen += count; if(headerLen < sizeof(header)) { break; } if(header.version != FCGI_VERSION_1) { exit(FCGX_UNSUPPORTED_VERSION); } if((header.requestIdB1 << 8) + header.requestIdB0 != requestId) { exit(FCGX_PROTOCOL_ERROR); } contentLen = (header.contentLengthB1 << 8) + header.contentLengthB0; paddingLen = header.paddingLength; } else { /* * Header is complete (possibly from previous call). What now? */ switch(header.type) { case FCGI_STDOUT: case FCGI_STDERR: /* * Write the buffered content to stdout or stderr. * Blocking writes are OK here; can't prevent a slow * client from tying up the app server without buffering * output in temporary files. */ count = GetPtr(&ptr, contentLen, &fromAS); contentLen -= count; if(count > 0) { outFD = (header.type == FCGI_STDOUT) ? STDOUT_FILENO : STDERR_FILENO; if(OS_Write(outFD, ptr, count) < 0) { exit(OS_Errno); } } break; case FCGI_END_REQUEST: if(!readingEndRequestBody) { if(contentLen != sizeof(erBody)) { exit(FCGX_PROTOCOL_ERROR); } readingEndRequestBody = TRUE; } count = GetPtr(&ptr, contentLen, &fromAS); if(count > 0) { memcpy(&erBody + sizeof(erBody) - contentLen, ptr, count); contentLen -= count; } if(contentLen == 0) { if(erBody.protocolStatus != FCGI_REQUEST_COMPLETE) { /* * XXX: What to do with FCGI_OVERLOADED? */ exit(FCGX_PROTOCOL_ERROR); } exitStatus = (erBody.appStatusB3 << 24) + (erBody.appStatusB2 << 16) + (erBody.appStatusB1 << 8) + (erBody.appStatusB0 ); exitStatusSet = TRUE; readingEndRequestBody = FALSE; } break; case FCGI_GET_VALUES_RESULT: /* coming soon */ case FCGI_UNKNOWN_TYPE: /* coming soon */ default: exit(FCGX_PROTOCOL_ERROR); } if(contentLen == 0) { if(paddingLen > 0) { paddingLen -= GetPtr(&ptr, paddingLen, &fromAS); } /* * If we've processed all the data and skipped all the * padding, discard the header and look for the next one. */ if(paddingLen == 0) { headerLen = 0; } } } /* headerLen >= sizeof(header) */ } /*while*/ ScheduleIo(); } static Buffer fromWS; /* Buffer for data read from Web server * and written to FastCGI application. Used * by WebServerReadHandler and * AppServerWriteHandler. */ static int webServerReadHandlerEOF; /* TRUE iff WebServerReadHandler has read EOF from * the Web server. Used in main to prevent * rescheduling WebServerReadHandler. */ static void WriteStdinEof(void) { static int stdin_eof_sent = 0; if (stdin_eof_sent) return; *((FCGI_Header *)fromWS.stop) = MakeHeader(FCGI_STDIN, requestId, 0, 0); fromWS.stop += sizeof(FCGI_Header); stdin_eof_sent = 1; } /* *---------------------------------------------------------------------- * * WebServerReadHandler -- * * Non-blocking reads data from the Web server into the fromWS * buffer. Called only when fromWS is empty, no EOF has been * received from the Web server, and there's data available to read. * *---------------------------------------------------------------------- */ static void WebServerReadHandler(ClientData dc, int bytesRead) { /* Touch unused parameters to avoid warnings */ dc = NULL; assert(fromWS.next == fromWS.stop); assert(fromWS.next == &fromWS.buff[0]); assert(wsReadPending == TRUE); wsReadPending = FALSE; if(bytesRead < 0) { exit(OS_Errno); } FCGI_Header *header = (FCGI_Header *) &fromWS.buff[0]; *header = MakeHeader(FCGI_STDIN, requestId, bytesRead, 0); bytesToRead -= bytesRead; fromWS.stop = &fromWS.buff[sizeof(FCGI_Header) + bytesRead]; webServerReadHandlerEOF = (bytesRead == 0); if (bytesToRead <= 0) WriteStdinEof(); ScheduleIo(); } /* *---------------------------------------------------------------------- * * AppServerWriteHandler -- * * Non-blocking writes data from the fromWS buffer to the FCGI * application server. Called only when fromWS is non-empty * and the socket is ready to accept some data. * *---------------------------------------------------------------------- */ static void AppServerWriteHandler(ClientData dc, int bytesWritten) { int length = fromWS.stop - fromWS.next; /* Touch unused parameters to avoid warnings */ dc = NULL; assert(length > 0); assert(fcgiWritePending == TRUE); fcgiWritePending = FALSE; if(bytesWritten < 0) { exit(OS_Errno); } if((int)bytesWritten < length) { fromWS.next += bytesWritten; } else { fromWS.stop = fromWS.next = &fromWS.buff[0]; } ScheduleIo(); } /* * ScheduleIo -- * * This functions is responsible for scheduling all I/O to move * data between a web server and a FastCGI application. * * Results: * None. * * Side effects: * This routine will signal the ioEvent upon completion. * */ static void ScheduleIo(void) { int length; /* * Move data between standard in and the FastCGI connection. */ if(!fcgiWritePending && appServerSock != -1 && ((length = fromWS.stop - fromWS.next) != 0)) { if(OS_AsyncWrite(appServerSock, 0, fromWS.next, length, AppServerWriteHandler, (ClientData)(intptr_t)appServerSock) == -1) { FCGIexit(OS_Errno); } else { fcgiWritePending = TRUE; } } /* * Schedule a read from the FastCGI application if there's not * one pending and there's room in the buffer. */ if(!fcgiReadPending && appServerSock != -1) { fromAS.next = &fromAS.buff[0]; if(OS_AsyncRead(appServerSock, 0, fromAS.next, BUFFLEN, AppServerReadHandler, (ClientData)(intptr_t)appServerSock) == -1) { FCGIexit(OS_Errno); } else { fcgiReadPending = TRUE; } } /* * Schedule a read from standard in if necessary. */ if((bytesToRead > 0) && !webServerReadHandlerEOF && !wsReadPending && !fcgiWritePending && fromWS.next == &fromWS.buff[0]) { if(OS_AsyncReadStdin(fromWS.next + sizeof(FCGI_Header), BUFFLEN - sizeof(FCGI_Header), WebServerReadHandler, STDIN_FILENO)== -1) { FCGIexit(OS_Errno); } else { wsReadPending = TRUE; } } } /* *---------------------------------------------------------------------- * * FCGI_Start -- * * Starts nServers copies of FCGI application appPath, all * listening to a Unix Domain socket at bindPath. * *---------------------------------------------------------------------- */ static void FCGI_Start(char *bindPath, char *appPath, int nServers) { int listenFd, i; /* @@@ Should be able to pick up the backlog as an arg */ if((listenFd = OS_CreateLocalIpcFd(bindPath, 5)) == -1) { exit(OS_Errno); } if(access(appPath, X_OK) == -1) { fprintf(stderr, "%s is not executable\n", appPath); exit(1); } /* * Create the server processes */ for(i = 0; i < nServers; i++) { if(OS_SpawnChild(appPath, listenFd) == -1) { exit(OS_Errno); } } OS_Close(listenFd, TRUE); } /* *---------------------------------------------------------------------- * * FCGIUtil_BuildNameValueHeader -- * * Builds a name-value pair header from the name length * and the value length. Stores the header into *headerBuffPtr, * and stores the length of the header into *headerLenPtr. * * Side effects: * Stores header's length (at most 8) into *headerLenPtr, * and stores the header itself into * headerBuffPtr[0 .. *headerLenPtr - 1]. * *---------------------------------------------------------------------- */ static void FCGIUtil_BuildNameValueHeader( int nameLen, int valueLen, unsigned char *headerBuffPtr, int *headerLenPtr) { unsigned char *startHeaderBuffPtr = headerBuffPtr; ASSERT(nameLen >= 0); if (nameLen < 0x80) { *headerBuffPtr++ = (unsigned char) nameLen; } else { *headerBuffPtr++ = (unsigned char) ((nameLen >> 24) | 0x80); *headerBuffPtr++ = (unsigned char) (nameLen >> 16); *headerBuffPtr++ = (unsigned char) (nameLen >> 8); *headerBuffPtr++ = (unsigned char) nameLen; } ASSERT(valueLen >= 0); if (valueLen < 0x80) { *headerBuffPtr++ = (unsigned char) valueLen; } else { *headerBuffPtr++ = (unsigned char) ((valueLen >> 24) | 0x80); *headerBuffPtr++ = (unsigned char) (valueLen >> 16); *headerBuffPtr++ = (unsigned char) (valueLen >> 8); *headerBuffPtr++ = (unsigned char) valueLen; } *headerLenPtr = headerBuffPtr - startHeaderBuffPtr; } #define MAXARGS 16 static int ParseArgs(int argc, char *argv[], int *doBindPtr, int *doStartPtr, char *connectPathPtr, char *appPathPtr, int *nServersPtr, int *doDaemonPtr) { int i, x, err = 0, ac; char *tp1, *tp2, *av[MAXARGS]; FILE *fp; char line[BUFSIZ]; *doBindPtr = TRUE; *doStartPtr = TRUE; *connectPathPtr = '\0'; *appPathPtr = '\0'; *nServersPtr = 0; *doDaemonPtr = TRUE; for(i = 0; i < MAXARGS; i++) av[i] = NULL; for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { if(!strcmp(argv[i], "-f")) { if(++i == argc) { fprintf(stderr, "Missing command file name after -f\n"); return 1; } if((fp = fopen(argv[i], "r")) == NULL) { fprintf(stderr, "Cannot open command file %s\n", argv[i]); return 1; } ac = 1; while(fgets(line, BUFSIZ, fp)) { if(line[0] == '#') { continue; } if((tp1 = (char *) strrchr(line,'\n')) != NULL) { *tp1-- = 0; while(*tp1 == ' ' || *tp1 =='\t') { *tp1-- = 0; } } else { fprintf(stderr, "Line to long\n"); return 1; } tp1 = line; while(tp1) { if((tp2 = strchr(tp1, ' ')) != NULL) { *tp2++ = 0; } if(ac >= MAXARGS) { fprintf(stderr, "To many arguments, " "%d is max from a file\n", MAXARGS); exit(-1); } if((av[ac] = (char *)malloc(strlen(tp1)+1)) == NULL) { fprintf(stderr, "Cannot allocate %d bytes\n", (int)strlen(tp1)+1); exit(-1); } strcpy(av[ac++], tp1); tp1 = tp2; } } fclose(fp); err = ParseArgs(ac, av, doBindPtr, doStartPtr, connectPathPtr, appPathPtr, nServersPtr, doDaemonPtr); for(x = 1; x < ac; x++) { ASSERT(av[x] != NULL); free(av[x]); } return err; #ifdef _WIN32 } else if (!strcmp(argv[i], "-jitcgi")) { DebugBreak(); } else if (!strcmp(argv[i], "-dbgfcgi")) { putenv("DEBUG_FCGI=TRUE"); #endif } else if(!strcmp(argv[i], "-start")) { *doBindPtr = FALSE; } else if(!strcmp(argv[i], "-bind")) { *doStartPtr = FALSE; } else if(!strcmp(argv[i], "-connect")) { if(++i == argc) { fprintf(stderr, "Missing connection name after -connect\n"); err++; } else if (strlen(argv[i]) < MAXPATHLEN) { strcpy(connectPathPtr, argv[i]); } else { fprintf(stderr, "bind path too long (>=%d): %s\n", MAXPATHLEN, argv[i]); err++; } } else if(!strcmp(argv[i], "-supervise")) { *doDaemonPtr = FALSE; } else { fprintf(stderr, "Unknown option %s\n", argv[i]); err++; } } else if (*appPathPtr == '\0') { if (strlen(argv[i]) < MAXPATHLEN) { strcpy(appPathPtr, argv[i]); } else { fprintf(stderr, "bind path too long (>=%d): %s\n", MAXPATHLEN, argv[i]); err++; } } else if(isdigit((int)argv[i][0]) && *nServersPtr == 0) { *nServersPtr = atoi(argv[i]); if(*nServersPtr <= 0) { fprintf(stderr, "Number of servers must be greater than 0\n"); err++; } } else { fprintf(stderr, "Unknown argument %s\n", argv[i]); err++; } } if(*doStartPtr && *appPathPtr == 0) { fprintf(stderr, "Missing application pathname\n"); err++; } if(*connectPathPtr == 0) { fprintf(stderr, "Missing -connect \n"); err++; } else if(strchr(connectPathPtr, ':')) { /* * XXX: Test to see if we can use IP connect locally... This hack lets me test the ability to create a local process listening to a TCP/IP port for connections and subsequently connect to the app like we do for Unix domain and named pipes. if(*doStartPtr && *doBindPtr) { fprintf(stderr, " of form hostName:portNumber " "requires -start or -bind\n"); err++; } */ } if(*nServersPtr == 0) { *nServersPtr = 1; } return err; } void handle_shutdown(int s) { /* Kill our children processes */ signal(s, SIG_IGN); #ifndef _WIN32 kill(0, s); #endif exit(0); } int main(int argc, char **argv) { char **envp = environ; int count; FCGX_Stream *paramsStream; unsigned char headerBuff[8]; int headerLen, valueLen; char *equalPtr; FCGI_BeginRequestRecord beginRecord; int doBind, doStart, nServers, doDaemon; char appPath[MAXPATHLEN], bindPath[MAXPATHLEN]; int pid; if(ParseArgs(argc, argv, &doBind, &doStart, (char *) &bindPath, (char *) &appPath, &nServers, &doDaemon)) { fprintf(stderr, "Usage:\n" " cgi-fcgi -f , or\n" " cgi-fcgi -connect [] , or\n" " cgi-fcgi -start -connect [-supervise] [] , or\n" " cgi-fcgi -bind -connect ,\n" "where is either the pathname of a UNIX domain socket\n" "or (if -bind is given) a hostName:portNumber specification\n" "or (if -start is given) a :portNumber specification (uses local host).\n" "-supervise is for running with runit or daemontools.\n"); exit(1); } if(OS_LibInit(stdinFds)) { fprintf(stderr, "Error initializing OS library: %lu\n", (long unsigned int)OS_Errno); exit(0); } equalPtr = getenv("CONTENT_LENGTH"); if(equalPtr != NULL) { bytesToRead = atoi(equalPtr); } else { bytesToRead = 0; } #ifndef _WIN32 /* Become a process group leader */ setsid(); /* Register our signal handler */ signal(SIGHUP, handle_shutdown); #endif signal(SIGINT, handle_shutdown); signal(SIGTERM, handle_shutdown); if(doBind) { appServerSock = OS_FcgiConnect(bindPath); } if(doStart && (!doBind || appServerSock < 0)) { FCGI_Start(bindPath, appPath, nServers); if(!doBind) { #ifndef _WIN32 if(!doDaemon) { for(pid=nServers; pid != 0; pid--) { wait(0); } } #endif signal(SIGTERM, SIG_IGN); #ifndef _WIN32 kill(0, SIGTERM); #endif exit(0); } else { appServerSock = OS_FcgiConnect(bindPath); } } if(appServerSock < 0) { fprintf(stderr, "Could not connect to %s\n", bindPath); exit(OS_Errno); } /* * Set an arbitrary non-null FCGI RequestId */ requestId = 1; /* * XXX: Send FCGI_GET_VALUES */ /* * XXX: Receive FCGI_GET_VALUES_RESULT */ /* * Send FCGI_BEGIN_REQUEST (XXX: hack, separate write) */ beginRecord.header = MakeHeader(FCGI_BEGIN_REQUEST, requestId, sizeof(beginRecord.body), 0); beginRecord.body = MakeBeginRequestBody(FCGI_RESPONDER, FALSE); count = OS_Write(appServerSock, (char *)&beginRecord, sizeof(beginRecord)); if(count != sizeof(beginRecord)) { exit(OS_Errno); } /* * Send environment to the FCGI application server */ paramsStream = FCGX_CreateWriter(appServerSock, requestId, 8192, FCGI_PARAMS); for( ; *envp != NULL; envp++) { equalPtr = strchr(*envp, '='); if(equalPtr == NULL) { exit(1000); } valueLen = strlen(equalPtr + 1); FCGIUtil_BuildNameValueHeader( equalPtr - *envp, valueLen, &headerBuff[0], &headerLen); if(FCGX_PutStr((char *) &headerBuff[0], headerLen, paramsStream) < 0 || FCGX_PutStr(*envp, equalPtr - *envp, paramsStream) < 0 || FCGX_PutStr(equalPtr + 1, valueLen, paramsStream) < 0) { exit(FCGX_GetError(paramsStream)); } } FCGX_FClose(paramsStream); FCGX_FreeStream(¶msStream); /* * Perform the event loop until AppServerReadHander sees FCGI_END_REQUEST */ fromWS.stop = fromWS.next = &fromWS.buff[0]; webServerReadHandlerEOF = FALSE; OS_SetFlags(appServerSock, O_NONBLOCK); if (bytesToRead <= 0) WriteStdinEof(); ScheduleIo(); while(!exitStatusSet) { /* * NULL = wait forever (or at least until there's something * to do. */ OS_DoIo(NULL); } if(exitStatusSet) { FCGIexit(exitStatus); } else { FCGIexit(999); } return 0; } fcgi2-2.4.5/cgi-fcgi/cgi-fcgi.mak000066400000000000000000000102471477724357700163610ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on cgifcgi.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "cgifcgi.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\cgi-fcgi\Release INTDIR=.\..\cgi-fcgi\Release # Begin Custom Macros OutDir=.\..\cgi-fcgi\Release # End Custom Macros ALL : "$(OUTDIR)\cgi-fcgi.exe" CLEAN : -@erase "$(INTDIR)\cgi-fcgi.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\cgi-fcgi.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\cgifcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\cgifcgi.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /out:"$(OUTDIR)\cgi-fcgi.exe" /libpath:"..\libfcgi\Release" LINK32_OBJS= \ "$(INTDIR)\cgi-fcgi.obj" \ "..\libfcgi\Release\libfcgi.lib" "$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\../cgi-fcgi/Debug INTDIR=.\../cgi-fcgi/Debug # Begin Custom Macros OutDir=.\../cgi-fcgi/Debug # End Custom Macros ALL : "$(OUTDIR)\cgi-fcgi.exe" "$(OUTDIR)\cgifcgi.bsc" CLEAN : -@erase "$(INTDIR)\cgi-fcgi.obj" -@erase "$(INTDIR)\cgi-fcgi.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\cgi-fcgi.exe" -@erase "$(OUTDIR)\cgifcgi.bsc" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\cgifcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\cgifcgi.bsc" BSC32_SBRS= \ "$(INTDIR)\cgi-fcgi.sbr" "$(OUTDIR)\cgifcgi.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /profile /debug /out:"$(OUTDIR)\cgi-fcgi.exe" /libpath:"..\libfcgi\Debug" LINK32_OBJS= \ "$(INTDIR)\cgi-fcgi.obj" \ "..\libfcgi\Debug\libfcgi.lib" "$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF "..\cgi-fcgi\cgi-fcgi.c" : \ "..\include\fastcgi.h"\ "..\include\fcgi_config.h"\ "..\include\fcgiapp.h"\ "..\include\fcgimisc.h"\ "..\include\fcgios.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE="..\cgi-fcgi\cgi-fcgi.c" !IF "$(CFG)" == "release" "$(INTDIR)\cgi-fcgi.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\cgi-fcgi.obj" "$(INTDIR)\cgi-fcgi.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF fcgi2-2.4.5/configure.ac000077500000000000000000000057001477724357700150360ustar00rootroot00000000000000dnl $Id: configure.in,v 1.27 2003/06/22 02:15:10 robs Exp $ dnl dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during the build dnl to configure the system for the local environment. AC_INIT([fcgi], [2.4.3]) AM_INIT_AUTOMAKE([1.11 foreign]) AC_CONFIG_MACRO_DIR([m4]) AM_CONFIG_HEADER(fcgi_config.h) LT_INIT([win32-dll]) AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LIBTOOL AC_PROG_CXX AC_LANG([C++]) dnl autoconf defaults CXX to 'g++', so its unclear whether it exists/works AC_MSG_CHECKING([whether $CXX works]) AC_TRY_COMPILE([#include ], [std::cout << "ok";], [AC_MSG_RESULT(yes) LIBFCGIXX=libfcgi++.la ECHO_CPP=echo-cpp${EXEEXT} AC_MSG_CHECKING([whether cin has a streambuf assignment operator]) AC_TRY_COMPILE([#include ], [cin = static_cast(0);], [AC_MSG_RESULT(yes) AC_DEFINE([HAVE_IOSTREAM_WITHASSIGN_STREAMBUF], [1], [Define if cin/cout/cerr has a streambuf assignment operator])], [AC_MSG_RESULT(no)]) AC_MSG_CHECKING([whether char_type is defined in the context of streambuf]) AC_TRY_COMPILE([#include ], [class fcgi_streambuf : public std::streambuf { char_type ct; }], [AC_MSG_RESULT(yes) AC_DEFINE([HAVE_STREAMBUF_CHAR_TYPE], [1], [Define if char_type is defined in the context of streambuf])], [AC_MSG_RESULT(no)])], [AC_MSG_RESULT(no)]) AC_SUBST(LIBFCGIXX) AC_SUBST(ECHO_CPP) AC_LANG([C]) AC_CHECK_LIB([nsl], [gethostbyname]) AC_CHECK_LIB([socket], [socket]) ACX_PTHREAD([THREADED=threaded${EXEEXT}]) AC_SUBST([THREADED]) FCGI_COMMON_CHECKS AC_REPLACE_FUNCS([strerror]) AC_C_INLINE AC_CANONICAL_HOST AS_CASE([$host_os], [*mingw*], [SYSTEM=win32], [SYSTEM=unix] ) AC_SUBST([SYSTEM]) AS_CASE([$host_os], [*mingw*], [EXTRA_LIBS=-lws2_32], [EXTRA_LIBS=] ) AC_SUBST([EXTRA_LIBS]) AC_PROG_CC_WARNINGS AC_ARG_ENABLE([examples], [AS_HELP_STRING([--disable-examples], [Disable examples])], [enable_examples=$enableval], [enable_examples=yes]) AM_CONDITIONAL([HAVE_EXAMPLES], [test x"$enable_examples" = xyes]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir=DIR], [pkgconfig files])], [pkgconfigdir=$withval], [pkgconfigdir="\${libdir}/pkgconfig"]) AC_SUBST([pkgconfigdir], [$pkgconfigdir]) AC_CONFIG_FILES([Makefile cgi-fcgi/Makefile include/Makefile libfcgi/Makefile examples/Makefile fcgi.pc fcgi++.pc]) AC_OUTPUT fcgi2-2.4.5/doc/000077500000000000000000000000001477724357700133105ustar00rootroot00000000000000fcgi2-2.4.5/doc/.gitignore000066400000000000000000000000271477724357700152770ustar00rootroot00000000000000input.html TidyOut.log fcgi2-2.4.5/doc/FCGI_Accept.3000066400000000000000000000121061477724357700153630ustar00rootroot00000000000000NAME FCGI_Accept, FCGI_ToFILE, FCGI_ToFcgiStream - fcgi_stdio compatibility library SYNOPSIS #include "fcgi_stdio.h" int FCGI_Accept(void); FILE * FCGI_ToFILE(FCGI_FILE *); FCGI_Stream * FCGI_ToFcgiStream(FCGI_FILE *); DESCRIPTION The FCGI_Accept function accepts a new request from the HTTP server and creates a CGI-compatible execution environment for the request. If the application was invoked as a CGI program, the first call to FCGI_Accept is essentially a no-op and the second call returns -1. This causes a correctly coded FastCGI Responder application to run a single request and exit, giving CGI behavior. If the application was invoked as a FastCGI server, the first call to FCGI_Accept indicates that the application has completed its initialization and is ready to accept its first request. Subsequent calls to FCGI_Accept indicate that the application has completed processing its current request and is ready to accept a new request. An application can complete the current request without accepting a new one by calling FCGI_Finish(3); later, when ready to accept a new request, the application calls FCGI_Accept. In completing the current request, FCGI_Accept may detect errors, e.g. a broken pipe to a client who has disconnected early. FCGI_Accept ignores such errors. An application that wishes to handle such errors should explicitly call fclose(stderr), then fclose(stdout); an EOF return from either one indicates an error. If the environment variable FCGI_WEB_SERVER_ADDRS is set when FCGI_Accept is called, it should contain a comma-separated list of IP addresses. Each IP address is written as four decimal numbers in the range [0..255] separated by decimal points. (nslookup(8) translates the more familiar symbolic IP hostname into this form.) So one legal binding for this variable is FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71 FCGI_Accept checks the peer IP address of each new connection for membership in the list. If the check fails (including the possibility that the connection didn't use TCP/IP transport), FCGI_Accept closes the connection and accepts another one (without returning in between). After accepting a new request, FCGI_Accept assigns new values to the global variables stdin, stdout, stderr, and environ. After FCGI_Accept returns, these variables have the same interpretation as on entry to a CGI program. FCGI_Accept frees any storage allocated by the previous call to FCGI_Accept. This has important consequences: DO NOT retain pointers to the environ array or any strings contained in it (e.g. to the result of calling getenv(3)), since these will be freed by the next call to FCGI_Finish or FCGI_Accept. DO NOT use setenv(3) or putenv(3) to modify the environ array created by FCGI_Accept, since this will either leak storage or cause the next call to FCGI_Finish or FCGI_Accept to free storage that should not be freed. If your application needs to use setenv or putenv to modify the environ array, it should follow this coding pattern: char **savedEnviron, **requestEnviron; int acceptStatus; savedEnviron = environ; acceptStatus = FCGI_Accept(); requestEnviron = environ; environ = savedEnviron; if(acceptStatus >= 0 && !FCGX_IsCGI()) { /* * requestEnviron points to name-value pairs in * storage allocated by FCGI_Accept. OK to read, * not OK to retain pointers -- make copies instead. */ } /* * OK to do setenv or putenv, but beware of storage leaks! */ In addition to the standard CGI environment variables, the environment variable FCGI_ROLE is always set to the role of the current request. The roles currently defined are RESPONDER, AUTHORIZER, and FILTER. In the FILTER role, the additional variables FCGI_DATA_LENGTH and FCGI_DATA_LAST_MOD are also defined. See the manpage FCGI_StartFilterData(3) for complete information. The macros FCGI_ToFILE and FCGI_ToFcgiStream are provided to allow escape to native functions that use the types FILE or FCGI_Stream. In the case of FILE, functions would have to be separately compiled, since fcgi_stdio.h replaces the standard FILE with FCGI_FILE. RETURN VALUES 0 for successful call, -1 for error (application should exit). SEE ALSO FCGI_Finish(3) FCGI_StartFilterData(3) FCGI_SetExitStatus(3) cgi-fcgi(1) nslookup(8) HISTORY Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. $Id: FCGI_Accept.3,v 1.1.1.1 1997/09/16 15:36:25 stanleyg Exp $ fcgi2-2.4.5/doc/FCGI_Finish.3000066400000000000000000000025501477724357700154060ustar00rootroot00000000000000NAME FCGI_Finish - fcgi_stdio compatibility library SYNOPSIS #include "fcgi_stdio.h" void FCGI_Finish(void); DESCRIPTION The FCGI_Finish function finishes the current request from the HTTP server. The current request was started by the most recent call to FCGI_Accept(3). FCGI_Finish allows an application to interleave other activities with the processing of requests. In an extreme case, an application would call FCGI_Finish to complete the current request before exiting, e.g. to reclaim leaked storage. In completing the current request, FCGI_Finish may detect errors, e.g. a broken pipe to a client who has disconnected early. FCGI_Finish ignores such errors. An application that wishes to handle such errors should explicitly call fclose(stderr), then fclose(stdout); an EOF return from either one indicates an error. FCGI_Finish frees any storage allocated by the most recent call to FCGI_Accept. See FCGI_Accept(3) for warnings against retaining pointers to this storage. SEE ALSO FCGI_Accept(3) HISTORY Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. $Id: FCGI_Finish.3,v 1.1.1.1 1997/09/16 15:36:25 stanleyg Exp $ fcgi2-2.4.5/doc/FCGI_SetExitStatus.3000066400000000000000000000014661477724357700167640ustar00rootroot00000000000000NAME FCGI_SetExitStatus - fcgi_stdio compatibility library SYNOPSIS #include "fcgi_stdio.h" void FCGI_SetExitStatus(int status); DESCRIPTION Sets the exit status for the current FastCGI request. The exit status is the status code the request would have exited with, had the request been run as a CGI program. You can call FCGI_SetExitStatus several times during a request; the last call before the request ends determines the value. SEE ALSO FCGI_Accept(3) FCGI_StartFilterData(3) cgi-fcgi(1) HISTORY Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. $Id: FCGI_SetExitStatus.3,v 1.1.1.1 1997/09/16 15:36:25 stanleyg Exp $ fcgi2-2.4.5/doc/FCGI_StartFilterData.3000066400000000000000000000034371477724357700172300ustar00rootroot00000000000000NAME FCGI_StartFilterData - fcgi_stdio compatibility library SYNOPSIS #include "fcgi_stdio.h" int FCGI_StartFilterData(void); DESCRIPTION Enables a FastCGI Filter application to begin reading its filter input data from stdin. In order to call FCGI_StartFilterData, the FastCGI application should have been invoked in the Filter role (getenv("FCGI_ROLE") == "FILTER"), and should have read stdin to EOF, consuming the entire FCGI_STDIN data stream. The call to FCGI_StartFilterData positions stdin at the start of FCGI_DATA. If the preconditions are not met (e.g. the application has not read stdin to EOF), FCGI_StartFilterData returns a negative result, and the application will get EOF on attempts to read from stdin. The application can determine the number of bytes available on FCGI_DATA by performing atoi(getenv("FCGI_DATA_LENGTH")). If fewer than this many bytes are delievered on stdin after calling FCGI_StartFilterData, the application should perform an application-specific error response. If the application normally makes an update, most likely it should abort the update. The application can determine last modification time of the filter input data by performing getenv("FCGI_DATA_LAST_MOD"). This allows applications to perform caching based on last modification time. RETURN VALUES 0 for successful call, < 0 for error. SEE ALSO FCGI_Accept(3) FCGI_SetExitStatus(3) cgi-fcgi(1) HISTORY Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. $Id: FCGI_StartFilterData.3,v 1.1.1.1 1997/09/16 15:36:26 stanleyg Exp $ fcgi2-2.4.5/doc/cgi-fcgi.1000066400000000000000000000104221477724357700150410ustar00rootroot00000000000000NAME cgi-fcgi - bridge from CGI to FastCGI SYNOPSIS cgi-fcgi -f cmdPath cgi-fcgi -bind -connect connName cgi-fcgi -start -connect connName appPath [nServers] cgi-fcgi -connect connName appPath [nServers] DESCRIPTION cgi-fcgi is a CGI/1.1 program that communicates with an already-running FastCGI application in order to respond to an HTTP request. cgi-fcgi is also capable of starting a FastCGI application. When you invoke cgi-fcgi as cgi-fcgi -f cmdPath then cgi-fcgi opens the file at cmdPath and reads its arguments from that file. cgi-fcgi will skip lines that begin with the comment character #. The first non-comment line should contain valid arguments in one of the other three forms. The -f form of cgi-fcgi is designed for Unix systems whose exec(2) family of system calls supports the execution of command interpreter files. For instance, if a file with execute permission contains the text #! /bin/cgi-fcgi -f -connect /httpd/root/sock/app /httpd/root/bin/app the effect is the same as executing /bin/cgi-fcgi -connect /httpd/root/sock/app /httpd/root/bin/app When you invoke cgi-fcgi as cgi-fcgi -bind -connect connName the connName argument is either the path name of a Unix domain listening socket or a host:port pair. If connName contains a colon, it is assumed to be host:port. cgi-fcgi performs a connect(2) using connName. If the connect succeeds, cgi-fcgi forwards the CGI environment variables and stdin data to the FastCGI application, and forwards the stdout and stderr data from the application to cgi-fcgi's stdout (most likely connected to a Web server). When the FastCGI application signals the end of its response, cgi-fcgi flushes its buffers and exits, and the Web server completes the http response. When you invoke cgi-fcgi as cgi-fcgi -start -connect connName appPath [nServers] then cgi-fcgi performs the function of starting one or more FastCGI application processes. The connName argument specifies either the path name of the Unix domain listening socket that cgi-fcgi will create, or is "localhost:NNN" where NNN is the port number of the TCP/IP listening socket that cgi-fcgi will create on the local machine. (cgi-fcgi will not create processes on remote machines.) After cgi-fcgi creates the listening socket, it forks nServers copies of a process running the executable file appPath. If nServers is omitted, the effect is as if the value "1" had been specified. The processes share the single listening socket. When you invoke cgi-fcgi as cgi-fcgi -connect connName appPath [nServers] cgi-fcgi performs -bind and then, if necssary, performs -start and repeats the -bind. That is, cgi-fcgi first operates as if the command had been cgi-fcgi -bind -connect connName If the connect fails, cgi-fcgi tries cgi-fcgi -start -connect connName appPath [nServers] and finally retries cgi-fcgi -bind -connect connName In this form, cgi-fcgi does not support TCP/IP connections. ENVIRONMENT VARIABLES The usual CGI ones, but they are not interpreted by cgi-fcgi. SEE ALSO FGCI_accept(3) BUGS cgi-fcgi doesn't generate useful HTTP responses in case of error, and it generates no response at all when run as start-fcgi. On Digital UNIX 3.0 systems the implementation of Unix Domain sockets does not work when such sockets are stored on NFS file systems. Symptom: cgi-fcgi may core dump or may exit with status 38. Work-around: store sockets in local file systems (/tmp often works) or use TCP/IP. On AIX systems the implementation of listening sockets does not support socket sharing, and the standard FastCGI application libraries can't synchronize access to AIX listening sockets. Work-around: Don't use the nServers argument on AIX. HISTORY Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. $Id: cgi-fcgi.1,v 1.1.1.1 1997/09/16 15:36:26 stanleyg Exp $ fcgi2-2.4.5/doc/fastcgi-prog-guide/000077500000000000000000000000001477724357700167705ustar00rootroot00000000000000fcgi2-2.4.5/doc/fastcgi-prog-guide/ap_guida.htm000077500000000000000000000210111477724357700212510ustar00rootroot00000000000000 FastCGI Application Programmer's Guide - Index [Top] [Prev]

[Next] [Bottom]



Index

A

applications in Tcl 19
Authorizer applications 4
environment variables 6
stdin and stderr 4

C

C language, writing FastCGI applications in 11

E

environment variables
differences from CGI 6
returned from Authorizer applications 4
examples
responder application in C 13
responder application in perl 18
responder application in Tcl 20
exit status, of FastCGI application 24

F

FastCGI
19
applications in C 11
applications in Perl 17
differences from CGI 1
FCGI_DATA_LAST_MOD 23
FCGI_DATA_LENGTH 23
FCGI_DATA_LENGTH (in Filter applications) 6
FCGI_ROLE 6, 22
FCGI_SetExitStatus 24
FCGI_StartFilterData 22
fcgi_stdio library 11
location of 15
manpages for 21
FCGI_ToFcgiStream 22
FCGI_ToFILE 22
fcgiapp library 11
FILE_LAST_MOD (in Filter applications) 6
Filter applications
last modification time 23
reading from stdin 23

G

Great Circle (C garbage collector) 16

I

Initial Environment Variables 5

M

manpages 21
memory leaks 16

P

Perl
writing FastCGI applications in 17
Purify (for checking storage leaks) 16

R

response loop 5
in C 12
in Perl 17
in Tcl 19


[Top] [Prev]

[Next] [Bottom]



fcgi2-2.4.5/doc/fastcgi-prog-guide/ap_guide.htm000077500000000000000000000162221477724357700212650ustar00rootroot00000000000000 FastCGI Programmer's Guide - Table of Contents [Top] [Prev] [Next] [Bottom]

1. The Fast Common Gateway Interface 1

Advantages of FastCGI 1
Long-lived Applications 1
Separating Application and Server 2
FastCGI "Roles" 2
Writing FastCGI Applications 4
Code Structure 5
Initial Environment Variables 5
Per-Request Environment Variables 6
Building FastCGI Applications in C 6
Building FastCGI Applications in Perl 7
Building FastCGI Applications in Tcl 7
Implementation Details 7
The fcgi_stdio Library: I/O Compatibility 9
The fcgi_stdio Library: Binary compatibility 10

2. Developing FastCGI Applications in C 11

The I/O Libraries 11
Code Structure 12
Example 1: TinyFastCGI 12
Example 2: Prime Number Generator 13
Building 15
Memory Leaks 16

3. Developing FastCGI Applications in Perl 17

Getting Started 17
Example: TinyFastCGI 18

4. Developing FastCGI Applications in Tcl 19

Getting Started 19
Example: TinyFastCGI 20

A. FastCGI Reference Pages 21

FCGI_Accept (3) 21
Name 21
Synopsis 21
Description 21
Return Values 22
FCGI_StartFilterData (3) 22
Name 22
Synopsis 22
Description 23
Return Values 23
Example 23
FCGI_SetExitStatus(3) 24
Name 24
Synopsis 24
Description 24


[Top] [Prev] [Next] [Bottom]

fcgi2-2.4.5/doc/fastcgi-prog-guide/apaman.htm000077500000000000000000000224641477724357700207520ustar00rootroot00000000000000 FCGI_Accept(2) Man Page [Top] [Prev] [Next] [Bottom]

A FastCGI
Reference Pages

This appendix contains reference pages for the following FastCGI routines from the fcgi_stdio library:



  • FCGI_Accept
  • FCGI_Start_Filter_Data
  • FCGI_SetExitStatus

FCGI_Accept (3)

Name

FCGI_Accept, FCGI_ToFILE, FCGI_ToFcgiStream

- fcgi_stdio compatibility library



Synopsis

#include <fcgi_stdio.h>

int
FCGI_Accept(void);
FILE *
FCGI_ToFILE(FCGI_FILE *);
FCGI_Stream *
FCGI_ToFcgiStream(FCGI_FILE *);

Description

The FCGI_Accept function accepts a new request from the HTTP server and creates a CGI-compatible execution environment for the request.

If the application was invoked as a CGI program, the first call to FCGI_Accept is essentially a no-op and the second call returns -1. This causes a correctly coded FastCGI application to run a single request and exit, giving CGI behavior.

If the application was invoked as a FastCGI server, the first call to FCGI_Accept indicates that the application has completed its initialization and is ready to accept its first request. Subsequent calls to FCGI_Accept indicate that the application has completed processing its current request and is ready to accept a new request.

In completing the current request, FCGI_Accept may detect errors, such as a broken pipe to a client who has disconnected early. FCGI_Accept ignores such errors. An application that wishes to handle such errors should explicitly call fclose(stderr), then fclose(stdout); an EOF return from either one indicates an error.

After accepting a new request, FCGI_Accept assigns new values to the global variables stdin, stdout, stderr, and environ. After FCGI_Accept returns, these variables have the same interpretation as on entry to a CGI program.

In addition to the standard CGI environment variables, the environment variable FCGI_ROLE is always set to the role of the current request. The roles currently defined are RESPONDER, AUTHORIZER, and FILTER.

In the FILTER role, the additional variables FCGI_DATA_LENGTH and FCGI_DATA_LAST_MOD are also defined. See FCGI_StartFilterData(3) for complete information.

The macros FCGI_ToFILE and FCGI_ToFcgiStream are provided to allow escape to native functions that use the types FILE or FCGI_Stream. In the case of FILE, functions would have to be separately compiled, since fcgi_stdio.h replaces the standard FILE with FCGI_FILE.



Return Values

0 for successful call, -1 for error (application should exit).



FCGI_StartFilterData (3)

Name

FCGI_StartFilterData

-fcgi_stdio compatibility library



Synopsis

#include <fcgi_stdio.h>

int FCGI_StartFilterData(void)

Description

Enables a FastCGI Filter application to begin reading its filter input data from stdin.

In order to call FCGI_StartFilterData, the FastCGI application should have been invoked in the filter role (getenv("FCGI_ROLE") == "FILTER"), and should have read stdin to EOF, consuming the entire FCGI_STDIN data stream. The call to FCGI_StartFilterData positions stdin at the start of FCGI_DATA.

If the preconditions are not met (e.g., the application has not read stdin to EOF), FCGI_StartFilterData returns a negative result, and the application will get EOF on attempts to read from stdin.

The application can determine the number of bytes available on FCGI_DATA by performing atoi(getenv("FCGI_DATA_LENGTH"). If fewer than this many bytes are delivered on stdin after calling FCGI_StartFilterData, the application should perform an application-specific error response. If the application normally makes an update, most likely it should abort the update.

The application can determine last modification time of the filter input data by performing getenv("FCGI_DATA_LAST_MOD"). This allows applications to perform caching based on last modification time.



Return Values

Returns 0 on success and a negative integer on failure.



Example

The following example reads in all the client data, but ignores it. Then, the code calls FCGI_StartFilterData. Finally, the code reads in the file to be filtered and simply echos it back to the client.



while (FCGI_Accept() >= 0) {

...

 /* Read data passed by client. */

  while (getchar () != OF) 

{

}



 /* Adjust standard input stream. */

  status = FCGI_StartFilterData();



 /* Read in filter data and echo it back to client. */

  while ((len = fread(tempBuffer, 1, 1024, stdin)) > 0) 

    fwrite(tempBuffer, 1, len, stdout);



} /* End FCGI_Accept loop */

FCGI_SetExitStatus(3)

Name

FCGI_SetExitStatus

- fcgi_stdio compatibility library



Synopsis

#include <fcgi_stdio.h>

void FCGI_SetExitStatus(int status);

Description

Sets the exit status for the current FastCGI request. The exit status is the status code the request would have exited with, had the request been run as a CGI program.

You can call FCGI_SetExitStatus several times during a request; the last call before the request ends determines the value.



[Top] [Prev] [Next] [Bottom]

fcgi2-2.4.5/doc/fastcgi-prog-guide/ch1inta1.gif000077500000000000000000000160321477724357700210740ustar00rootroot00000000000000GIF89a||ɋ}}@@>>``鹹}}}yyy0ssseee]]][[[WWWMMMCCC???999333///---?+++ ?ʲ@~~~|||tttpppnnnlll```^^^ZZZXXXVVVRRRF^FLLLHHHDDD@@@>>>>>:::444000...,,,***&&&@"""0 Þ ľ\Nf)/ľ<4 < I @)<4\@ x PP@Pxd`vIPv.K@@>1Omvlbvx@hldxWllDr֛l $T\(l,4ll,Hl6{<C S\E4RGEN|hPtg d+>@\P̔ 80x# #4  <\#hd |\!X,H*\ȰÇ#Jt>&jȱǏ1j!F|ɲ˗0cJH8sLƇNcC|Ĩɴӧ Dd%Xj5+^ Q I4Xf+0`kUװ|eխjŊÈ ½WE`<'9Cs9%D$͎}:3Ш:2N&WysgE+_lƸQ.}{z7k  ʁWcnk߳,q_wsӓ'|'`G}wRY'P|{񷒂U߀fhq\ Dڇ}xgQfjQƚkeFř8bj.cX`'ʦV]|)!@6Ly[1%Bm ԖZ} %bEfU嗔d*֕U)Nʵx|矀*蠄j衈&袌6裐F*)Ga:駢!hrV:뭸G+Nz*,atkS&[ٰ̺:fZDRW؆{kD@\a.$a¼))cGAxEʄİ^ oLT ^qrv@`rEaH,ʮڬ,y3L @*;ܲυX_n gr<7uHG`Db0 uYAg @n%c{s D2;儿M72*wnLlM?BqG7냮^zMӾ:;CN9Nj9/}ASO3<9}Eb|=ڗoagdms?x4gaݎpSr?uO[{W8m/w`|w4 lXtS A v߻L ټ',$Ff/d {Ӫ`!xx-v8ܞ]vtnxK% #BD%.aH)Rg!ljN]btU>3.}7d# HE1/McF~33kRB/ ׈Dd!GEz= 91M,an1Ӎ҅ O2BWB1$h*d! &77d*+J ?Oq.ɂǬ/\)VAanp#gɞȺ(FOvH:s@1 L"|rd"nE9rTKuG(=FO*li^jԡ4ݎMOStTl7e Ԡ2g+ z4SMyqS]Tzդ[hWcetT,cUQ^WAnu6жq \i ʄ _gֆj'2ւU 6AXfV! M( 'sfV0 %r.p .p/Pw²CY;|#hX1_,'HzkpŹ$HY W搬IkVꔓ Boz*"XUks QLp*Z ʰ7<Issq}\VvP`Y=QO5Z喊E8Bt.0A66 &V,:14ٖdG(-ƥt+nb~L17"棔]feyFm\H:{n;QB !rd|5n\zgvfp L@ucp>/vUBň@7Dէ:Ϡڹ6˚_ukp׳O+"lq&f%Y͖ ,0Od% ﮽l.ڐvoZ%ȸb=C3ά˖%]ȸ˭1#7X)>0U:P0xt3,O;Pf vGpy1poj!8Q30"1w6w*9:it-b}wBs8|}5X9/|h'p1Pdi2{&iHHf{r3PmUutI;1,be:Bm?}+vg6ma*ӧ? y}VRZXL?*5.ͥ8v*_xb:؉y8`=/؈NDգ445W&2h`d{x5{BGMrf@ރ[37P?^xm[*aEu Owy*AUp^!Hch'0Uyh&3K#A8L7ˆ3+.LH~u80ptxw20`wG$OHcNK>NÊB݈Bu@lX*0pu$X*I[eL(Y@IuaS`'*‚J-L<Ԃi*A=*r\~w8V`?`lpw*Mwn.szrUY7yr5/`]"RS0x4fyHGA3D/P[VQ#W^7tpY`YITГOH#. pV@I©#.FR~w~ҹ^淕3޹RCӜ©t,$Op"@Yt'%9c@y[3%;(v2wIh^YNER>7#69'V2 #`)'5zssb3Љ+PmjV?K`82#049-0I:#cA` y2y1@UZA+ 63$ؙ1ҦXӃfu"!29&w|FQm^,=d>ɹ0l+}W9_?jtO~=^ߧRa뺾뼋 + ^~Q;۴Ў-^,{k묾"KsKu*LfK|EQN1}b||ˤ>l$H,J<] I'Y"\,X'f*6ºm%3$G@-# a7vbQ"HȑM.,rmM>pA-lY&l"u[[ɱa˩b|Ĩ1eL3ię#H GEPEE͞M$!SUby5kN0]~VX`\E զlվM\uޥV^}i} `j<\X1VZ/yoԫ%_N(f̝=ThҖ?_HZQaYڵm۽}\pōG\r͝?]tխ_Ǟ]vݽ>9=]6K 0 'C@B>0 x'4<0"(Z8 x ACEdɥCL 2m)%b Q 4ȣ,($c $a#S;j †ZO"G2%L8b2аa#$ɄC["Ƴ\2P4 j v#4R\H8Bc$@7I!QIILTGJG7|( >JL̔Eo0MC[3C=V0,C?JcSboP =cIm[u)}RK3}mSX==wuv^uhRXy ]\_ۄU .o6iH+10X==]TSʽ[;.+o0bxabey>=-99(i=58r]2P>Hem#zҎ%=aa=cH0(dq c3oXEQ Cg,il۫Z4m-H |Y|N͵Tw6󄜔:.峻 %|VJ\˂,jϡO=gSN5S JO8{}x.2>>:::666444222000...,,,(((&&&""" @0@\xDl d>\   7\ O$/оt!D,D:eVA+V:D1V+ʻӹLLTLT>\ccLc\e1L]:DD:LױGZL!~t H‹3jn&*1֏H>qIƗ0cLc1dtbgΝ5IIѣH*]ʴӧPJJիXjKweXq6;h5z-˶['i)1!GDӸ{%=8߿}PF+>Ǎy$Yl0/m,i,HvC?L/ӨT)!oh˄zYDGMnẘߋPhxxWS+ߕ@'?O\na4Xnq&={5,띵sOd Cyāe *`sٷI'x5!sJ %`X {yXdXeX!w`@I_U&&L6iJrM@I` r`{\r%9aݘRiiY"nZy$NIwzɡ'ޢc&' \U0b̦tU5Q€"uB%50D pb))U|ĢwOxXE$[D 5wTigd[,e4v-O<&LEZ;o"ek+pOXpϺ&e 굃0n4@ Uªۡ8NE;{#I ){q :&d"ɶ csS&dì`"H߳t:UPp@/ }B[.*uZ:CEb { rK5 pӚ{= ۆ{53Nes|ld9͟7{?xꟇ".\1ôw:ۮ;7dۭ^LX&I X-> j殕iADC}Qճ =w !߇ ( 4Ӎ}J8y @i>(Ѻ_5C @'_FBh%4a|@ 0D+6bDxH061*Z5܆ur#\R928mZkvٖAxcҢ8A e!aYl $>{.W,!%Ā&{dD'x0$njjIf* ldЂEGfantK`ؚGd nU!^/:V &E_X@T ?^_R 콇Z .Ε9ԉdPOnc*fC4O.XpHDt UAX? `ƣJI~]UK~t}HKzt"(K3?PCX5Qh9hY-hJֲ4H`%IEk*H800%[ avm>ګq}mgGH˵mmW۹ji즇 *()ZD8]; 2>@B$,#P 3>onwV'Z2|\ C H4F4X&sH "oB(ɰ႖oT,8,b7-Kg3j$'4F hL*1R.41xv'EVqtwAvRt!㪨QԬ=񢀺MzSC1_ #]3mG5bpۗU2zþB$i}Znq Mco=S &Ч{۴d{X5}^m_ }COkqr-DwpGp p! JjwMr]O7 F #X2 -Tr ,@09X =_7 94h 7H?;5 A Nu ACX E8>GW8Aӄ0Pѓ@0~*2v׆5 b;fhy8> @va =Vp_&Ay XFe7 %Wvƅ p'1'mM!@`+HVhG!yA2HtH8( }c!27(H͈  yfmA#e ᘊ (8wHH(6Hcx YMsC) ) ׉ǑzGђ.0294qs(( IG)#=~*i m0HJLٔMyb:YyAIx0@:0`A#/B])W8b ixP0R!`P~aШ05ȓ8 w|yֱdZQ!;Y irI X%وYiy IȘm R 4tyIS 1X8 1T>lxLpD@(t'fqٓњGѝp t  yY) )hɐṘ OIj c3% ' q㠖Y y\[ 02oI>!j-z@B:DZF:yQU/h{W ! +Zj6)*zh' P )+pGʠy=gI %WUoFCQ\ y `)!1 A a j 'Q*xzMҘXȤ`s:9( X l~yj {3*xJy*J=Z J2Zv0PxP*1݀j+YjJf:ꭜ0SD:yt7gZpcAi C fƚ=Nq{+-:hUrw { /.42;V8~EHA[.! ;qyJ.y^`;ʢF˲R:l۶XX:ZZ5N˳rH[K`gKiYqak=k ?˵v[s4 Y[,[`Xٷʺs{K l[s{ K2+; |Kû۹Ȼ{a;+˹"ؽZ*+K+H9PkΫ6{+ ϛm\`yg[G {PGb\|Js  JL<ë`n@,./1<ͫ…aLLh[ cC|v'l)LÆWOQ 79,$ê# U0|^`(HDl&=ܽNPm|T<6 !Z`) XeIy0#.p?2E|Ljnɗdc#j0lÇq˗q+ìj\:[̕Їfc\ˇp˅}  Ĺ0| σ ֌aJ Mh@|0A"=q*Cu<a͸٬҃|e7D|Q%>m䜒&m8|z~QْЩڹ -lՄ ņ׼AaG`Iq] ұif ) CN٠ڢH]J 0 qy 3,i V 5ۼ۾M ǭ~ ia<=Ιhk!:v]ٸ -N 2&.}ݠB|;=s<-pkoUz fyͽ } 4F lrȨ3-HuiW̽yyݵM N&( l#8߃蛅x\ ~H9䝬$^7^~ ̣;0 fɸP >f ѫ=❫~ꖒ`^>d๺.b~9np..{+N{p=S, ,(&pQMMnQoql~⩞..s~ұN.IƩN4mƎ ~W$fz' 3޾ 8>^~ab!n`x#`O zqtg߁?]=5p达07Gᐠw Z3I;ׂ] t=9}|-wmC~Z xEw/Ia޾N!w0꜕Zot 4u Tl@ mɡ.g؉ wYE}yCA)L-y[))vʫ@(N@fa_ڍ*&: d))y$!>/-oᩏ5Og[ WX֪ݪʟ=̭w!ACDHX@XXDsPX(H(I 0*:JZjzHiII;{kxYI)[ FastCGI Programmer's Guide - Chapter 1, The Fast Common Gateway Interface [Top] [Prev] [Next] [Bottom]

1 The Fast Common
Gateway Interface

The Fast Common Gateway Interface (FastCGI) is an enhancement to the existing CGI (Common Gateway Interface), which is a standard for interfacing external applications with Web servers.

FastCGI is a proposed open standard and we expect both free and commercial Web servers to support it. FastCGI is included in Open Market WebServer and Secure WebServer, versions 2.0 and greater.



Advantages of FastCGI

FastCGI extends and enhances the CGI model in several ways:



  • FastCGI enables applications to persist between client requests, eliminating application start up overhead and allowing the application to maintain state between client calls.
  • FastCGI enables applications to reside on remote systems (rather than having to reside on the same system as the Web server)
  • FastCGI enables additional flexibility in application functionality, with explicit support for applications that do client authentication and filtering of input.

Long-lived Applications

CGI applications are ephemeral and short-lived: each time a client requests a CGI application, the server asks the operating system to spawn a new CGI process. After the CGI process satisfies the request, the server kills it. The server spawns and subsequently kills a new process for each client request.

FastCGI applications are long-lived, and can persist between client calls. The server spawns the FastCGI process once and it continues to run and satisfy client requests until it is explicitly terminated. You can also ask the Web server to start multiple copies of a FastCGI application, if you expect that concurrent processing will improve the application's performance.

Long-lived applications have two important advantages over short-lived applications:



  • A short-lived application pays start up overhead on every request; a long-lived application spreads the overhead over many requests. For an application that has a heavy start up cost, such as opening a database, doing initialization on every call can be very inefficient. Reinitializing for every client is also very inefficient for Perl programs, where the interpreter reads through the entire program before executing any of it.
  • A long-lived application can cache information in memory between requests, allowing it to respond more quickly to later requests.

FastCGI is not the only way to get a long-lived application on the Web, however. For example, there are many existing search engines that are implemented as long-lived applications.

In most cases, these applications rely on customized Web servers. In other words, since most Web servers do not support long-lived applications, a programmer must code this support into a Web server. This approach requires a tremendous amount of work and also ties the application to a particular server.

Another way to get a long-lived application is to write code that calls routines from the Web server's API. This alternative involves a lot of extra coding, ties the application to a particular Web server, and introduces problems of maintainability, scalability, and security.

We believe that FastCGI is the most general and flexible strategy for building long-lived Web applications.



Separating Application and Server

CGI applications must run on the same node as the Web server; FastCGI applications can run on any node that can be reached from your Web server using TCP/IP protocols. For example, you might want to run the FastCGI application on a high-speed computer server or database engine, and run the Web server on a different node.



FastCGI "Roles"

CGI and FastCGI applications are effective ways to allow an application to act as an extension to the Web server. CGI provides no explicit support for different kinds of applications: under CGI, every application receives an HTTP request, does something with it, and generates an HTTP response. FastCGI provides explicit support for several common "roles" that applications can play.

The three roles supported by the WebServer 2.0 are:



  • Responder
  • Filter
  • Authorizer

Responder Applications

A responder application is the most basic kind of FastCGI application: it receives the information associated with an HTTP request and generates an HTTP response. Responder is the role most similar to traditional CGI programming, and most FastCGI applications are responders.



Filter Applications

A filter FastCGI application receives the information associated with an HTTP request, plus an extra stream of data from a file stored on the Web server, and generates a "filtered" version of the data stream as an HTTP response.

With filter applications, the system administrator maps a particular MIME-type to a particular filter FastCGI application. When a client requests a URL with that MIME-type, the Web server invokes the filter application, which processes the file at the specified URL and sends a response (usually HTML text) back to the client.

For example, suppose you write a filter FastCGI application that converts SGML text to HTML, and map the extension .sgml (MIME-type SGML) to your filter FastCGI application. Now, suppose that a user requests the following URL:



/www.aerjug.com/docs/chap1.sgml

Given this URL, the Web server passes chap1.sgml as input to your filter FastCGI application, which processes chap1.sgml and returns an HTML version of it to the requesting client.



Authorizer Applications

An authorizer FastCGI application receives the information in an HTTP request header and generates a decision whether to authorize the request.

To mark a FastCGI application as having the authorizer role, the system administrator names the application inside the server configuration file, using a directive called AuthorizeRegion. (See the Open Market Web Server manual for information on server configuration directives.)

When a client requests a URL that meets the AuthorizeRegion criteria, the Web server calls your authorizer FastCGI application. If your application grants authorization (by returning a response code of 200), the Web server resumes execution of commands in the AuthorizeRegion section. If your application denies authorization (by returning any other response code), the Web server stops processing subsequent commands in the AuthorizeRegion section, and returns the response from your FastCGI application to the client.

Authorizer applications can return headers containing environment variables. Other CGI or FastCGI programs accessing this request (including other authorizers) can access these environment variables. The headers must have the following format:



Variable-name: value

For example, the following header



Variable-AUTH_METHOD: database lookup

causes the environment variable AUTH_METHOD to be set to "database lookup" for this request. Other CGI or FastCGI applications running on this request can access the value of AUTH_METHOD.

Authorizer applications cannot successfully read from standard input. Any attempts to read from standard input result in an immediate EOF.

All data that authorizer applications write to standard error will get written to the traditional server error logs.



Writing FastCGI Applications

The work involved in writing a FastCGI application depends in large part on the I/O libraries that you use. This manual describes how to write FastCGI applications in terms of the Open Market libraries, which are available for C, Perl, and Tcl. FastCGI is an open standard and you are welcome to build your own libraries for other languages as well, but this manual focuses on building FastCGI applications in the context of the Open Market libraries.

In general, the goal of the libraries is to make the job of writing a FastCGI application as much like writing a CGI application as possible. For example, you use the same techniques for query string decoding, HTML output to stdout, use of environment variables, and so on. When you use our libraries, porting CGI applications to FastCGI is mostly a matter of restructuring the code to take advantage of FastCGI features and libraries.



Code Structure

The main task of converting a CGI program into a FastCGI program is separating the initialization code from the code that needs to run for each request. The structure should look something like this:



Initialization code

Start of response loop

   body of response loop

End of response loop

The initialization code is run exactly once, when the application is initialized. Initialization code usually performs time-consuming operations such as opening databases or calculating values for tables or bitmaps.

The response loop runs continuously, waiting for client requests to arrive. The loop starts with a call to FCGI_Accept, a routine in the FastCGI library. The FCGI_Accept routine blocks program execution until a client requests the FastCGI application. When a client request comes in, FCGI_Accept unblocks, runs one iteration of the response loop body, and then blocks again waiting for another client request. The loop terminates only when the system administrator or the Web server kills the FastCGI application.



Initial Environment Variables

When a FastCGI process starts up, it has not yet accepted a request, and therefore none of the CGI environment variables are set.

You set the initial environment of a FastCGI process started by the AppClass directive using the -initial-env option. The process would use this environment to configure its options and locate files or databases.

In FastCGI processes started by the AppClass directive with the -affinity option, the FCGI_PROCESS_ID variable is set in the initial environment (not in the environment of a request). FCGI_PROCESS_ID is a decimal number in the range 0 to N - 1 where N is the number of processes (argument to the -processes option to AppClass). The process would use FCGI_PROCESS_ID in conjunction with other variables to locate session-related files or databases during restart.



Per-Request Environment Variables

In general, FastCGI uses the same per-request environment variables as CGI, and you access the values of environment variables in FastCGI applications just as you would in CGI applications. The only differences are as follows:



  • In Authorizer FastCGI applications, the Web server unsets the PATH_INFO, PATH_TRANSLATED, and CONTENT_LENGTH variables.
  • In Filter FastCGI applications, the Web server sets two additional environment variables:
    • FILE_LAST_MOD: The Web server sets FILE_LAST_MOD to the date and time that filter input file was last modified. The format is the number of seconds since midnight (UTC), January 1, 1970.
    • FCGI_DATA_LENGTH: The application reads at most FCGI_DATA_LENGTH bytes from the data stream before receiving the end-of-stream indication.
  • FastCGI sets FCGI_ROLE for each request to RESPONDER, AUTHORIZER, or FILTER.

Building FastCGI Applications in C

The Software Development Toolkit that accompanies WebServer 2.0 contains two libraries, fcgi_stdio and fcgiapp, for building FastCGI applications in C.

The fcgi_stdio library implements our philosophy of making FastCGI applications similar to CGI applications, and provides full binary compatibility between FastCGI applications and CGI applications: you can run the same C binary as either CGI or FastCGI.

The fcgiapp library is more specific to FastCGI, and doesn't attempt the veneer of CGI.

We recommend that you use the fcgi_stdio library, and this manual describes the routines in that library. The documentation for the fcgiapp library is in the code in the development kit.



Building FastCGI Applications in Perl

To build FastCGI applications in Perl, you need a FastCGI-savvy version of Perl, plus the FastCGI extension to Perl. We build FastCGI-savvy versions of the Perl interpreter for several common platforms and make them available on our Website. For details and examples, see Chapter 3, "Developing FastCGI Applications in Perl," on page 17.



Building FastCGI Applications in Tcl

To build FastCGI applications in Tcl, you need a FastCGI-savvy version of Tcl. We build FastCGI-savvy versions of the Tcl interpreter for several common platforms and make them available on our Website. For details and examples, see Chapter 4, "Developing FastCGI Applications in Tcl," on page 19.



Implementation Details

The FastCGI application libraries are designed to shield you from the details of the FastCGI design. This section is designed for the curious reader who would like some low-level understanding. If you are not curious about the implementation, you can happily skip this section.

As shown in the following figure, CGI applications use the three standard POSIX streams (stdin, stdout, and stderr), plus environment variables, to communicate with an HTTP server.

error-file:TidyOut.log



Figure 1: Flow of Data in CGI

The fundamental difference between FastCGI and CGI is that FastCGI applications are long-lived, which means that the Web Server needs to rendezvous with a running application, rather than starting the application in order to explicitly communicate with it.

The FastCGI implementation basically creates a bidirectional connection between two processes that have no relationship. FastCGI uses a single connection for all the data associated with an application -- stdin, stdout, stderr, and environment variables. The data on the connection is encapsulated using a FastCGI protocol that allows stdin and the environment variables to share the same half connection (on the way in) and stdout and stderr to share the half connection (on the way out).

On the input side, the FastCGI application receives data on the connection, unpacks it to separate stdin from the environment variables and then invokes the application. On the output side, FastCGI wraps stdout and stderr with appropriate protocol headers, and sends the encapsulated data out to the server.

Since a FastCGI application does not always run on the same node as the HTTP server, we support two implementations of the connection: a stream pipe1, for communications on the same machine, and TCP streams, for communication when the client and the server are on different machines.

error-file:TidyOut.log



Figure 2: Flow of Data in FastCGI when server and application are on different machines

The fcgi_stdio Library: I/O Compatibility

The implementation for I/O compatibility is that the library fcgi_stdio.h contains macros to translate the types and procedures defined in stdio.h into the appropriate FastCGI calls. For example, consider a FastCGI program written in C containing the following line of code:



fprintf(stdout, "<H2>Aerobic Juggling</H2>/n");

fcgi_stdio.h

header file contains the macro



#define fprintf FCGI_fprintf

So the preprocessor translates the fprintf call into the following call:



FCGI_fprintf(stdout, "<H2>Aerobic Juggling</H2>/n");

FCGI_fprintf

takes the same arguments as fprintf.

The implementation of FCGI_fprintf tests the file to see if it is a normal C stream or a FastCGI stream, and calls the appropriate implementation.

The fcgi_stdio.h header file contains macros to translate calls to all ISO stdio.h routines (and all conventional Posix additions, such as fileno, fdopen, popen, and pclose) into their FastCGI equivalents.



The fcgi_stdio Library: Binary compatibility

The fcgi_stdio library provides full binary compatibility between FastCGI applications and CGI applications: you can run the same C binary as either CGI or FastCGI.

The implementation is in FCGI_Accept: the FCGI_Accept function tests its environment to determine whether the application was invoked as a CGI program or an FastCGI program. If it was invoked as a CGI program, the request loop will satisfy a single client request and then exit, producing CGI behavior.



[Top] [Prev] [Next] [Bottom]

1

UNIX Network Programming, W. Richard Stevens, 1990 Prentice-Hall, Section 7.9

fcgi2-2.4.5/doc/fastcgi-prog-guide/ch2c.htm000077500000000000000000000316621477724357700203340ustar00rootroot00000000000000 FastCGI Programmer's Guide - Chapter 2, Developing FastCGI Applications in C [Top] [Prev] [Next] [Bottom]

2 Developing FastCGI
Applications in C

This chapter explains how to code FastCGI applications in C and how to build them into executables.

If you are converting a CGI application into a FastCGI application, in many cases you will only need to add a few lines of code. For more complex applications, you may also need to rearrange some code.



The I/O Libraries

The FastCGI Software Development Kit that accompanies Open Market WebServer 2.0 includes I/O libraries to simplify the job of converting existing CGI applications to FastCGI or writing new FastCGI applications. There are two libraries in the kit: fcgi_stdio and fcgiapp. You must include one of these header files in your program:



  • fcgi_stdio.h
  • fcgiapp.h

The fcgi_stdio library is a layer on top of the fcgiapp library, and we recommend strongly that you use it, both for converting existing CGI applications and for writing new FastCGI applications. The fcgi_stdio library offers several advantages:



  • Simplicity: there are only 3 new API calls to learn
  • Familiarity: If you are converting a CGI application to FastCGI, you will find few changes between CGI and FastCGI. We designed our library to make the job of building a FastCGI application as similar as possible to that of building a FastCGI application: you use the same environment variables, same techniques for parsing query strings, the same I/O routines, and so on.
  • Convenience: the library provides full binary compatibility between CGI and FastCGI. That is, you can run the same binary as either CGI or FastCGI.

The fcgiapp library is more specific to FastCGI, without trying to provide the veneer of familiarity with CGI. This manual describes the fcgi_stdio library; the fcgiapp library is documented in the header files that accompany the development kit.



Code Structure

To structure code for FastCGI, you separate your code into two sections:



  • Initialization section, which is executed only once.
  • Response loop section, which gets executed every time the FastCGI script gets called.

A response loop typically has the following format:



while (FCGI_Accept() >= 0) {

# body of response loop

}

The FCGI_Accept blocks until a client request comes in, and then returns 0. If there is a system failure, or the system administrator terminates the process, Accept will return -1.

If the application was invoked as a CGI program, the first call to Accept returns 0 and the second always returns -1, producing CGI behavior. (See "FCGI_Accept (3)" on page 21 for details.)

Also note that the CGI world encourages small scripts, whereas FastCGI encourages combining scripts. You may choose to rethink the overall structure of your applications to take better advantage of FastCGI performance gains.



Example 1: TinyFastCGI

Here is a simple example of a responder FastCGI application written in C:

#include "fcgi_stdio.h" /* fcgi library; put it first*/
#include <stdlib.h> int count; void initialize(void) { count=0; } void main(void) { /* Initialization. */ initialize(); /* Response loop. */ while (FCGI_Accept() >= 0) { printf("Content-type: text/html\r\n" "\r\n" "<title>FastCGI Hello! (C, fcgi_stdio library)</title>" "<h1>FastCGI Hello! (C, fcgi_stdio library)</h1>" "Request number %d running on host <i>%s</i>\n", ++count, getenv("SERVER_HOSTNAME")); } }

Example 2: Prime Number Generator

Consider a responder application that generates the n-th prime number.

A CGI application would have no efficient way of solving this problem. For example, if the user asks for the 50,000th prime number, a CGI application would have to calculate the first prime number, then the second, and so on, up until the 50,000th. The application would then terminate, taking with it all its hard-earned calculations. If a client then asks for the 49,000th prime number, the server will have to spawn a new CGI application which will have to start calculating prime numbers from scratch.

FastCGI applications can be much more efficient at this sort of problem, since they can maintain state. A FastCGI application can calculate an extensive table of prime numbers in its initialization phase and then keep the table around indefinitely. Whenever a client requests a particular prime number, the response loop merely needs to look it up in the table.

Here is the code for the prime number example:



#include "fcgi_stdio.h"
#include <stdlib.h>
#include <string.h>

#define POTENTIALLY_PRIME 0
#define COMPOSITE 1
#define VALS_IN_SIEVE_TABLE 1000000
#define MAX_NUMBER_OF_PRIME_NUMBERS 78600 

/* All initialized to POTENTIALLY_PRIME */
long int  sieve_table[VALS_IN_SIEVE_TABLE]; 
long int  prime_table[MAX_NUMBER_OF_PRIME_NUMBERS];  
/* Use Sieve of Erastothenes method of building 
   a prime number table. */
void
initialize_prime_table(void)
{
 long int prime_counter=1;
 long int current_prime=2, c, d; 
  
  prime_table[prime_counter]=current_prime;

  while (current_prime < VALS_IN_SIEVE_TABLE)   {
   /* Mark off composite numbers. */
     for (c = current_prime; c <= VALS_IN_SIEVE_TABLE; 
          c += current_prime)  {
        sieve_table[c] = COMPOSITE;  
     }

   /* Find the next prime number. */
     for (d=current_prime+1; sieve_table[d] == COMPOSITE; d++); 
   /* Put the new prime number into the table. */ 
     prime_table[++prime_counter]=d; 
     current_prime=d;
  }
}


void main(void)
{
    char *query_string;
    long int n;

    initialize_prime_table();

    while(FCGI_Accept() >= 0) {
        /*
         * Produce the necessary HTTP header.
         */
        printf("Content-type: text/html\r\n"
               "\r\n");
        /*
         * Produce the constant part of the HTML document.
         */
        printf("<title>Prime FastCGI</title>\n"
               "<h1>Prime FastCGI</h1>\n");
        /*
         * Read the query string and produce the variable part
         * of the HTML document.
         */
        query_string = getenv("QUERY_STRING");
        if(query_string == NULL) {
            printf("Usage: Specify a positive number in the query string.\n");
        } else {
            query_string = strchr(query_string, `=') + 1;
            n = strtol(query_string);
            if(n < 1) {
                printf("The query string `%s' is not a positive number.\n",
                       query_string);
            } else if(n > MAX_NUMBER_OF_PRIME_NUMBERS) {
                printf("The number %d is too large for this program.\n", n);
            } else
                printf("The %ldth prime number is %ld.\n", n, prime_table[n]);
            }
        }
    } /* while FCGI_Accept */
}

This application has a noticeable start up cost while it initializes the table, but subsequent accesses are fast.



Building

This section explains how to build and debug FastCGI applications written in C.

The C preprocessor needs to know the location of the fcgi_stdio.h header file, which is at the following pathname:



$toolkit/include/fcgi_stdio.h

where $toolkit symbolizes the directory in which you have installed the Software Development Kit for FastCGI.

The linker needs to know the location of the libfcgi.a library file, which is at the following pathname:



$toolkit/libfcgi/libfcgi.a 

If your linker does not search the Berkeley socket library, then you must add linker directives to force this search.

We provide a sample application Makefile at the following pathname:



$toolkit/examples/Makefile

This Makefile contains the necessary rules and pathnames to build the C FastCGI applications accompanying the toolkit. To build all the applications, type:



$ ./configure
$ make

Memory Leaks

Memory leaks are seldom a problem in CGI programming because CGI applications rarely run long enough to be concerned with leaks. However, memory leaks can become a problem in FastCGI applications, particularly if each call to a popular FastCGI application causes additional memory to leak.

When converting to FastCGI, you can either use a tool such as Valgrind to discover and fix storage leaks, or you can run a C garbage collector such as Boehm-Demers-Weiser conservative garbage collector



[Top] [Prev] [Next] [Bottom]

fcgi2-2.4.5/doc/fastcgi-prog-guide/ch3perl.htm000077500000000000000000000114441477724357700210510ustar00rootroot00000000000000 FastCGI Programmer's Guide - Chapter 3, Developing FastCGI Applications in Perl [Top] [Prev] [Next] [Bottom]

3 Developing FastCGI
Applications in Perl

This chapter explains how to code FastCGI applications in Perl. Before you can build FastCGI applications in Perl, you must have a FastCGI-savvy version of the Perl interpreter. Open Market develops such Perl binaries for popular platforms and makes them available with our developer's kit.

The FastCGI-savvy binaries are extensions of standard Perl, and are intended to replace your existing Perl installation. There is no need to maintain two versions of Perl: the version that we supply will work fine when invoked from a shell or a CGI program. There are also directions in the developer's kit for how to make your own FastCGI-savvy Perl, if you need a version for some platform that we don't supply.

FastCGI is ideal for applications written in Perl, because it provides a huge performance gain. When you run a Perl script, the Perl interpreter analyzes the entire script before executing any of it. With FastCGI, you can factor out this initialization cost and pay it only once, making execution of the actual script much faster in response to client calls.



Getting Started

The first line of any Perl script typically specifies the pathname of the Perl interpreter itself. You must specify the pathname of a FastCGI-savvy Perl.

Next, you must tell Perl to load the FastCGI extension. To do so, place the following line near the beginning of every FastCGI script:



use FCGI;

Then, you have to divide FastCGI scripts into the following two sections:



  • Initialization section, which is executed only once.
  • Response loop section, which gets executed every time the FastCGI script gets called.

A response loop typically has the following format:



while (FCGI::accept >= 0) {

# body of response loop

}

The accept call returns 0 whenever a client requests the FastCGI script. Otherwise, the accept call returns -1.



Example: TinyFastCGI

Here is a simple example of a FastCGI application written in Perl:




#!fcgi-savvy-perl

use FCGI; # Imports the library; required line

# Initialization code

$cnt = 0;

# Response loop

while (FCGI::accept >= 0) {
  print "Content-type: text/html\r\n\r\n";
  print "<head>\n<title>FastCGI Demo Page (perl)</title>\n</head>\n";
  print  "<h1>FastCGI Demo Page (perl)</h1>\n";
  print "This is coming from a FastCGI server.\n<BR>\n";
  print "Running on <EM>$ENV{SERVER_NAME}</EM> to <EM>$ENV{REMOTE_HOST}</EM>\n<BR>\n";
   $cnt++;
  print "This is connection number $cnt\n";
}



[Top] [Prev] [Next] [Bottom]

fcgi2-2.4.5/doc/fastcgi-prog-guide/ch4tcl.htm000077500000000000000000000101621477724357700206660ustar00rootroot00000000000000 FastCGI Programmer's Guide - Chapter 4, Developing FastCGI Applications in Tcl [Top] [Prev] [Next] [Bottom]

4 Developing FastCGI
Applications in Tcl

This chapter explains how to code FastCGI applications in Tcl. Prior to creating a FastCGI application, you must have a FastCGI-savvy version of the Tcl interpreter. Open Market develops Tcl binaries for popular platforms and makes them available with our developer's kit.

The FastCGI-savvy binaries are extensions of standard Tcl, and are intended to replace your existing Tcl installation. There is no need to maintain two versions of Tcl: the version that we supply will work fine when invoked from a shell or a CGI program. There are also directions in the developer's kit for how to make your own FastCGI-savvy Tcl, if you need a version for some platform that we don't supply.

In many cases, you can convert a Tcl script from CGI to FastCGI by adding a few lines of code to an existing script. For more complex scripts, you may also need to rearrange some existing code.



Getting Started

The first line of any Tcl script typically specifies the pathname of the Tcl interpreter itself. You must specify the pathname of a FastCGI-savvy Tcl.

Then, you have to divide FastCGI scripts into the following two sections:



  • Initialization section, which is executed only once.
  • Response loop section, which gets executed every time the FastCGI script gets called.

A response loop typically has the following format:



while {[FCGI_Accept] >= 0 } {

# body of response loop

}

The FCGI_Accept call returns 0 whenever a client requests the FastCGI script. Otherwise, the FCGI_Accept call returns -1.



Example: TinyFastCGI

Here is a simple example of a FastCGI application written in Tcl:




#!fcgi-savvy-tcl

set count 0

# Response Loop
while {[FCGI_Accept] >= 0 } {
        incr count
        puts -nonewline "Content-type: text/html\r\n\r\n"
        puts "<title>FastCGI Hello! (Tcl)</title>"
        puts "<h1>FastCGI Hello! (Tcl)</h1>"
        puts "Request number $count running on host  <i>$env(SERVER_NAME)</i>"
}



[Top] [Prev] [Next] [Bottom]

fcgi2-2.4.5/doc/fastcgi-prog-guide/cover.htm000077500000000000000000000102361477724357700206250ustar00rootroot00000000000000 FastCGI Programmer's Guide

[Top] [Prev] [Next] [Bottom]



Open Market FastCGI 1.0

Open Market, Inc.
245 First Street, Cambridge, MA 02142
T: 617-621-9500 F: 617-252-3492



error-file:TidyOut.log

Programmer's Guide

April 15, 1996 s p/n 42-10530-001 Rev. A



OPEN MARKET, INC., PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. In no event shall Open Market be liable for any loss of profits, loss of business, loss of use of data, interruption of business, or for indirect, special, incidental, or consequential damages of any kind, even if Open Market has been advised of the possibility of such damages arising from any defect or error in this publication.

Open Market may revise this publication from time to time without notice. Some states or jurisdictions do not allow disclaimer of express or implied warranties in certain transactions; therefore, this statement may not apply to you.

Copyright © 1996 Open Market, Inc.

All rights reserved.

Alpha/OSF is a trademark of Digital Equipment Corporation.

Digital UNIX is a trademark of Digital Equipment Corporation.
BSD/386 is a trademark of Berkeley Software Design, Inc.
BSD/OS is a trademark of Berkeley Software Design, Inc.

Great Circle is a trademark of Geodesic Systems, Inc.
HP-UX is a trademark of Hewlett-Packard Co., Inc.
IBM AIX is a trademark of International Business Machines, Inc.
Word is a trademark of Microsoft Corporation.
Netscape is a trademark of Netscape Communications Company.
PostScript is a trademark of Adobe Systems Inc.

Purify is a trademark of Pure Software, Inc.
SecureWeb is a trademark of Terisa Systems, Inc.
HylaFAX is a trademark of Silicon Graphics, Inc.
SGI IRIX is a trademark of Silicon Graphics, Inc.
Solaris is a trademark of Sun Microsystems, Inc.
SunOS is a trademark of Sun Microsystems, Inc.
UNIX is a trademark of UNIX Systems Laboratories, Inc.

Any other trademarks and product names used herein may be the trademarks of their respective companies.

[Top] [Prev] [Next] [Bottom]



fcgi2-2.4.5/doc/fastcgi-prog-guide/covera.gif000077500000000000000000001222201477724357700207400ustar00rootroot00000000000000GIF89aVl}}}{{{yyywwwuuusssqqqooommmkkkiiigggeeecccaaa___]]][[[YYYWWWUUUSSSQQQOOOMMMKKKIIIGGGEEECCCAAA???===;;;999777555333111///---+++)))'''%%%###!!! ~~~|||zzzxxxvvvtttrrrpppnnnllljjjhhhfffdddbbb```^^^\\\ZZZXXXVVVTTTRRRPPPNNNLLLJJJHHHFFFDDDBBB@@@>>><<<:::888666444222000...,,,***(((&&&$$$"""  \#hd |\!,Vl/ I`K,qĐJhȫ"tBjH I!R%*n{IjU=*V*.j/|;n!$I:lEGZ{G\_oDl)l|UN@VMAgPt,5 ;G?wÿؠ/Ȅfwņ;,1XBi7V%(&Q <azXgD}D%O4Җ iY.v `,е9nflҨ @PۘDGGz<#T d +Ls@")IJX$ 0`@(:7RʔJ^RYfDQd)X܀kJ.K0'WB64-2WA6tQ %|z'wsE`;k 1p@jO5܁n>j^FP|Y1VpPxAT:] ^U f;6w-@('uX]YPT]a PW^5@uQVW|7 /0 -dt0/#"0Yp~ c ]@ U 5'a \ppaA >S wqɀ8c X"[ b`r0@Fo6.F1G7F$'HgLUdWb p69ȃ" 8?(uE, l8[s N 1(U: )E ZwXdA9f[ P@0itPPUpya  >w-X@@izV7{#/!@pȗmh)_mw0U0NPb ow{@ ,\[ KΰؐK& &  3(q 4D 9Kc5\ 5TÏ(Bd#N$1" А>(e##T t "٠@pf(Y0 Iu   RTeYJ060yRG;RgTiia1iS eX@XX= `y?PCkpY]t>5.]] r{flQ{oQ} 3 @dfzhNa .vp %j fP pY° wܱ@r̐ 0 c` 4p a# p2k5YI qY! U "Te5P#,wF KK*б 0Z +PW'<;U 1{I>jAX` K \P 0` X[j]sI `Ұ]ȗa|k{`)2 pl@7hJ|[P N |p)p I Ԁ a& H%Щb=u38gjcFs4RqTd0jѺ0#֪Jցۺ0ZsY;t 08 J; &SZ@ †6 +Y$pd( ;qKR!  !*#0$@!/+,` ,P l &L2I',|pH =K̀L봉1krt  x]` 4'_c{A`)Hqbl;uG{` 7 WQ  pQ %K ]P  $ 0.C C ; RgIۻ=!JUdx+s ;`u$@`7 PݫpL͂BH仒A,L0 P 8*F`Yg-q > `rW*1 !;#%# $ 0= [9p())0 , E xw. u L ` ]>vu-E@`Ǘzp rl֦ " вexw\ {P^lܨKPYxܫfl @ D% YM 7'Ss 0'S?H`!DRyqʪ|!!"U%W|`,Wytp ˛༿f*9GT##Wǜ 0 ||A4ݼ;^Q+  gdwY(}0FI MНݫmѣ.!}xw ~p E 쑛 eA0Ĉ;MtSpV|.կ yp tJTA*ztP]jݫS^K %G y0 0E *ؘ,Ӛؑ=ٲvF ܙTH»H d@>b ̼r 9wS. ޛ T1]aP'37 & @ ?Ü 3t".k:==r0 (  TW,3KSq*4p hPv2s2|AfW P׍p @ I9ْ 4q3DDȪqȬ{@~d Lt @ۍ> Krne $L =.GJ% - P`mfדP01 (б]s ^ 5P? Ҟߢ0 Ѫ@'\1G K 0P %a  %ł .xr`N pMm9p +00OnK.`W̘0O"(B"ex%>qzD/f 6\@ļS=~/^֥B9P $Q\O^#>" c F :9*h W.QJJ \iB_N0;a\G} P#HRIl}.3,st03ϡǖ83OevWP$.Q' ()Q7R.:T,COQ.TIKdХQ*iU# 5T_Y"!H H?-DԐV3,0E)N2me#@EF+-l9$e.uK^T4azR*0,Bp d$3B` (4 ll#+@"j2DpAwfS' g8q`&4f6F;B+8q `h Rl6S$/`X $QxB& JpGp ЉFH@*fP_pK1QNa ̐QIA؊r ,+A +hs)$DEBx] RPb,m!=P0\AG;ʱ mQE jHGXŪD` g})n CA_2( ŋX eLBn XESu#Pb;7@|'Q O(ZJN1PB844 @`D)V_|x,q$KZ[!^rt7pa Zڝy:񎣘_RXd7܄o| HG#P $`+vϝF:y:a  (q&dE)ZOlBc\CALFQãIE%UfZXxUz ,P'0S EnI&AwP/POxN5]Y]# sT uᘇ2cȘYϊJl"oP !H2d9F0`yQ%:X=Jӄ]86vCh/xOxO(5Bxr0X|bpL!*U М'nlL"3> Ih̥;p[΃H:)0ԥJ͔ TlbTwFc/=ozALT8o)"٦~4#lb&pSD!Ús箨W IQJ@+&@Wrӱ81dOtvA8 OM/289Q&>UWC;Qyz@p>0@iҬ5GN[kËhb4L/B!._b~ZBh=n"eq1! ,JQ'uR 6E8bb!`X7(шGXb+A)Bw㚫[a7ml0F `4pA"a5 P Fq XL/C,dt,#(!}/(/ uj'_&SV0OVD+X T؂'IXKssbN3HaN3b .`3 s1 KC)CلSh'p耰O(J 80d&zC#2(AA$?@č9EH@; D0< =3?[<3"]OP,: P<;B4`\y+\0 -5%xO%YGxIZ(Rb%x>9Fx{i3jVPI14`?fZ& @@xE0XC@zd@IA<2\ ;K/0UH'x `L( $8\AIDᕈ*PJ((3$P kj9Sl49 s)HLP0+%Hi^8=rhL`VtErH6ڎ\XNOARh<eN XFSgƏbhI@(& w ; |< xoL4D"H_(FXa7Q Yz P&5>|.RRIJYW`M`''.Ab ( ¦#BԅRplD™"-LF@O(kKpOTXAPBSPF  LJP(@`BP@DD,?JETHAL٨K؄jhL+uڌۼxJ:2lێH51TK9Aa=$g$"DgJ8 Qbd  .[0X(HLx>=xG@C8 h4$[ \2vNhY@c؄TylSDd0bWadhJ]V\4^#M_ӎi]\HfICz=P([eP{  RFX  F+ (_#\#`G`^%8`x@ @0P0R M*<Бvȥrs`lfK1qNtFt^Nvd]H8YYDQHp(Hxfy`d 8Zhrw ^gWThhYYL,$%P,af  .Eȱ6.D~ӟ6 0M[BX XE)  'X_@@AT0n) P k4Nμ>ipE 3۔^lL UrITa(_N7MVϢ]P3Wdl"4!dX`X Hh H*xf_Rx|Ru[\Ah;a>8H7n5 H nC0V*>؃1Ӄ<0; fvv8v60Z]iݒVw~?uPFNU7ȄtojpE*n12؃R&?ĬHpCUMt0*谤 (eqqLu qᱪ.i!'=rpExLxHTp?SHQTJj373ypyO7/cB /R:+e+NL`H[(tm&W JXAf`+LHXPNOu 1RE8#$@ hq  &Hxk9Pp0$8ic h\ &1d%,TYb%- ]>`$.Mt& @qPPA `"XN#e[ xx椆4$BߔKG #qPbu@Jq<aGOVz0# 9foY̚K D# hD`xc@ 5ATp])z P4KaY pEZX :XG6jj/?2D=ZąPD&u-/j e?98Xsz`-~8B~X3"h$B"x0B\ ' /@ Ï$I#URInH:LEFI L0@Md+)\@.]nR X@ V `?5pyo@>E0ar*(Xdy$)TBVE2 "C7*a-=)lite_V̲) h3;0} ` x@*0" Fp.Z( .px23O,B_T-oمxd&045:v:*UA TbkC4W ºCaAsQ+26b0a]:]4 B ^V80=@;#H ($lY-wԭr[:h̵֟/)nN6M'إn-@h@>熥!# T<3#31g= 0a0"3w'VΈBcY?p "b.q-2[Q.,<]$d#$@  ⋕E ]\+ڡ*|M@7<]aYq\X܏1 BLIQ̠ nQ-Z$5 a  h&'~#]!( 5"( Bt彈(aU05@Rs=auR;H6dAG$JBA9u t⧄CD U!CȀYEx(@ $ 'OL=:=M%<(C8 Z¡0`@qW4"D$@XTE#H@$*5 yX,"A80H91F0"#FV C%h\b&5ph2L9MA#(,+P&6^UdD|c8l8Ω#е#L&cˀ@&h>c9XE@A ]=|]aYFn]+,U@' *)@(fY&d =d@I,lY4B(c?R]@U0'|֠|D.A J~2D20+ZE'^,Bd@ފ%4V< !܀4tG 孑h7x(PƒX'\Ƃ&d&x% p#j šԒ )^> D^WP e9<&&Z-99,1d*k2 @Jd'B(J&BzjY.,(G܀@ ,|Bu"-}mI2CThgtqc$Dn#9F:tjv v@+&|@ &+XiA@w%0]]+!$DE|kR}38-(+) H!hk&$ݬL.x|7C.XHV,]flrggY},[V$g_H&Y^WZWVW,uR+X, B8 LėD8tC9';ڤ2=P #܀*PM#A7n@Vw@A2x R.B`ErFA|_$ފ)@0 ؞H+͐0^%"BVxD'(iR$x `p_PQ%`&,ejw~g\P%l@pj$\$GU Pb&lXNWCWR;C_:f00F@D_fe7p(C09C(0Y14f ((@rEϟ)XA@57q87@j 2]d–B#'+@&h|ñ^>fP ٳ:2e]L*M]N(C/ ,/U-C,/@0K B*Ut@k5sj \R`sRsA 艞$B `vc;C@ (8"0—I|PB%p77gs?$#32Flfe,V6R,GDtN V)FcdAC_C_SD' ȯ :K9C00A%|@(AO ,)J&+ WSCE:sQTĘ2fcx4 @(uj~"r^o4 G\! *@#Z׸'h7 ag U즕@1 -) B) ) Ā B ~E<0`R+3LVE j416-)<2 ^N@ X$oap9pt!L /@.;;;HA5< B!4(,aH48BXif]uep3IxX*D_8OAA{8D%CϓCBG0P x AT,@ d|BlgfCLa10H5/@'!PŧU;l-\\B)x0T\#2@$P,̠c |&DT2b?UC'<|VB~K$~>{@0D0r\s1Kk(CBQWHoO:5bGr(3+)Ļӿ=?+A1$@4EM;< $X]0AEEX!DtpAP. PA/,YHfM, N>'ȒhQМ9;R85F-p g\W_/`(RB(~Jl;wS#ItɂcTN"D+iҮMYcf 716y 1b0X_x:1aӋ6*Z @,'QT%M0]dI"%'D3zu{ =@z3/^X93MV[_V}Bp 2p0)Ћkf,Šg |F 3,1 1< !0`GlmJ*dPna|":lN:ēNHēO>y!fa f%Xn!UJ儸lCL1cf0jؤƚ7F9;nH"$H6!\Et!e"0K`(  $Tb8ec (bSHR+ق`vRҤIa$H`BTApijb%BD' >Nqb1hd15ȕ*q0Xw]cT‹R,.Lpb ctN N+M"]l ǚl2c*lC ?FNC-5 R66hm(83䄡$礫NzQQd{O>W2N2`@1$0\#ZAPRt  ĺ5Er5b)( =$ W8#y ѐ%1oxc c0 6&R0q [B8E X&<$NZ-, fTL`SH+0@ꐇQq<І".+i8!j4щHE P SP֨B 5_< |ub+3Vmƅ3(ٲqCyh``-5dz NPR޳+ a `jBLIB t1o742@r3jԍ0&4`@I&l&v1A#'!6|IFfECi x9^4ia%@%yHaaD,!&u'?'FA >9,c mc$> 'q'\*sMc H@4,g,-VN@J̉Pc !RlHwRSy$GJS m0*Q2OTKa@2B!S>o4?%TTL/X3 5X! HbI`/ 7<#|q\N(9%[s'1E %a !u^\,7D7׈ )TAԌ`@26,c>A,hA TB)\teiv'@8 brH`b@d-ag NaLdMY\X$„뇠A4l8R*Y$jT_Z@8ԉӦ0yaXɺQ 2T!L 8P  H:U3j_}-ܹsY͘*o+1~`P΍`6F.q83ȐY lȁQѢƐy,(8(# "! U9dɢungUtSV 4i8(;(`pkDܻwB/]dѐ)Q&D4 Ն?~a~@bH@ NB*JJh'ThB'$2.#EPA3&Z"*b1MpqM6缳M/8#1l0E .?҃-v,@) SNZ@ߘaFbh XF8riAkAN~ hgOJ/ P'ChXsrC+!'$BAv IFyTDxȱN;rs1~" *O&@xB`r²&BJ '@9IᎰ暛&"tb -Xc_ruAoإ^1I !Q8h.֘c@6Yegށ ilDJmn As q'lq': (R+dBs0T(2(`Cq  7,R*)„jR&X2χ!Hv͊-X5`F!0"F=.LFĒmR֐ d%!]ڤtؘeykA@G0jD{z҂P&һ9`S1Hs(޼ );ΠgE 8h <AP2(Pb(R&mj<Av% E+0Xj]|V!K ,t,,bdEf2\1`d C2` F44Xk:M@d@RRXYp`(p !'(*^BZ{bE+Tp&hQʫ-)p0PƦ B+'ֶOp\[Vg(E*ڛ^q^iQ-PRh(B*3`E+:ANtSP0nugre S8nn$:SlP|Wo- z @4A"12"W C  рC#, D"0|e*iIҔlKEBzp}@(|͠_(D:Tܮu!C7&laM*y'! E?5ପE09P(4F420b<0`-,_A%q^"I-0WaҠ2X+8ev&Y8$&2uj,v. r7RkH7\(`K(yBypE+L ,4< B9Hp XRP4Oʁo\xǬ`C>V#l5 6tzIU ԽD1t*dqyЪ2b|1 Ab5us('8T^ z g8)NF0.H'BA ¶Hb<1chH+XS(X--hGeFPjaU &l#7U}n@~ + "DE-uPܠrŜvW(xqLB@kBEjguހ~.5c3)HPޥ!GA85A)"|N Wp[5̌)("^IȀLlb# *p1rAhӃЃKJ7o`{٥;,`Sej8c3Z7^hehB`^92ZC (%G0J1N0)4\aApekN j$'J0Xb44? jz=؁,N}D X1Y峉ϭHndS @ Ft`+ Y R i ~pZ| p `6R@$0}0m`0SB `rP"  n x f@ sG9s U^iB\0 pj@@;(ⶰ ' s4L0՟" e<(GQ 9ݏ*}@ @!q ̀ n SR\Qшz0@++u-%^~XbV'j0\)17t=nVCΧ~ǂŅhPX (.PPIl 嶲 Is C0 o $@ 7@ BRk H k*@A\&?p  @A  l~5K# P,-` pL|\œ^P1ty4q.(%  ;'Ԡ W(a\*0W@{/6餳OS8 ";O &` Q@ \R~u}U (0 /4n;WNA436,MLqV*m+Ddj)URҲ51avI/WfM<EU4d1D&ȤNJUY |i'ǫurTCؓR4IRW9Zx"ʼnDQnF01c0 $KnV$)J! "8`@,! gP:$$>Eʢ exB me20;X'7XPw tBȍԏb_G#Z=SϕrMI&^c&`o2vN>APQhA_X"-lyEv4"pA)`ha. `9ÈApQQSA3@=1o>;5:$A 1PnqtpdHl6 faFħR p3TƘ`<ޟD0%uVdF"4*_~x–\9TJ*RK.SLe{ f%Tt\p9bwZBHe!Yay Dzi\IWQ<@:8 G!TOm +N9bVC$]sݢ^&kXXxEAe ^ARUW*Ah 2q!B*Ja tB=,!m0`7VK(L!*d!OtB،'ƴFcK,!K`@A0~ {@"p)78UClbKD#5-=:Gɇ>fP3:ц E < F$=I A *D;i NqHO4A o$ "E,hQH''`^Sҕ4GՅ)3 _@],;Z"4v?ti , xB Doz+D MaC<lȤV3b*8.No~%J?d#ņ6LAD`) WȂ* ; ^Ra: %aKX0D( dE VL`bZ! 3@D`*taI BAd` Z."3*+r1E~(2apB"(C($8BB,dKUh 0.v0IIEÃrVB`C;TnMC> ) \#y gi؂_EGܟ/!j N P(xhX3R\(S4Po ffh!@BSX)gdb,BHJ[pc)D/@8pT4? ?hjIPHK@a:NJЃ; x]5@$HqiH-Qv8 i3eU*caYpn˙ 4`HsXΪ`P   vp2-Sx:SxRI8Vp9Zp)I 30UQ _);G,0jh;DQ SP !Zl\NOG=(`%ua ^!5"Y.SH2M+( ]K@K@!„;/HsD=|E A%VHp4^V* ȁ0Ń>H?:`Ы!r8%g?PQeQ-n 58LQI7UkX"@kA#iR^R*)2\xOWph@wBVlSs*SMwDQxDSFph8LD:x=t C@ٿp( TLGP _pXCpEQM5U\㱳Q 1 0P]P(!(PPąJa,MBN2 CJ<5I@-`! ~k ʳ<- V  8?#rYЅ 6yl(`0t pWE3Y0E@?H P5I> ء? XU8"6cS:xH4VsY!ƶ)6-p#U`P@U-=ᑌ_ZW(HchŴ#h$>Oȁ&'%e+9T8'P g(ku`Ѕ'(W1\50ΈO`]-ݛO\ " G0( J J GKL՟Ux +J b0{ =EVU^5ӆUR$q4UQRO@!,B"h9 /@`.m2 xH4f"KP;PDh+P I0.#Ԉ(8B8JH pCy<@v `B@ǎN h0APpxMzAii=0:H΃oq Y, x0jHvq0,᪂$JcQwPpF#ծ.A #a;E(BP^kbH_U*?[ )cB7mK dmTpU_uhSxuX7[/O0J<=INmQ 5__P>r`n&!j0e]nq#n@p4@d oJ(ffLgkXS0'fS8β .2R)fZ0 lZWdR5b4`JЃy0hyY3pIP``Q (tEaTr=Ѓ0 mr@rY>(tF+}ax6ih]p09wSkDs>b*v&x @RA8L_R|*Յ(?n[8}AvX?S[uXI %F@bhLPzpH0]P`5v2Il[neE !rOp& &ohMXM"egaC Z+u"8Xv9ntܜxrp}q/>YEȞ &Z@UT'510ȩSgJFȌl #fn@%cF,QXP`+׮^iI$F p%\tZOyˋg/_z~#XO?!&`1wnc*9jT3Å7 ΝьS2ǩI}z5k֩U.&$[nέ{79mUЂu*TMƏdk5H3)Q#oJ! [E坡GόY%4aV 1Z ,X A  .+0Rj83@J)B@@O( T;tB 9D pđ!5U\ڄ11=*/B hN<%B6nS}cF8 "*&\R!1 7 $  4"/Đ  ^|LO1a`@5bmd-e]gyF:|h֚$F9Bs7)I5\tUwq6]tgweqyT3SE!32#%b0 `ܧPwva)ݷߞp(Ȣ+@z@:DS“QgH=qaY&f =HM7İ LpBsR( CE1'(CЧ"$GJ.YPEM!oM]c? 2B \b2Y ߘ1J H"AJ"@VB@QnE(\p0X_и a8 0 X(#4fődZLTs,g X@#0^zr8:ҁuM@GKC i8OHmdGbAVk3 %! Z+7 .XASAD Hp#@$h"1ohl,{&Gqh,wh ?Jze(M8d6GG1H$F @ Պkܦҭ4*V!,[QB(p-D0=aIj<@=E' C\@ )4mXx0BāSOh_<+̍.=4 `g'o;"Ȧ NMA(3PP,CR%tB8A6lW#rzۛ+s,40ay4E `+ nܧ\PC 8@*n bP0Mp,laoX=%!Jhg#r.[5{8E)Z:O! ѱDz<Bƫ1X Y2nHV`CSUϜI'p\zzgB\b ( Ї@xA.Ѓl5=`siBcI ~f;d֨&3FlXx-jQhbv9Qh*y'<5 [ 8VؤJR? `.7~Q?(H*x!x{AAL]By78DlE\&hV$  ljh(.DA6xʹ`=&a't )@*B XOЙ2B х5DEA<2XIITH`5\I-'hN7%(۝ ܩ E9  ,'t# p# P!̙!0B*@)#4 LU_Q]  ]QQlZ_y]Ehd@ h 1jA^nxD/* tByV%r (%҈j !Qk=A"!^M[ِ #aJ<$A*t)( c_(d(B\ .^yBE%{MX@*dB8pdSā@JtP$L N(':-5A t\ b$h vtB ¢*@ $TbOPϨ4lA'd5TTd]8I 0 #N!y $A\e @l*.Bh",\@"\B( @%IB BȀ TB|Ptb|[a(W] +P"mC" @Dt@&&| x )d4Ch @@H +# AC(xA:#hJd&`|&XL”ZSD ,T6aB$ et@?%q B * @ DJYCE+;B~9aC4BwXiT71Cl0 t^f7/-tuLFD$I:@B*&`@TB h>LU-B+@ %@ L[( @ޛyn"*Bo ŘaQx54A+#=Ё`jVtLgu`CB xJ#@O62Mj|z C~K>*/ƽ+7\1+("nB 6,y@ e['I!'cR/|l#C#d $XJ9@ ؂/| °dK 1B)D$]*)J%P1cTW,4-DXڢq`#1%dݭqx@B*+c@peW.L-h:]l.8tA3x%&E-PܙcUD,D+C&/)xD". Xh/ !T ȳf/e޴soy^ũ EtP//A6@C//B+R*\ԁiY+jouBGS ,F MiuIyzL4,X,blo--(C/1XOE)X(HpAj) {%'д'(HR(` @ Ď* ]ătWqw%X# ^ZlOW"lSi.%{4$qOB&(6AIO,J] /@ O!E$$4t亊# $?fNIeA@4@'v B f)7.+;@!L(,B'$g!ܪ $$Ā+Ԛ$QQ7H1,(J!M(lT$b= '4c0ty,CF B۱R'U<@Ew:/tGk{,LsI_J$(Kt#H"D@ ,AA&T@N i5(q(%D<409w?9U_B!sqb.4@ -YYmB PZ[@$`[õx xBA1ISO+:-(6#JZn!pP@`& 8P!AERJ-TT0P<4PDRMĄP*(F(fn Z;yĀRZ%70qh"&+~'( )8Zj)VvTnUk$:WP.G,.ͫRK)0hE^Y%$ .Ѻ5\0V rfڼ)e+qMT#:6.3l< !S>d"v>ӊ?  qP@pC 7CC OLqE4pZla%R.3%\3AX%e+f$GL2DSM6pV:GOjOh tU*(QeGDKRQI=ON0dV5NTe6$U%0K6$`m‰J'LJ6`&V U T T̠GƹR2K g M\#( e_A(Bb&0tbAX`&:]5f&7!C481H,f2NfQa z;!hi@pB ǎ|#N (k4 )l4@<FmiFd" IΑ$ |;H LpL"HcPE0,VTas+әҴtX(:;u{hHQXuScx#N9@3 F@⃟e|X8PQQ$8ʾI \`a 1n Z1!r# (HQSbxq1OD(8І`:4 jH ub8"",IPQD&Rflzq;i3BIc?<X IRdvA X"6AmF!Imm+놢L lG#$ S#Є6Ј?С9h.,!Y: 0tT8 ,1 tƴ'pe64B@ 0!m*!P_D>QɀI#S"H v?w " 7LQ5H|ZIXP1:ʾ 8&D-9N PS\2rL|B >R1h,l H]4q^F3Qe<&jw1#&c 6pߤu8 `(! OCtͶ<,aw̏(͇Qq|BL>pld$AoV,:q|B&80 j.,{\rA+N^p[.7[Lm?6I.҈4,l`&,Ƽ=y.A 5,qνdpzE<[?wA'4xJXxOBBDŽ5iQ"@G RP,"P)\' , xO^nhlATHA# 4!*̨@^66>FvJϊCA,atƯʈ',j/f0/%>&@ .6Nk8mXKnD BVN)A`dhrxO Js.F5vi rZqtʏίVxFZQ菟lAe2(  :JP0  ]* )ѱױ&`@Iq@xd{ aOb6կ<HPayVP1 2 2Ra`4zȇv$=,pc?S&$C".%C @&ͨ 'vR?p ϲ)?k)'TM4oFD+&RcOE@FG0"a΁Hl4qЁښ%/ Π/+a0Y0IܨD:Sw"2P!34G34;lAT5]6(.! ;fcgi2-2.4.5/doc/fastcgi-whitepaper/000077500000000000000000000000001477724357700170765ustar00rootroot00000000000000fcgi2-2.4.5/doc/fastcgi-whitepaper/fastcgi.htm000066400000000000000000001132031477724357700212300ustar00rootroot00000000000000 FastCGI

Open Market, Inc.

Technical White Paper

FastCGI:
A High-Performance Web Server Interface

April 1996


1. Introduction

The surge in the use of the Web by business has created a tremendous need for server extension applications that create dynamic content. These are the applications that will allow businesses to deliver products, services, and messages whose shape and content are in part determined by the interaction with, and knowledge of, the customers to which they are delivered.

This important movement away from static Web content is pushing the limits and exposing the weaknesses of the environment in which these applications are currently bound: CGI (Common Gateway Interface). Most importantly it does not offer the performance these applications require. A new communication infrastructure is needed to connect Web servers with these new applications. This is what led Open Market to develop FastCGI.

FastCGI is a fast, open, and secure Web server interface that solves the performance problems inherent in CGI, without introducing the overhead and complexity of proprietary APIs (Application Programming Interfaces).

This paper assumes that the reader has basic familiarity with Web technology and developing Web applications.

Common Gateway Interface

The de facto standard interface for Web server applications is CGI, which was first implemented in the NCSA server. CGI has many benefits:

  • Simplicity. It is easy to understand.
  • Language independence. CGI applications can be written in nearly any language.
  • Process isolation. Since applications run in separate processes, buggy applications cannot crash the Web server or access the server's private internal state.
  • Open standard. Some form of CGI has been implemented on every Web server.
  • Architecture independence. CGI is not tied to any particular server architecture (single threaded, multi-threaded, etc.).

CGI also has some significant drawbacks. The leading problem is performance: Since a new process is created for each request and thrown away when the request is done, efficiency is poor.

CGI also has limited functionality: It only supports a simple "responder" role, where the application generates the response that is returned to the client. CGI programs can't link into other stages of Web server request processing, such as authorization and logging.

Server APIs

In response to the performance problems for CGI, several vendors have developed APIs for their servers. The two most notable are NSAPI from Netscape and ISAPI from Microsoft. The freely available Apache server also has an API.

Applications linked into the server API may be significantly faster than CGI programs. The CGI startup/initialization problem is improved, because the application runs in the server process and is persistent across requests. Web server APIs also offer more functionality than CGI: you can write extensions that perform access control, get access to the server's log file, and link in to other stages in the server's request processing.

However, APIs sacrifice all of CGI's benefits. Vendor APIs have the following problems:

  • Complexity. Vendor APIs introduce a steep learning curve, with increased implementation and maintenance costs.
  • Language dependence. Applications have to be written in a language supported by the vendor API (usually C/C++). Perl, the most popular language for CGI programs, can't be used with any existing vendor API.
  • No process isolation. Since the applications run in the server's address space, buggy applications can corrupt the core server (or each other). A malicious or buggy application can compromise server security, and bugs in the core server can corrupt applications.
  • Proprietary. Coding your application to a particular API locks you into a particular vendor's server.
  • Tie-in to server architecture. API applications have to share the same architecture as the server: If the Web server is multi-threaded, the application has to be thread-safe. If the Web server has single-threaded processes, multi-threaded applications don't gain any performance advantage. Also, when the vendor changes the server's architecture, the API will usually have to change, and applications will have to be adapted or rewritten.

FastCGI

The FastCGI interface combines the best aspects of CGI and vendor APIs. Like CGI, FastCGI applications run in separate, isolated processes. FastCGI's advantages include:

  • Performance. FastCGI processes are persistent-they are reused to handle multiple requests. This solves the CGI performance problem of creating new processes for each request.
  • Simplicity, with easy migration from CGI. The FastCGI application library (described on page 9) simplifies the migration of existing CGI applications. Applications built with the application library can also run as CGI programs, for backward compatibility with old Web servers.
  • Language independence. Like CGI, FastCGI applications can be written in any language, not just languages supported by the vendor API.
  • Process isolation. A buggy FastCGI application cannot crash or corrupt the core server or other applications. A malicious FastCGI application cannot steal any secrets (such as session keys for encryption) from the Web server.
  • Non-proprietary. FastCGI is supported in all of Open Market's server products, and support is under development for other Web servers, including the freely available Apache and NCSA servers, as well as commercial servers from Microsoft and Netscape.
  • Architecture independence. The FastCGI interface is not tied to a particular server architecture. Any Web server can implement the FastCGI interface. Also, FastCGI does not impose any architecture on the application: applications can be single or multi-threaded, regardless of the threading architecture of the Web server.
  • Support for distributed computing. FastCGI provides the ability to run applications remotely, which is useful for distributing load and managing external Web sites.

The following sections describe the FastCGI interface, protocol, application library, and support in Open Market's WebServer products.

2. FastCGI Interface

The functionality provided by the FastCGI interface is very similar to that provided by CGI. To best understand the FastCGI protocol, we review the CGI interface here. Basic CGI request processing proceeds as follows:

  1. For each request, the server creates a new process and the process initializes itself.
  2. The Web server passes the request information (such as remote host, username, HTTP headers, etc.) to the CGI program in environment variables.
  3. The Web server sends any client input (such as user-entered field values from an HTML form) to the CGI program's standard input.
  4. The CGI program writes any output to be returned to the client on standard output. Error information written to standard error is logged by the Web server.
  5. When the CGI process exits, the request is complete.

FastCGI is conceptually very similar to CGI, with two major differences:

  • FastCGI processes are persistent: after finishing a request, they wait for a new request instead of exiting.
  • Instead of using operating system environment variables and pipes, the FastCGI protocol multiplexes the environment information, standard input, output and error over a single full-duplex connection. This allows FastCGI programs to run on remote machines, using TCP connections between the Web server and the FastCGI application.

Request processing in a single-threaded FastCGI application proceeds as follows:

  1. The Web server creates FastCGI application processes to handle requests. The processes may be created at startup, or created on demand.
  2. The FastCGI program initializes itself, and waits for a new connection from the Web server.
  3. When a client request comes in, the Web server opens a connection to the FastCGI process. The server sends the CGI environment variable information and standard input over the connection.
  4. The FastCGI process sends the standard output and error information back to the server over the same connection.
  5. When the FastCGI process closes the connection, the request is complete. The FastCGI process then waits for another connection from the Web server.

FastCGI applications can run locally (on the same machine as the Web server) or remotely. For local applications, the server uses a full-duplex pipe to connect to the FastCGI application process. For remote applications, the server uses a TCP connection.

FastCGI applications can be single-threaded or multi-threaded. For single threaded applications, the Web server maintains a pool of processes (if the application is running locally) to handle client requests. The size of the pool is user configurable. Multi-threaded FastCGI applications may accept multiple connections from the Web server and handle them simultaneously in a single process. (For example, Java's built-in multi-threading, garbage collection, synchronization primitives, and platform independence make it a natural implementation language for multi-threaded FastCGI applications.)

Remote FastCGI

FastCGI's ability to run applications remotely (over a TCP connection) provides some major benefits. These benefits are described in this section, along with some of the security issues that affect remote FastCGI applications.

FastCGI with Firewalls

Applications that run on organizational (external) Web servers and depend on internal databases can be a challenge to administer. Figure 1 shows a typical organization, with an external Web server, a firewall restricting access to the internal network, and internal databases and applications.

With CGI and vendor APIs, the application has to run on the Web server machine. This means the server administrator has to replicate the necessary database information onto the system hosting the Web server (which may be difficult to do in an automated way without compromising firewall security). Or, the administrator may build a "bridge" that allows access through the Web server to internal databases and applications (which is effectively re-inventing remote FastCGI).

With remote FastCGI, the applications can run on the internal network, simplifying the administrator's job. When used with appropriate firewall configuration and auditing, this approach provides a secure, high-performance, scalable way to bring internal applications and data to the external network.

Load Distribution

For resource-intensive CGI and API applications, the Web server machine quickly becomes the bottleneck for overall throughput. The usual way to solve this performance problem is to buy a bigger, faster Web server machine, or to partition the Web site across several Web servers.

With remote FastCGI, the resource-intensive applications can be moved off the Web server machine, giving the server administrator additional flexibility in configuring the Web server. The administrator can configure FastCGI applications "behind the scenes" without having to change any content links or the external view of the Web site. The administrator can use several smaller, inexpensive server machines for applications, and can tailor each machine to the application it is hosting.

Security Issues with Remote FastCGI

The two security issues with remote FastCGI connections are authentication and privacy. FastCGI applications should only accept connections from Web servers that they trust (the application library includes support for IP address validation). Future versions of the protocol will include support for applications authenticating Web servers, as well as support for running remote connections over secure transport protocols such as SSL or PCT.

The FastCGI Protocol

This section offers a brief introduction to the protocol used on the connection between the Web server and FastCGI application. Most application developers will use the FastCGI application library and won't have to worry about the protocol details. However, specialized applications are free to implement the FastCGI protocol directly.

FastCGI uses a simple packet record format on the connection between the application and the Web server. The same record format is used in both directions and is outlined in Figure 2.

The protocol version field specifies the version of the FastCGI protocol that is in use. The type field specifies the type of the record (described in the following section). The request ID identifies this record to a particular request, allowing multiple requests to be multiplexed over a single connection. The data length field specifies the number of data bytes that follow.

The different FastCGI packet types are:

FCGI_PARAMS Used for sending name/value pairs (CGI environment variables) from the Web server to the application.
FCGI_STDIN Used for sending the standard input from the Web server to the application.
FCGI_DATA Used for sending filter data to the application (for more information, see the filter role described on page 7.)
FCGI_STDOUT Used to send standard output from the application to the Web server.
FCGI_STDERR Used to send standard error information from the application to the Web server.
FCGI_END_REQUEST Ends the request (can be sent by either the server or the application).

For complete protocol details, see the FastCGI Protocol Specification, available from the Web site listed at the end of this paper.

3. Application Roles

A major problem with CGI is its limited functionality: CGI programs can only provide simple responses to requests. FastCGI provides expanded functionality with support for three different application "roles":

  • Responder. This is the basic FastCGI role, and corresponds to the simple functionality offered by CGI today.
  • Filter. The FastCGI application filters the requested Web server file before sending it to the client.
  • Authorizer. The FastCGI program performs an access control decision for the request (such as performing a username/password database lookup).

Other roles will be defined in the future. For instance, a "logger" role would be useful, where the FastCGI program would receive the server's log entries for real-time processing and analysis.

The roles are described in more detail in the following sections.

Responder Role

FastCGI's Responder role is identical to the functionality provided by CGI today. When a request comes into the server, the FastCGI program generates the response that's returned to the client (typically an HTML page).

Filter Role

The Filter role allows a FastCGI application to process a requested file before it is returned to the client.

Let's assume that the Web server is configured so that all files with the .sgml extension are processed by a SGML-to-HTML FastCGI filter application, and the user accesses the following URL:

/document.sgml

After the Web server makes an access control decision and maps this URL to a content file, it invokes the FastCGI filter application with this file available as input. The FastCGI program's HTML output is sent back to the client, just as in the responder role. The process is outlined in Figure 3.

Filter applications can significantly improve performance by caching filter results (the server provides the modification time in the request information so that applications can flush the cache when the server file has been modified).

The Filter role is useful for:

  • On-the-fly format conversions
  • Dynamic documents (such as documents with embedded SQL queries, or dynamic advertisement insertion)
  • Applying a standard template: headers, footers, and backgrounds

Authorizer Role

The Authorizer role allows a FastCGI application to make an access control decision for a request. The FastCGI application is invoked with all of the request information, just as in the Responder role. If the authorizer application generates a "200 OK" HTTP result, the Web server assumes that access is allowed and proceeds with the request. (The Web server may process other access checks, including other FastCGI authorizers, before access is ultimately allowed.) If the application generates any other response, that response is returned to the client and the request is ended. The response can be any valid HTTP response, including "Access Denied" or "Redirect".

The Authorizer role is useful for:

  • Access control based on username and password, where the user information is looked up in an external database.
  • Complex access policies, such as time-of-day based access.
  • Smart-card challenge/response authentication.
  • Dynamic redirects, where the user is sent to different pages based on the request profile.

4. FastCGI Application Library

Open Market has developed a FastCGI application library that implements the FastCGI protocol (hiding the protocol details from the developer). This library makes implementing FastCGI programs as easy as writing CGI applications.

The application library provides a replacement for the C language standard I/O (stdio) routines, such as printf() and gets(). The library converts references to standard input, standard output, and standard error to the FastCGI protocol. References to other files "fall through" to the underlying operating system standard I/O routines.

This approach has several benefits:

  • Developers don't have to learn a new API to develop FastCGI applications.
  • Existing CGI programs can be migrated with minimal source changes (CGI migration is described in more detail in the following section).
  • FastCGI interpreters for Perl, Tcl, and other interpreted languages can be built without modifying the interpreter source code.

Here's a simple FastCGI application:



    #include <fcgi_stdio.h>

    void main(void)
    {
        int count = 0;
        while(FCGI_Accept() >= 0) {
            printf("Content-type: text/html\r\n");
            printf("\r\n");
            printf("Hello world!<br>\r\n");
            printf("Request number %d.", count++);
        }
        exit(0);
    }

This application returns a "Hello world" HTML response to the client. It also keeps a counter of the number of times it has been accessed, displaying the value of the counter at each request.

The fcgi_stdio.h header file provides the FastCGI replacement routines for the C standard I/O library. The FCGI_Accept() routine accepts a new request from the Web server.

Migrating Existing CGI Programs

The application library was designed to make migration of existing CGI programs as simple as possible. Many applications can be converted by adding a loop around the main request processing code and recompiling with the FastCGI application library. FastCGI applications have the following structure, with an initialization section and a request processing loop:

Initialize application;
while(FCGI_Accept() >= 0) {
Process request;
}

To ease migration to FastCGI, executables built with the application library can run as either CGI or FastCGI programs, depending on how they are invoked. The library detects the execution environment and automatically selects FastCGI or regular I/O routines, as appropriate.

After migration, developers can clean up their FastCGI applications for best performance:

  • Fix any resource leaks. Many CGI programs do not attempt to manage memory or close files, because they assume the world is going to be cleaned up when they exit. (If you don't want to clean up your program, you can just have your process assume that it is leaking memory and exit after processing some fixed number of requests.) Purify from Pure Software is one of a number of excellent tools for finding leaks and other memory use problems.
  • Fix any problems with retained application state. The application must ensure that any state that it creates in processing one request has no unintended effects on later requests.
  • Collapse functionality. A common practice with CGI applications is to implement many small programs, with one function per program. CGI encourages this, because smaller programs load faster. With FastCGI, it's better to have related functionality in a single executable, so there are fewer processes to manage and applications can take advantage of sharing cached information across functions.

Applications written in Perl, Tcl, and other scripting languages can be migrated by using a language interpreter built with the application library. FastCGI-integrated Tcl and Perl interpreters for popular Unix platforms are available from Open Market. The interpreters are backward-compatible: They can run standard Tcl and Perl applications.

5. FastCGI in the Open Market WebServer

This section describes the FastCGI support in the following Open Market server products:

  • Open Market WebServer V2.0
  • Open Market Secure WebServer V2.0
  • Open Market Secure WebServer (Global) V2.0

For more information about FastCGI support, see the Open Market WebServer Installation and Configuration Guide.

Server Configuration

FastCGI applications are configured with the server's configuration file. Configuration has two parts.

First, the server administrator defines an application class. For local applications, the application class specifies the details of running the FastCGI application, such as:

  • The pathname of the application executable.
  • Any arguments and environment variables to pass to the process at startup.
  • The number of processes to run.

For remote applications, the class configuration information includes the host and TCP port to connect to. The Web server assumes that the FastCGI application has been started on the remote host. If a request comes in and the server can't connect to the FastCGI TCP port, the server logs an error and returns an error page to the client.

The second configuration step is mapping the application class to a role:

  • For responder roles, the administrator configures some part of the URL space to be handled by the FastCGI application. For example, all URLs beginning with /rollcall/ might be handled by the employee database application.
  • For filter roles, the administrator configures a file extension to be handled by a filter application. For example, all files with the .sql extension could be handled by a SQL query lookup filter.
  • For authorizer roles, the administrator configures an authorizer application in the same manner as other access methods (hostname, username/password, etc.) A request must pass all access control checks (possibly including multiple FastCGI authorizers) before access is allowed.

Basic FastCGI

To simplify migration for existing CGI programs, the WebServer provides a simple way to install new FastCGI programs without having to reconfigure the server. However, this approach doesn't offer all of the performance benefits of FastCGI application classes.

The WebServer treats any file with the extension .fcg as a FastCGI application. When a request corresponds to such a file, the WebServer creates a new FastCGI process to handle the request, and shuts down the process when the request is complete (just as in CGI). In this mode of operation performance is comparable to CGI. Future versions of the WebServer will improve performance by automatically caching processes and re-using them for subsequent requests.

Session Affinity

FastCGI programs can improve performance by caching information in the application process. For applications that require frequent but expensive operations such as validating a username/password in an external database for each request, this technique can significantly improve performance.

To improve the effectiveness of this technique, the WebServer implements session affinity. When session affinity is enabled, the WebServer arranges for all requests in a user session to be handled by the same FastCGI application process. What constitutes a "session" is configurable. The default configuration uses the WebServer's built-in session tracking facility to identify user sessions. However, the server administrator can use any part of the request information for the session affinity mapping: the URL path, the client's hostname, the username, etc.

6. FastCGI Performance Analysis

How fast is FastCGI? The answer depends on the application. This section contains some real FastCGI performance measurements, as well as guidelines for estimating the FastCGI speedup.

FastCGI vs CGI

We measured the relative performance of CGI, FastCGI, and static files on the Open Market WebServer, using a simple application that generates a fixed number of output bytes. The following table shows the measured request processing time for different request types on a typical platform. The times are measured from the client perspective and include client, server, and application processing time.

Static file
21ms + 0.19ms per Kbyte
FastCGI
22ms + 0.28ms per Kbyte
CGI
59ms + 0.37ms per Kbyte

FastCGI performance is comparable to serving static files, and significantly better than CGI (clearly showing the high overhead for process creation). Real applications have an additional time component: process initialization, which should be added to overall request processing time.

Let's use this data to estimate the speedup from migrating a typical database CGI application to FastCGI. Assume the application takes 50ms to initialize the database connection and generates 5K of output data. Request performance can be computed as follows:

CGI 59ms + 50ms + (0.37ms)(5) = 111ms
FastCGI 22ms + (0.28ms)(5) = 23ms

In this example, FastCGI has a 5x performance advantage over CGI, mostly due to savings from not having to create and initialize new processes for each request.

7. Conclusions

Today's Web business applications need a platform that's fast, open, maintainable, straightforward, stable, and secure. FastCGI's design meets these requirements, and provides for a logical extension from proven and widely deployed CGI technology. This allows developers to take advantage of FastCGI's benefits without losing their existing investment in CGI applications.

8. For More Information

For more information about Open Market and our products, visit our Web site at:http://www.openmarket.com/

For more information about the FastCGI protocol and the developer's kit, and the latest information about FastCGI standardization and support in other Web servers, visit the FastCGI project page at:http://www.openmarket.com/fastcgi/

fcgi2-2.4.5/doc/fastcgi-whitepaper/img00001.gif000066400000000000000000000100401477724357700207150ustar00rootroot00000000000000GIF89a@ @ @@@@@``@``@@@@ @ @ @ @@ @ @ ` `@ ` ` @ @ @ @ @@@@@@ @ @@ @ @@@@@@@@@@`@`@@`@`@@@@@@@@@@@@@@@@@@@@``@``` ` @` ` `@`@@`@`@````@``````@````@````@````@``@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`*@سh5[kʝ+4.ݻxq˷˽~ & x #^Ɛ#|,2dʖ3Ƭ_ΞC-[ҦSEuW֮cc]JƸ[Nin `&[dj*8g_.rC[}X]*{[?}7{m'_}g`{5Ԝs!~A{_pM݇Rء}7" *{:uǁxȡycx)&\M5z6&!=2#$*ie57dzGXׁXgVJxf:%zKnޗ'yw  i"Q8!pٶf>_ 2(Mߋ\"*fj_VꩬTjjk`Efk],,_됲*z[",H*FG_~:+Gz{kNtk%FfdXIg觔WV.k.Oa!֚t7m%prya ųTz䨎,卵 ]⹤/'Y^ W|=΋>ڮ`%N9м dV;&4:QA5e/|}]->8ǽǀǭ|K6c?] yv_l?Ǘ F_v+qby&5⳧ޫ%^|֠'ݏP۴`?[|<5W :?F'p  DXo!B(q"Ŋ8PcƄ? )r$ɒ&+&L)i%˔OpfÈ.oY?<)t(ѢELtʗF?&Y&ě%h)מVnu,ٲ02MO)u`mW/B:n4/†MۨSAP}H׮{/3>l1s?W1} #v-_ٶoo.4dHnZq|9s7 n7u@ ["QǮz.z`UUz+:PK{N{F8gnת{\owO-n8ms+[|jݛ~RM5k~9K%2sI^5+m~_;tfY\4%xi>4/;ʠz,sXЪ֤=+&IlF>k? /C9.J֌NfA`- (IY%E-IWw%t?<#sN%̄CYE:.'LR#Aė|Gdk%E@n- tH܄JR8f䏐Oz J§%jG'N3rSVgOp˓(%,ilE_,SDVFQY~e+DѪ*V1"X`Nd98i/7stJ7ejQű\42nyAa"jvC D)DIpI󗾭pvSRx&HWC;h>*m@)Z>*MR~3!DIMCP`4BeQKO4i@U9%6OBmj1|H8L%AUouDYVS$ccŠ K9+EJ&&+o[VJWhDf#$ߴ:$;+Vf &MCO@ DPB >xD +FĘ"I4~R;DRJISL)^DNy $KEO TRoZlǨUshTkǤI~iBa4H[Q%6\JdަrŲ| .x/m2鷦dȕ*^l&J͓ YRFZjԜG -3kڲ?t[apط!F\%q gݮqmn];a۽]̓g<ݫwݽ׈@! O At0:7R4pC:4B. =DS+1J@WE(FLڸ=pL0<'qƫ,JlJ"E$H$;ӑF,'MRJ*C5|ӳt2-M+6T, tL4|?C˜R4s"E,J @KTOF@iUFNԔZu@4QKR\e^BfA֫XsWXMOg% +YSK^otaG)plX++ݺ&w+WLM26=Wtm$nrVa)D1%Q5UMK$N8h)AcqWaKr!C9 8`y9وkF7w[)J)H/ߤVhvI:ri[lɎhV;lY::gWvm;p^OfHVx'xG>ygy矇>z駧z>{{?|';fcgi2-2.4.5/doc/fastcgi-whitepaper/img00002.gif000066400000000000000000000045721477724357700207330ustar00rootroot00000000000000GIF89a4@ @ @@@@@``@``@@@@ @ @ @ @@ @ @ ` `@ ` ` @ @ @ @ @@@@@@ @ @@ @ @@@@@@@@@@`@`@@`@`@@@@@@@@@@@@@@@@@@@@``@``` ` @` ` `@`@@`@`@````@``````@````@````@````@``@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@!,4 H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳh*۷n ݻx˷Z"7aa‚<ܐ1CB^+WqeΌAϑ.8xӘ-#N:RӋ/F=ڸ]KV [mmu$1mઙ6Xz䋯Wn揣vٹ.6yM7%=~gi_n\| Gf'~w RV|ӑgEa*x*ԁ"P0ǢjvP9c2X:3IdH&[{%69gQLPfyS_u=Hٝf\ip gHJtd؞ h|gq~i``vNĨaz(2ZicbyJj)} juh>ߨj**hҺ+j yqVXj]()hV&Fܱb嬃e{(^XuM+jj.Z&mFg^;!k[w&.[]sRo ދI /lI0XULS(7kF4t0jt}7gSHwc+ |lE]n9n-`x}$9Et]R 0qMx/7iodH.O{jJO@ DX 6haD-^ĘQF=~HADRJ-S3K5męSɘѲ_on_P Ƚ@l/?dAT0'о0C  "A\OO Xl걹L$BF\tQo2s9Y8%dIV/!cQ KlJڪR˖&r{;l+J N9gsD;$O?DL@SPBsOD>7TKC(J#t$;C/,=4A=tNUt|JWr2>5W]w%m'.[uȼ\ 0/5YeX]UHYU 7MK_S[n%\sE7]ue]w߅7^y祷^{87`686H[x, r#$%ic%`EY m e8~WfydmVyb:~aF蛅Fb"iNjk;l&lF;mfm߆;fcgi2-2.4.5/doc/fastcgi-whitepaper/img00003.gif000066400000000000000000000073031477724357700207270ustar00rootroot00000000000000GIF89a@ @ @@@@@``@``@@@@ @ @ @ @@ @ @ ` `@ ` ` @ @ @ @ @@@@@@ @ @@ @ @@@@@@@@@@`@`@@`@`@@@@@@@@@@@@@@@@@@@@``@``` ` @` ` `@`@@`@`@````@``````@````@````@````@``@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@@ @ @@@@@``@``@@@@!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹gBMӨS^ͺװQ `vno{֍{-oο{ m4f xzȕ/:s%Mߨںڷg<]u-g?y@M_y=A/Wh_DDe 8hQi@ud }H@-G`M,Z@$!#bd ~8mHn#ި("bH"L8 ᑸ#A9t'CJ"L6Tf8>RlmE[r ,*Bc9IiJR5'UhD]ibZc}))sTS Ig0B:X(k"ԙԨ:i꩏륝hS4!+TjhHM|͊DzxZ0)Ӷ=u+0V;|-$gv6-nZl&$1*Va0ĆRGqkeA*$2a;Ip i菶.4`1Gx?Sg_젷Hs1VL]]$1(Pl4Rʴb1sV(!_%Fz' U)h58E*acRh qW@GUE^IR l |!\#1@Ȝ}ْ{'xar3c^ hyF"x}5y^92"EЩ$Ah(zVN5*y~~X4 RXn8QAay,b*b"t+Ui锦k*]dE%mH޷vְEK_eS˾٬YkRt^It綏anrTt:  JtA[&Tc W+n*rd lvld,j9)Ȫx>6)EnAHUS2e6=1=뭽Fɪ괯2H2m-a-!m=|?b1I@JѪ-n^{u-Ԕ8MzG&,/n*wxXB޵٦;p^})Wc>.6Sڻ5Sڙɘr=黧97;=s\o};;[/_o/G|Y||DG>xJ_LgoxuDD5P{,IJ f큓(σ ]BO72VxpA{cdظ ^M#< d!>.t!o ZЈ4R!D9XK'->'LD/ft,21G 6pH1bǶ0 X2$b1XI\24N>擡LeR6I*c,gIZa.kFOѯ 0Xac/fsD4='Lh:ؔ59n/8#s )t,d^RiTVŚU+ĭ]+^VlPcÖ5VԵmuWEsֵWA{x.`< FlJ6X` sV $ɝrt 7{F=x(ƭ! [ ]m;"ٮ~-ө-w;inHz۹W?^lڭC'Z|zץiɋ痧94z/\9c0L?c>Ok0Ļ6L2ͷR쏵1Tކ1ǥ@L HN|G!ədR&#J)Oz*TJ&RH.KRL3="4͐t3<8s3 O;;ӓ>L=A%PCE4QEeQ4RI'RK/4SM7SO?5TQG%TSOE5UUWeUW_45VYgV[o5W]wW_6Xa%XcE6YeeYg6ZiZk( ;fcgi2-2.4.5/doc/fcgi-devel-kit.htm000066400000000000000000000774641477724357700166360ustar00rootroot00000000000000 FastCGI Developer's Kit

[[FastCGI]]

FastCGI Developer's Kit

Mark R. Brown
Open Market, Inc.

Document Version: 1.08
11 June 1996

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/
$Id: fcgi-devel-kit.htm,v 1.6 2002/02/25 00:42:59 robs Exp $


1. Introduction

FastCGI is an open extension to CGI that provides high performance for all Internet applications without the penalties of Web server APIs.

FastCGI is designed to be layered on top of existing Web server APIs. For instance, the mod_fastcgi Apache module adds FastCGI support to the Apache server. FastCGI can also be used, with reduced functionality and reduced performance, on any Web server that supports CGI.

This FastCGI Developer's Kit is designed to make developing FastCGI applications easy. The kit currently supports FastCGI applications written in C/C++, Perl, Tcl, and Java.

This document:

  • Describes how to configure and build the kit for your development platform.
  • Tells how to write applications using the libraries in the kit.
  • Tells how to run applications using Web servers that support FastCGI or using any Web server and cgi-fcgi.

The kit includes a technical white paper, doc/fastcgi-whitepaper/fastcgi.htm. You should read at least the first three sections of the technical white paper before starting to write FastCGI applications. The performance paper will help you understand how application design affects performance with FastCGI.

The FastCGI Specification, doc/fcgi-spec.html, defines the interface between a FastCGI application and a Web server that supports FastCGI. The software in the kit implements the specification. You don't need to read the specification in order to write applications.

Additional information is provided in the FAQ document, which contains frequently asked questions about application development using FastCGI, as well as some general information.

Experience with CGI programming will be extremely valuable in writing FastCGI applications. If you don't have enough experience with CGI programming, you should read one information on the W3C CGI page.

2. Getting started

The kit is a compressed tar (tar.Z) file, distributed via the fastcgi-archives.github.io Web page. Unpacking the tar file creates a new directory fcgi-devel-kit.

Open the kit's index page, fcgi-devel-kit/index.html, using the "Open File" command in your Web browser. The index page gives you an overview of the kit structure and helps you navigate the kit. The index page also contains links that run some example applications, but the applications won't work when index.html is opened using the "Open File" command because they aren't aren't being accessed through a Web server.

In order to use the kit in earnest you'll need a Web server that you control, a Web server running with your user ID. The Web server will be starting FastCGI applications that you will need to debug; this will be a lot more convenient for you if these processes run with your user ID. It is best to have a Web server that supports FastCGI. Section 4 discusses Web server issues.

If you can, keep the kit on a file system accessible from your personal workstation, do your builds on your workstation, and run your Web server on your workstation. If that's not possible, arrange a configuration such that the kit is accessible from the machine that's going to run your Web server, and build the kit and your applications on a machine that's configured exactly the same way (same processor architecture, operating system, etc.) as the machine that's going to run your Web server.

To build the kit you execute this sequence of commands in the fcgi-devel-kit directory:

    % ./configure
    % make

We've built and exercised the kit on these platforms (listed in alphabetical order):

  • BSD/OS 1.1 (Intel Pentium), gcc
  • Digital UNIX V3.2 148 (Alpha), gcc/cc
  • Hewlett-Packard HP-UX A.09.05 C and B.10.01 A (PA-RISC), gcc/cc
  • IBM AIX 1 4 (RS/6000), gcc
  • Silicon Graphics IRIX 5.3 11091812 (MIPS), gcc
  • Sun Solaris 2.4 and 2.5 (SPARC), gcc/cc
  • Sun SunOS 4.1.4 (SPARC), gcc

Once you've built the kit, follow the directions in Section 4 to bring up your Web server and run the example applications.

3. Writing applications

3.1 Using the fcgi_stdio library

The fcgi_stdio library provides the easiest transition for C CGI programs and C CGI programmers to FastCGI. Using this library your application can run using either CGI or FastCGI, with the same binary for both situations.

To introduce the fcgi_stdio library we give a pair of examples: a tiny CGI program and the translation of this program to FastCGI. These two example programs are included in the kit.

The CGI program is examples/tiny-cgi.c:

    #include <stdio.h>
    #include <stdlib.h>

    void main(void)
    {
        int count = 0;
        printf("Content-type: text/html\r\n"
               "\r\n"
               "<title>CGI Hello!</title>"
               "<h1>CGI Hello!</h1>"
               "Request number %d running on host <i>%s</i>\n",
               ++count, getenv("SERVER_NAME"));
    }

The key features of this tiny CGI program are:

  • The program sends data to the Web server by writing to stdout, using printf in this example. The CGI program first sends a Content-type header, then a small HTML document. The program includes stdio.h in order to get access to the printf function.
  • The program obtains parameters provided by the Web server by reading environment variables. The CGI program reads the SERVER_NAME variable using getenv and includes the value in the HTML document. The program includes stdlib.h in order to get access to the getenv function.

The count variable is degenerate in this example; the CGI program runs a single request, so the request number is always one. This variable will be more interesting in the FastCGI example.

The corresponding FastCGI program is examples/tiny-fcgi.c:

    #include "fcgi_stdio.h"
    #include <stdlib.h>

    void main(void)
    {
        int count = 0;
        while(FCGI_Accept() >= 0)
            printf("Content-type: text/html\r\n"
                   "\r\n"
                   "<title>FastCGI Hello!</title>"
                   "<h1>FastCGI Hello!</h1>"
                   "Request number %d running on host <i>%s</i>\n",
                    ++count, getenv("SERVER_NAME"));
    }

The key features of this tiny FastCGI program are:

  • The program is structured as a loop that begins by calling the function FCGI_Accept. The FCGI_Accept function blocks until a new request arrives for the program to execute. The program includes fcgi_stdio.h in order to get access to the FCGI_Accept function.
  • Within the loop, FCGI_Accept creates a CGI-compatible world. printf and getenv operate just as in the CGI program. stdin and stderr, not used by this tiny program, also operate just as in a CGI program.

The count variable increments each time through the loop, so the program displays a new request number each time. You can use the reload button in your browser to demonstrate this, once you've got the program built and running.

Building the program

If you can build examples/tiny-cgi.c, it will be straightforward for you to build examples/tiny-fcgi.c. You need to:

  • Add the directory containing the fcgi_stdio.h header to the compiler's include search path. The kit calls this directory include.
  • Add the library libfcgi.a to the linker's command line so that it will be searched when linking. The libfcgi.a library implements the functions defined in fcgi_stdio.h. The kit calls the directory containing this library libfcgi.
  • Determine whether or not the linker on your platform searches the Berkeley socket library by default, and if not, add linker directives to force this search.

See examples/Makefile (created by configure) for a Makefile that builds both programs. Autoconf handles the platform-dependent linking issues; to see how, examine configure.in and examples/Makefile.in.

Running the program

Section 4 is all about how to run FastCGI applications.

You can use CGI to run application binaries built with the fcgi_stdio library. The FCGI_Accept function tests its environment to determine how the application was invoked. If it was invoked as a CGI program, the first call to FCGI_Accept is essentially a no-op and the second call returns -1. In effect, the request loop disappears.

Of course, when a FastCGI application is run using CGI it does not get the benefits of FastCGI. For instance, the application exits after servicing a single request, so it cannot maintain cached information.

Implementation details

fcgi_stdio.h works by first including stdio.h, then defining macros to replace essentially all of the types and procedures defined in stdio.h. (stdio.h defines a few procedures that have nothing to do with FILE *, such as sprintf and sscanf; fcgi_stdio.h doesn't replace these.) For instance, FILE becomes FCGI_FILE and printf becomes FCGI_printf. You'll only see these new names if you read fcgi_stdio.h or examine your C source code after preprocessing.

Here are some consequences of this implementation technique:

  • On some platforms the implementation will break if you include stdio.h after including fcgi_stdio.h, because stdio.h often defines macros for functions such as getc and putc. Fortunately, on most platforms stdio.h is protected against multiple includes by lines near the top of the file that look like
        #ifndef _STDIO_H
        #define _STDIO_H
       
    

    The specific symbol used for multiple-include protection, _STDIO_H in this example, varies from platform to platform. As long as your platform protects stdio.h against multiple includes, you can forget about this issue.

  • If your application passes FILE * to functions implemented in libraries for which you have source code, then you'll want to recompile these libraries with fcgi_stdio.h included. Most C compilers provide a command-line option for including headers in a program being compiled; using such a compiler feature allows you to rebuild your libraries without making source changes. For instance the gcc command line
        gcc -include /usr/local/include/fcgi_stdio.h wonderlib.c
       
    

    causes gcc to include fcgi_stdio.h before it even begins to read the module wonderlib.c.

  • If your application passes FILE * to functions implemented in libraries for which you do not have source code, then you'll need to include the headers for these libraries before you include fcgi_stdio.h. You can't pass the stdin, stdout, or stderr streams produced by FCGI_Accept to any functions implemented by these libraries. You can pass a stream on a Unix file to a library function by following this pattern:
        FILE *myStream = fopen(path, "r");
        answer = MungeStream(FCGI_ToFile(myStream));
       
    

    Here MungeStream is a library function that you can't recompile and FCGI_ToFile is a macro that converts from FCGI_FILE * to FILE *. The macro FCGI_ToFile is defined in fcgi_stdio.h.

Converting CGI programs

The main task in converting a CGI program into a FastCGI program is separating the code that needs to execute once, initializing the program, from the code that needs to run for each request. In our tiny example, initializing the count variable is outside the loop, while incrementing the count variable goes inside.

Retained application state may be an issue. You must ensure that any application state created in processing one request has no unintended effects on later requests. FastCGI offers the possibility of significant application performance improvements, through caching; it is up to you to make the caches work correctly.

Storage leaks may be an issue. Many CGI programs don't worry about storage leaks because the programs don't run for long enough for bloating to be a problem. When converting to FastCGI, you can either use a tool such as Valgrind to discover and fix storage leaks, or you can run a C garbage collector such as Boehm-Demers-Weiser conservative garbage collector

Limitations

Currently there are some limits to the compatibility provided by the fcgi_stdio library:

  • The library does not provide FastCGI versions of the functions fscanf and scanf. If you wish to apply fscanf or scanf to stdin of a FastCGI program, the workaround is to read lines or other natural units into memory and then call sscanf. If you wish to apply fscanf to a stream on a Unix file, the workaround is to follow the pattern:
        FILE *myStream = fopen(path, "r");
        count = fscanf(FCGI_ToFile(myStream), format, ...);
       
    

Reference documentation

The FCGI_Accept manpage, doc/FCGI_Accept.3, describes the function in the traditional format.

The FCGI_Finish (doc/FCGI_Finish.3), FCGI_SetExitStatus (doc/FCGI_SetExitStatus.3), and FCGI_StartFilterData (doc/FCGI_StartFilterData.3) manpages document capabilities of the fcgi-stdio library that are not illustrated above.

3.2 Using the fcgiapp library

The fcgiapp library is a second C library for FastCGI. It does not provide the high degree of source code compatibility provided by fcgi_stdio; in return, it does not make such heavy use of #define. fcgi_stdio is implemented as a thin layer on top of fcgiapp.

Applications built using the fcgiapp library cannot run as CGI programs; that feature is provided at the fcgi_stdio level.

Functions defined in fcgiapp are named using the prefix FCGX_ rather than FCGI_. For instance, FCGX_Accept is the fcgiapp version of FCGI_Accept.

Documentation of the fcgiapp library takes the form of extensive comments in the header file include/fcgiapp.h. The sample programs examples/tiny-fcgi2.c and examples/echo2.c illustrate how to use fcgiapp.

3.3 Using Perl and Tcl

A major advantage of the FastCGI approach to high-performance Web applications is its language-neutrality. CGI scripts written in popular languages such as Perl and Tcl can be evolved into high-performance FastCGI applications.

We have produced FastCGI-integrated Perl and Tcl interpreters. Doing so was easy, since Perl and Tcl are conventional C applications and fcgi_stdio was designed for converting conventional C applications. Essentially no source code changes were required in these programs; a small amount of code was added in order to make FCGI_Accept and other FastCGI primitives available in these languages. And because these interpreters were developed using fcgi_stdio, they run standard Perl and Tcl applications (e.g. CGI scripts) as well as FastCGI applications.

See the fastcgi-archives.github.io Web page for more information about the Perl and Tcl libraries.

Here are the Perl and Tcl versions of tiny-fcgi:

#!./perl
use FCGI;
$count = 0;
while(FCGI::accept() >= 0) {
    print("Content-type: text/html\r\n\r\n",
          "<title>FastCGI Hello! (Perl)</title>\n",
          "<h1>FastCGI Hello! (Perl)</h1>\n";
          "Request number ",  ++$count,
          " running on host <i>";$env(SERVER_NAME)</i>");
}
#!./tclsh
set count 0 
while {[FCGI_Accept] >= 0 } {
    incr count
    puts -nonewline "Content-type: text/html\r\n\r\n"
    puts "<title>FastCGI Hello! (Tcl)</title>"
    puts "<h1>FastCGI Hello! (Tcl)</h1>"
    puts "Request number $count running on host <i>$env(SERVER_NAME)</i>"
}

Converting a Perl or Tcl CGI application to FastCGI is not fundamentally different from converting a C CGI application to FastCGI. You separate the portion of the application that performs one-time initialization from the portion that performs per-request processing. You put the per-request processing into a loop controlled by FCGI::accept (Perl) or FCGI_Accept (Tcl).

3.4 Using Java

Java is not just for browser-based applets. It is already suitable for writing some Web server applications, and its range of applicability will only grow as Java compilers and other Java tools improve. Java's modules, garbage collection, and threads are especially valuable for writing long-lived application servers.

The FCGIInterface class provides facilities for Java applications analogous to what fcgi_stdio provides for C applications. Using this library your Java application can run using either CGI or FastCGI.

The kit includes separate companion document on using FastCGI with Java. The source code for FastCGI classes is contained in directory java/src and the compiled code in java/classes.

Here is the Java version of tiny-fcgi:

import FCGIInterface;

class TinyFCGI { 
    public static void main (String args[]) {  
        int count = 0;
        while(new FCGIInterface().FCGIaccept()>= 0) {
            count ++;
            System.out.println("Content-type: text/html\r\n\r\n");
            System.out.println(
                    "<title>FastCGI Hello! (Java)</title>");
            System.out.println("<h1>FastCGI Hello! (Java)</h1>");
            System.out.println(
                    "request number " + count + " running on host <i>" +
                    System.getProperty("SERVER_NAME") + "</i>");
        }
    }
}

4. Running applications

4.1 Using a Web server that supports FastCGI

For a current listing of Web servers that support FastCGI, see the fastcgi-archives.github.io Web page.

Some of the Web servers that support FastCGI perform management of FastCGI applications. You don't need to start and stop FastCGI applications; the Web server takes care of this. If an application process should crash, the Web server restarts it.

Web servers support FastCGI via new configuration directives. Since these directives are server-specific, get more information from the documentation that accompanies each server.

4.2 Using cgi-fcgi with any Web server

The program cgi-fcgi allows you to run FastCGI applications using any Web server that supports CGI.

Here is how cgi-fcgi works. cgi-fcgi is a standard CGI program that uses Unix domain or TCP/IP sockets to communicate with a FastCGI application. cgi-fcgi takes the path name or host/port name of a listening socket as a parameter and connects to the FastCGI application listening on that socket. cgi-fcgi then forwards the CGI environment variables and stdin data to the FastCGI application, and forwards the stdout and stderr data from the FastCGI application to the Web server. When the FastCGI application signals the end of its response, cgi-fcgi flushes its buffers and exits.

Obviously, having cgi-fcgi is not as good as having a server with integrated FastCGI support:

  • Communication is slower than with a Web server that avoids the fork/exec overhead on every FastCGI request.
  • cgi-fcgi does not perform application management, so you need to provide this yourself.
  • cgi-fcgi supports only the Responder role.

But cgi-fcgi does allow you to develop applications that retain state in memory between connections, which often provides a major performance boost over normal CGI. And all the applications you develop using cgi-fcgi will work with Web servers that have integrated support for FastCGI.

The file examples/tiny-fcgi.cgi demonstrates a way to use cgi-fcgi to run a typical application, in this case the examples/tiny-fcgi application:

    #!../cgi-fcgi/cgi-fcgi -f
    -connect sockets/tiny-fcgi tiny-fcgi

On most Unix platforms, executing this command-interpreter file runs cgi-fcgi with arguments -f and examples/tiny-fcgi.cgi. (Beware: On some Unix platforms, including HP-UX, the first line of a command-interpreter file cannot contain more than 32 characters, including the newline; you may need to install the cgi-fcgi application in a standard place like /usr/local/bin or create a symbolic link to the cgi-fcgi application in the directory containing your application.) The cgi-fcgi program reads the command-interpreter file and connects to the FastCGI application whose listening socket is examples/sockets/tiny-fcgi.

Continuing the example, if cgi-fcgi's connection attempt fails, it creates a new process running the program examples/tiny-fcgi and listening on socket examples/sockets/tiny-fcgi. Then cgi-fcgi retries the connection attempt, which now should succeed.

The cgi-fcgi program has two other modes of operation. In one mode it connects to applications but does not start them; in the other it starts applications but does not connect to them. These modes are required when using TCP/IP. The cgi-fcgi manpage, doc/cgi-fcgi.1, tells the full story.

To run the example applications using cgi-fcgi, start your Web server and give it the directory fcgi-devel-kit as the root of its URL space. If the machine running your server is called bowser and your server is running on port 8888, you'd then open the URL http://bowser:8888/index.html to reach the kit's index page. Now the links on the index page that run example applications via cgi-fcgi should be active.

5. Known problems

On Digital UNIX 3.0 there's a problem with Unix domain listening sockets on NFS file systems. The symptom when using cgi-fcgi is an exit status of 38 (ENOTSOCK: socket operation on non-socket), but cgi-fcgi may dump core in this case when compiled optimized. Work-around: Store your Unix domain listening sockets on a non NFS file system, upgrade to Digital UNIX 3.2, or use TCP sockets.

On AIX there's a problem with shared listening sockets. The symptoms can include application core dumps and kernel panic. Work-around: Run a single FastCGI application server per listening socket.

6. Getting support

The FastCGI Archives Issues are used for discussions of issues in developing FastCGI applications, see https://github.com/FastCGI-Archives/fcgi2/issues.


© 1996, Open Market, Inc. / mbrown@openmarket.com
fcgi2-2.4.5/doc/fcgi-java.htm000066400000000000000000000500631477724357700156550ustar00rootroot00000000000000 Integrating FastCGI with Java
[[FastCGI]]

Integrating FastCGI with Java

Steve Harris
Open Market, Inc.
7 May 1996

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/

1. Introduction

Java is an object-oriented programming language developed by Sun Microsystems. The Java Depvelopers Kit (JDK), which contains the basic Java class packages, is available from Sun in both source and binary forms at Sun's JavaSoft site. This document assumes that you have some familiarity with the basics of compiling and running Java programs.

There are two kinds of applications built using Java.

  • Java Applets are graphical components which are run off HTML pages via the <APPLET> HTML extension tag.

  • Java Applications (Apps) are stand-alone programs that are run by invoking the Java interpreter directly. Like C programs, they have a main() method which the interpreter uses as an entry point.

The initial emphasis on using Java for client side applets should not obscure the fact that Java is a full strength programming language which can be used to develop server side stand alone applications, including CGI and now FastCGI applications.

The remainder of this document explains how to write and run FastCGI Java applications. It also illustrates the conversion of a sample Java CGI program to a FastCGI program.

2. Writing FastCGI applications in Java

Writing a FastCGI application in Java is as simple as writing one in C.

  1. Import the FCGIInterface class.
  2. Perform one-time initialization at the top of the main() method.
  3. Create a new FCGIInterface object and send it an FCGIaccept() message in a loop.
  4. Put the per-request application code inside that loop.

On return from FCGIaccept() you can access the request's environment variables using System.getProperty and perform request-related I/O through the standard variables System.in, System.out, and System.err.

To illustrate these points, the kit includes examples/TinyCGI, a CGI Java application, and examples/TinyFCGI, the FastCGI version of TinyCGI. These programs perform the same functions as the C programs examples/tiny-cgi.c and examples/tiny-fcgi.c that are used as examples in the FastCGI Developer's Kit document.

A. TinyCGI

 
class TinyCGI { 
 public static void main (String args[]) {  
  int count = 0;
                ++count;
  System.out.println("Content-type: text/html\n\n");
  System.out.println("<html>");
  System.out.println(
                 "<head><TITLE>CGI Hello</TITLE></head>");
  System.out.println("<body>");
  System.out.println("<H3>CGI-Hello</H3>");
  System.out.println("request number " + count + 
     " running on host " 
    + System.getProperty<"SERVER_NAME"));
  System.out.println("</body>");
  System.out.println("</html>"); 
  }
 }

B. TinyFCGI

 
import FCGIInterface;

class TinyFCGI { 
 public static void main (String args[]) {  
  int count = 0;
   while(new FCGIInterface().FCGIaccept()>= 0) {
   count ++;
   System.out.println("Content-type: text/html\n\n");
   System.out.println("<html>");
   System.out.println(
     "<head><TITLE>FastCGI-Hello Java stdio</TITLE></head>");
   System.out.println("<body>");
   System.out.println("<H3>FastCGI-HelloJava stdio</H3>");
   System.out.println("request number " + count + 
     " running on host " 
    + System.getProperty<"SERVER_NAME"));
   System.out.println("</body>");
   System.out.println("</html>"); 
   }
  }
 }

C. Running these Examples

We assume that you have downloaded the JDK and the FastCGI Developer's Kit, and that you have a Web server running that can access the fcgi-devel-kit/examples directory. In all cases where we specify paths, we are using relative paths within fcgi-devel-kit or the JDK which will need to be enlarged to a full path by the user.

Configuring
  1. Add your JDK's java/bin directory to your Unix PATH if it isn't there already.

  2. Add the directories fcgi-devel-kit/examples and fcgi-devel-kit/java/classes to your Java CLASSPATH.

  3. In your Open Market Secure WebServer configuration file, httpd.config, add the following two lines:

    ExternalAppClass TinyFCGI -host hostName:portNum
    Responder TinyFCGI fcgi-devel-kit/examples/TinyFCGI

    • hostName is the name of your host machine.
    • portNum is the port that you've selected for communication between the Web server and the Java application.

    On other servers you can use cgi-fcgi to get a similar effect.
  4. Create a soft link examples/javexe to the java/bin directory in your JDK. This link is required only to run the CGI scripts examples/TinyCGI.cgi and examples/TinyFCGI.cgi, which use it to invoke the Java interpreter java/bin/java. It is not used by FastCGI applications.
  5. You might have to modify examples/TinyFCGI.cgi to use a Unix shell for which your CLASSPATH is defined.
Running
  • To run TinyFCGI as FastCGI, you invoke the Java interpreter with the -D option, giving it the FCGI_PORT environment variable and the same portNum that was used in the Web server configuration. The command is:

    java -DFCGI_PORT=portNum TinyFCGI

    Then point your browser at fcgi-devel-kit/examples/TinyFCGI. Notice that each time you reload, the count increments.

  • To run TinyCGI, point your browser at fcgi-devel-kit/examples/TinyCGI.cgi on your host machine. Notice that the count does not increment.

  • Finally, you can run TinyFCGI as a straight CGI program by pointing your browser at fcgi-devel-kit/examplesi/TinyFCGI.cgi. The results are exactly the same as when you ran TinyCGI. Invoking a FastCGI program without an FCGI_PORT parameter tells the FastCGI interface to leave the normal CGI environment in place.

Due to gaps in the Java interpreter's support for listening sockets, Java FastCGI applications are currently limited to being started as external applications. They can't be started and managed by the Web server because they are incapable of using a listening socket that the Web server creates.

3. Standard I/O and Application Libraries

As we have seen above, FastCGI for Java offers a redefinition of standard I/O corresponding to the the fcgi_stdio functionality. It also offers a set of directly callable I/O methods corresponding to the fcgiapp C library. To understand where these methods occur we need to look briefly at the FastCGI redefinition of standard I/O.

Java defines standard I/O in the java.System class as follows:

public static InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in), 128);
public static PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true);
public static PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true);

The File Descriptors in, out, err are constants set to 0, 1 and 2 respectively.

The FastCGI interface redefines java.System in, out, and err by replacing the File streams with Socket streams and inserting streams which know how to manage the FastCGI protocol between the Socket streams and the Buffered streams in the above definitions.

For those cases where the FCGI application needs to bypass the standard I/O streams, it can directly access the methods of the FCGI input and output streams which roughly correspond to the functions in the C fcgiapp library. These streams can be accessed via the request class variable in FCGIInterface. Each Request object has instance variables that refer to an FCGIInputStream, and to two FCGIOutputStreams associated with that request.

4. Environment Variables

Java does not use the C environ list. Nor is there a getenv command that reads system environment variables. This is intentional for reasons of portability and security. Java has an internal dictionary of properties which belongs to the System class. These System properties are name/value associations that constitute the Java environment. When a Java application starts up, it reads in a file with default properties. As we have seen, additional System properties may be inserted by using the -D Java command argument.

For CGI, where the Java application is invoked from a .cgi script that, in turn, invokes the Java interpreter, this script could read the environment and pass the variables to the Java application either by writing a file or by creating -D options on the fly. Both of these methods are somewhat awkward.

For FastCGI Java applications, the environment variables are obtained from the FastCGI web server via FCGI_PARAMS records that are sent to the application at the start of each request. The FastCGI interface stores the original startup properties, combines these with the properties obtained from the server, and puts the new set of properties in the System properties dictionary. The only parameter that has to be specifically added at startup time is the FCGI_PORT parameter for the Socket creation. In the future, we expect that even this parameter won't be needed, since its use is due to an acknowledged rigidity in the JDK's implementation of sockets.

5. Further examples: EchoFCGI and Echo2FCGI

The next two examples illustrate the points made in the last two sections. EchoFCGI and Echo2FCGI both echo user input and display the application's environment variables. EchoFCGI reads the user input from System.in, while Echo2FCGI reads the user input directly from the intermediate FastCGI input stream.

A. EchoFCGI

import FCGIInterface;
import FCGIGlobalDefs;
import java.io.*;

class EchoFCGI {
 
 public static void main (String args[]) {
  int status = 0;
   while(new FCGIInterface().FCGIaccept()>= 0) {
  System.out.println("Content-type: text/html\n\n");
   System.out.println("<html>");
   System.out.println(
    "<head%gt;<TITLE>FastCGI echo
                                      </TITLE></head>");
   System.out.println("<body>"); 
   System.out.println(
                                         "<H2>FastCGI echo</H2>");
   System.out.println("<H3>STDIN</H3>");
   for ( int c = 0; c != -1; ) {
    try {
     c = System.in.read();
    } catch(IOException e) {
     System.out.println(
     "<br><b>SYSTEM EXCEPTION");
     Runtime rt = Runtime.getRuntime();
     rt.exit(status);
     }
    if (c != -1) { 
     System.out.print((char)c);
     }
    }
   System.out.println(
    "<H3>Environment Variables:</H3>");
 
   System.getProperties().list(System.out);
   System.out.println("</body>");
   System.out.println("</html>");
      }
  }
   }

B. Echo2FCGI

import FCGIInterface;
import FCGIGlobalDefs;
import FCGIInputStream;
import FCGIOutputStream;
import FCGIMessage;
import FCGIRequest;
import java.io.*;

class Echo2FCGI {

 public static void main (String args[]) {
  int status = 0;
                FCGIInterface intf = new FCGIInterface();
   while(intf.FCGIaccept()>= 0) {
  System.out.println("Content-type: text/html\n\n");
   System.out.println("<html>");
   System.out.println(
    "<head><TITLE>FastCGI echo
                                    </TITLE></head>");
   System.out.println("<body>");   
   System.out.println("<H2>FastCGI echo</H2>");
   System.out.println("<H3>STDIN:</H3">);
   for ( int c = 0; c != -1; ) {
    try {
     c = intf.request.inStream.read();
    } catch(IOException e) {
     System.out.println(
     "<br><b>SYSTEM EXCEPTION");
     Runtime rt = Runtime.getRuntime();
     rt.exit(status);
     }
    if (c != -1) { 
     System.out.print((char)c);
     }
    }
   System.out.println(
    "<H3>Environment Variables:</H3>");
 
   System.getProperties().list(System.out);
   System.out.println(<"/body>");
   System.out.println("</html>");
      }
  }
   }

C. Running these Examples

Configuring

As with TinyFCGI, you need to configure the web server to recognize these two FastCGI applications. Your configuration now looks like this:

ExternalAppClass java1 -host hostname:portNum
Responder java1 fcgi-devel-kit/examples/TinyFCGI
ExternalAppClass java2 -host hostname:portNumA
Responder java2 fcgi-devel-kit/examples/EchoFCGI
ExternalAppClass java3 -host hostname:porNumB
Responder java3 fcgi-devel-kit/examples/Echo2FCGI

Note that the application classes and port numbers are different for each application.

Running

As with TinyFCGI, you need to run these programs with the -D option using FCGI_PORT and the appropriate port number. To get some data for standard input we have created two html pages with forms that use a POST method. These are echo.html and echo2.html. You must edit these .html files to expand the path to fcgi-devel-kit/examples to a full path. Once the appropriate Java program is running, point your browser at the corresponding HTML page, enter some data and select the go_find button.

6. FastCGI Java Classes

The Java FastCGI classes are included in both source and byte code format in fcgi-devel-kit/java/src and :fcgi-devel-kit/java/classes respectively. The following is a brief description of these classes:

FCGIInterface
This class contains the FCGIaccept method called by the FastCGI user application. This method sets up the appropriate FastCGI environment for communication with the web server and manages FastCGI requests.
FCGIInputStream
This input stream manages FastCGI internal buffers to ensure that the user gets all of the FastCGI messages associated with a request. It uses FCGIMessage objects to interpret these incoming messages.
FCGIOutputStream
This output stream manages FastCGI internal buffers to send user data back to the web server and to notify the server of various FCGI protocol conditions. It uses FCGIMessage objects to format outgoing FastCGI messages.
FCGIMessage
This is the only class that understands the actual structure of the FastCGI messages. It interprets incoming FastCGI records and constructs outgoing ones..
FCGIRequest
This class currently contains data fields used by FastCGI to manage user requests. In a multi-threaded version of FastCGI, the role of this class will be expanded.
FCGIGlobalDefs
This class contains definitions of FastCGI constants.

Steve Harris // harris@openmarket.com
fcgi2-2.4.5/doc/fcgi-perf.htm000066400000000000000000000546771477724357700157070ustar00rootroot00000000000000 Understanding FastCGI Application Performance
[[FastCGI]]

Understanding FastCGI Application Performance

Mark R. Brown
Open Market, Inc.

10 June 1996

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/
$Id: fcgi-perf.htm,v 1.4 2002/02/25 00:42:59 robs Exp $


1. Introduction

Just how fast is FastCGI? How does the performance of a FastCGI application compare with the performance of the same application implemented using a Web server API?

Of course, the answer is that it depends upon the application. A more complete answer is that FastCGI often wins by a significant margin, and seldom loses by very much.

Papers on computer system performance can be laden with complex graphs showing how this varies with that. Seldom do the graphs shed much light on why one system is faster than another. Advertising copy is often even less informative. An ad from one large Web server vendor says that its server "executes web applications up to five times faster than all other servers," but the ad gives little clue where the number "five" came from.

This paper is meant to convey an understanding of the primary factors that influence the performance of Web server applications and to show that architectural differences between FastCGI and server APIs often give an "unfair" performance advantage to FastCGI applications. We run a test that shows a FastCGI application running three times faster than the corresponding Web server API application. Under different conditions this factor might be larger or smaller. We show you what you'd need to measure to figure that out for the situation you face, rather than just saying "we're three times faster" and moving on.

This paper makes no attempt to prove that FastCGI is better than Web server APIs for every application. Web server APIs enable lightweight protocol extensions, such as Open Market's SecureLink extension, to be added to Web servers, as well as allowing other forms of server customization. But APIs are not well matched to mainstream applications such as personalized content or access to corporate databases, because of API drawbacks including high complexity, low security, and limited scalability. FastCGI shines when used for the vast majority of Web applications.

2. Performance Basics

Since this paper is about performance we need to be clear on what "performance" is.

The standard way to measure performance in a request-response system like the Web is to measure peak request throughput subject to a response time constriaint. For instance, a Web server application might be capable of performing 20 requests per second while responding to 90% of the requests in less than 2 seconds.

Response time is a thorny thing to measure on the Web because client communications links to the Internet have widely varying bandwidth. If the client is slow to read the server's response, response time at both the client and the server will go up, and there's nothing the server can do about it. For the purposes of making repeatable measurements the client should have a high-bandwidth communications link to the server.

[Footnote: When designing a Web server application that will be accessed over slow (e.g. 14.4 or even 28.8 kilobit/second modem) channels, pay attention to the simultaneous connections bottleneck. Some servers are limited by design to only 100 or 200 simultaneous connections. If your application sends 50 kilobytes of data to a typical client that can read 2 kilobytes per second, then a request takes 25 seconds to complete. If your server is limited to 100 simultaneous connections, throughput is limited to just 4 requests per second.]

Response time is seldom an issue when load is light, but response times rise quickly as the system approaches a bottleneck on some limited resource. The three resources that typical systems run out of are network I/O, disk I/O, and processor time. If short response time is a goal, it is a good idea to stay at or below 50% load on each of these resources. For instance, if your disk subsystem is capable of delivering 200 I/Os per second, then try to run your application at 100 I/Os per second to avoid having the disk subsystem contribute to slow response times. Through careful management it is possible to succeed in running closer to the edge, but careful management is both difficult and expensive so few systems get it.

If a Web server application is local to the Web server machine, then its internal design has no impact on network I/O. Application design can have a big impact on usage of disk I/O and processor time.

3. Caching

It is a rare Web server application that doesn't run fast when all the information it needs is available in its memory. And if the application doesn't run fast under those conditions, the possible solutions are evident: Tune the processor-hungry parts of the application, install a faster processor, or change the application's functional specification so it doesn't need to do so much work.

The way to make information available in memory is by caching. A cache is an in-memory data structure that contains information that's been read from its permanent home on disk. When the application needs information, it consults the cache, and uses the information if it is there. Otherwise is reads the information from disk and places a copy in the cache. If the cache is full, the application discards some old information before adding the new. When the application needs to change cached information, it changes both the cache entry and the information on disk. That way, if the application crashes, no information is lost; the application just runs more slowly for awhile after restarting, because the cache doesn't improve performance when it is empty.

Caching can reduce both disk I/O and processor time, because reading information from disk uses more processor time than reading it from the cache. Because caching addresses both of the potential bottlenecks, it is the focal point of high-performance Web server application design. CGI applications couldn't perform in-memory caching, because they exited after processing just one request. Web server APIs promised to solve this problem. But how effective is the solution?

Today's most widely deployed Web server APIs are based on a pool-of-processes server model. The Web server consists of a parent process and a pool of child processes. Processes do not share memory. An incoming request is assigned to an idle child at random. The child runs the request to completion before accepting a new request. A typical server has 32 child processes, a large server has 100 or 200.

In-memory caching works very poorly in this server model because processes do not share memory and incoming requests are assigned to processes at random. For instance, to keep a frequently-used file available in memory the server must keep a file copy per child, which wastes memory. When the file is modified all the children need to be notified, which is complex (the APIs don't provide a way to do it).

FastCGI is designed to allow effective in-memory caching. Requests are routed from any child process to a FastCGI application server. The FastCGI application process maintains an in-memory cache.

In some cases a single FastCGI application server won't provide enough performance. FastCGI provides two solutions: session affinity and multi-threading.

With session affinity you run a pool of application processes and the Web server routes requests to individual processes based on any information contained in the request. For instance, the server can route according to the area of content that's been requested, or according to the user. The user might be identified by an application-specific session identifier, by the user ID contained in an Open Market Secure Link ticket, by the Basic Authentication user name, or whatever. Each process maintains its own cache, and session affinity ensures that each incoming request has access to the cache that will speed up processing the most.

With multi-threading you run an application process that is designed to handle several requests at the same time. The threads handling concurrent requests share process memory, so they all have access to the same cache. Multi-threaded programming is complex -- concurrency makes programs difficult to test and debug -- but with FastCGI you can write single threaded or multithreaded applications.

4. Database Access

Many Web server applications perform database access. Existing databases contain a lot of valuable information; Web server applications allow companies to give wider access to the information.

Access to database management systems, even within a single machine, is via connection-oriented protocols. An application "logs in" to a database, creating a connection, then performs one or more accesses. Frequently, the cost of creating the database connection is several times the cost of accessing data over an established connection.

To a first approximation database connections are just another type of state to be cached in memory by an application, so the discussion of caching above applies to caching database connections.

But database connections are special in one respect: They are often the basis for database licensing. You pay the database vendor according to the number of concurrent connections the database system can sustain. A 100-connection license costs much more than a 5-connection license. It follows that caching a database connection per Web server child process is not just wasteful of system's hardware resources, it could break your software budget.

5. A Performance Test

We designed a test application to illustrate performance issues. The application represents a class of applications that deliver personalized content. The test application is quite a bit simpler than any real application would be, but still illustrates the main performance issues. We implemented the application using both FastCGI and a current Web server API, and measured the performance of each.

5.1 Application Scenario

The application is based on a user database and a set of content files. When a user requests a content file, the application performs substitutions in the file using information from the user database. The application then returns the modified content to the user.

Each request accomplishes the following:

  1. authentication check: The user id is used to retrieve and check the password.

  2. attribute retrieval: The user id is used to retrieve all of the user's attribute values.

  3. file retrieval and filtering: The request identifies a content file. This file is read and all occurrences of variable names are replaced with the user's corresponding attribute values. The modified HTML is returned to the user.

Of course, it is fair game to perform caching to shortcut any of these steps.

Each user's database record (including password and attribute values) is approximately 100 bytes long. Each content file is 3,000 bytes long. Both database and content files are stored on disks attached to the server platform.

A typical user makes 10 file accesses with realistic think times (30-60 seconds) between accesses, then disappears for a long time.

5.2 Application Design

The FastCGI application maintains a cache of recently-accessed attribute values from the database. When the cache misses the application reads from the database. Because only a small number of FastCGI application processes are needed, each process opens a database connection on startup and keeps it open.

The FastCGI application is configured as multiple application processes. This is desirable in order to get concurrent application processing during database reads and file reads. Requests are routed to these application processes using FastCGI session affinity keyed on the user id. This way all a user's requests after the first hit in the application's cache.

The API application does not maintain a cache; the API application has no way to share the cache among its processes, so the cache hit rate would be too low to make caching pay. The API application opens and closes a database connection on every request; keeping database connections open between requests would result in an unrealistically large number of database connections open at the same time, and very low utilization of each connection.

5.3 Test Conditions

The test load is generated by 10 HTTP client processes. The processes represent disjoint sets of users. A process makes a request for a user, then a request for a different user, and so on until it is time for the first user to make another request.

For simplicity the 10 client processes run on the same machine as the Web server. This avoids the possibility that a network bottleneck will obscure the test results. The database system also runs on this machine, as specified in the application scenario.

Response time is not an issue under the test conditions. We just measure throughput.

The API Web server is in these tests is Netscape 1.1.

5.4 Test Results and Discussion

Here are the test results:

    FastCGI  12.0 msec per request = 83 requests per second
    API      36.6 msec per request = 27 requests per second

Given the big architectural advantage that the FastCGI application enjoys over the API application, it is not surprising that the FastCGI application runs a lot faster. To gain a deeper understanding of these results we measured two more conditions:

  • API with sustained database connections. If you could afford the extra licensing cost, how much faster would your API application run?

        API      16.0 msec per request = 61 requests per second
    
    Answer: Still not as fast as the FastCGI application.

  • FastCGI with cache disabled. How much benefit does the FastCGI application get from its cache?

        FastCGI  20.1 msec per request = 50 requests per second
    
    Answer: A very substantial benefit, even though the database access is quite simple.

What these two extra experiments show is that if the API and FastCGI applications are implemented in exactly the same way -- caching database connections but not caching user profile data -- the API application is slightly faster. This is what you'd expect, since the FastCGI application has to pay the cost of inter-process communication not present in the API application.

In the real world the two applications would not be implemented in the same way. FastCGI's architectural advantage results in much higher performance -- a factor of 3 in this test. With a remote database or more expensive database access the factor would be higher. With more substantial processing of the content files the factor would be smaller.

6. Multi-threaded APIs

Web servers with a multi-threaded internal structure (and APIs to match) are now starting to become more common. These servers don't have all of the disadvantages described in Section 3. Does this mean that FastCGI's performance advantages will disappear?

A superficial analysis says yes. An API-based application in a single-process, multi-threaded server can maintain caches and database connections the same way a FastCGI application can. The API-based application does not pay for inter-process communication, so the API-based application will be slightly faster than the FastCGI application.

A deeper analysis says no. Multi-threaded programming is complex, because concurrency makes programs much more difficult to test and debug. In the case of multi-threaded programming to Web server APIs, the normal problems with multi-threading are compounded by the lack of isolation between different applications and between the applications and the Web server. With FastCGI you can write programs in the familiar single-threaded style, get all the reliability and maintainability of process isolation, and still get very high performance. If you truly need multi-threading, you can write multi-threaded FastCGI and still isolate your multi-threaded application from other applications and from the server. In short, multi-threading makes Web server APIs unusable for practically all applications, reducing the choice to FastCGI versus CGI. The performance winner in that contest is obviously FastCGI.

7. Conclusion

Just how fast is FastCGI? The answer: very fast indeed. Not because it has some specially-greased path through the operating system, but because its design is well matched to the needs of most applications. We invite you to make FastCGI the fast, open foundation for your Web server applications.


OMI Home Page
© 1995, Open Market, Inc. / mbrown@openmarket.com
fcgi2-2.4.5/doc/fcgi-perl.htm000066400000000000000000000034431477724357700156760ustar00rootroot00000000000000 Integrating FastCGI with Perl-5
[[FastCGI]]

Integrating FastCGI with Perl-5

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-949-7000 URL: http://www.openmarket.com/
$Id: fcgi-perl.htm,v 1.5 2002/02/25 00:42:59 robs Exp $

Perl (Practical Extraction and Report Language) is a scripting language that is often used for CGI programming. Perl is freely available.

FastCGI support is available for Perl via the FCGI.pm Perl module. FCGI.pm no longer requires SFIO or a specially-built Perl. FCGI.pm is available via CPAN as well as in the perl directory of this kit.

Please see the FCGI.pm documentation for examples and details.

fcgi2-2.4.5/doc/fcgi-spec.html000066400000000000000000001617451477724357700160540ustar00rootroot00000000000000 FastCGI Specification

FastCGI Specification

Mark R. Brown
Open Market, Inc.

Document Version: 1.0
29 April 1996

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/

$Id: fcgi-spec.html,v 1.4 2002/02/25 00:42:59 robs Exp $


1. Introduction

FastCGI is an open extension to CGI that provides high performance for all Internet applications without the penalties of Web server APIs.

This specification has narrow goal: to specify, from an application perspective, the interface between a FastCGI application and a Web server that supports FastCGI. Many Web server features related to FastCGI, e.g. application management facilities, have nothing to do with the application to Web server interface, and are not described here.

This specification is for Unix (more precisely, for POSIX systems that support Berkeley Sockets). The bulk of the specification is a simple communications protocol that is independent of byte ordering and will extend to other systems.

We'll introduce FastCGI by comparing it with conventional Unix implementations of CGI/1.1. FastCGI is designed to support long-lived application processes, i.e. application servers. That's a major difference compared with conventional Unix implementations of CGI/1.1, which construct an application process, use it respond to one request, and have it exit.

The initial state of a FastCGI process is more spartan than the initial state of a CGI/1.1 process, because the FastCGI process doesn't begin life connected to anything. It doesn't have the conventional open files stdin, stdout, and stderr, and it doesn't receive much information through environment variables. The key piece of initial state in a FastCGI process is a listening socket, through which it accepts connections from a Web server.

After a FastCGI process accepts a connection on its listening socket, the process executes a simple protocol to receive and send data. The protocol serves two purposes. First, the protocol multiplexes a single transport connection between several independent FastCGI requests. This supports applications that are able to process concurrent requests using event-driven or multi-threaded programming techniques. Second, within each request the protocol provides several independent data streams in each direction. This way, for instance, both stdout and stderr data pass over a single transport connection from the application to the Web server, rather than requiring separate pipes as with CGI/1.1.

A FastCGI application plays one of several well-defined roles. The most familiar is the Responder role, in which the application receives all the information associated with an HTTP request and generates an HTTP response; that's the role CGI/1.1 programs play. A second role is Authorizer, in which the application receives all the information associated with an HTTP request and generates an authorized/unauthorized decision. A third role is Filter, in which the application receives all the information associated with an HTTP request, plus an extra stream of data from a file stored on the Web server, and generates a "filtered" version of the data stream as an HTTP response. The framework is extensible so that more FastCGI can be defined later.

In the remainder of this specification the terms "FastCGI application," "application process," or "application server" are abbreviated to "application" whenever that won't cause confusion.

2. Initial Process State

2.1 Argument list

By default the Web server creates an argument list containing a single element, the name of the application, taken to be the last component of the executable's path name. The Web server may provide a way to specify a different application name, or a more elaborate argument list.

Note that the file executed by the Web server might be an interpreter file (a text file that starts with the characters #!), in which case the application's argument list is constructed as described in the execve manpage.

2.2 File descriptors

The Web server leaves a single file descriptor, FCGI_LISTENSOCK_FILENO, open when the application begins execution. This descriptor refers to a listening socket created by the Web server.

FCGI_LISTENSOCK_FILENO equals STDIN_FILENO. The standard descriptors STDOUT_FILENO and STDERR_FILENO are closed when the application begins execution. A reliable method for an application to determine whether it was invoked using CGI or FastCGI is to call getpeername(FCGI_LISTENSOCK_FILENO), which returns -1 with errno set to ENOTCONN for a FastCGI application.

The Web server's choice of reliable transport, Unix stream pipes (AF_UNIX) or TCP/IP (AF_INET), is implicit in the internal state of the FCGI_LISTENSOCK_FILENO socket.

2.3 Environment variables

The Web server may use environment variables to pass parameters to the application. This specification defines one such variable, FCGI_WEB_SERVER_ADDRS; we expect more to be defined as the specification evolves. The Web server may provide a way to bind other environment variables, such as the PATH variable.

2.4 Other state

The Web server may provide a way to specify other components of an application's initial process state, such as the priority, user ID, group ID, root directory, and working directory of the process.

3. Protocol Basics

3.1 Notation

We use C language notation to define protocol message formats. All structure elements are defined in terms of the unsigned char type, and are arranged so that an ISO C compiler lays them out in the obvious manner, with no padding. The first byte defined in the structure is transmitted first, the second byte second, etc.

We use two conventions to abbreviate our definitions.

First, when two adjacent structure components are named identically except for the suffixes "B1" and "B0," it means that the two components may be viewed as a single number, computed as B1<<8 + B0. The name of this single number is the name of the components, minus the suffixes. This convention generalizes in an obvious way to handle numbers represented in more than two bytes.

Second, we extend C structs to allow the form

        struct {
            unsigned char mumbleLengthB1;
            unsigned char mumbleLengthB0;
            ... /* other stuff */
            unsigned char mumbleData[mumbleLength];
        };

meaning a structure of varying length, where the length of a component is determined by the values of the indicated earlier component or components.

3.2 Accepting Transport Connections

A FastCGI application calls accept() on the socket referred to by file descriptor FCGI_LISTENSOCK_FILENO to accept a new transport connection. If the accept() succeeds, and the FCGI_WEB_SERVER_ADDRS environment variable is bound, the application application immediately performs the following special processing:

  • FCGI_WEB_SERVER_ADDRS: The value is a list of valid IP addresses for the Web server.

    If FCGI_WEB_SERVER_ADDRS was bound, the application checks the peer IP address of the new connection for membership in the list. If the check fails (including the possibility that the connection didn't use TCP/IP transport), the application responds by closing the connection.

    FCGI_WEB_SERVER_ADDRS is expressed as a comma-separated list of IP addresses. Each IP address is written as four decimal numbers in the range [0..255] separated by decimal points. So one legal binding for this variable is FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71.



An application may accept several concurrent transport connections, but it need not do so.

3.3 Records

Applications execute requests from a Web server using a simple protocol. Details of the protocol depend upon the application's role, but roughly speaking the Web server first sends parameters and other data to the application, then the application sends result data to the Web server, and finally the application sends the Web server an indication that the request is complete.

All data that flows over the transport connection is carried in FastCGI records. FastCGI records accomplish two things. First, records multiplex the transport connection between several independent FastCGI requests. This multiplexing supports applications that are able to process concurrent requests using event-driven or multi-threaded programming techniques. Second, records provide several independent data streams in each direction within a single request. This way, for instance, both stdout and stderr data can pass over a single transport connection from the application to the Web server, rather than requiring separate connections.

        typedef struct {
            unsigned char version;
            unsigned char type;
            unsigned char requestIdB1;
            unsigned char requestIdB0;
            unsigned char contentLengthB1;
            unsigned char contentLengthB0;
            unsigned char paddingLength;
            unsigned char reserved;
            unsigned char contentData[contentLength];
            unsigned char paddingData[paddingLength];
        } FCGI_Record;

A FastCGI record consists of a fixed-length prefix followed by a variable number of content and padding bytes. A record contains seven components:

  • version: Identifies the FastCGI protocol version. This specification documents FCGI_VERSION_1.

  • type: Identifies the FastCGI record type, i.e. the general function that the record performs. Specific record types and their functions are detailed in later sections.

  • requestId: Identifies the FastCGI request to which the record belongs.

  • contentLength: The number of bytes in the contentData component of the record.

  • paddingLength: The number of bytes in the paddingData component of the record.

  • contentData: Between 0 and 65535 bytes of data, interpreted according to the record type.

  • paddingData: Between 0 and 255 bytes of data, which are ignored.

We use a relaxed C struct initializer syntax to specify constant FastCGI records. We omit the version component, ignore padding, and treat requestId as a number. Thus {FCGI_END_REQUEST, 1, {FCGI_REQUEST_COMPLETE,0}} is a record with type == FCGI_END_REQUEST, requestId == 1, and contentData == {FCGI_REQUEST_COMPLETE,0}.

Padding

The protocol allows senders to pad the records they send, and requires receivers to interpret the paddingLength and skip the paddingData. Padding allows senders to keep data aligned for more efficient processing. Experience with the X window system protocols shows the performance benefit of such alignment.

We recommend that records be placed on boundaries that are multiples of eight bytes. The fixed-length portion of a FCGI_Record is eight bytes.

Managing Request IDs

The Web server re-uses FastCGI request IDs; the application keeps track of the current state of each request ID on a given transport connection. A request ID R becomes active when the application receives a record {FCGI_BEGIN_REQUEST, R, ...} and becomes inactive when the application sends a record {FCGI_END_REQUEST, R, ...} to the Web server.

While a request ID R is inactive, the application ignores records with requestId == R, except for FCGI_BEGIN_REQUEST records as just described.

The Web server attempts to keep FastCGI request IDs small. That way the application can keep track of request ID states using a short array rather than a long array or a hash table. An application also has the option of accepting only one request at a time. In this case the application simply checks incoming requestId values against the current request ID.

Types of Record Types

There are two useful ways of classifying FastCGI record types.

The first distinction is between management records and application records. A management record contains information that is not specific to any Web server request, such as information about the protocol capabilities of the application. An application record contains information about a particular request, identified by the requestId component.

Management records have a requestId value of zero, also called the null request ID. Application records have a nonzero requestId.

The second distinction is between discrete and stream records. A discrete record contains a meaningful unit of data all by itself. A stream record is part of a stream, i.e. a series of zero or more non-empty records (length != 0) of the stream type, followed by an empty record (length == 0) of the stream type. The contentData components of a stream's records, when concatenated, form a byte sequence; this byte sequence is the value of the stream. Therefore the value of a stream is independent of how many records it contains or how its bytes are divided among the non-empty records.

These two classifications are independent. Among the record types defined in this version of the FastCGI protocol, all management record types are also discrete record types, and nearly all application record types are stream record types. But three application record types are discrete, and nothing prevents defining a management record type that's a stream in some later version of the protocol.

3.4 Name-Value Pairs

In many of their roles, FastCGI applications need to read and write varying numbers of variable-length values. So it is useful to adopt a standard format for encoding a name-value pair.

FastCGI transmits a name-value pair as the length of the name, followed by the length of the value, followed by the name, followed by the value. Lengths of 127 bytes and less can be encoded in one byte, while longer lengths are always encoded in four bytes:

        typedef struct {
            unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
            unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
            unsigned char nameData[nameLength];
            unsigned char valueData[valueLength];
        } FCGI_NameValuePair11;

        typedef struct {
            unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
            unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
            unsigned char valueLengthB2;
            unsigned char valueLengthB1;
            unsigned char valueLengthB0;
            unsigned char nameData[nameLength];
            unsigned char valueData[valueLength
                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
        } FCGI_NameValuePair14;

        typedef struct {
            unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
            unsigned char nameLengthB2;
            unsigned char nameLengthB1;
            unsigned char nameLengthB0;
            unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
            unsigned char nameData[nameLength
                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
            unsigned char valueData[valueLength];
        } FCGI_NameValuePair41;

        typedef struct {
            unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
            unsigned char nameLengthB2;
            unsigned char nameLengthB1;
            unsigned char nameLengthB0;
            unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
            unsigned char valueLengthB2;
            unsigned char valueLengthB1;
            unsigned char valueLengthB0;
            unsigned char nameData[nameLength
                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
            unsigned char valueData[valueLength
                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
        } FCGI_NameValuePair44;

The high-order bit of the first byte of a length indicates the length's encoding. A high-order zero implies a one-byte encoding, a one a four-byte encoding.

This name-value pair format allows the sender to transmit binary values without additional encoding, and enables the receiver to allocate the correct amount of storage immediately even for large values.

3.5 Closing Transport Connections

The Web server controls the lifetime of transport connections. The Web server can close a connection when no requests are active. Or the Web server can delegate close authority to the application (see FCGI_BEGIN_REQUEST). In this case the application closes the connection at the end of a specified request.

This flexibility accommodates a variety of application styles. Simple applications will process one request at a time and accept a new transport connection for each request. More complex applications will process concurrent requests, over one or multiple transport connections, and will keep transport connections open for long periods of time.

A simple application gets a significant performance boost by closing the transport connection when it has finished writing its response. The Web server needs to control the connection lifetime for long-lived connections.

When an application closes a connection or finds that a connection has closed, the application initiates a new connection.

4. Management Record Types

4.1 FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT

The Web server can query specific variables within the application. The server will typically perform a query on application startup in order to to automate certain aspects of system configuration.

The application receives a query as a record {FCGI_GET_VALUES, 0, ...}. The contentData portion of a FCGI_GET_VALUES record contains a sequence of name-value pairs with empty values.

The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied. If the application doesn't understand a variable name that was included in the query, it omits that name from the response.

FCGI_GET_VALUES is designed to allow an open-ended set of variables. The initial set provides information to help the server perform application and connection management:

  • FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept, e.g. "1" or "10".

  • FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. "1" or "50".

  • FCGI_MPXS_CONNS: "0" if this application does not multiplex connections (i.e. handle concurrent requests over each connection), "1" otherwise.

An application may receive a FCGI_GET_VALUES record at any time. The application's response should not involve the application proper but only the FastCGI library.

4.2 FCGI_UNKNOWN_TYPE

The set of management record types is likely to grow in future versions of this protocol. To provide for this evolution, the protocol includes the FCGI_UNKNOWN_TYPE management record. When an application receives a management record whose type T it does not understand, the application responds with {FCGI_UNKNOWN_TYPE, 0, {T}}.

The contentData component of a FCGI_UNKNOWN_TYPE record has the form:

        typedef struct {
            unsigned char type;    
            unsigned char reserved[7];
        } FCGI_UnknownTypeBody;

The type component is the type of the unrecognized management record.

5. Application Record Types

5.1 FCGI_BEGIN_REQUEST

The Web server sends a FCGI_BEGIN_REQUEST record to start a request.

The contentData component of a FCGI_BEGIN_REQUEST record has the form:

        typedef struct {
            unsigned char roleB1;
            unsigned char roleB0;
            unsigned char flags;
            unsigned char reserved[5];
        } FCGI_BeginRequestBody;

The role component sets the role the Web server expects the application to play. The currently-defined roles are:

  • FCGI_RESPONDER
  • FCGI_AUTHORIZER
  • FCGI_FILTER

Roles are described in more detail in Section 6 below.

The flags component contains a bit that controls connection shutdown:

  • flags & FCGI_KEEP_CONN: If zero, the application closes the connection after responding to this request. If not zero, the application does not close the connection after responding to this request; the Web server retains responsibility for the connection.

5.2 Name-Value Pair Stream: FCGI_PARAMS

FCGI_PARAMS

is a stream record type used in sending name-value pairs from the Web server to the application. The name-value pairs are sent down the stream one after the other, in no specified order.

5.3 Byte Streams: FCGI_STDIN, FCGI_DATA, FCGI_STDOUT, FCGI_STDERR

FCGI_STDIN

is a stream record type used in sending arbitrary data from the Web server to the application. FCGI_DATA is a second stream record type used to send additional data to the application.

FCGI_STDOUT and FCGI_STDERR are stream record types for sending arbitrary data and error data respectively from the application to the Web server.

5.4 FCGI_ABORT_REQUEST

The Web server sends a FCGI_ABORT_REQUEST record to abort a request. After receiving {FCGI_ABORT_REQUEST, R}, the application responds as soon as possible with {FCGI_END_REQUEST, R, {FCGI_REQUEST_COMPLETE, appStatus}}. This is truly a response from the application, not a low-level acknowledgement from the FastCGI library.

A Web server aborts a FastCGI request when an HTTP client closes its transport connection while the FastCGI request is running on behalf of that client. The situation may seem unlikely; most FastCGI requests will have short response times, with the Web server providing output buffering if the client is slow. But the FastCGI application may be delayed communicating with another system, or performing a server push.

When a Web server is not multiplexing requests over a transport connection, the Web server can abort a request by closing the request's transport connection. But with multiplexed requests, closing the transport connection has the unfortunate effect of aborting all the requests on the connection.

5.5 FCGI_END_REQUEST

The application sends a FCGI_END_REQUEST record to terminate a request, either because the application has processed the request or because the application has rejected the request.

The contentData component of a FCGI_END_REQUEST record has the form:

        typedef struct {
            unsigned char appStatusB3;
            unsigned char appStatusB2;
            unsigned char appStatusB1;
            unsigned char appStatusB0;
            unsigned char protocolStatus;
            unsigned char reserved[3];
        } FCGI_EndRequestBody;

The appStatus component is an application-level status code. Each role documents its usage of appStatus.

The protocolStatus component is a protocol-level status code; the possible protocolStatus values are:

  • FCGI_REQUEST_COMPLETE: normal end of request.

  • FCGI_CANT_MPX_CONN: rejecting a new request. This happens when a Web server sends concurrent requests over one connection to an application that is designed to process one request at a time per connection.

  • FCGI_OVERLOADED: rejecting a new request. This happens when the application runs out of some resource, e.g. database connections.

  • FCGI_UNKNOWN_ROLE: rejecting a new request. This happens when the Web server has specified a role that is unknown to the application.

6. Roles

6.1 Role Protocols

Role protocols only include records with application record types. They transfer essentially all data using streams.

To make the protocols reliable and to simplify application programming, role protocols are designed to use nearly sequential marshalling. In a protocol with strictly sequential marshalling, the application receives its first input, then its second, etc. until it has received them all. Similarly, the application sends its first output, then its second, etc. until it has sent them all. Inputs are not interleaved with each other, and outputs are not interleaved with each other.

The sequential marshalling rule is too restrictive for some FastCGI roles, because CGI programs can write to both stdout and stderr without timing restrictions. So role protocols that use both FCGI_STDOUT and FCGI_STDERR allow these two streams to be interleaved.

All role protocols use the FCGI_STDERR stream just the way stderr is used in conventional applications programming: to report application-level errors in an intelligible way. Use of the FCGI_STDERR stream is always optional. If an application has no errors to report, it sends either no FCGI_STDERR records or one zero-length FCGI_STDERR record.

When a role protocol calls for transmitting a stream other than FCGI_STDERR, at least one record of the stream type is always transmitted, even if the stream is empty.

Again in the interests of reliable protocols and simplified application programming, role protocols are designed to be nearly request-response. In a truly request-response protocol, the application receives all of its input records before sending its first output record. Request-response protocols don't allow pipelining.

The request-response rule is too restrictive for some FastCGI roles; after all, CGI programs aren't restricted to read all of stdin before starting to write stdout. So some role protocols allow that specific possibility. First the application receives all of its inputs except for a final stream input. As the application begins to receive the final stream input, it can begin writing its output.

When a role protocol uses FCGI_PARAMS to transmit textual values, such as the values that CGI programs obtain from environment variables, the length of the value does not include the terminating null byte, and the value itself does not include a null byte. An application that needs to provide environ(7) format name-value pairs must insert an equal sign between the name and value and append a null byte after the value.

Role protocols do not support the non-parsed header feature of CGI. FastCGI applications set response status using the Status and Location CGI headers.

6.2 Responder

A Responder FastCGI application has the same purpose as a CGI/1.1 program: It receives all the information associated with an HTTP request and generates an HTTP response.

It suffices to explain how each element of CGI/1.1 is emulated by a Responder:



  • The Responder application receives CGI/1.1 environment variables from the Web server over FCGI_PARAMS.

  • Next the Responder application receives CGI/1.1 stdin data from the Web server over FCGI_STDIN. The application receives at most CONTENT_LENGTH bytes from this stream before receiving the end-of-stream indication. (The application receives less than CONTENT_LENGTH bytes only if the HTTP client fails to provide them, e.g. because the client crashed.)

  • The Responder application sends CGI/1.1 stdout data to the Web server over FCGI_STDOUT, and CGI/1.1 stderr data over FCGI_STDERR. The application sends these concurrently, not one after the other. The application must wait to finish reading FCGI_PARAMS before it begins writing FCGI_STDOUT and FCGI_STDERR, but it needn't finish reading from FCGI_STDIN before it begins writing these two streams.

  • After sending all its stdout and stderr data, the Responder application sends a FCGI_END_REQUEST record. The application sets the protocolStatus component to FCGI_REQUEST_COMPLETE and the appStatus component to the status code that the CGI program would have returned via the exit system call.

A Responder performing an update, e.g. implementing a POST method, should compare the number of bytes received on FCGI_STDIN with CONTENT_LENGTH and abort the update if the two numbers are not equal.

6.3 Authorizer

An Authorizer FastCGI application receives all the information associated with an HTTP request and generates an authorized/unauthorized decision. In case of an authorized decision the Authorizer can also associate name-value pairs with the HTTP request; when giving an unauthorized decision the Authorizer sends a complete response to the HTTP client.

Since CGI/1.1 defines a perfectly good way to represent the information associated with an HTTP request, Authorizers use the same representation:

  • The Authorizer application receives HTTP request information from the Web server on the FCGI_PARAMS stream, in the same format as a Responder. The Web server does not send CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED, and SCRIPT_NAME headers.

  • The Authorizer application sends stdout and stderr data in the same manner as a Responder. The CGI/1.1 response status specifies the disposition of the request. If the application sends status 200 (OK), the Web server allows access. Depending upon its configuration the Web server may proceed with other access checks, including requests to other Authorizers.

    An Authorizer application's 200 response may include headers whose names are prefixed with Variable-. These headers communicate name-value pairs from the application to the Web server. For instance, the response header

            Variable-AUTH_METHOD: database lookup
    
    transmits the value "database lookup" with name AUTH-METHOD. The server associates such name-value pairs with the HTTP request and includes them in subsequent CGI or FastCGI requests performed in processing the HTTP request. When the application gives a 200 response, the server ignores response headers whose names aren't prefixed with Variable- prefix, and ignores any response content.

    For Authorizer response status values other than "200" (OK), the Web server denies access and sends the response status, headers, and content back to the HTTP client.



6.4 Filter

A Filter FastCGI application receives all the information associated with an HTTP request, plus an extra stream of data from a file stored on the Web server, and generates a "filtered" version of the data stream as an HTTP response.

A Filter is similar in functionality to a Responder that takes a data file as a parameter. The difference is that with a Filter, both the data file and the Filter itself can be access controlled using the Web server's access control mechanisms, while a Responder that takes the name of a data file as a parameter must perform its own access control checks on the data file.

The steps taken by a Filter are similar to those of a Responder. The server presents the Filter with environment variables first, then standard input (normally form POST data), finally the data file input:



  • Like a Responder, the Filter application receives name-value pairs from the Web server over FCGI_PARAMS. Filter applications receive two Filter-specific variables: FCGI_DATA_LAST_MOD and FCGI_DATA_LENGTH.

  • Next the Filter application receives CGI/1.1 stdin data from the Web server over FCGI_STDIN. The application receives at most CONTENT_LENGTH bytes from this stream before receiving the end-of-stream indication. (The application receives less than CONTENT_LENGTH bytes only if the HTTP client fails to provide them, e.g. because the client crashed.)

  • Next the Filter application receives the file data from the Web server over FCGI_DATA. This file's last modification time (expressed as an integer number of seconds since the epoch January 1, 1970 UTC) is FCGI_DATA_LAST_MOD; the application may consult this variable and respond from a cache without reading the file data. The application reads at most FCGI_DATA_LENGTH bytes from this stream before receiving the end-of-stream indication.

  • The Filter application sends CGI/1.1 stdout data to the Web server over FCGI_STDOUT, and CGI/1.1 stderr data over FCGI_STDERR. The application sends these concurrently, not one after the other. The application must wait to finish reading FCGI_STDIN before it begins writing FCGI_STDOUT and FCGI_STDERR, but it needn't finish reading from FCGI_DATA before it begins writing these two streams.

  • After sending all its stdout and stderr data, the application sends a FCGI_END_REQUEST record. The application sets the protocolStatus component to FCGI_REQUEST_COMPLETE and the appStatus component to the status code that a similar CGI program would have returned via the exit system call.

A Filter should compare the number of bytes received on FCGI_STDIN with CONTENT_LENGTH and on FCGI_DATA with FCGI_DATA_LENGTH. If the numbers don't match and the Filter is a query, the Filter response should provide an indication that data is missing. If the numbers don't match and the Filter is an update, the Filter should abort the update.

7. Errors

A FastCGI application exits with zero status to indicate that it terminated on purpose, e.g. in order to perform a crude form of garbage collection. A FastCGI application that exits with nonzero status is assumed to have crashed. How a Web server or other application manager responds to applications that exit with zero or nonzero status is outside the scope of this specification.

A Web server can request that a FastCGI application exit by sending it SIGTERM. If the application ignores SIGTERM the Web server can resort to SIGKILL.

FastCGI applications report application-level errors with the FCGI_STDERR stream and the appStatus component of the FCGI_END_REQUEST record. In many cases an error will be reported directly to the user via the FCGI_STDOUT stream.

On Unix, applications report lower-level errors, including FastCGI protocol errors and syntax errors in FastCGI environment variables, to syslog. Depending upon the severity of the error, the application may either continue or exit with nonzero status.

8. Types and Constants

/*
 * Listening socket file number
 */
#define FCGI_LISTENSOCK_FILENO 0

typedef struct {
    unsigned char version;
    unsigned char type;
    unsigned char requestIdB1;
    unsigned char requestIdB0;
    unsigned char contentLengthB1;
    unsigned char contentLengthB0;
    unsigned char paddingLength;
    unsigned char reserved;
} FCGI_Header;

/*
 * Number of bytes in a FCGI_Header.  Future versions of the protocol
 * will not reduce this number.
 */
#define FCGI_HEADER_LEN  8

/*
 * Value for version component of FCGI_Header
 */
#define FCGI_VERSION_1           1

/*
 * Values for type component of FCGI_Header
 */
#define FCGI_BEGIN_REQUEST       1
#define FCGI_ABORT_REQUEST       2
#define FCGI_END_REQUEST         3
#define FCGI_PARAMS              4
#define FCGI_STDIN               5
#define FCGI_STDOUT              6
#define FCGI_STDERR              7
#define FCGI_DATA                8
#define FCGI_GET_VALUES          9
#define FCGI_GET_VALUES_RESULT  10
#define FCGI_UNKNOWN_TYPE       11
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)

/*
 * Value for requestId component of FCGI_Header
 */
#define FCGI_NULL_REQUEST_ID     0

typedef struct {
    unsigned char roleB1;
    unsigned char roleB0;
    unsigned char flags;
    unsigned char reserved[5];
} FCGI_BeginRequestBody;

typedef struct {
    FCGI_Header header;
    FCGI_BeginRequestBody body;
} FCGI_BeginRequestRecord;

/*
 * Mask for flags component of FCGI_BeginRequestBody
 */
#define FCGI_KEEP_CONN  1

/*
 * Values for role component of FCGI_BeginRequestBody
 */
#define FCGI_RESPONDER  1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER     3

typedef struct {
    unsigned char appStatusB3;
    unsigned char appStatusB2;
    unsigned char appStatusB1;
    unsigned char appStatusB0;
    unsigned char protocolStatus;
    unsigned char reserved[3];
} FCGI_EndRequestBody;

typedef struct {
    FCGI_Header header;
    FCGI_EndRequestBody body;
} FCGI_EndRequestRecord;

/*
 * Values for protocolStatus component of FCGI_EndRequestBody
 */
#define FCGI_REQUEST_COMPLETE 0
#define FCGI_CANT_MPX_CONN    1
#define FCGI_OVERLOADED       2
#define FCGI_UNKNOWN_ROLE     3

/*
 * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
 */
#define FCGI_MAX_CONNS  "FCGI_MAX_CONNS"
#define FCGI_MAX_REQS   "FCGI_MAX_REQS"
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"

typedef struct {
    unsigned char type;    
    unsigned char reserved[7];
} FCGI_UnknownTypeBody;

typedef struct {
    FCGI_Header header;
    FCGI_UnknownTypeBody body;
} FCGI_UnknownTypeRecord;

9. References

The WWW Common Gateway Interface at W3C, version CGI/1.1.

A. Table: Properties of the record types

The following chart lists all of the record types and indicates these properties of each:

  • WS->App: records of this type can only be sent by the Web server to the application. Records of other types can only be sent by the application to the Web server.

  • management: records of this type contain information that is not specific to a Web server request, and use the null request ID. Records of other types contain request-specific information, and cannot use the null request ID.

  • stream: records of this type form a stream, terminated by a record with empty contentData. Records of other types are discrete; each carries a meaningful unit of data.

                               WS->App   management  stream

        FCGI_GET_VALUES           x          x
        FCGI_GET_VALUES_RESULT               x
        FCGI_UNKNOWN_TYPE                    x

        FCGI_BEGIN_REQUEST        x
        FCGI_ABORT_REQUEST        x
        FCGI_END_REQUEST
        FCGI_PARAMS               x                    x
        FCGI_STDIN                x                    x
        FCGI_DATA                 x                    x
        FCGI_STDOUT                                    x 
        FCGI_STDERR                                    x     


B. Typical Protocol Message Flow

Additional notational conventions for the examples:

  • The contentData of stream records (FCGI_PARAMS, FCGI_STDIN, FCGI_STDOUT, and FCGI_STDERR) is represented as a character string. A string ending in " ... " is too long to display, so only a prefix is shown.
  • Messages sent to the Web server are indented with respect to messages received from the Web server.
  • Messages are shown in the time sequence experienced by the application.

1. A simple request with no data on stdin, and a successful response:

{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS,          1, ""}
{FCGI_STDIN,           1, ""}

    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
    {FCGI_STDOUT,      1, ""}
    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}

2. Similar to example 1, but this time with data on stdin. The Web server chooses to send the parameters using more FCGI_PARAMS records than before:

{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SER"}
{FCGI_PARAMS,          1, "VER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS,          1, ""}
{FCGI_STDIN,           1, "quantity=100&item=3047936"}
{FCGI_STDIN,           1, ""}

    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
    {FCGI_STDOUT,      1, ""}
    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}

3. Similar to example 1, but this time the application detects an error. The application logs a message to stderr, returns a page to the client, and returns non-zero exit status to the Web server. The application chooses to send the page using more FCGI_STDOUT records:

{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS,          1, ""}
{FCGI_STDIN,           1, ""}

    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n<ht"}
    {FCGI_STDERR,      1, "config error: missing SI_UID\n"}
    {FCGI_STDOUT,      1, "ml>\n<head> ... "}
    {FCGI_STDOUT,      1, ""}
    {FCGI_STDERR,      1, ""}
    {FCGI_END_REQUEST, 1, {938, FCGI_REQUEST_COMPLETE}}

4. Two instances of example 1, multiplexed onto a single connection. The first request is more difficult than the second, so the application finishes the requests out of order:

{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS,          1, ""}
{FCGI_BEGIN_REQUEST,   2, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
{FCGI_PARAMS,          2, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_STDIN,           1, ""}

    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n"}

{FCGI_PARAMS,          2, ""}
{FCGI_STDIN,           2, ""}

    {FCGI_STDOUT,      2, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
    {FCGI_STDOUT,      2, ""}
    {FCGI_END_REQUEST, 2, {0, FCGI_REQUEST_COMPLETE}}
    {FCGI_STDOUT,      1, "<html>\n<head> ... "}
    {FCGI_STDOUT,      1, ""}
    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}


© 1995, 1996 Open Market, Inc. / mbrown@openmarket.com
fcgi2-2.4.5/doc/fcgi-tcl.htm000066400000000000000000000362711477724357700155230ustar00rootroot00000000000000 Integrating FastCGI with Tcl
[[FastCGI]]

Integrating FastCGI with Tcl

Michael S. Shanzer
Open Market, Inc.
19 January 1995

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/
$Id: fcgi-tcl.htm,v 1.4 2002/02/25 00:42:59 robs Exp $

1. Introduction

Tcl (tool command language) is an embeddable scripting language that's often used for CGI programming. Tcl is freely available as a source kit.

We've built a Tcl interpreter that runs as a FastCGI application. Our purpose in doing so was twofold:

  • Create a useful artifact. Open Market has written many CGI applications using Tcl. Now we'd like to turn them into FastCGI applications.

  • Demonstrate how easy it is to integrate FastCGI with an existing program. The Tcl interpreter is a substantial program, so integrating FastCGI with the Tcl interpreter is a good test of the fcgi_stdio compatibility library.

We've succeeded on both counts. We now have a platform for migrating our Tcl-based CGI applications to FastCGI. And the integration required a very small effort. The only source code change to the Tcl interpreter was the routine addition of a handful of new commands: FCGI_Accept, FCGI_Finish, FCGI_SetExitStatus, and FCGI_StartFilterData.

The FastCGI-integrated Tcl interpreter works as usual when run from a shell or as a CGI program. You don't need two Tcls, one for FastCGI and one for other uses.

The remainder of this document gives a recipe you can follow to build FastCGI into Tcl, explains what's happening in the recipe, and illustrates the use of FastCGI Tcl with an example program.

2. Recipe

Here are the assumptions embedded in the following recipe:

  • You are building Tcl 7.4p3, the current stable Tcl release as this is written. You unpack the Tcl kit into a directory tcl7.4 that's a sibling of the FastCGI kit directory fcgi-devel-kit.

  • You have gcc version 2.7 installed on your system, and use it in the build. gcc is convenient because it supports the -include command-line option that instructs the C preprocessor to include a specific file before processing any other include files. This allows you to include fcgi_stdio.h without modifying Tcl source files. (The reason for specifying gcc version 2.7 is that I have experienced bad behavior with an earlier version and the -include flag -- the C preprocessor died with SIGABRT.)

  • You have GNU autoconf installed on your system. If you don't have GNU autoconf, you will have to make certain edits by hand and repeat these edits for each build platform.

If those are valid assumptions, follow these steps:

  1. Build the FastCGI Developer's Kit. Tcl needs to link against libfcgi.a, so build the FastCGI Developer's Kit in order to create this library for your platform.

  2. Pull the Tcl 7.4p3 kit. You'll need the files tcl7.4.tar.Z, tcl7.4p1.patch.gz, tcl7.4p2.patch.gz, and tcl7.4p3.patch.gz. (Some older Netscape browsers can't perform these retrievals because of a protocol conflict between Netscape and Sun's firewall.)

    Unpack the tar file in the parent directory of the FastCGI kit directory you used in the previous step, so that the directories tcl7.4 and fcgi-devel-kit are siblings. After unpacking the tar file, follow the directions in the README to apply the patches.

    The Tcl/Tk Project Page contains a wealth of information on Tcl, including up to date information on the latest kits.

  3. Copy the files tclFCGI.c, tclAppInit.c, Makefile.in, and configure.in from the FastCGI kit.
        > cd tcl7.4
        > mv tclAppInit.c tclAppInit.c.orig
        > mv Makefile.in.orig Makefile.in.orig.orig
        > mv Makefile.in Makefile.in.orig
        > mv configure.in configure.in.orig
        > cp ../fcgi-devel-kit/tcl/tcl7.4/* .
        > cp ../fcgi-devel-kit/tcl/common/* .
    
  4. Create a new configure script.
        > autoconf
    
  5. Configure and build.
        > ./configure
        > make
    
    The make creates the Tcl interpreter tclsh and library archive libtcl.a (for embedding Tcl in your own C applications). The Tcl README file explains how you can experiment with tclsh without installing it in a standard place.

3. Recipe Explained

The recipe alone is fine if you are using Tcl 7.4p3, you have gcc version 2.7, and you have GNU autoconf. In case one or more of these assumptions doesn't hold for you, and to illuminate how little work was involved in integrating FastCGI, here's an explanation of how and why you would modify the files tclAppInit.c, Makefile.in, and configure.in from the Tcl kit.

  • tclAppInit.c:

    • Add the following three lines of code to the function Tcl_AppInit after the call to Tcl_Init and after the comment about calling init procedures:
          if (FCGI_Init(interp) == TCL_ERROR) {
              return TCL_ERROR;
          }
      
      This registers four Tcl commands (FCGI_Accept, FCGI_Finish, FCGI_SetExitStatus, and FCGI_StartFilterData), implemented in tclFCGI.c, with the Tcl interpreter.

  • Makefile.in:

    • Add tclFCGI.o to the GENERIC_OBJS variable, and add tclFCGI.c to the SRCS variable.

      This builds the FastCGI Tcl commands and links them into the Tcl interpreter.

    • Add -I../fcgi-devel-kit/include -include ../fcgi-devel-kit/include/fcgi_stdio.h to the CFLAGS variable.

      This includes fcgi_stdio.h when compiling C code for the Tcl interpreter, overriding the normal stdio types, variables, and functions.

    • Add ../fcgi-devel-kit/libfcgi/libfcgi.a before the @LIBS@ part of the LIBS variable.

      This links the implementation of fcgi_stdio.h into the Tcl interpreter, for use by the FCGI_accept command and any code that uses stdio variables or calls stdio functions.

    The last two edits will vary if you use a compiler other than gcc or install the tcl7.4 directory somewhere else in relation to the fcgi-devel-kit directory.

  • configure.in:

    • Replace the lines
      AC_C_CROSS
      CC=${CC-cc}
      
      with the lines
      AC_PROG_CC
      AC_C_CROSS
      
      This selects gcc in preference to other C compilers.

    • Add the following lines just after the AC_SUBST(CC) line:
      AC_CHECK_LIB(socket, main, [LIBS="$LIBS -lsocket"])
      AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])
      AC_SUBST(LIBS)
      
      This ensures that the socket libraries used by FastCGI are linked into the Tcl interpreter.

    If GNU autoconf is not available to you, you'll leave configure.in alone and perform the following steps:

    • Execute
          > SETENV CC gcc
      
      before running configure.

    • If you are running on a SVR4-derived Unix platform, edit Makefile to add -lsocket -lnsl to the LIBS value after running configure.

    If you ever re-run configure, you'll need to repeat these steps.

4. Writing FastCGI applications in Tcl

The Tcl program tcl/tiny-tcl-fcgi performs the same function as the C program examples/tiny-fcgi.c that's used as an example in the FastCGI Developer's Kit document. Here's what the Tcl version looks like:

#!./tclsh
set count 0 
while {[FCGI_Accept] >= 0 } {
    incr count
    puts -nonewline "Content-type: text/html\r\n\r\n"
    puts "<title>FastCGI Hello! (Tcl)</title>"
    puts "<h1>FastCGI Hello! (Tcl)</h1>"
    puts "Request number $count running on host <i>$env(SERVER_NAME)</i>"
}

If you've built Tcl according to the recipe and you have a Web server set up to run FastCGI applications, load the FastCGI Developer's Kit Index Page in that server and run this Tcl application now.

The script invokes Tcl indirectly via the symbolic link examples/tclsh. It does this because HP-UX has a limit of 32 characters for the first line of a command-interpreter file such as examples/tiny-tcl-fcgi. If you run on HP-UX you won't want to sprinkle symbolic links to tclsh everywhere, so you should install tclsh with a shorter pathname than /usr/local/tcl7.4-fcgi/bin/tclsh7.4.

The Tcl command FCGI_Accept treats the initial environment differently than the C function FCGI_Accept. The first call to the C function FCGI_Accept replaces the initial environment with the environment of the first request. The first call to the Tcl command FCGI_Accept adds the variable bindings of the first request to the bindings present in the initial environment. So when the first call to FCGI_Accept returns, bindings from the initial environment are still there (unless, due to naming conflicts, some of them have been overwritten by the first request). The next call to FCGI_Accept removes the bindings made on the previous call before adding a new set for the request just accepted, again preserving the initial environment.

The FastCGI-integrated tclsh also includes commands FCGI_Finish, FCGI_SetExitStatus, and FCGI_StartFilterData that correspond to C functions in fcgi_stdio.h; see the manpages for full information.

Converting a Tcl CGI application to FastCGI is not fundamentally different from converting a C CGI application. You separate the portion of the application that performs one-time initialization from the portion that performs per-request processing. You put the per-request processing into a loop controlled by FCGI_Accept.


Mike Shanzer // shanzer@openmarket.com
fcgi2-2.4.5/doc/omi-logo.gif000066400000000000000000000003311477724357700155160ustar00rootroot00000000000000GIF89a:";!,:"-̋,h}^*cfdK26Ӣ wMVDTA࢓lJUL'OekJG.ҽG5CT$tx'5gRYX%YIf)D zg UjZ 7,;Q;fcgi2-2.4.5/doc/overview.html000066400000000000000000000132131477724357700160440ustar00rootroot00000000000000 FastCGI Developer's Kit Index Page

FastCGI Developer's Kit


© 1996, Open Market, Inc.
fcgi2-2.4.5/doc/www5-api-workshop.html000066400000000000000000000327431477724357700175410ustar00rootroot00000000000000 FastCGI: A High-Performance Gateway Interface

FastCGI: A High-Performance Gateway Interface

Position paper for the workshop "Programming the Web - a search for APIs",
Fifth International World Wide Web Conference, 6 May 1996, Paris, France.

Mark R. Brown
Open Market, Inc.

2 May 1996

Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge, MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL: http://www.openmarket.com/

Abstract

FastCGI is a fast, open, and secure Web server interface that solves the performance problems inherent in CGI without introducing any of the new problems associated with writing applications to lower-level Web server APIs. Modules to support FastCGI can be plugged into Web server APIs such as Apache API, NSAPI, and ISAPI. Key considerations in designing FastCGI included minimizing the cost of migrating CGI applications (including applications written in popular scripting languages such as Perl), supporting both single-threaded and multi-threaded application programming, supporting distributed configurations for scaling and high availability, and generalizing the roles that gateway applications can play beyond CGI's "responder" role.

For more information on FastCGI, including an interface specification and a module for the Apache server, visit the www.fastcgi-archives.github.io Web site.

1. Introduction

The surge in the use of the Web by business has created great demand for applications that create dynamic content. These applications allow businesses to deliver products, services, and messages whose shape and content are influenced by interaction with and knowledge of users.

This move towards dynamic Web content has highlighted the performance limits of CGI (Common Gateway Interface). In response there has been a proliferation of Web server APIs. These APIs address some (though not all) of the performance problems with CGI, but are not designed to meet the need of business applications. When applied to business applications, Web server APIs suffer from these problems:

  • Complexity. Server APIs introduce a steep learning curve, with increased implementation and maintenance costs.
  • Language dependence. Applications have to be written in a language supported by the server API (usually C/C++). Perl, the most popular language for CGI programs, can't be used with any existing server API.
  • No process isolation. Since the applications run in the server's address space, buggy applications can corrupt the core server (or each other). A malicious or buggy application can compromise server security, and bugs in the core server can corrupt applications.
  • Proprietary. Coding your application to a particular API locks you into a particular server.
  • Tie-in to server architecture. API applications have to share the same architecture as the server: If the Web server is multi-threaded, the application has to be thread-safe. If the Web server has single-threaded processes, multi-threaded applications don't gain any performance advantage. Also, when the server's architecture changes, the API will usually have to change, and applications will have to be adapted or rewritten.

Web server APIs are suitable for applications that require an intimate connection to the core Web server, such as security protocols. But using a Web server API for a Web business application would be much like using an old-fashioned TP monitor, which required linking applications right into the monitor, for a modern business transaction processing application. The old-fashioned solution suffers a huge development and maintenance cost penalty because it ignores 30 years of progress in computing technology, and may end up providing inferior performance to boot. Nobody uses the old technology unless they are already locked into it.

FastCGI is best viewed as a new implementation of CGI, designed to overcome CGI's performance problems. The major implementation differences are:

  • FastCGI processes are persistent: after finishing a request, they wait for a new request instead of exiting.
  • Instead of using operating system environment variables and pipes, the FastCGI protocol multiplexes the environment information, standard input, output, and error over a single full-duplex connection. This allows FastCGI programs to run on remote machines, using TCP connections between the Web server and the FastCGI application.

FastCGI communicates the exact same information as CGI in a different way. Because FastCGI is CGI, and like CGI runs applications in separate processes, it suffers none of the server API problems listed above.

2. Migration from CGI

Open Market has developed a FastCGI application library that implements the FastCGI protocol, hiding the protocol details from the developer. This library, which is freely available, makes writing FastCGI programs as easy as writing CGI applications.

The application library provides replacements for the C language standard I/O (stdio) routines such as printf() and gets(). The library converts references to environment variables, standard input, standard output, and standard error to the FastCGI protocol. References to other files "fall through" to the underlying operating system standard I/O routines. This approach has several benefits:

  • Developers don't have to learn a new API to develop FastCGI applications.
  • Existing CGI programs can be migrated with minimal source changes.
  • FastCGI interpreters for Perl, Tcl, and other interpreted languages can be built without modifying the interpreter source code.

Here's a simple FastCGI application:



    #include <fcgi_stdio.h>

    void main(void)
    {
        int count = 0;
        while(FCGI_Accept() >= 0) {
            printf("Content-type: text/html\r\n");
            printf("\r\n");
            printf("Hello world!<br>\r\n");
            printf("Request number %d.", count++);
        }
        exit(0);
    }

This application returns a "Hello world" HTML response to the client. It also keeps a counter of the number of times it has been accessed, displaying the value of the counter at each request. The fcgi_stdio.h header file provides the FastCGI replacement routines for the C standard I/O library. The FCGI_Accept() routine accepts a new request from the Web server.

The application library was designed to make migration of existing CGI programs as simple as possible. Many applications can be converted by adding a loop around the main request processing code and recompiling with the FastCGI application library. To ease migration to FastCGI, executables built with the application library can run as either CGI or FastCGI programs, depending on how they are invoked. The library detects the execution environment and automatically selects FastCGI or regular I/O routines, as appropriate.

Applications written in Perl, Tcl, and other scripting languages can be migrated by using a language interpreter built with the application library. FastCGI-integrated Tcl and Perl interpreters for popular Unix platforms are available from the www.fastcgi-archives.github.io Web site. The interpreters are backward-compatible: They can run standard Tcl and Perl applications.

3. Single-threaded and multi-threaded applications

FastCGI gives developers a free choice of whether to develop applications in a single-threaded or multi-threaded style. The FastCGI interface supports multi-threading in two ways:

  • Applications can accept concurrent Web server connections to provide concurrent requests to multiple application threads.
  • Applications can accept multiplexed Web server connections, in which concurrent requests are communicated over a single connection to multiple application threads.

Multi-threaded programming is complex -- concurrency makes programs difficult to test and debug -- so many developers will prefer to program in the familiar single-threaded style. By having several concurrent processes running the same application it is often possible to achieve high performance with single-threaded programming.

The FastCGI interface allows Web servers to implement session affinity, a feature that allows applications to maintain caches of user-related data. With session affinity, when several concurrent processes are running the same application, the Web server routes all requests from a particular user to the same application process. Web server APIs don't provide this functionality to single-threaded applications, so the performance of an API-based application is often inferior to the performance of the corresponding FastCGI application.

4. Distributed FastCGI

Because FastCGI can communicate over TCP/IP connections, it supports configurations in which applications run remotely from the Web server. This can provide scaling, load balancing, high availability, and connections to systems that don't have Web servers.

Distributed FastCGI can also provide security advantages. A Web server outside a corporate firewall can communicate through the firewall to internal databases. For instance, an application might need to authenticate incoming users as customers in order to give access to certain documents on the external Web site. With FastCGI this authentication can be done without replicating data and without compromising security.

5. Roles

A problem with CGI is its limited functionality: CGI programs can only provide responses to requests. FastCGI provides expanded functionality with support for three different application "roles":

  • Responder. This is the basic FastCGI role, and corresponds to the simple functionality offered by CGI today.
  • Filter. The FastCGI application filters the requested Web server file before sending it to the client.
  • Authorizer. The FastCGI program performs an access control decision for the request (such as performing a username/password database lookup).

Other roles will be defined in the future. For instance, a "logger" role would be useful, where the FastCGI program would receive the server's log entries for real-time processing and analysis.

6. Conclusions

Today's Web business applications need a platform that's fast, open, maintainable, straightforward, stable, and secure. FastCGI's design meets these requirements, and provides a logical migration path from the proven and widely deployed CGI technology. This allows developers to take advantage of FastCGI's benefits without losing their existing investment in CGI applications.

For more information about FastCGI, visit the www.fastcgi-archives.github.io Web site.

fcgi2-2.4.5/examples/000077500000000000000000000000001477724357700143615ustar00rootroot00000000000000fcgi2-2.4.5/examples/.gitignore000066400000000000000000000000671477724357700163540ustar00rootroot00000000000000authorizer size echo echo-x log-dump threaded echo-cpp fcgi2-2.4.5/examples/Makefile.am000066400000000000000000000024041477724357700164150ustar00rootroot00000000000000# $Id: Makefile.am,v 1.8 2001/12/22 03:16:21 robs Exp $ noinst_PROGRAMS = authorizer \ echo \ echo-x \ log-dump \ size \ @THREADED@ \ @ECHO_CPP@ DISTCLEANFILES= Makefile.in EXTRA_PROGRAMS = threaded echo-cpp INCLUDEDIR = ../include AM_CPPFLAGS = -I$(top_srcdir)/include INCLUDE_FILES = $(INCLUDEDIR)/fastcgi.h \ $(INCLUDEDIR)/fcgiapp.h \ $(INCLUDEDIR)/fcgimisc.h \ $(INCLUDEDIR)/fcgi_stdio.h LIBDIR = ../libfcgi LIBFCGI = $(LIBDIR)/libfcgi.la LDADD = $(LIBFCGI) echo_SOURCES = $(INCLUDE_FILES) echo.c echo_x_SOURCES = $(INCLUDE_FILES) echo-x.c log_dump_SOURCES = $(INCLUDE_FILES) log-dump.c authorizer_SOURCES = $(INCLUDE_FILES) authorizer.c threaded_SOURCES = $(INCLUDE_FILES) threaded.c threaded_CC = @PTHREAD_CC@ threaded_CFLAGS = @PTHREAD_CFLAGS@ -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast threaded_LDFLAGS = @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ echo_cpp_SOURCES = $(INCLUDE_FILES) $(INCLUDEDIR)/fcgio.h echo-cpp.cpp echo_cpp_LDADD = $(LIBDIR)/libfcgi++.la $(LIBFCGI) fcgi2-2.4.5/examples/authorizer.c000066400000000000000000000027451477724357700167310ustar00rootroot00000000000000/* * tiny-authorizer.c -- * * FastCGI example Authorizer program using fcgi_stdio library * * Copyright (c) 1996 Open Market, Inc. * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: authorizer.c,v 1.1 2001/06/19 15:30:02 robs Exp $ */ #include "fcgi_stdio.h" #include #include int main(void) { char *user, *password; user = getenv("USER"); if (user == NULL) { user = "doe"; } password = getenv("PASSWORD"); if (password == NULL) { password = "xxxx"; } while (FCGI_Accept() >= 0) { char *remoteUser, *remotePassword; remoteUser = getenv("REMOTE_USER"); remotePassword = getenv("REMOTE_PASSWD"); if ((remoteUser == NULL) || (remotePassword == NULL) || strcmp(remoteUser, user) || strcmp(remotePassword, password)) { printf("Status: 401 Unauthorized\r\n" "WWW-Authenticate: Basic realm=\"Test\"\r\n" "\r\n"); } else { char *processId = getenv("QUERY_STRING"); if (processId == NULL || strlen(processId) == 0) { processId = "0"; } printf("Status: 200 OK\r\n" "Variable-AUTH_TYPE: Basic\r\n" "Variable-REMOTE_PASSWD:\r\n" "Variable-PROCESS_ID: %s\r\n" "\r\n", processId); } } return 0; } fcgi2-2.4.5/examples/authorizer.mak000066400000000000000000000103341477724357700172500ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on authorizer.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "authorizer.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\examples\authorizer\Release INTDIR=.\..\examples\authorizer\Release # Begin Custom Macros OutDir=.\..\examples\authorizer\Release # End Custom Macros ALL : "$(OUTDIR)\authorizer.exe" CLEAN : -@erase "$(INTDIR)\authorizer.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\authorizer.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\authorizer.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\authorizer.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /out:"$(OUTDIR)\authorizer.exe" /libpath:"..\libfcgi\Release" LINK32_OBJS= \ "$(INTDIR)\authorizer.obj" \ "..\libfcgi\Release\libfcgi.lib" "$(OUTDIR)\authorizer.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\..\examples/authorizer/Debug INTDIR=.\..\examples/authorizer/Debug # Begin Custom Macros OutDir=.\..\examples/authorizer/Debug # End Custom Macros ALL : "$(OUTDIR)\authorizer.exe" "$(OUTDIR)\authorizer.bsc" CLEAN : -@erase "$(INTDIR)\authorizer.obj" -@erase "$(INTDIR)\authorizer.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\authorizer.bsc" -@erase "$(OUTDIR)\authorizer.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\authorizer.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\authorizer.bsc" BSC32_SBRS= \ "$(INTDIR)\authorizer.sbr" "$(OUTDIR)\authorizer.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /profile /debug /out:"$(OUTDIR)\authorizer.exe" /libpath:"..\libfcgi\Debug" LINK32_OBJS= \ "$(INTDIR)\authorizer.obj" \ "..\libfcgi\Debug\libfcgi.lib" "$(OUTDIR)\authorizer.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF ..\examples\authorizer.c : \ "..\include\fcgi_stdio.h"\ "..\include\fcgiapp.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE=..\examples\authorizer.c !IF "$(CFG)" == "release" "$(INTDIR)\authorizer.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\authorizer.obj" "$(INTDIR)\authorizer.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF fcgi2-2.4.5/examples/echo-cpp.cpp000066400000000000000000000130261477724357700165650ustar00rootroot00000000000000/* * A simple FastCGI application example in C++. * * $Id: echo-cpp.cpp,v 1.10 2002/02/25 00:46:17 robs Exp $ * * Copyright (c) 2001 Rob Saccoccio and Chelsea Networks * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 #ifdef _WIN32 #include #else #include extern char ** environ; #endif #include "fcgio.h" #include "fcgi_config.h" // HAVE_IOSTREAM_WITHASSIGN_STREAMBUF using namespace std; // Maximum number of bytes allowed to be read from stdin static const unsigned long STDIN_MAX = 1000000; static void penv(const char * const * envp) { cout << "
\n";
    for ( ; *envp; ++envp)
    {
        cout << *envp << "\n";
    }
    cout << "
\n"; } static long gstdin(FCGX_Request * request, char ** content) { char * clenstr = FCGX_GetParam("CONTENT_LENGTH", request->envp); unsigned long clen = STDIN_MAX; if (clenstr) { clen = strtol(clenstr, &clenstr, 10); if (*clenstr) { cerr << "can't parse \"CONTENT_LENGTH=" << FCGX_GetParam("CONTENT_LENGTH", request->envp) << "\"\n"; clen = STDIN_MAX; } // *always* put a cap on the amount of data that will be read if (clen > STDIN_MAX) clen = STDIN_MAX; *content = new char[clen]; cin.read(*content, clen); clen = cin.gcount(); } else { // *never* read stdin when CONTENT_LENGTH is missing or unparsable *content = 0; clen = 0; } // Chew up any remaining stdin - this shouldn't be necessary // but is because mod_fastcgi doesn't handle it correctly. // ignore() doesn't set the eof bit in some versions of glibc++ // so use gcount() instead of eof()... do cin.ignore(1024); while (cin.gcount() == 1024); return clen; } int main (void) { int count = 0; long pid = getpid(); streambuf * cin_streambuf = cin.rdbuf(); streambuf * cout_streambuf = cout.rdbuf(); streambuf * cerr_streambuf = cerr.rdbuf(); FCGX_Request request; FCGX_Init(); FCGX_InitRequest(&request, 0, 0); while (FCGX_Accept_r(&request) == 0) { // Note that the default bufsize (0) will cause the use of iostream // methods that require positioning (such as peek(), seek(), // unget() and putback()) to fail (in favour of more efficient IO). fcgi_streambuf cin_fcgi_streambuf(request.in); fcgi_streambuf cout_fcgi_streambuf(request.out); fcgi_streambuf cerr_fcgi_streambuf(request.err); #if HAVE_IOSTREAM_WITHASSIGN_STREAMBUF cin = &cin_fcgi_streambuf; cout = &cout_fcgi_streambuf; cerr = &cerr_fcgi_streambuf; #else cin.rdbuf(&cin_fcgi_streambuf); cout.rdbuf(&cout_fcgi_streambuf); cerr.rdbuf(&cerr_fcgi_streambuf); #endif // Although FastCGI supports writing before reading, // many http clients (browsers) don't support it (so // the connection deadlocks until a timeout expires!). char * content; unsigned long clen = gstdin(&request, &content); cout << "Content-type: text/html\r\n" "\r\n" "echo-cpp\n" "

echo-cpp

\n" "

PID: " << pid << "

\n" "

Request Number: " << ++count << "

\n"; cout << "

Request Environment

\n"; penv(request.envp); cout << "

Process/Initial Environment

\n"; penv(environ); cout << "

Standard Input - " << clen; if (clen == STDIN_MAX) cout << " (STDIN_MAX)"; cout << " bytes

\n"; if (clen) cout.write(content, clen); if (content) delete []content; // If the output streambufs had non-zero bufsizes and // were constructed outside of the accept loop (i.e. // their destructor won't be called here), they would // have to be flushed here. } #if HAVE_IOSTREAM_WITHASSIGN_STREAMBUF cin = cin_streambuf; cout = cout_streambuf; cerr = cerr_streambuf; #else cin.rdbuf(cin_streambuf); cout.rdbuf(cout_streambuf); cerr.rdbuf(cerr_streambuf); #endif return 0; } fcgi2-2.4.5/examples/echo-cpp.mak000066400000000000000000000104501477724357700165510ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on echo-cpp.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echo-cpp.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\examples\echo-cpp\Release INTDIR=.\..\examples\echo-cpp\Release # Begin Custom Macros OutDir=.\..\examples\echo-cpp\Release # End Custom Macros ALL : "$(OUTDIR)\echo-cpp.exe" CLEAN : -@erase "$(INTDIR)\echo-cpp.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\echo-cpp.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Gi /GX /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\echo-cpp.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echo-cpp.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /out:"$(OUTDIR)\echo-cpp.exe" /libpath:"..\libfcgi\Release" LINK32_OBJS= \ "$(INTDIR)\echo-cpp.obj" "$(OUTDIR)\echo-cpp.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\../examples/echo-cpp\Debug INTDIR=.\../examples/echo-cpp\Debug # Begin Custom Macros OutDir=.\../examples/echo-cpp\Debug # End Custom Macros ALL : "$(OUTDIR)\echo-cpp.exe" "$(OUTDIR)\echo-cpp.bsc" CLEAN : -@erase "$(INTDIR)\echo-cpp.obj" -@erase "$(INTDIR)\echo-cpp.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\echo-cpp.bsc" -@erase "$(OUTDIR)\echo-cpp.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\echo-cpp.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echo-cpp.bsc" BSC32_SBRS= \ "$(INTDIR)\echo-cpp.sbr" "$(OUTDIR)\echo-cpp.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /profile /debug /out:"$(OUTDIR)\echo-cpp.exe" /libpath:"..\libfcgi\Debug" LINK32_OBJS= \ "$(INTDIR)\echo-cpp.obj" "$(OUTDIR)\echo-cpp.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF "..\examples\echo-cpp.cpp" : \ "..\include\fcgiapp.h"\ "..\include\fcgio.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE="..\examples\echo-cpp.cpp" !IF "$(CFG)" == "release" "$(INTDIR)\echo-cpp.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\echo-cpp.obj" "$(INTDIR)\echo-cpp.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF fcgi2-2.4.5/examples/echo-x.c000066400000000000000000000037431477724357700157170ustar00rootroot00000000000000/* * echo2.c -- * * Produce a page containing all the inputs (fcgiapp version) * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "fcgi_config.h" #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #else extern char **environ; #endif #include "fcgiapp.h" static void PrintEnv(FCGX_Stream *out, char *label, char **envp) { FCGX_FPrintF(out, "%s:
\n
\n", label);
    for( ; *envp != NULL; envp++) {
        FCGX_FPrintF(out, "%s\n", *envp);
    }
    FCGX_FPrintF(out, "

\n"); } int main () { FCGX_Stream *in, *out, *err; FCGX_ParamArray envp; int count = 0; while (FCGX_Accept(&in, &out, &err, &envp) >= 0) { char *contentLength = FCGX_GetParam("CONTENT_LENGTH", envp); int len = 0; FCGX_FPrintF(out, "Content-type: text/html\r\n" "\r\n" "FastCGI echo (fcgiapp version)" "

FastCGI echo (fcgiapp version)

\n" "Request number %d, Process ID: %d

\n", ++count, getpid()); if (contentLength != NULL) len = strtol(contentLength, NULL, 10); if (len <= 0) { FCGX_FPrintF(out, "No data from standard input.

\n"); } else { int i, ch; FCGX_FPrintF(out, "Standard input:
\n

\n");
            for (i = 0; i < len; i++) {
                if ((ch = FCGX_GetChar(in)) < 0) {
                    FCGX_FPrintF(out,
                        "Error: Not enough bytes received on standard input

\n"); break; } FCGX_PutChar(ch, out); } FCGX_FPrintF(out, "\n

\n"); } PrintEnv(out, "Request environment", envp); PrintEnv(out, "Initial environment", environ); } /* while */ return 0; } fcgi2-2.4.5/examples/echo.c000066400000000000000000000033231477724357700154440ustar00rootroot00000000000000/* * echo.c -- * * Produce a page containing all FastCGI inputs * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "fcgi_config.h" #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #else extern char **environ; #endif #include "fcgi_stdio.h" static void PrintEnv(char *label, char **envp) { printf("%s:
\n

\n", label);
    for ( ; *envp != NULL; envp++) {
        printf("%s\n", *envp);
    }
    printf("

\n"); } int main () { char **initialEnv = environ; int count = 0; while (FCGI_Accept() >= 0) { char *contentLength = getenv("CONTENT_LENGTH"); int len; printf("Content-type: text/html\r\n" "\r\n" "FastCGI echo" "

FastCGI echo

\n" "Request number %d, Process ID: %d

\n", ++count, getpid()); if (contentLength != NULL) { len = strtol(contentLength, NULL, 10); } else { len = 0; } if (len <= 0) { printf("No data from standard input.

\n"); } else { int i, ch; printf("Standard input:
\n

\n");
            for (i = 0; i < len; i++) {
                if ((ch = getchar()) < 0) {
                    printf("Error: Not enough bytes received on standard input

\n"); break; } putchar(ch); } printf("\n

\n"); } PrintEnv("Request environment", environ); PrintEnv("Initial environment", initialEnv); } /* while */ return 0; } fcgi2-2.4.5/examples/echo.mak000066400000000000000000000101441477724357700157710ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on echo.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echo.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\examples\echo\Release INTDIR=.\..\examples\echo\Release # Begin Custom Macros OutDir=.\..\examples\echo\Release # End Custom Macros ALL : "$(OUTDIR)\echo.exe" CLEAN : -@erase "$(INTDIR)\echo.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\echo.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\echo.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echo.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /out:"$(OUTDIR)\echo.exe" /libpath:"..\libfcgi\Release" LINK32_OBJS= \ "$(INTDIR)\echo.obj" \ "..\libfcgi\Release\libfcgi.lib" "$(OUTDIR)\echo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\../examples/echo\Debug INTDIR=.\../examples/echo\Debug # Begin Custom Macros OutDir=.\../examples/echo\Debug # End Custom Macros ALL : "$(OUTDIR)\echo.exe" "$(OUTDIR)\echo.bsc" CLEAN : -@erase "$(INTDIR)\echo.obj" -@erase "$(INTDIR)\echo.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\echo.bsc" -@erase "$(OUTDIR)\echo.exe" -@erase "$(OUTDIR)\echo.map" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\echo.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echo.bsc" BSC32_SBRS= \ "$(INTDIR)\echo.sbr" "$(OUTDIR)\echo.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /profile /map:"$(INTDIR)\echo.map" /debug /out:"$(OUTDIR)\echo.exe" /libpath:"..\libfcgi\Debug" LINK32_OBJS= \ "$(INTDIR)\echo.obj" \ "..\libfcgi\Debug\libfcgi.lib" "$(OUTDIR)\echo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF ..\examples\echo.c : \ "..\include\fcgi_config.h"\ "..\include\fcgi_stdio.h"\ "..\include\fcgiapp.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE=..\examples\echo.c !IF "$(CFG)" == "release" "$(INTDIR)\echo.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\echo.obj" "$(INTDIR)\echo.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF fcgi2-2.4.5/examples/echox.mak000066400000000000000000000101171477724357700161610ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on echox.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "echox.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\examples\echo-x\Release INTDIR=.\..\examples\echo-x\Release # Begin Custom Macros OutDir=.\..\examples\echo-x\Release # End Custom Macros ALL : "$(OUTDIR)\echo-x.exe" CLEAN : -@erase "$(INTDIR)\echo-x.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\echo-x.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\echox.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echox.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /out:"$(OUTDIR)\echo-x.exe" /libpath:"..\libfcgi\Release" LINK32_OBJS= \ "$(INTDIR)\echo-x.obj" \ "..\libfcgi\Release\libfcgi.lib" "$(OUTDIR)\echo-x.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\../examples/echo-x\Debug INTDIR=.\../examples/echo-x\Debug # Begin Custom Macros OutDir=.\../examples/echo-x\Debug # End Custom Macros ALL : "$(OUTDIR)\echo-x.exe" "$(OUTDIR)\echox.bsc" CLEAN : -@erase "$(INTDIR)\echo-x.obj" -@erase "$(INTDIR)\echo-x.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\echo-x.exe" -@erase "$(OUTDIR)\echox.bsc" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\echox.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\echox.bsc" BSC32_SBRS= \ "$(INTDIR)\echo-x.sbr" "$(OUTDIR)\echox.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=libfcgi.lib /nologo /profile /debug /out:"$(OUTDIR)\echo-x.exe" /libpath:"..\libfcgi\Debug" LINK32_OBJS= \ "$(INTDIR)\echo-x.obj" \ "..\libfcgi\Debug\libfcgi.lib" "$(OUTDIR)\echo-x.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF "..\examples\echo-x.c" : \ "..\include\fcgi_config.h"\ "..\include\fcgiapp.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE="..\examples\echo-x.c" !IF "$(CFG)" == "release" "$(INTDIR)\echo-x.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\echo-x.obj" "$(INTDIR)\echo-x.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF fcgi2-2.4.5/examples/log-dump.c000066400000000000000000000071741477724357700162620ustar00rootroot00000000000000/* * log-dump.c -- * * FastCGI example program to illustrate both an Authorizer and a * Responder in a single application that are used to provide access * to an ascii text file. The intent of this application is to * show the basic mechanics needed to display a log file for example * though any ascii text file should work. * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "fcgi_config.h" #include #include #include #include #include #include #include #if defined __linux__ int kill(pid_t pid, int sig); #endif #ifdef HAVE_UNISTD_H #include #endif #include "fcgi_stdio.h" static int successCount = 0; static int failureCount = 0; int main(void) { char *queryString = NULL; char *rolePtr; char *authPtr; char *fileNamePtr = NULL; int fd, n, i, j; char temp[4096]; char temp2[5000]; while(FCGI_Accept() >= 0) { rolePtr = getenv("FCGI_ROLE"); if(rolePtr == NULL) { #ifndef _WIN32 kill(getpid(), SIGQUIT); #endif exit(-1); } if(strstr(rolePtr, "AUTHORIZER")) { queryString = getenv("QUERY_STRING"); if((queryString == NULL) || (strstr(queryString, "showme_the_log") == NULL)) { failureCount++; printf("Status: 403 Forbidden\r\n" "Content-type: text/html\r\n" "\r\n" "FastCGI Forbidden!" "

Access to URL: \"%s\" forbidden!

" "

This is password protected and you " "have not specified a valid password.

" "

Total Failed Accesses: %d

", getenv("URL_PATH"), failureCount); } else { successCount++; printf("Status: 200 OK\r\n" "Variable-LOG_ACCESS: ACCESS_OK.%d\r\n" "\r\n", successCount); } continue; } /* * If we're being invoked as a RESPONDER, make sure that we've * been granted access to return the file or that the file being * requested is beyond access control (ie. per request file data). */ if(strstr(rolePtr, "RESPONDER")) { authPtr = getenv("LOG_ACCESS"); if((authPtr == NULL) || (strstr(authPtr, "ACCESS_OK") == NULL)) { failureCount++; printf("Content-type: text/html\r\n\r\n" "

Access to log file \"%s\" denied

" "

Total Invalid Access Attempts: %d\r\n\r\n", fileNamePtr, failureCount); continue; } fileNamePtr = getenv("LOG_FILE"); if(fileNamePtr == NULL || *fileNamePtr == '\0') { failureCount++; printf("Content-type: text/html\r\n\r\n" "

No file specified.

>>" "

Total Invalid Access Attempts: %d\r\n\r\n", failureCount); continue; } fd = open(fileNamePtr, O_RDONLY, (S_IRGRP | S_IROTH | S_IRUSR)); if(fd < 0) { printf("Content-type: text/html\r\n\r\n" "

File Error trying to access file \"%s\".

" "Error = %s\r\n\r\n", fileNamePtr, strerror(errno)); continue; } printf("Content-type: text/html\r\n\r\n" "

Sending contents of file: %s

" "

Successful Accesses: %d

", fileNamePtr, successCount); while((n = read(fd, temp, 4096)) > 0) { j = 0; for(i = 0; i < n; i++) { temp2[j] = temp[i]; if(temp[i] == '\n') { strcpy(&temp2[j], "

"); printf(temp2); j = 0; } else { j++; } } } close(fd); continue; } } exit(0); } fcgi2-2.4.5/examples/size.c000066400000000000000000000015231477724357700155000ustar00rootroot00000000000000#include #include "fcgiapp.h" int main(void) { int i,scale; char* pathInfo; FCGX_Stream *in, *out, *err; FCGX_ParamArray envp; while (FCGX_Accept(&in, &out, &err, &envp) >= 0) { FCGX_FPrintF(out,"Content-type: text/plain\r\n\r\n"); scale = 0; pathInfo = FCGX_GetParam("PATH_INFO",envp); if (pathInfo) scale = atoi(pathInfo+1); if (scale == 0) scale = 500; FCGX_FPrintF(out,"Dumping %6d Bytes ...\n", scale); scale = (scale-26)/80; for (i=0;i test

checkbox

radio on off

menu

file


URI to submit to

fcgi2-2.4.5/examples/threaded.c000077500000000000000000000035221477724357700163120ustar00rootroot00000000000000/* * threaded.c -- A simple multi-threaded FastCGI application. */ #include "fcgi_config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include "fcgiapp.h" #define THREAD_COUNT 20 static int counts[THREAD_COUNT]; static void *doit(void *a) { int rc, i, thread_id = (int)a; pid_t pid = getpid(); FCGX_Request request; char *server_name; FCGX_InitRequest(&request, 0, 0); for (;;) { static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t counts_mutex = PTHREAD_MUTEX_INITIALIZER; /* Some platforms require accept() serialization, some don't.. */ pthread_mutex_lock(&accept_mutex); rc = FCGX_Accept_r(&request); pthread_mutex_unlock(&accept_mutex); if (rc < 0) break; server_name = FCGX_GetParam("SERVER_NAME", request.envp); FCGX_FPrintF(request.out, "Content-type: text/html\r\n" "\r\n" "FastCGI Hello! (multi-threaded C, fcgiapp library)" "

FastCGI Hello! (multi-threaded C, fcgiapp library)

" "Thread %d, Process %ld

" "Request counts for %d threads running on host %s

", thread_id, pid, THREAD_COUNT, server_name ? server_name : "?"); sleep(2); pthread_mutex_lock(&counts_mutex); ++counts[thread_id]; for (i = 0; i < THREAD_COUNT; i++) FCGX_FPrintF(request.out, "%5d " , counts[i]); pthread_mutex_unlock(&counts_mutex); FCGX_Finish_r(&request); } return NULL; } int main(void) { int i; pthread_t id[THREAD_COUNT]; FCGX_Init(); for (i = 1; i < THREAD_COUNT; i++) pthread_create(&id[i], NULL, doit, (void*)i); doit(0); return 0; } fcgi2-2.4.5/fcgi++.pc.in000066400000000000000000000003771477724357700145410ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=${exec_prefix}/include Name: fcgi Description: FastCGI C++ Developer's Kit URL: https://fastcgi-archives.github.io Version: @VERSION@ Libs: -L${libdir} -lfcgi++ Cflags: -I${includedir} fcgi2-2.4.5/fcgi.pc.in000066400000000000000000000003711477724357700144050ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=${exec_prefix}/include Name: fcgi Description: FastCGI Developer's Kit URL: https://fastcgi-archives.github.io Version: @VERSION@ Libs: -L${libdir} -lfcgi Cflags: -I${includedir} fcgi2-2.4.5/images/000077500000000000000000000000001477724357700140105ustar00rootroot00000000000000fcgi2-2.4.5/images/aplib-hd.gif000066400000000000000000000034601477724357700161620ustar00rootroot00000000000000GIF87aA!!!111BBBRRRcccsss{{{,A dihlp,tmx|@d(q4=HD9S@z0"KN Fa@C)o @Va_c'yyje#n ;+ u* $Xn8 y]*}y]+# h_nd9nK" +h mya`++h5$h& ofF;~PJ(SpX墘GM' )Jq )5mZx_p&vbxbl>4KLj@lx<#,t|MN68dQQ/i7 y%!pbV3'X@]"P| -?@k+BaʹYy@/'$0swҴ oE=M&-~ si%3VD>] mҖ~P<$4 _mڶ3RDDR v ~ hmX񆀂DkCHL:;fcgi2-2.4.5/images/mail-hd.gif000066400000000000000000000031051477724357700160110ustar00rootroot00000000000000GIF87aA!!!111BBBRRRcccsss{{{,A dihlp,tmx|@d(q4=HD9S@z0"KN Fa@C)o @Va_c'yyje#n ;+ u* $Xn8 y]*}y]+# h_nd9nK" +h mya`++h5$h& ofF;~PJ(SpX墘GM' )Jq )5mZx_p&vbxbl>4KLj@lx<#,t|MN68dQQ/i7 y%!pbV3'X@]"P| -?@k+BaʹYy@/'$0swҴ oE=M&-~ si%3VD>] mҖ~P<$4 q_C|@X b#;@bAu$E8@T`b  @Ϡ8FN.3Mq<u :֡y`ǘLQ@ 4 vNv egʊC@v# B=B.M,׍z`@ [ % ;) `,#H vW8R+% >bw~~ϫW~7ՠC}?@f7C_.S0c *! 1a ~s! J+Y:byNCRtڝ"TAa<m1 CfLFoheN[ovAA0[; hj}uU()B'ui1ɲ@.l2ǹrnR!!:]`'gD m6JsQ R#pu?5YO-H337gصg;vkw6oo3u{b`5oh'l^̊fDp{x*y=zVy<56IZ;IJ3@PcW~I $:jݨjѧF}ĩ0jlך3`حDٻlt#JXݢ(@}߳_Mh@M&v{G!<޽$8R I `pDeKa;E9C}rT02%as "!Fd/@PEO7)Uޞŧōnab8.p25&hGn a- ר5~ Gq DPH[RinWYS)`b@.'JRdb*J#28> Bֱ2p73:q & 18%=IU rOi%X"7˗MД) 3;7 HFP9eJN԰D%D3‰:<- IgYV6/ѣD$RHT %+it?i墘GM' )Jq )5mZx_p&vbxbl>4KLj@lx<#,t|MN68dQQ/i7 y%!pbV3'X@]"P| -?@k+BaʹYy@/'$0swҴ oE=M&-~ si%3VD>] mҖ~P<$4 墘GM' )Jq )5mZx_p&vbxbl>4KLj@lx<#,t|MN68dQQ/i7 y%!pbV3'X@]"P| -?@k+BaʹYy@/'$0swҴ oE=M&-~ si%3VD>] mҖ~P<$4 8yZ&=648W  ` $QO0 VOqz"6DqЅк"2QYH1!%r 3n@ Dݢ5ƨ$1fK(@Wd8H# -q3%Bg`dGԡK,\FP1DQOxS6 @ȗ+Sp# DuV&)`hq:iS VfУ&FXzd `suD!#,!:Cc(/!Dt)?1#ZFRbp)-AwPMBf 3OB7Q0GxTXI6ܨ 08^q) Tk-dJԢHMR;fcgi2-2.4.5/include/000077500000000000000000000000001477724357700141665ustar00rootroot00000000000000fcgi2-2.4.5/include/.gitignore000066400000000000000000000000161477724357700161530ustar00rootroot00000000000000fcgi_config.h fcgi2-2.4.5/include/Makefile.am000066400000000000000000000002711477724357700162220ustar00rootroot00000000000000DISTCLEANFILES= Makefile.in # $Id: Makefile.am,v 1.2 2001/09/24 18:03:05 skimo Exp $ include_HEADERS = \ fastcgi.h \ fcgi_stdio.h \ fcgiapp.h \ fcgimisc.h \ fcgio.h \ fcgios.h fcgi2-2.4.5/include/fastcgi.h000066400000000000000000000056231477724357700157650ustar00rootroot00000000000000/* * fastcgi.h -- * * Defines for the FastCGI protocol. * * * Copyright (c) 1995-1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fastcgi.h,v 1.1.1.1 1997/09/16 15:36:32 stanleyg Exp $ */ #ifndef _FASTCGI_H #define _FASTCGI_H /* * Listening socket file number */ #define FCGI_LISTENSOCK_FILENO 0 typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header; #define FCGI_MAX_LENGTH 0xffff /* * Number of bytes in a FCGI_Header. Future versions of the protocol * will not reduce this number. */ #define FCGI_HEADER_LEN 8 /* * Value for version component of FCGI_Header */ #define FCGI_VERSION_1 1 /* * Values for type component of FCGI_Header */ #define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) /* * Value for requestId component of FCGI_Header */ #define FCGI_NULL_REQUEST_ID 0 typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; typedef struct { FCGI_Header header; FCGI_BeginRequestBody body; } FCGI_BeginRequestRecord; /* * Mask for flags component of FCGI_BeginRequestBody */ #define FCGI_KEEP_CONN 1 /* * Values for role component of FCGI_BeginRequestBody */ #define FCGI_RESPONDER 1 #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3 typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody; typedef struct { FCGI_Header header; FCGI_EndRequestBody body; } FCGI_EndRequestRecord; /* * Values for protocolStatus component of FCGI_EndRequestBody */ #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 /* * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records */ #define FCGI_MAX_CONNS "FCGI_MAX_CONNS" #define FCGI_MAX_REQS "FCGI_MAX_REQS" #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" typedef struct { unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; typedef struct { FCGI_Header header; FCGI_UnknownTypeBody body; } FCGI_UnknownTypeRecord; #endif /* _FASTCGI_H */ fcgi2-2.4.5/include/fcgi_config.h.in000066400000000000000000000055061477724357700172070ustar00rootroot00000000000000/* include/fcgi_config.h.in. Generated automatically from configure.in by autoheader. */ /* Define if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define if you have the header file. */ #undef HAVE_DLFCN_H /* Define if there's a fileno() prototype in stdio.h */ #undef HAVE_FILENO_PROTO /* Define if the fpos_t typedef is in stdio.h */ #undef HAVE_FPOS /* Define if you have the header file. */ #undef HAVE_INTTYPES_H /* Define if you have the `dnet_stub' library (-ldnet_stub). */ #undef HAVE_LIBDNET_STUB /* Define if you have the `ieee' library (-lieee). */ #undef HAVE_LIBIEEE /* Define if you have the `nsl' library (-lnsl). */ #undef HAVE_LIBNSL /* Define if you have the pthread library */ #undef HAVE_LIBPTHREAD /* Define if you have the `resolv' library (-lresolv). */ #undef HAVE_LIBRESOLV /* Define if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define if you have the header file. */ #undef HAVE_LIMITS_H /* Define if you have the header file. */ #undef HAVE_MEMORY_H /* Define if you have the header file. */ #undef HAVE_NETDB_H /* Define if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define if sockaddr_un in sys/un.h contains a sun_len component */ #undef HAVE_SOCKADDR_UN_SUN_LEN /* Define if the socklen_t typedef is in sys/socket.h */ #undef HAVE_SOCKLEN /* Define if you have the header file. */ #undef HAVE_STDINT_H /* Define if you have the header file. */ #undef HAVE_STDLIB_H /* Define if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define if you have the header file. */ #undef HAVE_STRINGS_H /* Define if you have the header file. */ #undef HAVE_STRING_H /* Define if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define if you have the header file. */ #undef HAVE_UNISTD_H /* Define if va_arg(arg, long double) crashes the compiler */ #undef HAVE_VA_ARG_LONG_DOUBLE_BUG /* Name of package */ #undef PACKAGE /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define if cross-process locking is required by accept() */ #undef USE_LOCKING /* Version number of package */ #undef VERSION /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define as `__inline' if that's what the C compiler calls it, or to nothing if it is not supported. */ #undef inline /* Define to `int' if does not define. */ #undef ssize_t fcgi2-2.4.5/include/fcgi_config_x86.h000066400000000000000000000016421477724357700173040ustar00rootroot00000000000000/* * Copied to fcgi_config.h when building on WinNT without cygwin, * i.e. configure is not run. See fcgi_config.h.in for details. */ #define HAVE_FPOS 1 #define HAVE_LIMITS_H 1 #define HAVE_STREAMBUF_CHAR_TYPE 1 #define HAVE_STRERROR 1 #undef HAVE_ARPA_INET_H #undef HAVE_DLFCN_H #undef HAVE_FILENO_PROTO #undef HAVE_INTTYPES_H #undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF #undef HAVE_LIBNSL #undef HAVE_LIBSOCKET #undef HAVE_MEMORY_H #undef HAVE_NETDB_H #undef HAVE_NETINET_IN_H #undef HAVE_PTHREAD #undef HAVE_SOCKADDR_UN_SUN_LEN #undef HAVE_SOCKLEN #undef HAVE_STDINT_H #undef HAVE_STDLIB_H #undef HAVE_STRING_H #undef HAVE_STRINGS_H #undef HAVE_SYS_PARAM_H #undef HAVE_SYS_SOCKET_H #undef HAVE_SYS_STAT_H #undef HAVE_SYS_TIME_H #undef HAVE_SYS_TYPES_H #undef HAVE_UNISTD_H #undef HAVE_VA_ARG_LONG_DOUBLE_BUG #undef PTHREAD_CREATE_JOINABLE #undef STDC_HEADERS #undef USE_LOCKING #undef const #undef inline #undef ssize_t fcgi2-2.4.5/include/fcgi_stdio.h000066400000000000000000000132561477724357700164600ustar00rootroot00000000000000/* * fcgi_stdio.h -- * * FastCGI-stdio compatibility package * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgi_stdio.h,v 1.5 2001/06/22 13:21:15 robs Exp $ */ #ifndef _FCGI_STDIO #define _FCGI_STDIO 1 #include #include #include "fcgiapp.h" #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif #ifndef DLLAPI #if defined (_WIN32) && defined (_MSC_VER) #define DLLAPI __declspec(dllimport) #else #define DLLAPI #endif #endif /* * Wrapper type for FILE */ typedef struct { FILE *stdio_stream; FCGX_Stream *fcgx_stream; } FCGI_FILE; /* * The four new functions and two new macros */ DLLAPI int FCGI_Accept(void); DLLAPI void FCGI_Finish(void); DLLAPI int FCGI_StartFilterData(void); DLLAPI void FCGI_SetExitStatus(int status); #define FCGI_ToFILE(fcgi_file) (fcgi_file->stdio_stream) #define FCGI_ToFcgiStream(fcgi_file) (fcgi_file->fcgx_stream) /* * Wrapper stdin, stdout, and stderr variables, set up by FCGI_Accept() */ DLLAPI extern FCGI_FILE _fcgi_sF[]; #define FCGI_stdin (&_fcgi_sF[0]) #define FCGI_stdout (&_fcgi_sF[1]) #define FCGI_stderr (&_fcgi_sF[2]) /* * Wrapper function prototypes, grouped according to sections * of Harbison & Steele, "C: A Reference Manual," fourth edition, * Prentice-Hall, 1995. */ DLLAPI void FCGI_perror(const char *str); DLLAPI FCGI_FILE *FCGI_fopen(const char *path, const char *mode); DLLAPI int FCGI_fclose(FCGI_FILE *fp); DLLAPI int FCGI_fflush(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp); DLLAPI int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size); DLLAPI void FCGI_setbuf(FCGI_FILE *fp, char *buf); DLLAPI int FCGI_fseek(FCGI_FILE *fp, long offset, int whence); DLLAPI int FCGI_ftell(FCGI_FILE *fp); DLLAPI void FCGI_rewind(FCGI_FILE *fp); #ifdef HAVE_FPOS DLLAPI int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos); DLLAPI int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos); #endif DLLAPI int FCGI_fgetc(FCGI_FILE *fp); DLLAPI int FCGI_getchar(void); DLLAPI int FCGI_ungetc(int c, FCGI_FILE *fp); DLLAPI char *FCGI_fgets(char *str, int size, FCGI_FILE *fp); DLLAPI char *FCGI_gets(char *str); /* * Not yet implemented * * int FCGI_fscanf(FCGI_FILE *fp, const char *format, ...); * int FCGI_scanf(const char *format, ...); * */ DLLAPI int FCGI_fputc(int c, FCGI_FILE *fp); DLLAPI int FCGI_putchar(int c); DLLAPI int FCGI_fputs(const char *str, FCGI_FILE *fp); DLLAPI int FCGI_puts(const char *str); DLLAPI int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...); DLLAPI int FCGI_printf(const char *format, ...); DLLAPI int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap); DLLAPI int FCGI_vprintf(const char *format, va_list ap); DLLAPI size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); DLLAPI size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); DLLAPI int FCGI_feof(FCGI_FILE *fp); DLLAPI int FCGI_ferror(FCGI_FILE *fp); DLLAPI void FCGI_clearerr(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_tmpfile(void); DLLAPI int FCGI_fileno(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_fdopen(int fd, const char *mode); DLLAPI FCGI_FILE *FCGI_popen(const char *cmd, const char *type); DLLAPI int FCGI_pclose(FCGI_FILE *); /* * The remaining definitions are for application programs, * not for fcgi_stdio.c */ #ifndef NO_FCGI_DEFINES /* * Replace standard types, variables, and functions with FastCGI wrappers. * Use undef in case a macro is already defined. */ #undef FILE #define FILE FCGI_FILE #undef stdin #define stdin FCGI_stdin #undef stdout #define stdout FCGI_stdout #undef stderr #define stderr FCGI_stderr #undef perror #define perror FCGI_perror #undef fopen #define fopen FCGI_fopen #undef fclose #define fclose FCGI_fclose #undef fflush #define fflush FCGI_fflush #undef freopen #define freopen FCGI_freopen #undef setvbuf #define setvbuf FCGI_setvbuf #undef setbuf #define setbuf FCGI_setbuf #undef fseek #define fseek FCGI_fseek #undef ftell #define ftell FCGI_ftell #undef rewind #define rewind FCGI_rewind #undef fgetpos #define fgetpos FCGI_fgetpos #undef fsetpos #define fsetpos FCGI_fsetpos #undef fgetc #define fgetc FCGI_fgetc #undef getc #define getc FCGI_fgetc #undef getchar #define getchar FCGI_getchar #undef ungetc #define ungetc FCGI_ungetc #undef fgets #define fgets FCGI_fgets #undef gets #define gets FCGI_gets #undef fputc #define fputc FCGI_fputc #undef putc #define putc FCGI_fputc #undef putchar #define putchar FCGI_putchar #undef fputs #define fputs FCGI_fputs #undef puts #define puts FCGI_puts #undef fprintf #define fprintf FCGI_fprintf #undef printf #define printf FCGI_printf #undef vfprintf #define vfprintf FCGI_vfprintf #undef vprintf #define vprintf FCGI_vprintf #undef fread #define fread FCGI_fread #undef fwrite #define fwrite FCGI_fwrite #undef feof #define feof FCGI_feof #undef ferror #define ferror FCGI_ferror #undef clearerr #define clearerr FCGI_clearerr #undef tmpfile #define tmpfile FCGI_tmpfile #undef fileno #define fileno FCGI_fileno #undef fdopen #define fdopen FCGI_fdopen #undef popen #define popen FCGI_popen #undef pclose #define pclose FCGI_pclose #endif /* NO_FCGI_DEFINES */ #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGI_STDIO */ fcgi2-2.4.5/include/fcgiapp.h000066400000000000000000000446531477724357700157640ustar00rootroot00000000000000/* * fcgiapp.h -- * * Definitions for FastCGI application server programs * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgiapp.h,v 1.14 2003/06/22 00:16:44 robs Exp $ */ #ifndef _FCGIAPP_H #define _FCGIAPP_H /* Hack to see if we are building Tcl - Tcl needs varargs not stdarg */ #ifndef TCL_LIBRARY #include #else #include #endif #ifndef DLLAPI #if defined (_WIN32) && defined (_MSC_VER) #define DLLAPI __declspec(dllimport) #else #define DLLAPI #endif #endif #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif /* * Error codes. Assigned to avoid conflict with EOF and errno(2). */ #define FCGX_UNSUPPORTED_VERSION -2 #define FCGX_PROTOCOL_ERROR -3 #define FCGX_PARAMS_ERROR -4 #define FCGX_CALL_SEQ_ERROR -5 /* * This structure defines the state of a FastCGI stream. * Streams are modeled after the FILE type defined in stdio.h. * (We wouldn't need our own if platform vendors provided a * standard way to subclass theirs.) * The state of a stream is private and should only be accessed * by the procedures defined below. */ typedef struct FCGX_Stream { unsigned char *rdNext; /* reader: first valid byte * writer: equals stop */ unsigned char *wrNext; /* writer: first free byte * reader: equals stop */ unsigned char *stop; /* reader: last valid byte + 1 * writer: last free byte + 1 */ unsigned char *stopUnget; /* reader: first byte of current buffer * fragment, for ungetc * writer: undefined */ int isReader; int isClosed; int wasFCloseCalled; int FCGI_errno; /* error status */ void (*fillBuffProc) (struct FCGX_Stream *stream); void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose); void *data; } FCGX_Stream; /* * An environment (as defined by environ(7)): A NULL-terminated array * of strings, each string having the form name=value. */ typedef char **FCGX_ParamArray; /* * FCGX_Request Flags * * Setting FCGI_FAIL_ACCEPT_ON_INTR prevents FCGX_Accept() from * restarting upon being interrupted. */ #define FCGI_FAIL_ACCEPT_ON_INTR 1 /* * FCGX_Request -- State associated with a request. * * Its exposed for API simplicity, I expect parts of it to change! */ typedef struct FCGX_Request { int requestId; /* valid if isBeginProcessed */ int role; FCGX_Stream *in; FCGX_Stream *out; FCGX_Stream *err; char **envp; /* Don't use anything below here */ struct Params *paramsPtr; int ipcFd; /* < 0 means no connection */ int isBeginProcessed; /* FCGI_BEGIN_REQUEST seen */ int keepConnection; /* don't close ipcFd at end of request */ int appStatus; int nWriters; /* number of open writers (0..2) */ int flags; int listen_sock; int detached; } FCGX_Request; /* *====================================================================== * Control *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_IsCGI -- * * Returns TRUE iff this process appears to be a CGI process * rather than a FastCGI process. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_IsCGI(void); /* *---------------------------------------------------------------------- * * FCGX_Init -- * * Initialize the FCGX library. Call in multi-threaded apps * before calling FCGX_Accept_r(). * * Returns 0 upon success. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Init(void); /* *---------------------------------------------------------------------- * * FCGX_OpenSocket -- * * Create a FastCGI listen socket. * * path is the Unix domain socket (named pipe for WinNT), or a colon * followed by a port number. e.g. "/tmp/fastcgi/mysocket", ":5000" * * backlog is the listen queue depth used in the listen() call. * * Returns the socket's file descriptor or -1 on error. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_OpenSocket(const char *path, int backlog); /* *---------------------------------------------------------------------- * * FCGX_InitRequest -- * * Initialize a FCGX_Request for use with FCGX_Accept_r(). * * sock is a file descriptor returned by FCGX_OpenSocket() or 0 (default). * The only supported flag at this time is FCGI_FAIL_ON_INTR. * * Returns 0 upon success. *---------------------------------------------------------------------- */ DLLAPI int FCGX_InitRequest(FCGX_Request *request, int sock, int flags); /* *---------------------------------------------------------------------- * * FCGX_Accept_r -- * * Accept a new request (multi-thread safe). Be sure to call * FCGX_Init() first. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * * DON'T use the FCGX_Request, its structure WILL change. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Accept_r(FCGX_Request *request); /* *---------------------------------------------------------------------- * * FCGX_Finish_r -- * * Finish the request (multi-thread safe). * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Finish_r(FCGX_Request *request); /* *---------------------------------------------------------------------- * * FCGX_Free -- * * Free the memory and, if close is true, * IPC FD associated with the request (multi-thread safe). * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Free(FCGX_Request * request, int close); /* *---------------------------------------------------------------------- * * FCGX_Accept -- * * Accept a new request (NOT multi-thread safe). * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Accept( FCGX_Stream **in, FCGX_Stream **out, FCGX_Stream **err, FCGX_ParamArray *envp); /* *---------------------------------------------------------------------- * * FCGX_Finish -- * * Finish the current request (NOT multi-thread safe). * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Finish(void); /* *---------------------------------------------------------------------- * * FCGX_StartFilterData -- * * stream is an input stream for a FCGI_FILTER request. * stream is positioned at EOF on FCGI_STDIN. * Repositions stream to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF) sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ DLLAPI int FCGX_StartFilterData(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_SetExitStatus -- * * Sets the exit status for stream's request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * SetExitStatus several times during a request; the last call * before the request ends determines the value. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_SetExitStatus(int status, FCGX_Stream *stream); /* *====================================================================== * Parameters *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_GetParam -- obtain value of FCGI parameter in environment * * * Results: * Value bound to name, NULL if name not present in the * environment envp. Caller must not mutate the result * or retain it past the end of this request. * *---------------------------------------------------------------------- */ DLLAPI char *FCGX_GetParam(const char *name, FCGX_ParamArray envp); /* *====================================================================== * Readers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_GetChar -- * * Reads a byte from the input stream and returns it. * * Results: * The byte, or EOF (-1) if the end of input has been reached. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetChar(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_UnGetChar -- * * Pushes back the character c onto the input stream. One * character of pushback is guaranteed once a character * has been read. No pushback is possible for EOF. * * Results: * Returns c if the pushback succeeded, EOF if not. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_UnGetChar(int c, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetStr -- * * Reads up to n consecutive bytes from the input stream * into the character array str. Performs no interpretation * of the input bytes. * * Results: * Number of bytes read. If result is smaller than n, * the end of input has been reached. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetStr(char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetLine -- * * Reads up to n-1 consecutive bytes from the input stream * into the character array str. Stops before n-1 bytes * have been read if '\n' or EOF is read. The terminating '\n' * is copied to str. After copying the last byte into str, * stores a '\0' terminator. * * Results: * NULL if EOF is the first thing read from the input stream, * str otherwise. * *---------------------------------------------------------------------- */ DLLAPI char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_HasSeenEOF -- * * Returns EOF if end-of-file has been detected while reading * from stream; otherwise returns 0. * * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately * following FCGX_GetChar(s) may return EOF. This function, like * the standard C stdio function feof, does not provide the * ability to peek ahead. * * Results: * EOF if end-of-file has been detected, 0 if not. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_HasSeenEOF(FCGX_Stream *stream); /* *====================================================================== * Writers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_PutChar -- * * Writes a byte to the output stream. * * Results: * The byte, or EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutChar(int c, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_PutStr -- * * Writes n consecutive bytes from the character array str * into the output stream. Performs no interpretation * of the output bytes. * * Results: * Number of bytes written (n) for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_PutS -- * * Writes a null-terminated character string to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutS(const char *str, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_FPrintF, FCGX_VFPrintF -- * * Performs printf-style output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...); DLLAPI int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg); /* *---------------------------------------------------------------------- * * FCGX_FFlush -- * * Flushes any buffered output. * * Server-push is a legitimate application of FCGX_FFlush. * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept * does it implicitly. Calling FCGX_FFlush in non-push applications * results in extra writes and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FFlush(FCGX_Stream *stream); /* *====================================================================== * Both Readers and Writers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_FClose -- * * Closes the stream. For writers, flushes any buffered * output. * * Close is not a very useful operation since FCGX_Accept * does it implicitly. Closing the out stream before the * err stream results in an extra write if there's nothing * in the err stream, and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FClose(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetError -- * * Return the stream error code. 0 means no error, > 0 * is an errno(2) error, < 0 is an FastCGI error. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetError(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_ClearError -- * * Clear the stream error code and end-of-file indication. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_ClearError(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_CreateWriter -- * * Create a FCGX_Stream (used by cgi-fcgi). This shouldn't * be needed by a FastCGI applictaion. * *---------------------------------------------------------------------- */ DLLAPI FCGX_Stream *FCGX_CreateWriter( int socket, int requestId, int bufflen, int streamType); /* *---------------------------------------------------------------------- * * FCGX_FreeStream -- * * Free a FCGX_Stream (used by cgi-fcgi). This shouldn't * be needed by a FastCGI applictaion. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_FreeStream(FCGX_Stream **stream); /* ---------------------------------------------------------------------- * * Prevent the lib from accepting any new requests. Signal handler safe. * * ---------------------------------------------------------------------- */ DLLAPI void FCGX_ShutdownPending(void); /* * Attach/Detach an accepted request from its listen socket. * XXX This is not fully implemented at this time (patch welcome). */ DLLAPI int FCGX_Attach(FCGX_Request * r); DLLAPI int FCGX_Detach(FCGX_Request * r); #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGIAPP_H */ fcgi2-2.4.5/include/fcgimisc.h000066400000000000000000000011701477724357700161220ustar00rootroot00000000000000/* * fcgimisc.h -- * * Miscellaneous definitions * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgimisc.h,v 1.3 2001/06/18 14:25:47 robs Exp $ */ #ifndef _FCGIMISC_H #define _FCGIMISC_H #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (1) #endif #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef ASSERT #define ASSERT(assertion) assert(assertion) #endif #endif /* _FCGIMISC_H */ fcgi2-2.4.5/include/fcgio.h000066400000000000000000000072451477724357700154360ustar00rootroot00000000000000// // Provides support for FastCGI via C++ iostreams. // // $Id: fcgio.h,v 1.15 2002/02/25 13:16:11 robs Exp $ // // This work is based on routines written by George Feinberg. They // have been mostly re-written and extensively changed by // Michael Richards. // // Rewritten again with bug fixes and numerous enhancements by // Michael Shell. // // And rewritten again by Rob Saccoccio. // // Special Thanks to Dietmar Kuehl for his help and the numerous custom // streambuf examples on his web site. // // Copyright (c) 2000 Tux the Linux Penguin // Copyright (c) 2001 Rob Saccoccio and Chelsea Networks // // You are free to use this software without charge or royalty // as long as this notice is not removed or altered, and recognition // is given to the author(s) // // This code is offered as-is without any warranty either expressed or // implied; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. If it breaks, you get to keep // both halves. #ifndef FCGIO_H #define FCGIO_H #include #include "fcgiapp.h" #ifndef DLLAPI #if defined (_WIN32) && defined (_MSC_VER) #define DLLAPI __declspec(dllimport) #else #define DLLAPI #endif #endif #if ! HAVE_STREAMBUF_CHAR_TYPE typedef char char_type; #endif /* * fcgi_streambuf */ class DLLAPI fcgi_streambuf : public std::streambuf { public: // Note that if no buf is assigned (the default), iostream methods // such as peek(), unget() and putback() will fail. If a buf is // assigned, I/O is a bit less efficient and output streams will // have to be flushed (or the streambuf destroyed) before the next // call to "accept". fcgi_streambuf(FCGX_Stream * fcgx, char * buf, int len); fcgi_streambuf(char_type * buf, std::streamsize len); fcgi_streambuf(FCGX_Stream * fcgx = 0); ~fcgi_streambuf(void); int attach(FCGX_Stream * fcgx); protected: // Consume the put area (if buffered) and c (if c is not EOF). virtual int overflow(int); // Flush the put area (if buffered) and the FCGX buffer to the client. virtual int sync(); // Remove and return the current character. virtual int uflow(); // Fill the get area (if buffered) and return the current character. virtual int underflow(); // Use a buffer. The only reasons that a buffer would be useful is // to support the use of the unget()/putback() or seek() methods. Using // a buffer will result in less efficient I/O. Note: the underlying // FastCGI library (FCGX) maintains its own input and output buffers. virtual std::streambuf * setbuf(char_type * buf, std::streamsize len); virtual std::streamsize xsgetn(char_type * s, std::streamsize n); virtual std::streamsize xsputn(const char_type * s, std::streamsize n); private: FCGX_Stream * fcgx; // buf is just handy to have around char_type * buf; // this isn't kept by the base class std::streamsize bufsize; void init(FCGX_Stream * fcgx, char_type * buf, std::streamsize bufsize); void reset(void); }; /* * fcgi_istream - deprecated */ class DLLAPI fcgi_istream : public std::istream { public: // deprecated fcgi_istream(FCGX_Stream * fcgx = 0); // deprecated ~fcgi_istream(void) {} // deprecated virtual void attach(FCGX_Stream * fcgx); private: fcgi_streambuf fcgi_strmbuf; }; /* * fcgi_ostream - deprecated */ class DLLAPI fcgi_ostream : public std::ostream { public: // deprecated fcgi_ostream(FCGX_Stream * fcgx = 0); // deprecated ~fcgi_ostream(void) {} // deprecated virtual void attach(FCGX_Stream *fcgx); private: fcgi_streambuf fcgi_strmbuf; }; #endif /* FCGIO_H */ fcgi2-2.4.5/include/fcgios.h000077500000000000000000000070311477724357700156150ustar00rootroot00000000000000/* * fcgios.h -- * * Description of file. * * * Copyright (c) 1996 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com */ #ifndef _FCGIOS_H #define _FCGIOS_H #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #endif #include "fcgi_config.h" #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif #ifdef _WIN32 #define OS_Errno GetLastError() #define OS_SetErrno(err) SetLastError(err) #ifndef O_NONBLOCK #define O_NONBLOCK 0x0004 /* no delay */ #endif #else /* !_WIN32 */ #define OS_Errno errno #define OS_SetErrno(err) errno = (err) #endif /* !_WIN32 */ #ifndef DLLAPI #if defined (_WIN32) && defined (_MSC_VER) #define DLLAPI __declspec(dllimport) #else #define DLLAPI #endif #endif /* This is the initializer for a "struct timeval" used in a select() call * right after a new request is accept()ed to determine readablity. Its * a drop-dead timer. Its only used for AF_UNIX sockets (not TCP sockets). * Its a workaround for a kernel bug in Linux 2.0.x and SCO Unixware. * Making this as small as possible, yet remain reliable would be best. * 2 seconds is very conservative. 0,0 is not reliable. The shorter the * timeout, the faster request processing will recover. The longer the * timeout, the more likely this application being "busy" will cause other * requests to abort and cause more dead sockets that need this timeout. */ #define READABLE_UNIX_FD_DROP_DEAD_TIMEVAL 2,0 #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif #ifndef X_OK #define X_OK 0x01 #endif #ifndef _CLIENTDATA # if defined(__STDC__) || defined(__cplusplus) typedef void *ClientData; # else typedef int *ClientData; # endif /* __STDC__ */ #define _CLIENTDATA #endif typedef void (*OS_AsyncProc) (ClientData clientData, int len); DLLAPI int OS_LibInit(int stdioFds[3]); DLLAPI void OS_LibShutdown(void); DLLAPI int OS_CreateLocalIpcFd(const char *bindPath, int backlog); DLLAPI int OS_FcgiConnect(char *bindPath); DLLAPI int OS_Read(int fd, char * buf, size_t len); DLLAPI int OS_Write(int fd, char * buf, size_t len); DLLAPI int OS_SpawnChild(char *execPath, int listenFd); DLLAPI int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_Close(int fd, int shutdown); DLLAPI int OS_CloseRead(int fd); DLLAPI int OS_DoIo(struct timeval *tmo); DLLAPI int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs); DLLAPI int OS_IpcClose(int ipcFd, int shutdown); DLLAPI int OS_IsFcgi(int sock); DLLAPI void OS_SetFlags(int fd, int flags); DLLAPI void OS_ShutdownPending(void); #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGIOS_H */ fcgi2-2.4.5/java/000077500000000000000000000000001477724357700134645ustar00rootroot00000000000000fcgi2-2.4.5/java/FCGIGlobalDefs.java000066400000000000000000000062231477724357700167650ustar00rootroot00000000000000/* * @(#)FCGIGlobalDefs.java * * * FastCGi compatibility package Interface * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIGlobalDefs.java,v 1.3 2000/03/21 12:12:25 robs Exp $ */ /* This class contains FCGI global definitions corresponding to * the #defs in the C version. */ package com.fastcgi; import java.io.PrintStream; public abstract class FCGIGlobalDefs { private static final String RCSID = "$Id: FCGIGlobalDefs.java,v 1.3 2000/03/21 12:12:25 robs Exp $"; public static final int def_FCGIMaxLen = 0xffff; /* * Define Length of FCGI message bodies in bytes */ public static final int def_FCGIHeaderLen = 8; public static final int def_FCGIEndReqBodyLen = 8; public static final int def_FCGIBeginReqBodyLen = 8; public static final int def_FCGIUnknownBodyTypeBodyLen = 8; /* * Header defines */ public static int def_FCGIVersion1 = 1; /* FCGI Record Types */ public static final int def_FCGIBeginRequest = 1; public static final int def_FCGIAbortRequest = 2; public static final int def_FCGIEndRequest = 3; public static final int def_FCGIParams = 4; public static final int def_FCGIStdin = 5; public static final int def_FCGIStdout = 6; public static final int def_FCGIStderr = 7; public static final int def_FCGIData = 8; public static final int def_FCGIGetValues = 9; public static final int def_FCGIGetValuesResult = 10; public static final int def_FCGIUnknownType = 11; public static final int def_FCGIMaxType = def_FCGIUnknownType; /* Request ID Values */ public static final int def_FCGINullRequestID = 0; /* * Begin Request defines */ /* Mask flags */ public static int def_FCGIKeepConn = 1; /* Roles */ public static final int def_FCGIResponder = 1; public static final int def_FCGIAuthorizer = 2; public static final int def_FCGIFilter = 3; /* * End Request defines */ /* Protocol status */ public static final int def_FCGIRequestComplete = 0; public static final int def_FCGICantMpxConn = 1; public static final int def_FCGIOverload = 2; public static final int def_FCGIUnknownRole = 3; /* * Get Values, Get Values Results defines */ public static final String def_FCGIMaxConns = "FCGI_MAX_CONNS"; public static final String def_FCGIMaxReqs = "FCGI_MAX_REQS"; public static final String def_FCGIMpxsConns = "FCGI_MPXS_CONNS"; /* * Return codes for Process* functions */ public static final int def_FCGIStreamRecord = 0; public static final int def_FCGISkip = 1; public static final int def_FCGIBeginRecord = 2; public static final int def_FCGIMgmtRecord = 3; /* * Error Codes */ public static final int def_FCGIUnsupportedVersion = -2; public static final int def_FCGIProtocolError = -3; public static final int def_FCGIParamsError = -4; public static final int def_FCGICallSeqError = -5; } fcgi2-2.4.5/java/FCGIInputStream.java000066400000000000000000000257241477724357700172450ustar00rootroot00000000000000/* * @(#)FCGIInputStream.java * * FastCGi compatibility package Interface * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIInputStream.java,v 1.4 2000/03/21 12:12:25 robs Exp $ */ package com.fastcgi; import java.io.*; /** * This stream manages buffered reads of FCGI messages. */ public class FCGIInputStream extends InputStream { private static final String RCSID = "$Id: FCGIInputStream.java,v 1.4 2000/03/21 12:12:25 robs Exp $"; /* Stream vars */ public int rdNext; public int stop; public boolean isClosed; /* require methods to set, get and clear */ private int errno; private Exception errex; /* data vars */ public byte buff[]; public int buffLen; public int buffStop; public int type; public int contentLen; public int paddingLen; public boolean skip; public boolean eorStop; public FCGIRequest request; public InputStream in; /** * Creates a new input stream to manage fcgi prototcol stuff * @param in the input stream bufLen length of buffer streamType */ public FCGIInputStream(FileInputStream inStream, int bufLen, int streamType, FCGIRequest inReq) { in = inStream; buffLen = Math.min(bufLen,FCGIGlobalDefs.def_FCGIMaxLen); buff = new byte[buffLen]; type = streamType; stop = rdNext = buffStop = 0; isClosed = false; contentLen = 0; paddingLen = 0; skip = false; eorStop = false; request = inReq; } /** * Reads a byte of data. This method will block if no input is * available. * @return the byte read, or -1 if the end of the * stream is reached. * @exception IOException If an I/O error has occurred. */ public int read() throws IOException { if (rdNext != stop) { return buff[rdNext++] & 0xff; } if (isClosed){ return -1; } fill(); if (rdNext != stop){ return buff[rdNext++] & 0xff; } return -1; } /** * Reads into an array of bytes. This method will * block until some input is available. * @param b the buffer into which the data is read * @return the actual number of bytes read, -1 is * returned when the end of the stream is reached. * @exception IOException If an I/O error has occurred. */ public int read(byte b[]) throws IOException { return read(b, 0, b.length); } /** * Reads into an array of bytes. * Blocks until some input is available. * @param b the buffer into which the data is read * @param off the start offset of the data * @param len the maximum number of bytes read * @return the actual number of bytes read, -1 is * returned when the end of the stream is reached. * @exception IOException If an I/O error has occurred. */ public int read(byte b[], int off, int len) throws IOException { int m, bytesMoved; if (len <= 0){ return 0; } /* *Fast path: len bytes already available. */ if (len <= stop - rdNext){ System.arraycopy(buff, rdNext, b, off, len); rdNext += len; return len; } /* *General case: stream is closed or fill needs to be called */ bytesMoved = 0; for(;;){ if (rdNext != stop){ m = Math.min(len - bytesMoved, stop - rdNext); System.arraycopy(buff, rdNext, b, off, m); bytesMoved += m; rdNext += m; if (bytesMoved == len) return bytesMoved; off += m; } if (isClosed){ return bytesMoved; } fill(); } } /** * Reads into an array of bytes. This method will * block until some input is available. * @param b the buffer into which the data is read * @param off the start offset of the data * @param len the maximum number of bytes read * @return the actual number of bytes read, -1 is * returned when the end of the stream is reached. * @exception IOException If an I/O error has occurred. */ public void fill() throws IOException { byte[] headerBuf = new byte[FCGIGlobalDefs.def_FCGIHeaderLen]; int headerLen = 0; int status = 0; int count = 0; for(;;) { /* * If buffer is empty, do a read */ if (rdNext == buffStop) { try { count = in.read(buff, 0, buffLen); } catch (IOException e) { setException(e); return; } if (count == 0) { setFCGIError(FCGIGlobalDefs.def_FCGIProtocolError); return; } rdNext = 0; buffStop = count; // 1 more than we read } /* Now buf is not empty: If the current record contains more content * bytes, deliver all that are present in buff to callers buffer * unless he asked for less than we have, in which case give him less */ if (contentLen > 0) { count = Math.min(contentLen, buffStop - rdNext); contentLen -= count; if (!skip) { stop = rdNext + count; return; } else { rdNext += count; if (contentLen > 0) { continue; } else { skip = false; } } } /* Content has been consumed by client. * If record was padded, skip over padding */ if (paddingLen > 0) { count = Math.min(paddingLen, buffStop - rdNext); paddingLen -= count; rdNext += count; if (paddingLen > 0) { continue; // more padding to read } } /* All done with current record, including the padding. * If we are in a recursive call from Process Header, deliver EOF */ if (eorStop){ stop = rdNext; isClosed = true; return; } /* * Fill header with bytes from input buffer - get the whole header. */ count = Math.min(headerBuf.length - headerLen, buffStop - rdNext); System.arraycopy(buff,rdNext, headerBuf, headerLen, count); headerLen += count; rdNext += count; if (headerLen < headerBuf.length) { continue; } headerLen = 0; /* * Interperet the header. eorStop prevents ProcessHeader from * reading past the end of record when using stream to read content */ eorStop = true; stop = rdNext; status = 0; status = new FCGIMessage(this).processHeader(headerBuf); eorStop = false; isClosed = false; switch (status){ case FCGIGlobalDefs.def_FCGIStreamRecord: if (contentLen == 0) { stop = rdNext; isClosed = true; return; } break; case FCGIGlobalDefs.def_FCGISkip: skip = true; break; case FCGIGlobalDefs.def_FCGIBeginRecord: /* * If this header marked the beginning of a new * request, return role info to caller */ return; case FCGIGlobalDefs.def_FCGIMgmtRecord: break; default: /* * ASSERT */ setFCGIError(status); return; } } } /** * Skips n bytes of input. * @param n the number of bytes to be skipped * @return the actual number of bytes skipped. * @exception IOException If an I/O error has occurred. */ public long skip(long n) throws IOException { byte data[] = new byte[(int)n]; return in.read(data); } /* * An FCGI error has occurred. Save the error code in the stream * for diagnostic purposes and set the stream state so that * reads return EOF */ public void setFCGIError(int errnum) { /* * Preserve only the first error. */ if(errno == 0) { errno = errnum; } isClosed = true; } /* * An Exception has occurred. Save the Exception in the stream * for diagnostic purposes and set the stream state so that * reads return EOF */ public void setException(Exception errexpt) { /* * Preserve only the first error. */ if(errex == null) { errex = errexpt; } isClosed = true; } /* * Clear the stream error code and end-of-file indication. */ public void clearFCGIError() { errno = 0; /* * isClosed = false; * XXX: should clear isClosed but work is needed to make it safe * to do so. */ } /* * Clear the stream error code and end-of-file indication. */ public void clearException() { errex = null; /* * isClosed = false; * XXX: should clear isClosed but work is needed to make it safe * to do so. */ } /* * accessor method since var is private */ public int getFCGIError() { return errno; } /* * accessor method since var is private */ public Exception getException() { return errex; } /* * Re-initializes the stream to read data of the specified type. */ public void setReaderType(int streamType) { type = streamType; eorStop = false; skip = false; contentLen = 0; paddingLen = 0; stop = rdNext; isClosed = false; } /* * Close the stream. This method does not really exist for BufferedInputStream in java, * but is implemented here for compatibility with the FCGI structures being used. It * doesn't really throw any IOExceptions either, but that's there for compatibility with * the InputStreamInterface. */ public void close() throws IOException{ isClosed = true; stop = rdNext; } /* * Returns the number of bytes that can be read without blocking. */ public int available() throws IOException { return stop - rdNext + in.available(); } } fcgi2-2.4.5/java/FCGIInterface.java000066400000000000000000000204521477724357700166630ustar00rootroot00000000000000/* * @(#)FCGIInterface.java * * * FastCGi compatibility package Interface * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIInterface.java,v 1.4 2000/03/27 15:37:25 robs Exp $ */ package com.fastcgi; import java.net.*; import java.io.*; import java.util.Properties; /* * This is the FastCGI interface that the application calls to communicate with the * FastCGI web server. This version is single threaded, and handles one request at * a time, which is why we can have a static variable for it. */ public class FCGIInterface { private static final String RCSID = "$Id: FCGIInterface.java,v 1.4 2000/03/27 15:37:25 robs Exp $"; /* * Class variables */ public static FCGIRequest request = null; public static boolean acceptCalled = false; public static boolean isFCGI = true; public static Properties startupProps; public static ServerSocket srvSocket; /* * Accepts a new request from the HTTP server and creates * a conventional execution environment for the request. * If the application was invoked as a FastCGI server, * the first call to FCGIaccept indicates that the application * has completed its initialization and is ready to accept * a request. Subsequent calls to FCGI_accept indicate that * the application has completed its processing of the * current request and is ready to accept a new request. * If the application was invoked as a CGI program, the first * call to FCGIaccept is essentially a no-op and the second * call returns EOF (-1) as does an error. Application should exit. * * If the application was invoked as a FastCGI server, * and this is not the first call to this procedure, * FCGIaccept first flushes any buffered output to the HTTP server. * * On every call, FCGIaccept accepts the new request and * reads the FCGI_PARAMS stream into System.props. It also creates * streams that understand FastCGI protocol and take input from * the HTTP server send output and error output to the HTTP server, * and assigns these new streams to System.in, System.out and * System.err respectively. * * For now, we will just return an int to the caller, which is why * this method catches, but doen't throw Exceptions. * */ public int FCGIaccept() { int acceptResult = 0; /* * If first call, mark it and if fcgi save original system properties, * If not first call, and we are cgi, we should be gone. */ if (!acceptCalled){ isFCGI = System.getProperties().containsKey("FCGI_PORT"); acceptCalled = true; if (isFCGI) { /* * save original system properties (nonrequest) * and get a server socket */ startupProps = new Properties(System.getProperties()); String str = new String(System.getProperty("FCGI_PORT")); if (str.length() <= 0) { return -1; } int portNum = Integer.parseInt(str); try { srvSocket = new ServerSocket(portNum); } catch (IOException e) { if (request != null) { request.socket = null; } srvSocket = null; request = null; return -1; } } } else { if (!isFCGI){ return -1; } } /* * If we are cgi, just leave everything as is, otherwise set up env */ if (isFCGI){ try { acceptResult = FCGIAccept(); } catch (IOException e) { return -1; } if (acceptResult < 0){ return -1; } /* * redirect stdin, stdout and stderr to fcgi socket */ System.setIn(new BufferedInputStream(request.inStream, 8192)); System.setOut(new PrintStream(new BufferedOutputStream( request.outStream, 8192))); System.setErr(new PrintStream(new BufferedOutputStream( request.errStream, 512))); System.setProperties(request.params); } return 0; } /* * Accepts a new request from the HTTP server. * Finishes the request accepted by the previous call * to FCGI_Accept. Sets up the FCGI environment and reads * saved and per request environmental variables into * the request object. (This is redundant on System.props * as long as we can handle only one request object.) */ int FCGIAccept() throws IOException{ boolean isNewConnection; boolean errCloseEx = false; boolean outCloseEx = false; if (request != null) { /* * Complete the previous request */ System.err.close(); System.out.close(); boolean prevRequestfailed = (errCloseEx || outCloseEx || request.inStream.getFCGIError() != 0 || request.inStream.getException() != null); if (prevRequestfailed || !request.keepConnection ) { request.socket.close(); request.socket = null; } if (prevRequestfailed) { request = null; return -1; } } else { /* * Get a Request and initialize some variables */ request = new FCGIRequest(); request.socket = null; request.inStream = null; } isNewConnection = false; /* * if connection isn't open accept a new connection (blocking) */ for(;;) { if (request.socket == null){ try { request.socket = srvSocket.accept(); } catch (IOException e) { request.socket = null; request = null; return -1; } isNewConnection = true; } /* Try reading from new connection. If the read fails and * it was an old connection the web server probably closed it; * try making a new connection before giving up */ request.isBeginProcessed = false; request.inStream = new FCGIInputStream((FileInputStream)request. socket.getInputStream(), 8192, 0, request); request.inStream.fill(); if (request.isBeginProcessed) { break; } request.socket.close(); request.socket = null; if (isNewConnection) { return -1; } } /* * Set up the objects for the new request */ request.params = new Properties(startupProps); switch(request.role) { case FCGIGlobalDefs.def_FCGIResponder: request.params.put("ROLE","RESPONDER"); break; case FCGIGlobalDefs.def_FCGIAuthorizer: request.params.put("ROLE", "AUTHORIZER"); break; case FCGIGlobalDefs.def_FCGIFilter: request.params.put("ROLE", "FILTER"); break; default: return -1; } request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIParams); /* * read the rest of request parameters */ if (new FCGIMessage(request.inStream).readParams(request.params) < 0) { return -1; } request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIStdin); request.outStream = new FCGIOutputStream((FileOutputStream)request.socket. getOutputStream(), 8192, FCGIGlobalDefs.def_FCGIStdout,request); request.errStream = new FCGIOutputStream((FileOutputStream)request.socket. getOutputStream(), 512, FCGIGlobalDefs.def_FCGIStderr,request); request.numWriters = 2; return 0; } } fcgi2-2.4.5/java/FCGIMessage.java000066400000000000000000000340241477724357700163470ustar00rootroot00000000000000/* * @(#)FCGIMessage.java * * * FastCGi compatibility package Interface * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIMessage.java,v 1.4 2000/10/02 15:09:07 robs Exp $ */ package com.fastcgi; import java.io.*; import java.util.Properties; /* This class handles reading and building the fastcgi messages. * For reading incoming messages, we pass the input * stream as a param to the constructor rather than to each method. * Methods that build messages use and return internal buffers, so they * don't need a stream. */ public class FCGIMessage { private static final String RCSID = "$Id: FCGIMessage.java,v 1.4 2000/10/02 15:09:07 robs Exp $"; /* * Instance variables */ /* * FCGI Message Records * The logical structures of the FCGI Message Records. * Fields are originally 1 unsigned byte in message * unless otherwise noted. */ /* * FCGI Header */ private int h_version; private int h_type; private int h_requestID; // 2 bytes private int h_contentLength; // 2 bytes private int h_paddingLength; /* * FCGI BeginRequest body. */ private int br_role; // 2 bytes private int br_flags; private FCGIInputStream in; /* * constructor - Java would do this implicitly. */ public FCGIMessage(){ super(); } /* * constructor - get the stream. */ public FCGIMessage(FCGIInputStream instream){ in = instream; } /* * Message Reading Methods */ /* * Interpret the FCGI Message Header. Processes FCGI * BeginRequest and Management messages. Param hdr is the header. * The calling routine has to keep track of the stream reading * management or use FCGIInputStream.fill() which does just that. */ public int processHeader(byte[] hdr) throws IOException{ processHeaderBytes(hdr); if (h_version != FCGIGlobalDefs.def_FCGIVersion1) { return(FCGIGlobalDefs.def_FCGIUnsupportedVersion); } in.contentLen = h_contentLength; in.paddingLen = h_paddingLength; if (h_type == FCGIGlobalDefs.def_FCGIBeginRequest) { return processBeginRecord(h_requestID); } if (h_requestID == FCGIGlobalDefs.def_FCGINullRequestID) { return processManagementRecord(h_type); } if (h_requestID != in.request.requestID) { return(FCGIGlobalDefs.def_FCGISkip); } if (h_type != in.type) { return(FCGIGlobalDefs.def_FCGIProtocolError); } return(FCGIGlobalDefs.def_FCGIStreamRecord); } /* Put the unsigned bytes in the incoming FCGI header into * integer form for Java, concatinating bytes when needed. * Because Java has no unsigned byte type, we have to be careful * about signed numeric promotion to int. */ private void processHeaderBytes(byte[] hdrBuf){ h_version = hdrBuf[0] & 0xFF; h_type = hdrBuf[1] & 0xFF; h_requestID = ((hdrBuf[2] & 0xFF) << 8) | (hdrBuf[3] & 0xFF); h_contentLength = ((hdrBuf[4] & 0xFF) << 8) | (hdrBuf[5] & 0xFF); h_paddingLength = hdrBuf[6] & 0xFF; } /* * Reads FCGI Begin Request Record. */ public int processBeginRecord(int requestID) throws IOException { byte beginReqBody[]; byte endReqMsg[]; if (requestID == 0 || in.contentLen != FCGIGlobalDefs.def_FCGIEndReqBodyLen) { return FCGIGlobalDefs.def_FCGIProtocolError; } /* * If the webserver is multiplexing the connection, * this library can't deal with it, so repond with * FCGIEndReq message with protocolStatus FCGICantMpxConn */ if (in.request.isBeginProcessed) { endReqMsg = new byte[FCGIGlobalDefs.def_FCGIHeaderLen + FCGIGlobalDefs.def_FCGIEndReqBodyLen]; System.arraycopy(makeHeader( FCGIGlobalDefs.def_FCGIEndRequest, requestID, FCGIGlobalDefs.def_FCGIEndReqBodyLen, 0), 0, endReqMsg, 0, FCGIGlobalDefs.def_FCGIHeaderLen); System.arraycopy(makeEndrequestBody(0, FCGIGlobalDefs.def_FCGICantMpxConn), 0, endReqMsg, FCGIGlobalDefs.def_FCGIHeaderLen, FCGIGlobalDefs.def_FCGIEndReqBodyLen); /* * since isBeginProcessed is first set below,this * can't be out first call, so request.out is properly set */ try { in.request.outStream.write(endReqMsg, 0, FCGIGlobalDefs.def_FCGIHeaderLen + FCGIGlobalDefs.def_FCGIEndReqBodyLen); } catch (IOException e){ in.request.outStream.setException(e); return -1; } } /* * Accept this new request. Read the record body */ in.request.requestID = requestID; beginReqBody = new byte[FCGIGlobalDefs.def_FCGIBeginReqBodyLen]; if (in.read(beginReqBody, 0, FCGIGlobalDefs.def_FCGIBeginReqBodyLen) != FCGIGlobalDefs.def_FCGIBeginReqBodyLen) { return FCGIGlobalDefs.def_FCGIProtocolError; } br_flags = beginReqBody[2] & 0xFF; in.request.keepConnection = (br_flags & FCGIGlobalDefs.def_FCGIKeepConn) != 0; br_role = ((beginReqBody[0] & 0xFF) << 8) | (beginReqBody[1] & 0xFF); in.request.role = br_role; in.request.isBeginProcessed = true; return FCGIGlobalDefs.def_FCGIBeginRecord; } /* * Reads and Responds to a Management Message. The only type of * management message this library understands is FCGIGetValues. * The only variables that this library's FCGIGetValues understands * are def_FCGIMaxConns, def_FCGIMaxReqs, and def_FCGIMpxsConns. * Ignore the other management variables, and repsond to other * management messages with FCGIUnknownType. */ public int processManagementRecord(int type) throws IOException { byte[] response = new byte[64]; int wrndx = response[FCGIGlobalDefs.def_FCGIHeaderLen]; int value, len, plen; if (type == FCGIGlobalDefs.def_FCGIGetValues) { Properties tmpProps = new Properties(); readParams(tmpProps); if (in.getFCGIError() != 0 || in.contentLen != 0) { return FCGIGlobalDefs.def_FCGIProtocolError; } if (tmpProps.containsKey( FCGIGlobalDefs.def_FCGIMaxConns)) { makeNameVal( FCGIGlobalDefs.def_FCGIMaxConns, "1", response, wrndx); } else { if (tmpProps.containsKey( FCGIGlobalDefs.def_FCGIMaxReqs)) { makeNameVal( FCGIGlobalDefs.def_FCGIMaxReqs, "1", response, wrndx); } else { if (tmpProps.containsKey( FCGIGlobalDefs.def_FCGIMaxConns)) { makeNameVal( FCGIGlobalDefs.def_FCGIMpxsConns, "0", response, wrndx); } } } plen = 64 - wrndx; len = wrndx - FCGIGlobalDefs.def_FCGIHeaderLen; System.arraycopy(makeHeader( FCGIGlobalDefs.def_FCGIGetValuesResult, FCGIGlobalDefs.def_FCGINullRequestID, len, plen), 0, response, 0, FCGIGlobalDefs.def_FCGIHeaderLen); } else { plen = len = FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; System.arraycopy(makeHeader( FCGIGlobalDefs.def_FCGIUnknownType, FCGIGlobalDefs.def_FCGINullRequestID, len, 0), 0, response, 0, FCGIGlobalDefs.def_FCGIHeaderLen); System.arraycopy(makeUnknownTypeBodyBody(h_type), 0, response, FCGIGlobalDefs.def_FCGIHeaderLen, FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen); } /* * No guarantee that we have a request yet, so * don't use fcgi output stream to reference socket, instead * use the FileInputStream that references it. Also * nowhere to save exception, since this is not FCGI stream. */ try { in.request.socket.getOutputStream().write(response, 0, FCGIGlobalDefs.def_FCGIHeaderLen + FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen); } catch (IOException e){ return -1; } return FCGIGlobalDefs.def_FCGIMgmtRecord; } /* * Makes a name/value with name = string of some length, and * value a 1 byte integer. Pretty specific to what we are doing * above. */ void makeNameVal(String name, String value, byte[] dest, int pos) { int nameLen = name.length(); if (nameLen < 0x80) { dest[pos++] = (byte)nameLen; }else { dest[pos++] = (byte)(((nameLen >> 24) | 0x80) & 0xff); dest[pos++] = (byte)((nameLen >> 16) & 0xff); dest[pos++] = (byte)((nameLen >> 8) & 0xff); dest[pos++] = (byte)nameLen; } int valLen = value.length(); if (valLen < 0x80) { dest[pos++] = (byte)valLen; }else { dest[pos++] = (byte)(((valLen >> 24) | 0x80) & 0xff); dest[pos++] = (byte)((valLen >> 16) & 0xff); dest[pos++] = (byte)((valLen >> 8) & 0xff); dest[pos++] = (byte)valLen; } try { System.arraycopy(name.getBytes("UTF-8"), 0, dest, pos, nameLen); pos += nameLen; System.arraycopy(value.getBytes("UTF-8"), 0, dest, pos, valLen); pos += valLen; } catch (UnsupportedEncodingException x) {} } /* * Read FCGI name-value pairs from a stream until EOF. Put them * into a Properties object, storing both as strings. */ public int readParams(Properties props) throws IOException{ int nameLen, valueLen; byte lenBuff[] = new byte[3]; int i = 1; while ((nameLen = in.read()) != -1) { i++; if ((nameLen & 0x80) != 0) { if ((in.read( lenBuff, 0, 3)) != 3) { in.setFCGIError( FCGIGlobalDefs.def_FCGIParamsError); return -1; } nameLen = ((nameLen & 0x7f) << 24) | ((lenBuff[0] & 0xFF) << 16) | ((lenBuff[1] & 0xFF) << 8) | (lenBuff[2] & 0xFF); } if ((valueLen = in.read()) == -1) { in.setFCGIError( FCGIGlobalDefs.def_FCGIParamsError); return -1; } if ((valueLen & 0x80) != 0) { if ((in.read( lenBuff, 0, 3)) != 3) { in.setFCGIError( FCGIGlobalDefs.def_FCGIParamsError); return -1; } valueLen = ((valueLen & 0x7f) << 24) | ((lenBuff[0] & 0xFF) << 16) | ((lenBuff[1] & 0xFF) << 8) | (lenBuff[2] & 0xFF); } /* * nameLen and valueLen are now valid; read the name * and the value from the stream and construct a standard * environmental entity */ byte[] name = new byte[nameLen]; byte[] value = new byte[valueLen]; if (in.read(name ,0, nameLen) != nameLen) { in.setFCGIError( FCGIGlobalDefs.def_FCGIParamsError); return -1; } if(in.read(value, 0, valueLen) != valueLen) { in.setFCGIError( FCGIGlobalDefs.def_FCGIParamsError); return -1; } String strName = new String(name); String strValue = new String(value); props.put(strName, strValue); } return 0; } /* * Message Building Methods */ /* * Build an FCGI Message Header - */ public byte[] makeHeader(int type, int requestId, int contentLength, int paddingLength) { byte[] header = new byte[FCGIGlobalDefs.def_FCGIHeaderLen]; header[0] = (byte)FCGIGlobalDefs.def_FCGIVersion1; header[1] = (byte)type; header[2] = (byte)((requestId >> 8) & 0xff); header[3] = (byte)((requestId ) & 0xff); header[4] = (byte)((contentLength >> 8) & 0xff); header[5] = (byte)((contentLength ) & 0xff); header[6] = (byte)paddingLength; header[7] = 0; //reserved byte return header; } /* * Build an FCGI Message End Request Body */ public byte[] makeEndrequestBody(int appStatus,int protocolStatus){ byte body[] = new byte[FCGIGlobalDefs.def_FCGIEndReqBodyLen]; body[0] = (byte)((appStatus >> 24) & 0xff); body[1] = (byte)((appStatus >> 16) & 0xff); body[2] = (byte)((appStatus >> 8) & 0xff); body[3] = (byte)((appStatus ) & 0xff); body[4] = (byte)protocolStatus; for (int i = 5; i < 8; i++) { body[i] = 0; } return body; } /* * Build an FCGI Message UnknownTypeBodyBody */ public byte[] makeUnknownTypeBodyBody(int type){ byte body[] = new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen]; body[0] = (byte)type; for (int i = 1; i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) { body[i] = 0; } return body; } } //end class fcgi2-2.4.5/java/FCGIOutputStream.java000066400000000000000000000216571477724357700174470ustar00rootroot00000000000000/* * @(#)FCGIOutputStream.java * * FastCGi compatibility package Interface * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIOutputStream.java,v 1.3 2000/03/21 12:12:26 robs Exp $ */ package com.fastcgi; import java.io.*; /** * This stream understands FCGI prototcol. */ public class FCGIOutputStream extends OutputStream { private static final String RCSID = "$Id: FCGIOutputStream.java,v 1.3 2000/03/21 12:12:26 robs Exp $"; /* Stream vars */ public int wrNext; public int stop; public boolean isClosed; /* require methods to set, get and clear */ private int errno; private Exception errex; /* data vars */ public byte buff[]; public int buffLen; public int buffStop; public int type; public boolean isAnythingWritten; public boolean rawWrite; public FCGIRequest request; public FileOutputStream out; /** * Creates a new output stream to manage fcgi prototcol stuff * @param out the output stream buflen length of buffer streamType */ public FCGIOutputStream(FileOutputStream outStream, int bufLen, int streamType, FCGIRequest inreq) { out = outStream; buffLen = Math.min(bufLen, FCGIGlobalDefs.def_FCGIMaxLen); buff = new byte[buffLen]; type = streamType; stop = buffStop = buffLen; isAnythingWritten = false; rawWrite = false; wrNext = FCGIGlobalDefs.def_FCGIHeaderLen; isClosed = false; request = inreq; } /** * Writes a byte to the output stream. */ public void write(int c) throws IOException { if(wrNext != stop) { buff[wrNext++] = (byte)c; return; } if(isClosed) { throw new EOFException(); } empty(false); if(wrNext != stop) { buff[wrNext++] = (byte)c; return; } /* NOTE: ASSERT(stream->isClosed); */ /* bug in emptyBuffProc if not */ throw new EOFException(); } /** * Writes an array of bytes. This method will block until the bytes * are actually written. * @param b the data to be written */ public void write(byte b[]) throws IOException{ write(b, 0, b.length); } /** * Writes len consecutive bytes from off in the array b * into the output stream. Performs no interpretation * of the output bytes. Making the user convert the string to * bytes is in line with current Java practice. */ public void write(byte b[], int off, int len) throws IOException { int m, bytesMoved; /* * Fast path: room for n bytes in the buffer */ if(len <= (stop - wrNext)) { System.arraycopy(b, off, buff, wrNext, len); wrNext += len; return; } /* * General case: stream is closed or buffer empty procedure * needs to be called */ bytesMoved = 0; for (;;) { if(wrNext != stop) { m = Math.min(len - bytesMoved, stop - wrNext); System.arraycopy(b, off, buff, wrNext, m); bytesMoved += m; wrNext += m; if(bytesMoved == len) { return; } off += m; } if(isClosed) { throw new EOFException(); } empty(false); } } /** * Encapsulates any buffered stream content in a FastCGI * record. If !doClose, writes the data, making the buffer * empty. */ public void empty(boolean doClose) throws IOException { int cLen; /* * Alignment padding omitted in Java */ if (!rawWrite) { cLen = wrNext - FCGIGlobalDefs.def_FCGIHeaderLen; if(cLen > 0) { System.arraycopy(new FCGIMessage().makeHeader(type, request.requestID, cLen, 0), 0, buff, 0, FCGIGlobalDefs.def_FCGIHeaderLen); } else { wrNext = 0; } } if (doClose) { writeCloseRecords(); } if (wrNext != 0) { isAnythingWritten = true; try { out.write(buff, 0, wrNext); } catch (IOException e) { setException(e); return; } wrNext = 0; } /* * The buffer is empty. */ if(!rawWrite) { wrNext += FCGIGlobalDefs.def_FCGIHeaderLen; } } /** * Close the stream. */ public void close() throws IOException { if (isClosed) { return; } empty(true); /* * if isClosed, will return with EOFException from write. */ isClosed = true; stop = wrNext; return; } /** * Flushes any buffered output. * Server-push is a legitimate application of flush. * Otherwise, it is not very useful, since FCGIAccept * does it implicitly. flush may reduce performance * by increasing the total number of operating system calls * the application makes. */ public void flush() throws IOException { if (isClosed) { return; } empty(false); /* * if isClosed, will return with EOFException from write. */ return; } /** * An FCGI error has occurred. Save the error code in the stream * for diagnostic purposes and set the stream state so that * reads return EOF */ public void setFCGIError(int errnum) { /* * Preserve only the first error. */ if (errno == 0) { errno = errnum; } isClosed = true; } /** * An Exception has occurred. Save the Exception in the stream * for diagnostic purposes and set the stream state so that * reads return EOF */ public void setException(Exception errexpt) { /* * Preserve only the first error. */ if (errex == null) { errex = errexpt; } isClosed = true; } /** * Clear the stream error code and end-of-file indication. */ public void clearFCGIError() { errno = 0; /* * isClosed = false; * XXX: should clear isClosed but work is needed to make it safe * to do so. */ } /** * Clear the stream error code and end-of-file indication. */ public void clearException() { errex = null; /* * isClosed = false; * XXX: should clear isClosed but work is needed to make it safe * to do so. */ } /** * accessor method since var is private */ public int etFCGIError() { return errno; } /** * accessor method since var is private */ public Exception getException() { return errex; } /** * Writes an EOF record for the stream content if necessary. * If this is the last writer to close, writes an FCGI_END_REQUEST * record. */ public void writeCloseRecords() throws IOException { FCGIMessage msg = new FCGIMessage(); /* * Enter rawWrite mode so final records won't be * encapsulated as * stream data. */ rawWrite = true; /* * Generate EOF for stream content if needed. */ if(!(type == FCGIGlobalDefs.def_FCGIStderr && wrNext == 0 && !isAnythingWritten)) { byte hdr[] = new byte[FCGIGlobalDefs.def_FCGIHeaderLen]; System.arraycopy(msg.makeHeader(type, request.requestID, 0, 0), 0, hdr,0, FCGIGlobalDefs.def_FCGIHeaderLen); write(hdr, 0, hdr.length); } /* * Generate FCGI_END_REQUEST record if needed. */ if(request.numWriters == 1) { byte endReq[] = new byte[FCGIGlobalDefs.def_FCGIHeaderLen + FCGIGlobalDefs.def_FCGIEndReqBodyLen]; System.arraycopy(msg.makeHeader( FCGIGlobalDefs.def_FCGIEndRequest, request.requestID, FCGIGlobalDefs.def_FCGIEndReqBodyLen,0), 0, endReq, 0, FCGIGlobalDefs.def_FCGIHeaderLen); System.arraycopy(msg.makeEndrequestBody( request.appStatus, FCGIGlobalDefs.def_FCGIRequestComplete), 0,endReq, FCGIGlobalDefs.def_FCGIHeaderLen, FCGIGlobalDefs.def_FCGIEndReqBodyLen); write(endReq,0, FCGIGlobalDefs.def_FCGIHeaderLen + FCGIGlobalDefs.def_FCGIEndReqBodyLen); } request.numWriters--; } } fcgi2-2.4.5/java/FCGIRequest.java000066400000000000000000000030061477724357700164070ustar00rootroot00000000000000/* * @(#)FCGIRequest.java * * FastCGi compatibility package Interface * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: FCGIRequest.java,v 1.3 2000/03/21 12:12:26 robs Exp $ */ package com.fastcgi; import java.net.*; import java.io.FileDescriptor; import java.util.Properties; public class FCGIRequest { private static final String RCSID = "$Id: FCGIRequest.java,v 1.3 2000/03/21 12:12:26 robs Exp $"; /* This class has no methods. Right now we are single threaded * so there is only one request object at any given time which * is referenced by an FCGIInterface class variable . All of this * object's data could just as easily be declared directly there. * When we thread, this will change, so we might as well use a * separate class. In line with this thinking, though somewhat * more perversely, we kept the socket here. */ /* * class variables */ /*public static Socket socket; */ // same for all requests /* * instance variables */ public Socket socket; public boolean isBeginProcessed; public int requestID; public boolean keepConnection; public int role; public int appStatus; public int numWriters; public FCGIInputStream inStream; public FCGIOutputStream outStream; public FCGIOutputStream errStream; public Properties params; } fcgi2-2.4.5/libfcgi/000077500000000000000000000000001477724357700141425ustar00rootroot00000000000000fcgi2-2.4.5/libfcgi/Makefile.am000066400000000000000000000020341477724357700161750ustar00rootroot00000000000000# $Id: Makefile.am,v 1.9 2001/12/22 03:16:20 robs Exp $ INCLUDEDIR = ../include AM_CPPFLAGS = -I$(top_srcdir)/include -W -Wall -pedantic -Wno-unused-parameter INCLUDE_FILES = $(INCLUDEDIR)/fastcgi.h \ $(INCLUDEDIR)/fcgiapp.h \ $(INCLUDEDIR)/fcgimisc.h \ $(INCLUDEDIR)/fcgi_stdio.h \ $(INCLUDEDIR)/fcgios.h lib_LTLIBRARIES = libfcgi.la @LIBFCGIXX@ DISTCLEANFILES= Makefile.in EXTRA_LTLIBRARIES = libfcgi++.la libfcgi_la_SOURCES = $(INCLUDE_FILES) \ fcgiapp.c \ fcgi_stdio.c \ os_@SYSTEM@.c libfcgi_la_CC = @PTHREAD_CC@ libfcgi_la_CFLAGS = @PTHREAD_CFLAGS@ libfcgi_la_LIBADD = -lm libfcgi_la_LDFLAGS = @EXTRA_LIBS@ -no-undefined libfcgi___la_SOURCES = $(INCLUDE_FILES) \ $(INCLUDEDIR)/fcgio.h \ fcgio.cpp libfcgi___la_CFLAGS = @PTHREAD_CFLAGS@ libfcgi___la_LIBADD = libfcgi.la libfcgi___la_LDFLAGS = -rpath @libdir@ -no-undefined fcgi2-2.4.5/libfcgi/fcgi_stdio.c000066400000000000000000000475631477724357700164370ustar00rootroot00000000000000/* * fcgi_stdio.c -- * * FastCGI-stdio compatibility package * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include /* for errno */ #include /* for va_arg */ #include /* for malloc */ #include /* for strerror */ #include "fcgi_config.h" #ifdef HAVE_UNISTD_H #include #endif #include "fcgiapp.h" #include "fcgios.h" #include "fcgimisc.h" #define NO_FCGI_DEFINES #include "fcgi_stdio.h" #undef NO_FCGI_DEFINES #ifndef _WIN32 #if defined(__APPLE__) #include #define environ (*_NSGetEnviron()) #else extern char **environ; #endif #ifdef HAVE_FILENO_PROTO #include #else extern int fileno(FILE *stream); #endif extern FILE *fdopen(int fildes, const char *type); extern FILE *popen(const char *command, const char *type); extern int pclose(FILE *stream); #else /* _WIN32 */ #define popen _popen #define pclose _pclose #endif /* _WIN32 */ FCGI_FILE _fcgi_sF[3]; /* *---------------------------------------------------------------------- * * FCGI_Accept -- * * Accepts a new request from the HTTP server and creates * a conventional execution environment for the request. * * If the application was invoked as a FastCGI server, * the first call to FCGI_Accept indicates that the application * has completed its initialization and is ready to accept * a request. Subsequent calls to FCGI_Accept indicate that * the application has completed its processing of the * current request and is ready to accept a new request. * * If the application was invoked as a CGI program, the first * call to FCGI_Accept is essentially a no-op and the second * call returns EOF (-1). * * Results: * 0 for successful call, -1 for error (application should exit). * * Side effects: * If the application was invoked as a FastCGI server, * and this is not the first call to this procedure, * FCGI_Accept first performs the equivalent of FCGI_Finish. * * On every call, FCGI_Accept accepts the new request and * reads the FCGI_PARAMS stream into an environment array, * i.e. a NULL-terminated array of strings of the form * ``name=value''. It assigns a pointer to this array * to the global variable environ, used by the standard * library function getenv. It creates new FCGI_FILE *s * representing input from the HTTP server, output to the HTTP * server, and error output to the HTTP server, and assigns these * new files to stdin, stdout, and stderr respectively. * * DO NOT mutate or retain pointers to environ or any values * contained in it (e.g. to the result of calling getenv(3)), * since these are freed by the next call to FCGI_Finish or * FCGI_Accept. In particular do not use setenv(3) or putenv(3) * in conjunction with FCGI_Accept. * *---------------------------------------------------------------------- */ static int acceptCalled = FALSE; static int isCGI = FALSE; int FCGI_Accept(void) { if(!acceptCalled) { /* * First call to FCGI_Accept. Is application running * as FastCGI or as CGI? */ isCGI = FCGX_IsCGI(); acceptCalled = TRUE; atexit(&FCGI_Finish); } else if(isCGI) { /* * Not first call to FCGI_Accept and running as CGI means * application is done. */ return(EOF); } if(isCGI) { FCGI_stdin->stdio_stream = stdin; FCGI_stdin->fcgx_stream = NULL; FCGI_stdout->stdio_stream = stdout; FCGI_stdout->fcgx_stream = NULL; FCGI_stderr->stdio_stream = stderr; FCGI_stderr->fcgx_stream = NULL; } else { FCGX_Stream *in, *out, *error; FCGX_ParamArray envp; int acceptResult = FCGX_Accept(&in, &out, &error, &envp); if(acceptResult < 0) { return acceptResult; } FCGI_stdin->stdio_stream = NULL; FCGI_stdin->fcgx_stream = in; FCGI_stdout->stdio_stream = NULL; FCGI_stdout->fcgx_stream = out; FCGI_stderr->stdio_stream = NULL; FCGI_stderr->fcgx_stream = error; environ = envp; } return 0; } /* *---------------------------------------------------------------------- * * FCGI_Finish -- * * Finishes the current request from the HTTP server. * * Side effects: * * Flushes any buffered output to the HTTP server. Then frees * all storage allocated by the previous call, including all * storage reachable from the value of environ set by the previous * call to FCGI_Accept. * * DO NOT use stdin, stdout, stderr, or environ between calling * FCGI_Finish and calling FCGI_Accept. * * DO NOT mutate or retain pointers to environ or any values * contained in it (e.g. to the result of calling getenv(3)), * since these are freed by the next call to FCGI_Finish or * FCGI_Accept. In particular do not use setenv(3) or putenv(3) * in conjunction with FCGI_Accept. * *---------------------------------------------------------------------- */ void FCGI_Finish(void) { if(!acceptCalled || isCGI) { return; } FCGX_Finish(); FCGI_stdin->fcgx_stream = NULL; FCGI_stdout->fcgx_stream = NULL; FCGI_stderr->fcgx_stream = NULL; environ = NULL; } /* *---------------------------------------------------------------------- * * FCGI_StartFilterData -- * * * The current request is for the filter role, and stdin is * positioned at EOF of FCGI_STDIN. The call repositions * stdin to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF), the call sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ int FCGI_StartFilterData(void) { if(FCGI_stdin->stdio_stream) { return -1; } else { return FCGX_StartFilterData(FCGI_stdin->fcgx_stream); } } /* *---------------------------------------------------------------------- * * FCGI_SetExitStatus -- * * Sets the exit status for the current request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * FCGI_SetExitStatus several times during a request; the last call * before the request ends (by calling FCGI_Accept) determines the * value. * *---------------------------------------------------------------------- */ void FCGI_SetExitStatus(int status) { if(FCGI_stdin->fcgx_stream) { FCGX_SetExitStatus(status, FCGI_stdin->fcgx_stream); } } /* *---------------------------------------------------------------------- * * FCGI_perror -- * * Wrapper for function defined in H&S Section 11.2 * *---------------------------------------------------------------------- */ void FCGI_perror(const char *str) { FCGI_fputs(str, FCGI_stderr); FCGI_fputs(": ", FCGI_stderr); FCGI_fputs(strerror(OS_Errno), FCGI_stderr); return; } /* *---------------------------------------------------------------------- * * FCGI_OpenFromFILE -- * * Constructs a new FCGI_FILE * from the FILE *stream. * * Results: * NULL if stream == NULL or storage could not be allocated, * otherwise the new FCGI_FILE *. * *---------------------------------------------------------------------- */ static FCGI_FILE *FCGI_OpenFromFILE(FILE *stream) { FCGI_FILE *fp; if (stream == NULL) return NULL; fp = (FCGI_FILE *) malloc(sizeof(FCGI_FILE)); if (fp != NULL) { fp->stdio_stream = stream; fp->fcgx_stream = NULL; } return fp; } /* *---------------------------------------------------------------------- * * FCGI_fopen, FCGI_fclose, FCGI_fflush, FCGI_freopen -- * * Wrappers for functions defined in H&S Section 15.2 * *---------------------------------------------------------------------- */ FCGI_FILE *FCGI_fopen(const char *path, const char *mode) { FILE * file = fopen(path, mode); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } int FCGI_fclose(FCGI_FILE *fp) { int n = EOF; if(fp->stdio_stream) { n = fclose(fp->stdio_stream); fp->stdio_stream = NULL; } else if(fp->fcgx_stream) { n = FCGX_FClose(fp->fcgx_stream); fp->fcgx_stream = NULL; } if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { free(fp); } return n; } int FCGI_fflush(FCGI_FILE *fp) { if(fp == NULL) return fflush(NULL); if(fp->stdio_stream) return fflush(fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_FFlush(fp->fcgx_stream); return EOF; } FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp) { if(fp->stdio_stream) { if(freopen(path, mode, fp->stdio_stream) == NULL) return NULL; else return fp; } else if(fp->fcgx_stream) { (void) FCGX_FClose(fp->fcgx_stream); fp->stdio_stream = fopen(path, mode); if(fp->stdio_stream == NULL) return NULL; else { fp->fcgx_stream = NULL; return fp; } } return NULL; } /* *---------------------------------------------------------------------- * * FCGI_setvbuf, FCGI_setbuf -- * * Wrappers for functions defined in H&S Section 15.3 * *---------------------------------------------------------------------- */ int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size) { if(fp->stdio_stream) return setvbuf(fp->stdio_stream, buf, bufmode, size); else { return -1; } } void FCGI_setbuf(FCGI_FILE *fp, char *buf) { if(fp->stdio_stream) setbuf(fp->stdio_stream, buf); } /* *---------------------------------------------------------------------- * * FCGI_fseek, FCGI_ftell, FCGI_rewind, FCGI_fgetpos, FCGI_fsetpos -- * * Wrappers for functions defined in H&S Section 15.5 * *---------------------------------------------------------------------- */ int FCGI_fseek(FCGI_FILE *fp, long offset, int whence) { if(fp->stdio_stream) return fseek(fp->stdio_stream, offset, whence); else { OS_SetErrno(ESPIPE); return -1; } } int FCGI_ftell(FCGI_FILE *fp) { if(fp->stdio_stream) return ftell(fp->stdio_stream); else { OS_SetErrno(ESPIPE); return -1; } } void FCGI_rewind(FCGI_FILE *fp) { if(fp->stdio_stream) rewind(fp->stdio_stream); else OS_SetErrno(ESPIPE); } #ifdef HAVE_FPOS int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos) { if(fp->stdio_stream) return fgetpos(fp->stdio_stream, pos); else { OS_SetErrno(ESPIPE); return -1; } } int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos) { if(fp->stdio_stream) return fsetpos(fp->stdio_stream, pos); else { OS_SetErrno(ESPIPE); return -1; } } #endif /* *---------------------------------------------------------------------- * * FCGI_fgetc, FCGI_getchar, FCGI_ungetc -- * * Wrappers for functions defined in H&S Section 15.6 * * XXX: getc and getchar are generally defined as macros * for performance reasons * *---------------------------------------------------------------------- */ int FCGI_fgetc(FCGI_FILE *fp) { if(fp->stdio_stream) return fgetc(fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_GetChar(fp->fcgx_stream); return EOF; } int FCGI_getchar(void) { return FCGI_fgetc(FCGI_stdin); } int FCGI_ungetc(int c, FCGI_FILE *fp) { if(fp->stdio_stream) return ungetc(c, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_UnGetChar(c, fp->fcgx_stream); return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fgets, FCGI_gets -- * * Wrappers for functions defined in H&S Section 15.7 * *---------------------------------------------------------------------- */ char *FCGI_fgets(char *str, int size, FCGI_FILE *fp) { if(fp->stdio_stream) return fgets(str, size, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_GetLine(str, size, fp->fcgx_stream); return NULL; } /* * The gets() function reads characters from the standard input stream * into the array pointed to by str until a newline character is read * or an end-of-file condition is encountered. The newline character * is discarded and the string is terminated with a null character. */ char *FCGI_gets(char *str) { char *s; int c; for (s = str; ((c = FCGI_getchar()) != '\n');) { if(c == EOF) { if(s == str) return NULL; else break; } else *s++ = (char) c; } *s = 0; return str; } /* *---------------------------------------------------------------------- * * Wrappers for functions defined in H&S Section 15.8 * * XXX: missing: fscanf, scanf * *---------------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * * FCGI_fputc, FCGI_putchar -- * * Wrappers for functions defined in H&S Section 15.9 * * XXX: putc and putchar are generally defined as macros * for performance reasons * *---------------------------------------------------------------------- */ int FCGI_fputc(int c, FCGI_FILE *fp) { if(fp->stdio_stream) return fputc(c, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_PutChar(c, fp->fcgx_stream); else return EOF; } int FCGI_putchar(int c) { return FCGI_fputc(c, FCGI_stdout); } /* *---------------------------------------------------------------------- * * FCGI_fputs, FCGI_puts * * Wrappers for functions defined in H&S Section 15.10 * *---------------------------------------------------------------------- */ int FCGI_fputs(const char *str, FCGI_FILE *fp) { if(fp->stdio_stream) return fputs(str, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_PutS(str, fp->fcgx_stream); return EOF; } int FCGI_puts(const char *str) { int n; if(FCGI_stdout->stdio_stream) { n = fputs(str, FCGI_stdout->stdio_stream); if(n < 0) return n; else return fputc('\n', FCGI_stdout->stdio_stream); } else if(FCGI_stdout->fcgx_stream) { n = FCGX_PutS(str, FCGI_stdout->fcgx_stream); if(n < 0) return n; else return FCGX_PutChar('\n', FCGI_stdout->fcgx_stream); } return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fprintf, FCGI_printf -- * * Wrappers for functions defined in H&S Section 15.11 * *---------------------------------------------------------------------- */ int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...) { va_list ap; int n = 0; va_start(ap, format); if(fp->stdio_stream) n = vfprintf(fp->stdio_stream, format, ap); else if(fp->fcgx_stream) n = FCGX_VFPrintF(fp->fcgx_stream, format, ap); va_end(ap); return n; } int FCGI_printf(const char *format, ...) { va_list ap; int n; va_start(ap, format); n = FCGI_vfprintf(FCGI_stdout, format, ap); va_end(ap); return n; } /* *---------------------------------------------------------------------- * * FCGI_vfprintf, FCGI_vprintf -- * * Wrappers for functions defined in H&S Section 15.12 * *---------------------------------------------------------------------- */ int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap) { if(fp->stdio_stream) return vfprintf(fp->stdio_stream, format, ap); else if(fp->fcgx_stream) return FCGX_VFPrintF(fp->fcgx_stream, format, ap); return EOF; } int FCGI_vprintf(const char *format, va_list ap) { if(FCGI_stdout->stdio_stream) return vfprintf(FCGI_stdout->stdio_stream, format, ap); else if(FCGI_stdout->fcgx_stream) return FCGX_VFPrintF(FCGI_stdout->fcgx_stream, format, ap); return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fread, FCGI_fwrite -- * * Wrappers for functions defined in H&S Section 15.13 * *---------------------------------------------------------------------- */ size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) { int n; if(fp->stdio_stream) return fread(ptr, size, nmemb, fp->stdio_stream); else if(fp->fcgx_stream) { if((size * nmemb) == 0) { return 0; } n = FCGX_GetStr((char *) ptr, size * nmemb, fp->fcgx_stream); return (n/size); } return (size_t)EOF; } size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) { int n; if(fp->stdio_stream) return fwrite(ptr, size, nmemb, fp->stdio_stream); else if(fp->fcgx_stream) { if((size * nmemb) == 0) { return 0; } n = FCGX_PutStr((char *) ptr, size * nmemb, fp->fcgx_stream); return (n/size); } return (size_t)EOF; } /* *---------------------------------------------------------------------- * * FCGI_feof, FCGI_ferror, FCGI_clearerr -- * * Wrappers for functions defined in H&S Section 15.14 * *---------------------------------------------------------------------- */ int FCGI_feof(FCGI_FILE *fp) { if(fp->stdio_stream) { return feof(fp->stdio_stream); } else if (fp->fcgx_stream){ return FCGX_HasSeenEOF(fp->fcgx_stream); } return -1; } int FCGI_ferror(FCGI_FILE *fp) { if(fp->stdio_stream) { return ferror(fp->stdio_stream); } else if(fp->fcgx_stream) { return FCGX_GetError(fp->fcgx_stream); } return -1; } void FCGI_clearerr(FCGI_FILE *fp) { if(fp->stdio_stream) { clearerr(fp->stdio_stream); } else if(fp->fcgx_stream) { FCGX_ClearError(fp->fcgx_stream); } return; } /* *---------------------------------------------------------------------- * * FCGI_tmpfile -- * * Wrappers for function defined in H&S Section 15.16 * *---------------------------------------------------------------------- */ FCGI_FILE *FCGI_tmpfile(void) { FILE * file = tmpfile(); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } /* *---------------------------------------------------------------------- * * FCGI_fileno, FCGI_fdopen, FCGI_popen, FCGI_pclose -- * * Wrappers for POSIX, X/OPEN functions not in ISO C * *---------------------------------------------------------------------- */ int FCGI_fileno(FCGI_FILE *fp) { if(fp->stdio_stream) return fileno(fp->stdio_stream); else return -1; } FCGI_FILE *FCGI_fdopen(int fd, const char *mode) { FILE * file = fdopen(fd, mode); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } FCGI_FILE *FCGI_popen(const char *cmd, const char *type) { FILE * file = popen(cmd, type); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) pclose(file); return fcgi_file; } int FCGI_pclose(FCGI_FILE *fp) { int n = EOF; if (fp->stdio_stream) { n = pclose(fp->stdio_stream); fp->stdio_stream = NULL; } else if(fp->fcgx_stream) { /* * The caller is deeply confused; don't free the storage. */ return EOF; } if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { free(fp); } return n; } fcgi2-2.4.5/libfcgi/fcgiapp.c000066400000000000000000002174601477724357700157310ustar00rootroot00000000000000/* * fcgiapp.c -- * * FastCGI application library: request-at-a-time * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include #include #include /* for fcntl */ #include #include /* for memchr() */ #include #include #include #include #include #include "fcgi_config.h" #ifdef HAVE_SYS_SOCKET_H #include /* for getpeername */ #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include "fcgimisc.h" #include "fastcgi.h" #include "fcgios.h" #include "fcgiapp.h" /* * This is a workaround for one version of the HP C compiler * (c89 on HP-UX 9.04, also Stratus FTX), which will dump core * if given 'long double' for varargs. */ #ifdef HAVE_VA_ARG_LONG_DOUBLE_BUG #define LONG_DOUBLE double #else #define LONG_DOUBLE long double #endif /* * Globals */ static int libInitialized = 0; static int isFastCGI = -1; static char *webServerAddressList = NULL; static FCGX_Request the_request; void FCGX_ShutdownPending(void) { OS_ShutdownPending(); } static void *Malloc(size_t size) { void *result = malloc(size); ASSERT(size == 0 || result != NULL); return result; } static char *StringCopy(char *str) { int strLen = strlen(str); char *newString = (char *)Malloc(strLen + 1); memcpy(newString, str, strLen); newString[strLen] = '\000'; return newString; } /* *---------------------------------------------------------------------- * * FCGX_GetChar -- * * Reads a byte from the input stream and returns it. * * Results: * The byte, or EOF (-1) if the end of input has been reached. * *---------------------------------------------------------------------- */ int FCGX_GetChar(FCGX_Stream *stream) { if (stream->isClosed || ! stream->isReader) return EOF; if (stream->rdNext != stream->stop) return *stream->rdNext++; stream->fillBuffProc(stream); if (stream->isClosed) return EOF; stream->stopUnget = stream->rdNext; if (stream->rdNext != stream->stop) return *stream->rdNext++; ASSERT(stream->isClosed); /* bug in fillBufProc if not */ return EOF; } /* *---------------------------------------------------------------------- * * FCGX_GetStr -- * * Reads up to n consecutive bytes from the input stream * into the character array str. Performs no interpretation * of the input bytes. * * Results: * Number of bytes read. If result is smaller than n, * the end of input has been reached. * *---------------------------------------------------------------------- */ int FCGX_GetStr(char *str, int n, FCGX_Stream *stream) { int m, bytesMoved; if (stream->isClosed || ! stream->isReader || n <= 0) { return 0; } /* * Fast path: n bytes are already available */ if(n <= (stream->stop - stream->rdNext)) { memcpy(str, stream->rdNext, n); stream->rdNext += n; return n; } /* * General case: stream is closed or buffer fill procedure * needs to be called */ bytesMoved = 0; for (;;) { if(stream->rdNext != stream->stop) { m = min(n - bytesMoved, stream->stop - stream->rdNext); memcpy(str, stream->rdNext, m); bytesMoved += m; stream->rdNext += m; if(bytesMoved == n) return bytesMoved; str += m; } if(stream->isClosed || !stream->isReader) return bytesMoved; stream->fillBuffProc(stream); if (stream->isClosed) return bytesMoved; stream->stopUnget = stream->rdNext; } } /* *---------------------------------------------------------------------- * * FCGX_GetLine -- * * Reads up to n-1 consecutive bytes from the input stream * into the character array str. Stops before n-1 bytes * have been read if '\n' or EOF is read. The terminating '\n' * is copied to str. After copying the last byte into str, * stores a '\0' terminator. * * Results: * NULL if EOF is the first thing read from the input stream, * str otherwise. * *---------------------------------------------------------------------- */ char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream) { int c; char *p = str; n--; while (n > 0) { c = FCGX_GetChar(stream); if(c == EOF) { if(p == str) return NULL; else break; } *p++ = (char) c; n--; if(c == '\n') break; } *p = '\0'; return str; } /* *---------------------------------------------------------------------- * * FCGX_UnGetChar -- * * Pushes back the character c onto the input stream. One * character of pushback is guaranteed once a character * has been read. No pushback is possible for EOF. * * Results: * Returns c if the pushback succeeded, EOF if not. * *---------------------------------------------------------------------- */ int FCGX_UnGetChar(int c, FCGX_Stream *stream) { if(c == EOF || stream->isClosed || !stream->isReader || stream->rdNext == stream->stopUnget) return EOF; --(stream->rdNext); *stream->rdNext = (unsigned char) c; return c; } /* *---------------------------------------------------------------------- * * FCGX_HasSeenEOF -- * * Returns EOF if end-of-file has been detected while reading * from stream; otherwise returns 0. * * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately * following FCGX_GetChar(s) may return EOF. This function, like * the standard C stdio function feof, does not provide the * ability to peek ahead. * * Results: * EOF if end-of-file has been detected, 0 if not. * *---------------------------------------------------------------------- */ int FCGX_HasSeenEOF(FCGX_Stream *stream) { return (stream->isClosed) ? EOF : 0; } /* *---------------------------------------------------------------------- * * FCGX_PutChar -- * * Writes a byte to the output stream. * * Results: * The byte, or EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutChar(int c, FCGX_Stream *stream) { if(stream->wrNext != stream->stop) return (*stream->wrNext++ = (unsigned char) c); if(stream->isClosed || stream->isReader) return EOF; stream->emptyBuffProc(stream, FALSE); if(stream->wrNext != stream->stop) return (*stream->wrNext++ = (unsigned char) c); ASSERT(stream->isClosed); /* bug in emptyBuffProc if not */ return EOF; } /* *---------------------------------------------------------------------- * * FCGX_PutStr -- * * Writes n consecutive bytes from the character array str * into the output stream. Performs no interpretation * of the output bytes. * * Results: * Number of bytes written (n) for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream) { int m, bytesMoved; /* * Fast path: room for n bytes in the buffer */ if(n <= (stream->stop - stream->wrNext)) { memcpy(stream->wrNext, str, n); stream->wrNext += n; return n; } /* * General case: stream is closed or buffer empty procedure * needs to be called */ bytesMoved = 0; for (;;) { if(stream->wrNext != stream->stop) { m = min(n - bytesMoved, stream->stop - stream->wrNext); memcpy(stream->wrNext, str, m); bytesMoved += m; stream->wrNext += m; if(bytesMoved == n) return bytesMoved; str += m; } if(stream->isClosed || stream->isReader) return -1; stream->emptyBuffProc(stream, FALSE); } } /* *---------------------------------------------------------------------- * * FCGX_PutS -- * * Writes a character string to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutS(const char *str, FCGX_Stream *stream) { return FCGX_PutStr(str, strlen(str), stream); } /* *---------------------------------------------------------------------- * * FCGX_FPrintF -- * * Performs output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...) { int result; va_list ap; va_start(ap, format); result = FCGX_VFPrintF(stream, format, ap); va_end(ap); return result; } /* *---------------------------------------------------------------------- * * FCGX_VFPrintF -- * * Performs output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ #define PRINTF_BUFFLEN 100 /* * More than sufficient space for all unmodified conversions * except %s and %f. */ #define FMT_BUFFLEN 25 /* * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop */ static void CopyAndAdvance(char **destPtr, char **srcPtr, int n); int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg) { char *f, *fStop, *percentPtr, *p, *fmtBuffPtr, *buffPtr; int op, performedOp, sizeModifier, buffCount = 0, buffLen, specifierLength; int fastPath, n, auxBuffLen = 0, buffReqd, minWidth, precision, exp; char *auxBuffPtr = NULL; int streamCount = 0; char fmtBuff[FMT_BUFFLEN]; char buff[PRINTF_BUFFLEN]; int intArg; short shortArg; long longArg; long long longLongArg; unsigned unsignedArg; unsigned long uLongArg; unsigned long long uLongLongArg; unsigned short uShortArg; char *charPtrArg = NULL; void *voidPtrArg; int *intPtrArg; long *longPtrArg; short *shortPtrArg; double doubleArg = 0.0; LONG_DOUBLE lDoubleArg = 0.0L; fmtBuff[0] = '%'; f = (char *) format; fStop = f + strlen(f); while (f != fStop) { percentPtr = (char *)memchr(f, '%', fStop - f); if(percentPtr == NULL) percentPtr = fStop; if(percentPtr != f) { if(FCGX_PutStr(f, percentPtr - f, stream) < 0) goto ErrorReturn; streamCount += percentPtr - f; f = percentPtr; if(f == fStop) break; } fastPath = TRUE; /* * The following loop always executes either once or twice. */ for (;;) { if(fastPath) { /* * Fast path: Scan optimistically, hoping that no flags, * minimum field width, or precision are specified. * Use the preallocated buffer, which is large enough * for all fast path cases. If the conversion specifier * is really more complex, run the loop a second time * using the slow path. * Note that fast path execution of %s bypasses the buffer * and %f is not attempted on the fast path due to * its large buffering requirements. */ op = *(percentPtr + 1); switch(op) { case 'l': if(op == 'l' && *(percentPtr + 2) == 'l') { sizeModifier = 'L'; op = *(percentPtr + 3); fmtBuff[1] = 'l'; fmtBuff[2] = 'l'; fmtBuff[3] = (char) op; fmtBuff[4] = '\0'; specifierLength = 4; break; } /* fall through */ case 'L': case 'h': sizeModifier = op; op = *(percentPtr + 2); fmtBuff[1] = (char) sizeModifier; fmtBuff[2] = (char) op; fmtBuff[3] = '\0'; specifierLength = 3; break; default: sizeModifier = ' '; fmtBuff[1] = (char) op; fmtBuff[2] = '\0'; specifierLength = 2; break; } buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { /* * Slow path: Scan the conversion specifier and construct * a new format string, compute an upper bound on the * amount of buffering that sprintf will require, * and allocate a larger buffer if necessary. */ p = percentPtr + 1; fmtBuffPtr = &fmtBuff[1]; /* * Scan flags */ n = strspn(p, "-0+ #"); if(n > 5) goto ErrorReturn; CopyAndAdvance(&fmtBuffPtr, &p, n); /* * Scan minimum field width */ n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { minWidth = va_arg(arg, int); if(abs(minWidth) > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, "%d", minWidth); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else { minWidth = 0; } } else if(n <= 6) { minWidth = strtol(p, NULL, 10); CopyAndAdvance(&fmtBuffPtr, &p, n); } else { goto ErrorReturn; } /* * Scan precision */ if(*p == '.') { CopyAndAdvance(&fmtBuffPtr, &p, 1); n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { precision = va_arg(arg, int); if(precision < 0) precision = 0; if(precision > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, "%d", precision); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else { precision = 0; } } else if(n <= 6) { precision = strtol(p, NULL, 10); CopyAndAdvance(&fmtBuffPtr, &p, n); } else { goto ErrorReturn; } } else { precision = -1; } /* * Scan size modifier and conversion operation */ switch(*p) { case 'l': if(*p == 'l' && *(p + 1) == 'l') { sizeModifier = 'L'; CopyAndAdvance(&fmtBuffPtr, &p, 2); break; } /* fall through */ case 'L': case 'h': sizeModifier = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); break; default: sizeModifier = ' '; break; } op = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); ASSERT(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); *fmtBuffPtr = '\0'; specifierLength = p - percentPtr; /* * Bound the required buffer size. For s and f * conversions this requires examining the argument. */ switch(op) { case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'c': case 'p': buffReqd = max(precision, 46); break; case 's': charPtrArg = va_arg(arg, char *); if (!charPtrArg) charPtrArg = "(null)"; if(precision == -1) { buffReqd = strlen(charPtrArg); } else { p = (char *)memchr(charPtrArg, '\0', precision); buffReqd = (p == NULL) ? precision : p - charPtrArg; } break; case 'f': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); frexp(doubleArg, &exp); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); /* XXX Need to check for the presence of * frexpl() and use it if available */ frexp((double) lDoubleArg, &exp); break; default: goto ErrorReturn; } if(precision == -1) precision = 6; buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0); break; case 'e': case 'E': case 'g': case 'G': if(precision == -1) precision = 6; buffReqd = precision + 8; break; case 'n': case '%': default: goto ErrorReturn; break; } buffReqd = max(buffReqd + 10, minWidth); /* * Allocate the buffer */ if(buffReqd <= PRINTF_BUFFLEN) { buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { if(auxBuffPtr == NULL || buffReqd > auxBuffLen) { if(auxBuffPtr != NULL) free(auxBuffPtr); auxBuffPtr = (char *)Malloc(buffReqd); auxBuffLen = buffReqd; if(auxBuffPtr == NULL) goto ErrorReturn; } buffPtr = auxBuffPtr; buffLen = auxBuffLen; } } /* * This giant switch statement requires the following variables * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. * When fastPath == FALSE and op == 's' or 'f', the argument * has been read into charPtrArg, doubleArg, or lDoubleArg. * The statement produces the boolean performedOp, TRUE if * the op/sizeModifier were executed and argument consumed; * if performedOp, the characters written into buffPtr[] * and the character count buffCount (== EOF meaning error). * * The switch cases are arranged in the same order as in the * description of fprintf in section 15.11 of Harbison and Steele. */ performedOp = TRUE; switch(op) { case 'd': case 'i': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': longArg = va_arg(arg, long); sprintf(buffPtr, fmtBuff, longArg); buffCount = strlen(buffPtr); break; case 'L': longLongArg = va_arg(arg, long long); sprintf(buffPtr, fmtBuff, longLongArg); buffCount = strlen(buffPtr); break; case 'h': shortArg = (short) va_arg(arg, int); sprintf(buffPtr, fmtBuff, shortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'u': case 'o': case 'x': case 'X': switch(sizeModifier) { case ' ': unsignedArg = va_arg(arg, unsigned); sprintf(buffPtr, fmtBuff, unsignedArg); buffCount = strlen(buffPtr); break; case 'l': uLongArg = va_arg(arg, unsigned long); sprintf(buffPtr, fmtBuff, uLongArg); buffCount = strlen(buffPtr); break; case 'L': uLongLongArg = va_arg(arg, unsigned long long); sprintf(buffPtr, fmtBuff, uLongLongArg); buffCount = strlen(buffPtr); break; case 'h': uShortArg = (unsigned short) va_arg(arg, int); sprintf(buffPtr, fmtBuff, uShortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'c': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': /* * XXX: Allowed by ISO C Amendment 1, but * many platforms don't yet support wint_t */ goto ErrorReturn; default: goto ErrorReturn; } break; case 's': switch(sizeModifier) { case ' ': if(fastPath) { buffPtr = va_arg(arg, char *); buffCount = strlen(buffPtr); buffLen = buffCount + 1; } else { sprintf(buffPtr, fmtBuff, charPtrArg); buffCount = strlen(buffPtr); } break; case 'l': /* * XXX: Don't know how to convert a sequence * of wide characters into a byte stream, or * even how to predict the buffering required. */ goto ErrorReturn; default: goto ErrorReturn; } break; case 'p': if(sizeModifier != ' ') goto ErrorReturn; voidPtrArg = va_arg(arg, void *); sprintf(buffPtr, fmtBuff, voidPtrArg); buffCount = strlen(buffPtr); break; case 'n': switch(sizeModifier) { case ' ': intPtrArg = va_arg(arg, int *); *intPtrArg = streamCount; break; case 'l': longPtrArg = va_arg(arg, long *); *longPtrArg = streamCount; break; case 'h': shortPtrArg = (short *) va_arg(arg, short *); *shortPtrArg = (short) streamCount; break; default: goto ErrorReturn; } buffCount = 0; break; case 'f': if(fastPath) { performedOp = FALSE; break; } switch(sizeModifier) { case ' ': sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'e': case 'E': case 'g': case 'G': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case '%': if(sizeModifier != ' ') goto ErrorReturn; buff[0] = '%'; buffCount = 1; break; case '\0': goto ErrorReturn; default: performedOp = FALSE; break; } /* switch(op) */ if(performedOp) break; if(!fastPath) goto ErrorReturn; fastPath = FALSE; } /* for (;;) */ ASSERT(buffCount < buffLen); if(buffCount > 0) { if(FCGX_PutStr(buffPtr, buffCount, stream) < 0) goto ErrorReturn; streamCount += buffCount; } else if(buffCount < 0) { goto ErrorReturn; } f += specifierLength; } /* while(f != fStop) */ goto NormalReturn; ErrorReturn: streamCount = -1; NormalReturn: if(auxBuffPtr != NULL) free(auxBuffPtr); return streamCount; } /* * Copy n characters from *srcPtr to *destPtr, then increment * both *srcPtr and *destPtr by n. */ static void CopyAndAdvance(char **destPtr, char **srcPtr, int n) { char *dest = *destPtr; char *src = *srcPtr; int i; for (i = 0; i < n; i++) *dest++ = *src++; *destPtr = dest; *srcPtr = src; } /* *---------------------------------------------------------------------- * * FCGX_FFlush -- * * Flushes any buffered output. * * Server-push is a legitimate application of FCGX_FFlush. * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept * does it implicitly. FCGX_FFlush may reduce performance * by increasing the total number of operating system calls * the application makes. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FFlush(FCGX_Stream *stream) { if(stream->isClosed || stream->isReader) return 0; stream->emptyBuffProc(stream, FALSE); return (stream->isClosed) ? -1 : 0; } /* *---------------------------------------------------------------------- * * FCGX_FClose -- * * Performs FCGX_FFlush and closes the stream. * * This is not a very useful operation, since FCGX_Accept * does it implicitly. Closing the out stream before the * err stream results in an extra write if there's nothing * in the err stream, and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FClose(FCGX_Stream *stream) { if (stream == NULL) return 0; if(!stream->wasFCloseCalled) { if(!stream->isReader) { stream->emptyBuffProc(stream, TRUE); } stream->wasFCloseCalled = TRUE; stream->isClosed = TRUE; if(stream->isReader) { stream->wrNext = stream->stop = stream->rdNext; } else { stream->rdNext = stream->stop = stream->wrNext; } } return (stream->FCGI_errno == 0) ? 0 : EOF; } /* *---------------------------------------------------------------------- * * SetError -- * * An error has occurred; save the error code in the stream * for diagnostic purposes and set the stream state so that * reads return EOF and writes have no effect. * *---------------------------------------------------------------------- */ static void SetError(FCGX_Stream *stream, int FCGI_errno) { /* * Preserve only the first error. */ if(stream->FCGI_errno == 0) { stream->FCGI_errno = FCGI_errno; } stream->isClosed = TRUE; } /* *---------------------------------------------------------------------- * * FCGX_GetError -- * * Return the stream error code. 0 means no error, > 0 * is an errno(2) error, < 0 is an FCGX_errno error. * *---------------------------------------------------------------------- */ int FCGX_GetError(FCGX_Stream *stream) { return stream->FCGI_errno; } /* *---------------------------------------------------------------------- * * FCGX_ClearError -- * * Clear the stream error code and end-of-file indication. * *---------------------------------------------------------------------- */ void FCGX_ClearError(FCGX_Stream *stream) { stream->FCGI_errno = 0; /* * stream->isClosed = FALSE; * XXX: should clear isClosed but work is needed to make it safe * to do so. For example, if an application calls FClose, gets * an I/O error on the write, calls ClearError and retries * the FClose, FClose (really EmptyBuffProc) will write a second * EOF record. If an application calls PutChar instead of FClose * after the ClearError, the application will write more data. * The stream's state must discriminate between various states * of the stream that are now all lumped under isClosed. */ } /* *====================================================================== * Parameters *====================================================================== */ /* * A vector of pointers representing the parameters received * by a FastCGI application server, with the vector's length * and last valid element so adding new parameters is efficient. */ typedef struct Params { FCGX_ParamArray vec; /* vector of strings */ int length; /* number of string vec can hold */ char **cur; /* current item in vec; *cur == NULL */ } Params; typedef Params *ParamsPtr; /* *---------------------------------------------------------------------- * * NewParams -- * * Creates a new Params structure. * * Results: * Pointer to the new structure. * *---------------------------------------------------------------------- */ static ParamsPtr NewParams(int length) { ParamsPtr result; result = (Params *)Malloc(sizeof(Params)); result->vec = (char **)Malloc(length * sizeof(char *)); result->length = length; result->cur = result->vec; *result->cur = NULL; return result; } /* *---------------------------------------------------------------------- * * FreeParams -- * * Frees a Params structure and all the parameters it contains. * * Side effects: * env becomes invalid. * *---------------------------------------------------------------------- */ static void FreeParams(ParamsPtr *paramsPtrPtr) { ParamsPtr paramsPtr = *paramsPtrPtr; char **p; if(paramsPtr == NULL) { return; } for (p = paramsPtr->vec; p < paramsPtr->cur; p++) { free(*p); } free(paramsPtr->vec); free(paramsPtr); *paramsPtrPtr = NULL; } /* *---------------------------------------------------------------------- * * PutParam -- * * Add a name/value pair to a Params structure. * * Results: * None. * * Side effects: * Parameters structure updated. * *---------------------------------------------------------------------- */ static void PutParam(ParamsPtr paramsPtr, char *nameValue) { int size; *paramsPtr->cur++ = nameValue; size = paramsPtr->cur - paramsPtr->vec; if(size >= paramsPtr->length) { paramsPtr->length *= 2; paramsPtr->vec = (FCGX_ParamArray)realloc(paramsPtr->vec, paramsPtr->length * sizeof(char *)); paramsPtr->cur = paramsPtr->vec + size; } *paramsPtr->cur = NULL; } /* *---------------------------------------------------------------------- * * FCGX_GetParam -- obtain value of FCGI parameter in environment * * * Results: * Value bound to name, NULL if name not present in the * environment envp. Caller must not mutate the result * or retain it past the end of this request. * *---------------------------------------------------------------------- */ char *FCGX_GetParam(const char *name, FCGX_ParamArray envp) { int len; char **p; if (name == NULL || envp == NULL) return NULL; len = strlen(name); for (p = envp; *p; ++p) { if((strncmp(name, *p, len) == 0) && ((*p)[len] == '=')) { return *p+len+1; } } return NULL; } /* *---------------------------------------------------------------------- * * Start of FastCGI-specific code * *---------------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * * ReadParams -- * * Reads FastCGI name-value pairs from stream until EOF. Converts * each pair to name=value format and adds it to Params structure. * *---------------------------------------------------------------------- */ static int ReadParams(Params *paramsPtr, FCGX_Stream *stream) { int nameLen, valueLen; unsigned char lenBuff[3]; char *nameValue; while((nameLen = FCGX_GetChar(stream)) != EOF) { /* * Read name length (one or four bytes) and value length * (one or four bytes) from stream. */ if((nameLen & 0x80) != 0) { if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } nameLen = ((nameLen & 0x7f) << 24) + (lenBuff[0] << 16) + (lenBuff[1] << 8) + lenBuff[2]; if (nameLen >= INT_MAX) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } } if((valueLen = FCGX_GetChar(stream)) == EOF) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } if((valueLen & 0x80) != 0) { if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } valueLen = ((valueLen & 0x7f) << 24) + (lenBuff[0] << 16) + (lenBuff[1] << 8) + lenBuff[2]; if (valueLen >= INT_MAX) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } } /* * nameLen and valueLen are now valid; read the name and value * from stream and construct a standard environment entry. */ nameValue = (char *)Malloc(nameLen + valueLen + 2); if(FCGX_GetStr(nameValue, nameLen, stream) != nameLen) { SetError(stream, FCGX_PARAMS_ERROR); free(nameValue); return -1; } *(nameValue + nameLen) = '='; if(FCGX_GetStr(nameValue + nameLen + 1, valueLen, stream) != valueLen) { SetError(stream, FCGX_PARAMS_ERROR); free(nameValue); return -1; } *(nameValue + nameLen + valueLen + 1) = '\0'; PutParam(paramsPtr, nameValue); } return 0; } /* *---------------------------------------------------------------------- * * MakeHeader -- * * Constructs an FCGI_Header struct. * *---------------------------------------------------------------------- */ static FCGI_Header MakeHeader( int type, int requestId, int contentLength, int paddingLength) { FCGI_Header header; ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH); ASSERT(paddingLength >= 0 && paddingLength <= 0xff); header.version = FCGI_VERSION_1; header.type = (unsigned char) type; header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); header.requestIdB0 = (unsigned char) ((requestId ) & 0xff); header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); header.contentLengthB0 = (unsigned char) ((contentLength ) & 0xff); header.paddingLength = (unsigned char) paddingLength; header.reserved = 0; return header; } /* *---------------------------------------------------------------------- * * MakeEndRequestBody -- * * Constructs an FCGI_EndRequestBody struct. * *---------------------------------------------------------------------- */ static FCGI_EndRequestBody MakeEndRequestBody( int appStatus, int protocolStatus) { FCGI_EndRequestBody body; body.appStatusB3 = (unsigned char) ((appStatus >> 24) & 0xff); body.appStatusB2 = (unsigned char) ((appStatus >> 16) & 0xff); body.appStatusB1 = (unsigned char) ((appStatus >> 8) & 0xff); body.appStatusB0 = (unsigned char) ((appStatus ) & 0xff); body.protocolStatus = (unsigned char) protocolStatus; memset(body.reserved, 0, sizeof(body.reserved)); return body; } /* *---------------------------------------------------------------------- * * MakeUnknownTypeBody -- * * Constructs an FCGI_MakeUnknownTypeBody struct. * *---------------------------------------------------------------------- */ static FCGI_UnknownTypeBody MakeUnknownTypeBody( int type) { FCGI_UnknownTypeBody body; body.type = (unsigned char) type; memset(body.reserved, 0, sizeof(body.reserved)); return body; } /* *---------------------------------------------------------------------- * * AlignInt8 -- * * Returns the smallest integer greater than or equal to n * that's a multiple of 8. * *---------------------------------------------------------------------- */ static int AlignInt8(unsigned n) { return (n + 7) & (UINT_MAX - 7); } /* *---------------------------------------------------------------------- * * AlignPtr8 -- * * Returns the smallest pointer greater than or equal to p * that's a multiple of 8. * *---------------------------------------------------------------------- */ static unsigned char *AlignPtr8(unsigned char *p) { unsigned long u = (unsigned long) p; u = ((u + 7) & (ULONG_MAX - 7)) - u; return p + u; } /* * State associated with a stream */ typedef struct FCGX_Stream_Data { unsigned char *buff; /* buffer after alignment */ int bufflen; /* number of bytes buff can store */ unsigned char *mBuff; /* buffer as returned by Malloc */ unsigned char *buffStop; /* reader: last valid byte + 1 of entire buffer. * stop generally differs from buffStop for * readers because of record structure. * writer: buff + bufflen */ int type; /* reader: FCGI_PARAMS or FCGI_STDIN * writer: FCGI_STDOUT or FCGI_STDERR */ int eorStop; /* reader: stop stream at end-of-record */ int skip; /* reader: don't deliver content bytes */ int contentLen; /* reader: bytes of unread content */ int paddingLen; /* reader: bytes of unread padding */ int isAnythingWritten; /* writer: data has been written to ipcFd */ int rawWrite; /* writer: write data without stream headers */ FCGX_Request *reqDataPtr; /* request data not specific to one stream */ } FCGX_Stream_Data; /* *---------------------------------------------------------------------- * * WriteCloseRecords -- * * Writes an EOF record for the stream content if necessary. * If this is the last writer to close, writes an FCGI_END_REQUEST * record. * *---------------------------------------------------------------------- */ static void WriteCloseRecords(struct FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; /* * Enter rawWrite mode so final records won't be encapsulated as * stream data. */ data->rawWrite = TRUE; /* * Generate EOF for stream content if needed. */ if(!(data->type == FCGI_STDERR && stream->wrNext == data->buff && !data->isAnythingWritten)) { FCGI_Header header; header = MakeHeader(data->type, data->reqDataPtr->requestId, 0, 0); FCGX_PutStr((char *) &header, sizeof(header), stream); }; /* * Generate FCGI_END_REQUEST record if needed. */ if(data->reqDataPtr->nWriters == 1) { FCGI_EndRequestRecord endRequestRecord; endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, data->reqDataPtr->requestId, sizeof(endRequestRecord.body), 0); endRequestRecord.body = MakeEndRequestBody( data->reqDataPtr->appStatus, FCGI_REQUEST_COMPLETE); FCGX_PutStr((char *) &endRequestRecord, sizeof(endRequestRecord), stream); } data->reqDataPtr->nWriters--; } static int write_it_all(int fd, char *buf, int len) { int wrote; while (len) { wrote = OS_Write(fd, buf, len); if (wrote < 0) return wrote; len -= wrote; buf += wrote; } return len; } /* *---------------------------------------------------------------------- * * EmptyBuffProc -- * * Encapsulates any buffered stream content in a FastCGI * record. Writes the data, making the buffer empty. * *---------------------------------------------------------------------- */ static void EmptyBuffProc(struct FCGX_Stream *stream, int doClose) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; int cLen, eLen; /* * If the buffer contains stream data, fill in the header. * Pad the record to a multiple of 8 bytes in length. Padding * can't overflow the buffer because the buffer is a multiple * of 8 bytes in length. If the buffer contains no stream * data, reclaim the space reserved for the header. */ if(!data->rawWrite) { cLen = stream->wrNext - data->buff - sizeof(FCGI_Header); if(cLen > 0) { eLen = AlignInt8(cLen); /* * Giving the padding a well-defined value keeps Purify happy. */ memset(stream->wrNext, 0, eLen - cLen); stream->wrNext += eLen - cLen; *((FCGI_Header *) data->buff) = MakeHeader(data->type, data->reqDataPtr->requestId, cLen, eLen - cLen); } else { stream->wrNext = data->buff; } } if(doClose) { WriteCloseRecords(stream); }; if (stream->wrNext != data->buff) { data->isAnythingWritten = TRUE; if (write_it_all(data->reqDataPtr->ipcFd, (char *)data->buff, stream->wrNext - data->buff) < 0) { SetError(stream, OS_Errno); return; } stream->wrNext = data->buff; } /* * The buffer is empty. */ if(!data->rawWrite) { stream->wrNext += sizeof(FCGI_Header); } } /* * Return codes for Process* functions */ #define STREAM_RECORD 0 #define SKIP 1 #define BEGIN_RECORD 2 #define MGMT_RECORD 3 /* *---------------------------------------------------------------------- * * ProcessManagementRecord -- * * Reads and responds to a management record. The only type of * management record this library understands is FCGI_GET_VALUES. * The only variables that this library's FCGI_GET_VALUES * understands are FCGI_MAX_CONNS, FCGI_MAX_REQS, and FCGI_MPXS_CONNS. * Ignore other FCGI_GET_VALUES variables; respond to other * management records with a FCGI_UNKNOWN_TYPE record. * *---------------------------------------------------------------------- */ static int ProcessManagementRecord(int type, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; ParamsPtr paramsPtr; char **pPtr; char response[64]; /* 64 = 8 + 3*(1+1+14+1)* + padding */ char *responseP = &response[FCGI_HEADER_LEN]; char *name, value = '\0'; int len, paddedLen; if(type == FCGI_GET_VALUES) { paramsPtr = NewParams(3); ReadParams(paramsPtr, stream); if((FCGX_GetError(stream) != 0) || (data->contentLen != 0)) { FreeParams(¶msPtr); return FCGX_PROTOCOL_ERROR; } for (pPtr = paramsPtr->vec; pPtr < paramsPtr->cur; pPtr++) { name = *pPtr; char *tmpPtr = strchr(name, '='); if(tmpPtr != NULL) { *tmpPtr = '\0'; } if(strcmp(name, FCGI_MAX_CONNS) == 0) { value = '1'; } else if(strcmp(name, FCGI_MAX_REQS) == 0) { value = '1'; } else if(strcmp(name, FCGI_MPXS_CONNS) == 0) { value = '0'; } else { name = NULL; } if(name != NULL) { len = strlen(name); sprintf(responseP, "%c%c%s%c", len, 1, name, value); responseP += len + 3; } } len = responseP - &response[FCGI_HEADER_LEN]; paddedLen = AlignInt8(len); FCGI_Header *header = (FCGI_Header *)response; *header = MakeHeader(FCGI_GET_VALUES_RESULT, FCGI_NULL_REQUEST_ID, len, paddedLen - len); FreeParams(¶msPtr); } else { paddedLen = len = sizeof(FCGI_UnknownTypeBody); FCGI_UnknownTypeRecord *utr = (FCGI_UnknownTypeRecord *) response; utr->header = MakeHeader(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, len, 0); utr->body = MakeUnknownTypeBody(type); } if (write_it_all(data->reqDataPtr->ipcFd, response, FCGI_HEADER_LEN + paddedLen) < 0) { SetError(stream, OS_Errno); return -1; } return MGMT_RECORD; } /* *---------------------------------------------------------------------- * * ProcessBeginRecord -- * * Reads an FCGI_BEGIN_REQUEST record. * * Results: * BEGIN_RECORD for normal return. FCGX_PROTOCOL_ERROR for * protocol error. SKIP for attempt to multiplex * connection. -1 for error from write (errno in stream). * * Side effects: * In case of BEGIN_RECORD return, stores requestId, role, * keepConnection values, and sets isBeginProcessed = TRUE. * *---------------------------------------------------------------------- */ static int ProcessBeginRecord(int requestId, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; FCGI_BeginRequestBody body; if(requestId == 0 || data->contentLen != sizeof(body)) { return FCGX_PROTOCOL_ERROR; } if(data->reqDataPtr->isBeginProcessed) { /* * The Web server is multiplexing the connection. This library * doesn't know how to handle multiplexing, so respond with * FCGI_END_REQUEST{protocolStatus = FCGI_CANT_MPX_CONN} */ FCGI_EndRequestRecord endRequestRecord; endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, requestId, sizeof(endRequestRecord.body), 0); endRequestRecord.body = MakeEndRequestBody(0, FCGI_CANT_MPX_CONN); if (write_it_all(data->reqDataPtr->ipcFd, (char *)&endRequestRecord, sizeof(endRequestRecord)) < 0) { SetError(stream, OS_Errno); return -1; } return SKIP; } /* * Accept this new request. Read the record body. */ data->reqDataPtr->requestId = requestId; if(FCGX_GetStr((char *) &body, sizeof(body), stream) != sizeof(body)) { return FCGX_PROTOCOL_ERROR; } data->reqDataPtr->keepConnection = (body.flags & FCGI_KEEP_CONN); data->reqDataPtr->role = (body.roleB1 << 8) + body.roleB0; data->reqDataPtr->isBeginProcessed = TRUE; return BEGIN_RECORD; } /* *---------------------------------------------------------------------- * * ProcessHeader -- * * Interprets FCGI_Header. Processes FCGI_BEGIN_REQUEST and * management records here; extracts information from stream * records (FCGI_PARAMS, FCGI_STDIN) into stream. * * Results: * >= 0 for a normal return, < 0 for error * * Side effects: * XXX: Many (more than there used to be). * If !stream->isRequestIdSet, ProcessHeader initializes * stream->requestId from header and sets stream->isRequestIdSet * to TRUE. ProcessHeader also sets stream->contentLen to header's * contentLength, and sets stream->paddingLen to the header's * paddingLength. * *---------------------------------------------------------------------- */ static int ProcessHeader(FCGI_Header header, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; int requestId; if(header.version != FCGI_VERSION_1) { return FCGX_UNSUPPORTED_VERSION; } requestId = (header.requestIdB1 << 8) + header.requestIdB0; data->contentLen = (header.contentLengthB1 << 8) + header.contentLengthB0; data->paddingLen = header.paddingLength; if(header.type == FCGI_BEGIN_REQUEST) { return ProcessBeginRecord(requestId, stream); } if(requestId == FCGI_NULL_REQUEST_ID) { return ProcessManagementRecord(header.type, stream); } if(requestId != data->reqDataPtr->requestId) { return SKIP; } if(header.type != data->type) { return FCGX_PROTOCOL_ERROR; } return STREAM_RECORD; } /* *---------------------------------------------------------------------- * * FillBuffProc -- * * Reads bytes from the ipcFd, supplies bytes to a stream client. * *---------------------------------------------------------------------- */ static void FillBuffProc(FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; FCGI_Header header; int headerLen = 0; int status, count; for (;;) { /* * If data->buff is empty, do a read. */ if(stream->rdNext == data->buffStop) { count = OS_Read(data->reqDataPtr->ipcFd, (char *)data->buff, data->bufflen); if(count <= 0) { SetError(stream, (count == 0 ? FCGX_PROTOCOL_ERROR : OS_Errno)); return; } stream->rdNext = data->buff; data->buffStop = data->buff + count; } /* * Now data->buff is not empty. If the current record contains * more content bytes, deliver all that are present in data->buff. */ if(data->contentLen > 0) { count = min(data->contentLen, data->buffStop - stream->rdNext); data->contentLen -= count; if(!data->skip) { stream->wrNext = stream->stop = stream->rdNext + count; return; } else { stream->rdNext += count; if(data->contentLen > 0) { continue; } else { data->skip = FALSE; } } } /* * If the current record (whose content has been fully consumed by * the client) was padded, skip over the padding bytes. */ if(data->paddingLen > 0) { count = min(data->paddingLen, data->buffStop - stream->rdNext); data->paddingLen -= count; stream->rdNext += count; if(data->paddingLen > 0) { continue; } } /* * All done with the current record, including the padding. * If we're in a recursive call from ProcessHeader, deliver EOF. */ if(data->eorStop) { stream->stop = stream->rdNext; stream->isClosed = TRUE; return; } /* * Fill header with bytes from the input buffer. */ count = min((int)sizeof(header) - headerLen, data->buffStop - stream->rdNext); memcpy(((char *)(&header)) + headerLen, stream->rdNext, count); headerLen += count; stream->rdNext += count; if(headerLen < (int)sizeof(header)) { continue; }; headerLen = 0; /* * Interpret header. eorStop prevents ProcessHeader from reading * past the end-of-record when using stream to read content. */ data->eorStop = TRUE; stream->stop = stream->rdNext; status = ProcessHeader(header, stream); data->eorStop = FALSE; stream->isClosed = FALSE; switch(status) { case STREAM_RECORD: /* * If this stream record header marked the end of stream * data deliver EOF to the stream client, otherwise loop * and deliver data. * * XXX: If this is final stream and * stream->rdNext != data->buffStop, buffered * data is next request (server pipelining)? */ if(data->contentLen == 0) { stream->wrNext = stream->stop = stream->rdNext; stream->isClosed = TRUE; return; } break; case SKIP: data->skip = TRUE; break; case BEGIN_RECORD: /* * If this header marked the beginning of a new * request, return role information to caller. */ return; break; case MGMT_RECORD: break; default: ASSERT(status < 0); SetError(stream, status); return; break; } } } /* *---------------------------------------------------------------------- * * NewStream -- * * Creates a stream to read or write from an open ipcFd. * The stream performs reads/writes of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewStream( FCGX_Request *reqDataPtr, int bufflen, int isReader, int streamType) { /* * XXX: It would be a lot cleaner to have a NewStream that only * knows about the type FCGX_Stream, with all other * necessary data passed in. It appears that not just * data and the two procs are needed for initializing stream, * but also data->buff and data->buffStop. This has implications * for procs that want to swap buffers, too. */ FCGX_Stream *stream = (FCGX_Stream *)Malloc(sizeof(FCGX_Stream)); FCGX_Stream_Data *data = (FCGX_Stream_Data *)Malloc(sizeof(FCGX_Stream_Data)); data->reqDataPtr = reqDataPtr; bufflen = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1)); data->bufflen = bufflen; data->mBuff = (unsigned char *)Malloc(bufflen); data->buff = AlignPtr8(data->mBuff); if(data->buff != data->mBuff) { data->bufflen -= 8; } if(isReader) { data->buffStop = data->buff; } else { data->buffStop = data->buff + data->bufflen; } data->type = streamType; data->eorStop = FALSE; data->skip = FALSE; data->contentLen = 0; data->paddingLen = 0; data->isAnythingWritten = FALSE; data->rawWrite = FALSE; stream->data = data; stream->isReader = isReader; stream->isClosed = FALSE; stream->wasFCloseCalled = FALSE; stream->FCGI_errno = 0; if(isReader) { stream->fillBuffProc = FillBuffProc; stream->emptyBuffProc = NULL; stream->rdNext = data->buff; stream->stop = stream->rdNext; stream->stopUnget = data->buff; stream->wrNext = stream->stop; } else { stream->fillBuffProc = NULL; stream->emptyBuffProc = EmptyBuffProc; stream->wrNext = data->buff + sizeof(FCGI_Header); stream->stop = data->buffStop; stream->stopUnget = NULL; stream->rdNext = stream->stop; } return stream; } /* *---------------------------------------------------------------------- * * FCGX_FreeStream -- * * Frees all storage allocated when *streamPtr was created, * and nulls out *streamPtr. * *---------------------------------------------------------------------- */ void FCGX_FreeStream(FCGX_Stream **streamPtr) { FCGX_Stream *stream = *streamPtr; FCGX_Stream_Data *data; if(stream == NULL) { return; } data = (FCGX_Stream_Data *)stream->data; data->reqDataPtr = NULL; free(data->mBuff); free(data); free(stream); *streamPtr = NULL; } /* *---------------------------------------------------------------------- * * SetReaderType -- * * Re-initializes the stream to read data of the specified type. * *---------------------------------------------------------------------- */ static FCGX_Stream *SetReaderType(FCGX_Stream *stream, int streamType) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; ASSERT(stream->isReader); data->type = streamType; data->eorStop = FALSE; data->skip = FALSE; data->contentLen = 0; data->paddingLen = 0; stream->wrNext = stream->stop = stream->rdNext; stream->isClosed = FALSE; return stream; } /* *---------------------------------------------------------------------- * * NewReader -- * * Creates a stream to read streamType records for the given * request. The stream performs OS reads of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewReader(FCGX_Request *reqDataPtr, int bufflen, int streamType) { return NewStream(reqDataPtr, bufflen, TRUE, streamType); } /* *---------------------------------------------------------------------- * * NewWriter -- * * Creates a stream to write streamType FastCGI records, using * the ipcFd and RequestId contained in *reqDataPtr. * The stream performs OS writes of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewWriter(FCGX_Request *reqDataPtr, int bufflen, int streamType) { return NewStream(reqDataPtr, bufflen, FALSE, streamType); } /* *---------------------------------------------------------------------- * * FCGX_CreateWriter -- * * Creates a stream to write streamType FastCGI records, using * the given ipcFd and request Id. This function is provided * for use by cgi-fcgi. In order to be defensive against misuse, * this function leaks a little storage; cgi-fcgi doesn't care. * *---------------------------------------------------------------------- */ FCGX_Stream *FCGX_CreateWriter( int ipcFd, int requestId, int bufflen, int streamType) { FCGX_Request *reqDataPtr = (FCGX_Request *)Malloc(sizeof(FCGX_Request)); reqDataPtr->ipcFd = ipcFd; reqDataPtr->requestId = requestId; /* * Suppress writing an FCGI_END_REQUEST record. */ reqDataPtr->nWriters = 2; return NewWriter(reqDataPtr, bufflen, streamType); } /* *====================================================================== * Control *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_IsCGI -- * * This routine determines if the process is running as a CGI or * FastCGI process. The distinction is made by determining whether * FCGI_LISTENSOCK_FILENO is a listener ipcFd or the end of a * pipe (ie. standard in). * * Results: * TRUE if the process is a CGI process, FALSE if FastCGI. * *---------------------------------------------------------------------- */ int FCGX_IsCGI(void) { if (isFastCGI != -1) { return !isFastCGI; } if (!libInitialized) { int rc = FCGX_Init(); if (rc) { /* exit() isn't great, but hey */ exit((rc < 0) ? rc : -rc); } } isFastCGI = OS_IsFcgi(FCGI_LISTENSOCK_FILENO); return !isFastCGI; } /* *---------------------------------------------------------------------- * * FCGX_Finish -- * * Finishes the current request from the HTTP server. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ void FCGX_Finish(void) { FCGX_Finish_r(&the_request); } /* *---------------------------------------------------------------------- * * FCGX_Finish_r -- * * Finishes the current request from the HTTP server. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ void FCGX_Finish_r(FCGX_Request *reqDataPtr) { int close; if (reqDataPtr == NULL) { return; } close = !reqDataPtr->keepConnection; /* This should probably use a 'status' member instead of 'in' */ if (reqDataPtr->in) { close |= FCGX_FClose(reqDataPtr->err); close |= FCGX_FClose(reqDataPtr->out); close |= FCGX_GetError(reqDataPtr->in); } FCGX_Free(reqDataPtr, close); } void FCGX_Free(FCGX_Request * request, int close) { if (request == NULL) return; FCGX_FreeStream(&request->in); FCGX_FreeStream(&request->out); FCGX_FreeStream(&request->err); FreeParams(&request->paramsPtr); if (close) { OS_IpcClose(request->ipcFd, ! request->detached); request->ipcFd = -1; request->detached = 0; } } int FCGX_OpenSocket(const char *path, int backlog) { int rc = OS_CreateLocalIpcFd(path, backlog); if (rc == FCGI_LISTENSOCK_FILENO && isFastCGI == 0) { /* XXX probably need to call OS_LibInit() again for Win */ isFastCGI = 1; } return rc; } int FCGX_InitRequest(FCGX_Request *request, int sock, int flags) { memset(request, 0, sizeof(FCGX_Request)); /* @@@ Should check that sock is open and listening */ request->listen_sock = sock; /* @@@ Should validate against "known" flags */ request->flags = flags; request->ipcFd = -1; return 0; } /* *---------------------------------------------------------------------- * * FCGX_Init -- * * Initialize the FCGX library. This is called by FCGX_Accept() * but must be called by the user when using FCGX_Accept_r(). * * Results: * 0 for successful call. * *---------------------------------------------------------------------- */ int FCGX_Init(void) { char *p; if (libInitialized) { return 0; } FCGX_InitRequest(&the_request, FCGI_LISTENSOCK_FILENO, 0); if (OS_LibInit(NULL) == -1) { return OS_Errno ? OS_Errno : -9997; } p = getenv("FCGI_WEB_SERVER_ADDRS"); webServerAddressList = p ? StringCopy(p) : NULL; libInitialized = 1; return 0; } /* *---------------------------------------------------------------------- * * FCGX_Accept -- * * Accepts a new request from the HTTP server. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ int FCGX_Accept( FCGX_Stream **in, FCGX_Stream **out, FCGX_Stream **err, FCGX_ParamArray *envp) { int rc; if (! libInitialized) { rc = FCGX_Init(); if (rc) { return rc; } } rc = FCGX_Accept_r(&the_request); *in = the_request.in; *out = the_request.out; *err = the_request.err; *envp = the_request.envp; return rc; } /* *---------------------------------------------------------------------- * * FCGX_Accept_r -- * * Accepts a new request from the HTTP server. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ int FCGX_Accept_r(FCGX_Request *reqDataPtr) { if (!libInitialized) { return -9998; } /* Finish the current request, if any. */ FCGX_Finish_r(reqDataPtr); for (;;) { /* * If a connection isn't open, accept a new connection (blocking). * If an OS error occurs in accepting the connection, * return -1 to the caller, who should exit. */ if (reqDataPtr->ipcFd < 0) { int fail_on_intr = reqDataPtr->flags & FCGI_FAIL_ACCEPT_ON_INTR; reqDataPtr->ipcFd = OS_Accept(reqDataPtr->listen_sock, fail_on_intr, webServerAddressList); if (reqDataPtr->ipcFd < 0) { return (errno > 0) ? (0 - errno) : -9999; } } /* * A connection is open. Read from the connection in order to * get the request's role and environment. If protocol or other * errors occur, close the connection and try again. */ reqDataPtr->isBeginProcessed = FALSE; reqDataPtr->in = NewReader(reqDataPtr, 8192, 0); FillBuffProc(reqDataPtr->in); if(!reqDataPtr->isBeginProcessed) { goto TryAgain; } { char *roleStr; switch(reqDataPtr->role) { case FCGI_RESPONDER: roleStr = "FCGI_ROLE=RESPONDER"; break; case FCGI_AUTHORIZER: roleStr = "FCGI_ROLE=AUTHORIZER"; break; case FCGI_FILTER: roleStr = "FCGI_ROLE=FILTER"; break; default: goto TryAgain; } reqDataPtr->paramsPtr = NewParams(30); PutParam(reqDataPtr->paramsPtr, StringCopy(roleStr)); } SetReaderType(reqDataPtr->in, FCGI_PARAMS); if(ReadParams(reqDataPtr->paramsPtr, reqDataPtr->in) >= 0) { /* * Finished reading the environment. No errors occurred, so * leave the connection-retry loop. */ break; } /* * Close the connection and try again. */ TryAgain: FCGX_Free(reqDataPtr, 1); } /* for (;;) */ /* * Build the remaining data structures representing the new * request and return successfully to the caller. */ SetReaderType(reqDataPtr->in, FCGI_STDIN); reqDataPtr->out = NewWriter(reqDataPtr, 8192, FCGI_STDOUT); reqDataPtr->err = NewWriter(reqDataPtr, 512, FCGI_STDERR); reqDataPtr->nWriters = 2; reqDataPtr->envp = reqDataPtr->paramsPtr->vec; return 0; } /* *---------------------------------------------------------------------- * * FCGX_StartFilterData -- * * stream is an input stream for a FCGI_FILTER request. * stream is positioned at EOF on FCGI_STDIN. * Repositions stream to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF) sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ int FCGX_StartFilterData(FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; if(data->reqDataPtr->role != FCGI_FILTER || !stream->isReader || !stream->isClosed || data->type != FCGI_STDIN) { SetError(stream, FCGX_CALL_SEQ_ERROR); return -1; } SetReaderType(stream, FCGI_DATA); return 0; } /* *---------------------------------------------------------------------- * * FCGX_SetExitStatus -- * * Sets the exit status for stream's request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * SetExitStatus several times during a request; the last call * before the request ends determines the value. * *---------------------------------------------------------------------- */ void FCGX_SetExitStatus(int status, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; data->reqDataPtr->appStatus = status; } int FCGX_Attach(FCGX_Request * r) { r->detached = FALSE; return 0; } int FCGX_Detach(FCGX_Request * r) { if (r->ipcFd <= 0) { return -1; } r->detached = TRUE; return 0; } fcgi2-2.4.5/libfcgi/fcgio.cpp000066400000000000000000000100221477724357700157300ustar00rootroot00000000000000// // $Id: fcgio.cpp,v 1.14 2003/06/22 00:51:27 robs Exp $ // // Allows you communicate with FastCGI streams using C++ iostreams // // ORIGINAL AUTHOR: George Feinberg // REWRITTEN BY: Michael Richards 06/20/1999 // REWRITTEN AGAIN BY: Michael Shell 02/23/2000 // REWRITTEN AGAIN BY: Rob Saccoccio 11 Nov 2001 // // Copyright (c) 2000 Tux the Linux Penguin // // You are free to use this software without charge or royalty // as long as this notice is not removed or altered, and recognition // is given to the author(s) // // This code is offered as-is without any warranty either expressed or // implied; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. #include #include #include "fcgio.h" using std::streambuf; using std::istream; using std::ostream; using std::streamsize; fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs, char * b, int bs) { init(fs, b, bs); } fcgi_streambuf::fcgi_streambuf(char_type * b, streamsize bs) { init(0, b, bs); } fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs) { init(fs, 0, 0); } fcgi_streambuf::~fcgi_streambuf(void) { overflow(EOF); // FCGX_Finish()/FCGX_Accept() will flush and close } void fcgi_streambuf::init(FCGX_Stream * fs, char_type * b, streamsize bs) { this->fcgx = fs; this->buf = 0; this->bufsize = 0; setbuf(b, bs); } int fcgi_streambuf::overflow(int c) { if (this->bufsize) { int plen = pptr() - pbase(); if (plen) { if (FCGX_PutStr(pbase(), plen, this->fcgx) != plen) return EOF; pbump(-plen); } } if (c != EOF) { if (FCGX_PutChar(c, this->fcgx) != c) return EOF; } return 0; } // default base class behaviour seems to be inconsistent int fcgi_streambuf::sync() { if (overflow(EOF)) return EOF; if (FCGX_FFlush(this->fcgx)) return EOF; return 0; } // uflow() removes the char, underflow() doesn't int fcgi_streambuf::uflow() { if (this->bufsize) { int c = underflow(); gbump(1); return c; } else { return FCGX_GetChar(this->fcgx); } } int fcgi_streambuf::underflow() { if (this->bufsize) { if (in_avail() == 0) { int glen = FCGX_GetStr(eback(), this->bufsize, this->fcgx); if (glen <= 0) return EOF; setg(eback(), eback(), eback() + glen); } return (unsigned char) *gptr(); } else { return FCGX_UnGetChar(FCGX_GetChar(this->fcgx), this->fcgx); } } void fcgi_streambuf::reset(void) { // it should be ok to set up both the get and put areas setg(this->buf, this->buf, this->buf); setp(this->buf, this->buf + this->bufsize); } std::streambuf * fcgi_streambuf::setbuf(char_type * b, streamsize bs) { // XXX support moving data from an old buffer if (this->bufsize) return 0; this->buf = b; this->bufsize = bs; // the base setbuf() *has* to be called streambuf::setbuf(b, bs); reset(); return this; } int fcgi_streambuf::attach(FCGX_Stream * fs) { this->fcgx = fs; if (this->bufsize) { reset(); } return 0; } streamsize fcgi_streambuf::xsgetn(char_type * s, streamsize n) { return (this->bufsize) ? streambuf::xsgetn(s, n) : (streamsize) FCGX_GetStr((char *) s, (int) n, this->fcgx); } streamsize fcgi_streambuf::xsputn(const char_type * s, streamsize n) { return (this->bufsize) ? streambuf::xsputn(s, n) : (streamsize) FCGX_PutStr((char *) s, (int) n, this->fcgx); } // deprecated fcgi_istream::fcgi_istream(FCGX_Stream * fs) : istream(&fcgi_strmbuf) { fcgi_strmbuf.attach(fs); } // deprecated void fcgi_istream::attach(FCGX_Stream * fs) { fcgi_strmbuf.attach(fs); } // deprecated fcgi_ostream::fcgi_ostream(FCGX_Stream * fs) : ostream(&fcgi_strmbuf) { fcgi_strmbuf.attach(fs); } // deprecated void fcgi_ostream::attach(FCGX_Stream * fs) { fcgi_strmbuf.attach(fs); } fcgi2-2.4.5/libfcgi/libfcgi.mak000066400000000000000000000154371477724357700162450ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Based on libfcgi.dsp !IF "$(CFG)" == "" CFG=release !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "libfcgi.mak" CFG="debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\..\libfcgi\Release INTDIR=.\..\libfcgi\Release # Begin Custom Macros OutDir=.\..\libfcgi\Release # End Custom Macros ALL : "$(OUTDIR)\libfcgi.dll" CLEAN : -@erase "$(INTDIR)\*.obj" -@erase "$(INTDIR)\*.idb" -@erase "$(OUTDIR)\*.dll" -@erase "$(OUTDIR)\*.exp" -@erase "$(OUTDIR)\*.lib" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "DLLAPI=__declspec(dllexport)" /Fp"$(INTDIR)\libfcgi.pch" /nologo /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\libfcgi.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=Ws2_32.lib Kernel32.lib Windowsapp.lib /APPCONTAINER /nologo /dll /pdb:none /out:"$(OUTDIR)\libfcgi.dll" /implib:"$(OUTDIR)\libfcgi.lib" LINK32_OBJS= \ "$(INTDIR)\fcgi_stdio.obj" \ "$(INTDIR)\fcgiapp.obj" \ "$(INTDIR)\fcgio.obj" \ "$(INTDIR)\os_win32.obj" "$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\..\libfcgi\Debug INTDIR=.\..\libfcgi\Debug # Begin Custom Macros OutDir=.\..\libfcgi\Debug # End Custom Macros ALL : "$(OUTDIR)\libfcgi.dll" "$(OUTDIR)\libfcgi.bsc" CLEAN : -@erase "$(INTDIR)\*.obj" -@erase "$(INTDIR)\*.sbr" -@erase "$(INTDIR)\*.idb" -@erase "$(INTDIR)\*.pdb" -@erase "$(OUTDIR)\*.bsc" -@erase "$(OUTDIR)\*.dll" -@erase "$(OUTDIR)\*.exp" -@erase "$(OUTDIR)\*.lib" -@erase "$(OUTDIR)\*.map" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W4 /Gm- /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DLLAPI=__declspec(dllexport)" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libfcgi.pch" /nologo /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\libfcgi.pdb" /FD /GZ /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\libfcgi.bsc" BSC32_SBRS= \ "$(INTDIR)\fcgi_stdio.sbr" \ "$(INTDIR)\fcgiapp.sbr" \ "$(INTDIR)\fcgio.sbr" \ "$(INTDIR)\os_win32.sbr" "$(OUTDIR)\libfcgi.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=Ws2_32.lib Kernel32.lib Windowsapp.lib /APPCONTAINER /nologo /dll /profile /map:"$(INTDIR)\libfcgi.map" /debug /out:"$(OUTDIR)\libfcgi.dll" /implib:"$(OUTDIR)\libfcgi.lib" LINK32_OBJS= \ "$(INTDIR)\fcgi_stdio.obj" \ "$(INTDIR)\fcgiapp.obj" \ "$(INTDIR)\fcgio.obj" \ "$(INTDIR)\os_win32.obj" "$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF ..\libfcgi\fcgi_stdio.c : \ "..\include\fcgi_config.h"\ "..\include\fcgi_stdio.h"\ "..\include\fcgiapp.h"\ "..\include\fcgimisc.h"\ "..\include\fcgios.h"\ ..\libfcgi\fcgiapp.c : \ "..\include\fastcgi.h"\ "..\include\fcgi_config.h"\ "..\include\fcgiapp.h"\ "..\include\fcgimisc.h"\ "..\include\fcgios.h"\ ..\libfcgi\fcgio.cpp : \ "..\include\fcgiapp.h"\ "..\include\fcgio.h"\ ..\libfcgi\os_win32.c : \ "..\include\fcgi_config.h"\ "..\include\fcgimisc.h"\ "..\include\fcgios.h"\ !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE=..\libfcgi\fcgi_stdio.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_stdio.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_stdio.obj" "$(INTDIR)\fcgi_stdio.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\libfcgi\fcgiapp.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgiapp.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgiapp.obj" "$(INTDIR)\fcgiapp.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\libfcgi\fcgio.cpp !IF "$(CFG)" == "release" CPP_SWITCHES=/nologo /MD /W3 /EHsc /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "DLLAPI=__declspec(dllexport)" /Fp"$(INTDIR)\libfcgi.pch" /nologo /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c "$(INTDIR)\fcgio.obj" : $(SOURCE) "$(INTDIR)" $(CPP) @<< $(CPP_SWITCHES) $(SOURCE) << !ELSEIF "$(CFG)" == "debug" CPP_SWITCHES=/nologo /MDd /W3 /Gm- /Gi /EHsc /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DLLAPI=__declspec(dllexport)" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libfcgi.pch" /nologo /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c "$(INTDIR)\fcgio.obj" "$(INTDIR)\fcgio.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) @<< $(CPP_SWITCHES) $(SOURCE) << !ENDIF SOURCE=..\libfcgi\os_unix.c SOURCE=..\libfcgi\os_win32.c !IF "$(CFG)" == "release" "$(INTDIR)\os_win32.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\os_win32.obj" "$(INTDIR)\os_win32.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\libfcgi\strerror.c !ENDIF fcgi2-2.4.5/libfcgi/os_unix.c000077500000000000000000001016261477724357700160030ustar00rootroot00000000000000/* * os_unix.c -- * * Description of file. * * * Copyright (c) 1995 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com */ #include "fcgi_config.h" #include #ifdef HAVE_NETINET_IN_H #include #endif #include #include #include #include /* for fcntl */ #include #include /* for memchr() */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include /* for getpeername */ #endif #ifdef HAVE_UNISTD_H #include #endif #include "fastcgi.h" #include "fcgimisc.h" #include "fcgios.h" #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long) -1) #endif /* * This structure holds an entry for each outstanding async I/O operation. */ typedef struct { OS_AsyncProc procPtr; /* callout completion procedure */ ClientData clientData; /* caller private data */ int fd; int len; int offset; void *buf; int inUse; } AioInfo; /* * Entries in the async I/O table are allocated 2 per file descriptor. * * Read Entry Index = fd * 2 * Write Entry Index = (fd * 2) + 1 */ #define AIO_RD_IX(fd) (fd * 2) #define AIO_WR_IX(fd) ((fd * 2) + 1) static int asyncIoInUse = FALSE; static int asyncIoTableSize = 16; static AioInfo *asyncIoTable = NULL; static int libInitialized = FALSE; static fd_set readFdSet; static fd_set writeFdSet; static fd_set readFdSetPost; static int numRdPosted = 0; static fd_set writeFdSetPost; static int numWrPosted = 0; static int volatile maxFd = -1; static int shutdownPending = FALSE; static int shutdownNow = FALSE; static int libfcgiOsClosePollTimeout = 2000; static int libfcgiIsAfUnixKeeperPollTimeout = 2000; void OS_ShutdownPending() { shutdownPending = TRUE; } static void OS_Sigusr1Handler(int signo) { OS_ShutdownPending(); } static void OS_SigpipeHandler(int signo) { ; } static void installSignalHandler(int signo, const struct sigaction * act, int force) { struct sigaction sa; sigaction(signo, NULL, &sa); if (force || sa.sa_handler == SIG_DFL || sa.sa_handler == SIG_IGN) { sigaction(signo, act, NULL); } } static void OS_InstallSignalHandlers(int force) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = OS_SigpipeHandler; installSignalHandler(SIGPIPE, &sa, force); sa.sa_handler = OS_Sigusr1Handler; installSignalHandler(SIGUSR1, &sa, force); installSignalHandler(SIGTERM, &sa, 0); } /* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * NOTE: This function is really only needed for application * asynchronous I/O. It will most likely change in the * future to setup the multi-threaded environment. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Async I/O table allocated and initialized. * *-------------------------------------------------------------- */ int OS_LibInit(int stdioFds[3]) { if(libInitialized) return 0; char *libfcgiOsClosePollTimeoutStr = getenv( "LIBFCGI_OS_CLOSE_POLL_TIMEOUT" ); if(libfcgiOsClosePollTimeoutStr) { libfcgiOsClosePollTimeout = atoi(libfcgiOsClosePollTimeoutStr); } char *libfcgiIsAfUnixKeeperPollTimeoutStr = getenv( "LIBFCGI_IS_AF_UNIX_KEEPER_POLL_TIMEOUT" ); if(libfcgiIsAfUnixKeeperPollTimeoutStr) { libfcgiIsAfUnixKeeperPollTimeout = atoi(libfcgiIsAfUnixKeeperPollTimeoutStr); } asyncIoTable = (AioInfo *)malloc(asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; return -1; } memset((char *) asyncIoTable, 0, asyncIoTableSize * sizeof(AioInfo)); FD_ZERO(&readFdSet); FD_ZERO(&writeFdSet); FD_ZERO(&readFdSetPost); FD_ZERO(&writeFdSetPost); OS_InstallSignalHandlers(FALSE); libInitialized = TRUE; return 0; } /* *-------------------------------------------------------------- * * OS_LibShutdown -- * * Shutdown the OS library. * * Results: * None. * * Side effects: * Memory freed, fds closed. * *-------------------------------------------------------------- */ void OS_LibShutdown() { if(!libInitialized) return; free(asyncIoTable); asyncIoTable = NULL; libInitialized = FALSE; return; } /* *---------------------------------------------------------------------- * * OS_BuildSockAddrUn -- * * Using the pathname bindPath, fill in the sockaddr_un structure * *servAddrPtr and the length of this structure *servAddrLen. * * The format of the sockaddr_un structure changed incompatibly in * 4.3BSD Reno. Digital UNIX supports both formats, other systems * support one or the other. * * Results: * 0 for normal return, -1 for failure (bindPath too long). * *---------------------------------------------------------------------- */ static int OS_BuildSockAddrUn(const char *bindPath, struct sockaddr_un *servAddrPtr, int *servAddrLen) { int bindPathLen = strlen(bindPath); #ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ if(bindPathLen >= (int)sizeof(servAddrPtr->sun_path)) { return -1; } #else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ if(bindPathLen > (int)sizeof(servAddrPtr->sun_path)) { return -1; } #endif memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr)); servAddrPtr->sun_family = AF_UNIX; memcpy(servAddrPtr->sun_path, bindPath, bindPathLen); #ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ *servAddrLen = sizeof(servAddrPtr->sun_len) + sizeof(servAddrPtr->sun_family) + bindPathLen + 1; servAddrPtr->sun_len = *servAddrLen; #else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen; #endif return 0; } union SockAddrUnion { struct sockaddr_un unixVariant; struct sockaddr_in inetVariant; }; /* * OS_CreateLocalIpcFd -- * * This procedure is responsible for creating the listener socket * on Unix for local process communication. It will create a * domain socket or a TCP/IP socket bound to "localhost" and return * a file descriptor to it to the caller. * * Results: * Listener socket created. This call returns either a valid * file descriptor or -1 on error. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { int listenSock, len; union SockAddrUnion sa; unsigned long tcp_ia = 0; char *tp; short port = 0; char host[MAXPATHLEN]; len = strlen(bindPath); if (len >= MAXPATHLEN) { fprintf(stderr, "bind path too long (>=%d): %s\n", MAXPATHLEN, bindPath); exit(1); } memcpy(host, bindPath, len + 1); tp = strchr(host, ':'); if (tp) { *tp = 0; port = atoi(++tp); if (port == 0) { *--tp = ':'; } } if (port) { if (!*host || !strcmp(host,"*")) { tcp_ia = htonl(INADDR_ANY); } else { tcp_ia = inet_addr(host); if (tcp_ia == INADDR_NONE) { struct hostent * hep = gethostbyname(host); if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { fprintf(stderr, "Cannot resolve host name %s -- exiting!\n", host); exit(1); } if (hep->h_addr_list[1]) { fprintf(stderr, "Host %s has multiple addresses ---\n", host); fprintf(stderr, "you must choose one explicitly!!!\n"); exit(1); } tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr; } } listenSock = socket(AF_INET, SOCK_STREAM, 0); if(listenSock >= 0) { int flag = 1; if(setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(flag)) < 0) { fprintf(stderr, "Can't set SO_REUSEADDR.\n"); exit(1001); } } } else { listenSock = socket(AF_UNIX, SOCK_STREAM, 0); } if(listenSock < 0) { return -1; } /* * Bind the listening socket. */ if (port) { memset((char *) &sa.inetVariant, 0, sizeof(sa.inetVariant)); sa.inetVariant.sin_family = AF_INET; sa.inetVariant.sin_addr.s_addr = tcp_ia; sa.inetVariant.sin_port = htons(port); len = sizeof(sa.inetVariant); } else { unlink(bindPath); if (OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &len)) { fprintf(stderr, "Listening socket's path name is too long.\n"); exit(1000); } } if (bind(listenSock, (struct sockaddr *) &sa.unixVariant, len) < 0 || listen(listenSock, backlog) < 0) { perror("bind/listen"); exit(errno); } return listenSock; } /* *---------------------------------------------------------------------- * * OS_FcgiConnect -- * * Create the socket and connect to the remote application if * possible. * * This was lifted from the cgi-fcgi application and was abstracted * out because Windows NT does not have a domain socket and must * use a named pipe which has a different API altogether. * * Results: * -1 if fail or a valid file descriptor if connection succeeds. * * Side effects: * Remote connection established. * *---------------------------------------------------------------------- */ int OS_FcgiConnect(char *bindPath) { union SockAddrUnion sa; int len, resultSock; int connectStatus; char *tp; char host[MAXPATHLEN]; short port = 0; len = strlen(bindPath); if (len >= MAXPATHLEN) { fprintf(stderr, "bind path too long (>=%d): %s\n", MAXPATHLEN, bindPath); exit(1); } memcpy(host, bindPath, len + 1); tp = strchr(host, ':'); if (tp) { *tp = 0; port = atoi(++tp); if (port == 0) { *--tp = ':'; } } if (port) { struct hostent *hp = gethostbyname(*host ? host : "localhost"); if (hp == NULL) { fprintf(stderr, "Unknown host: %s\n", host); exit(1000); } sa.inetVariant.sin_family = AF_INET; memcpy(&sa.inetVariant.sin_addr, hp->h_addr, hp->h_length); sa.inetVariant.sin_port = htons(port); len = sizeof(sa.inetVariant); resultSock = socket(AF_INET, SOCK_STREAM, 0); } else { if (OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &len)) { fprintf(stderr, "Listening socket's path name is too long.\n"); exit(1000); } resultSock = socket(AF_UNIX, SOCK_STREAM, 0); } ASSERT(resultSock >= 0); connectStatus = connect(resultSock, (struct sockaddr *) &sa.unixVariant, len); if(connectStatus >= 0) { return resultSock; } else { /* * Most likely (errno == ENOENT || errno == ECONNREFUSED) * and no FCGI application server is running. */ close(resultSock); return -1; } } /* *-------------------------------------------------------------- * * OS_Read -- * * Pass through to the unix read function. * * Results: * Returns number of byes read, 0, or -1 failure: errno * contains actual error. * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Read(int fd, char * buf, size_t len) { if (shutdownNow) return -1; return(read(fd, buf, len)); } /* *-------------------------------------------------------------- * * OS_Write -- * * Pass through to unix write function. * * Results: * Returns number of byes read, 0, or -1 failure: errno * contains actual error. * * Side effects: * none. * *-------------------------------------------------------------- */ int OS_Write(int fd, char * buf, size_t len) { if (shutdownNow) return -1; return(write(fd, buf, len)); } /* *---------------------------------------------------------------------- * * OS_SpawnChild -- * * Spawns a new FastCGI listener process. * * Results: * 0 if success, -1 if error. * * Side effects: * Child process spawned. * *---------------------------------------------------------------------- */ int OS_SpawnChild(char *appPath, int listenFd) { int forkResult; forkResult = fork(); if(forkResult < 0) { exit(errno); } if(forkResult == 0) { /* * Close STDIN unconditionally. It's used by the parent * process for CGI communication. The FastCGI applciation * will be replacing this with the FastCGI listenFd IF * STDIN_FILENO is the same as FCGI_LISTENSOCK_FILENO * (which it is on Unix). Regardless, STDIN, STDOUT, and * STDERR will be closed as the FastCGI process uses a * multiplexed socket in their place. */ close(STDIN_FILENO); /* * If the listenFd is already the value of FCGI_LISTENSOCK_FILENO * we're set. If not, change it so the child knows where to * get the listen socket from. */ if(listenFd != FCGI_LISTENSOCK_FILENO) { dup2(listenFd, FCGI_LISTENSOCK_FILENO); close(listenFd); } close(STDOUT_FILENO); close(STDERR_FILENO); /* * We're a child. Exec the application. * * XXX: entire environment passes through */ execl(appPath, appPath, NULL); /* * XXX: Can't do this as we've already closed STDERR!!! * * perror("exec"); */ exit(errno); } return 0; } /* *-------------------------------------------------------------- * * OS_AsyncReadStdin -- * * This initiates an asynchronous read on the standard * input handle. * * The abstraction is necessary because Windows NT does not * have a clean way of "select"ing a file descriptor for * I/O. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous bit is set in the readfd variable and * request is enqueued. * *-------------------------------------------------------------- */ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(STDIN_FILENO); asyncIoInUse = TRUE; ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = STDIN_FILENO; asyncIoTable[index].len = len; asyncIoTable[index].offset = 0; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(STDIN_FILENO, &readFdSet); if(STDIN_FILENO > maxFd) maxFd = STDIN_FILENO; return 0; } static void GrowAsyncTable(void) { int oldTableSize = asyncIoTableSize; asyncIoTableSize = asyncIoTableSize * 2; asyncIoTable = (AioInfo *)realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; exit(errno); } memset((char *) &asyncIoTable[oldTableSize], 0, oldTableSize * sizeof(AioInfo)); } /* *-------------------------------------------------------------- * * OS_AsyncRead -- * * This initiates an asynchronous read on the file * handle which may be a socket or named pipe. * * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the ReadFile may * return data if it is cached) but do all completion * processing in OS_Select when we get the io completion * port done notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(fd); ASSERT(asyncIoTable != NULL); asyncIoInUse = TRUE; if(fd > maxFd) maxFd = fd; while (index >= asyncIoTableSize) { GrowAsyncTable(); } ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = fd; asyncIoTable[index].len = len; asyncIoTable[index].offset = offset; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(fd, &readFdSet); return 0; } /* *-------------------------------------------------------------- * * OS_AsyncWrite -- * * This initiates an asynchronous write on the "fake" file * descriptor (which may be a file, socket, or named pipe). * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the WriteFile generally * completes immediately) but do all completion processing * in OS_DoIo when we get the io completion port done * notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_WR_IX(fd); asyncIoInUse = TRUE; if(fd > maxFd) maxFd = fd; while (index >= asyncIoTableSize) { GrowAsyncTable(); } ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = fd; asyncIoTable[index].len = len; asyncIoTable[index].offset = offset; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(fd, &writeFdSet); return 0; } /* *-------------------------------------------------------------- * * OS_Close -- * * Closes the descriptor. This is a pass through to the * Unix close. * * Results: * 0 for success, -1 on failure * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Close(int fd, int shutdown_ok) { if (fd == -1) return 0; if (asyncIoInUse) { int index = AIO_RD_IX(fd); FD_CLR(fd, &readFdSet); FD_CLR(fd, &readFdSetPost); if (asyncIoTable[index].inUse != 0) { asyncIoTable[index].inUse = 0; } FD_CLR(fd, &writeFdSet); FD_CLR(fd, &writeFdSetPost); index = AIO_WR_IX(fd); if (asyncIoTable[index].inUse != 0) { asyncIoTable[index].inUse = 0; } if (maxFd == fd) { maxFd--; } } /* * shutdown() the send side and then read() from client until EOF * or a timeout expires. This is done to minimize the potential * that a TCP RST will be sent by our TCP stack in response to * receipt of additional data from the client. The RST would * cause the client to discard potentially useful response data. */ if (shutdown_ok) { if (shutdown(fd, 1) == 0) { struct pollfd pfd; int rv; char trash[1024]; pfd.fd = fd; pfd.events = POLLIN; do { rv = poll(&pfd, 1, libfcgiOsClosePollTimeout); } while (rv > 0 && read(fd, trash, sizeof(trash)) > 0); } } return close(fd); } /* *-------------------------------------------------------------- * * OS_CloseRead -- * * Cancel outstanding asynchronous reads and prevent subsequent * reads from completing. * * Results: * Socket or file is shutdown. Return values mimic Unix shutdown: * 0 success, -1 failure * *-------------------------------------------------------------- */ int OS_CloseRead(int fd) { if(asyncIoTable[AIO_RD_IX(fd)].inUse != 0) { asyncIoTable[AIO_RD_IX(fd)].inUse = 0; FD_CLR(fd, &readFdSet); } return shutdown(fd, 0); } /* *-------------------------------------------------------------- * * OS_DoIo -- * * This function was formerly OS_Select. It's purpose is * to pull I/O completion events off the queue and dispatch * them to the appropriate place. * * Results: * Returns 0. * * Side effects: * Handlers are called. * *-------------------------------------------------------------- */ int OS_DoIo(struct timeval *tmo) { int fd, len, selectStatus; OS_AsyncProc procPtr; ClientData clientData; AioInfo *aioPtr; fd_set readFdSetCpy; fd_set writeFdSetCpy; asyncIoInUse = TRUE; FD_ZERO(&readFdSetCpy); FD_ZERO(&writeFdSetCpy); for(fd = 0; fd <= maxFd; fd++) { if(FD_ISSET(fd, &readFdSet)) { FD_SET(fd, &readFdSetCpy); } if(FD_ISSET(fd, &writeFdSet)) { FD_SET(fd, &writeFdSetCpy); } } /* * If there were no completed events from a prior call, see if there's * any work to do. */ if(numRdPosted == 0 && numWrPosted == 0) { selectStatus = select((maxFd+1), &readFdSetCpy, &writeFdSetCpy, NULL, tmo); if(selectStatus < 0) { exit(errno); } for(fd = 0; fd <= maxFd; fd++) { /* * Build up a list of completed events. We'll work off of * this list as opposed to looping through the read and write * fd sets since they can be affected by a callbacl routine. */ if(FD_ISSET(fd, &readFdSetCpy)) { numRdPosted++; FD_SET(fd, &readFdSetPost); FD_CLR(fd, &readFdSet); } if(FD_ISSET(fd, &writeFdSetCpy)) { numWrPosted++; FD_SET(fd, &writeFdSetPost); FD_CLR(fd, &writeFdSet); } } } if(numRdPosted == 0 && numWrPosted == 0) return 0; for(fd = 0; fd <= maxFd; fd++) { /* * Do reads and dispatch callback. */ if(FD_ISSET(fd, &readFdSetPost) && asyncIoTable[AIO_RD_IX(fd)].inUse) { numRdPosted--; FD_CLR(fd, &readFdSetPost); aioPtr = &asyncIoTable[AIO_RD_IX(fd)]; len = read(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; aioPtr->procPtr = NULL; clientData = aioPtr->clientData; aioPtr->inUse = 0; (*procPtr)(clientData, len); } /* * Do writes and dispatch callback. */ if(FD_ISSET(fd, &writeFdSetPost) && asyncIoTable[AIO_WR_IX(fd)].inUse) { numWrPosted--; FD_CLR(fd, &writeFdSetPost); aioPtr = &asyncIoTable[AIO_WR_IX(fd)]; len = write(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; aioPtr->procPtr = NULL; clientData = aioPtr->clientData; aioPtr->inUse = 0; (*procPtr)(clientData, len); } } return 0; } /* * Not all systems have strdup(). * @@@ autoconf should determine whether or not this is needed, but for now.. */ static char * str_dup(const char * str) { int len = strlen(str) + 1; char * sdup = (char *) malloc(len); if (sdup) { memcpy(sdup, str, len); } return sdup; } /* *---------------------------------------------------------------------- * * ClientAddrOK -- * * Checks if a client address is in a list of allowed addresses * * Results: * TRUE if address list is empty or client address is present * in the list, FALSE otherwise. * *---------------------------------------------------------------------- */ static int ClientAddrOK(struct sockaddr_in *saPtr, const char *clientList) { int result = FALSE; char *clientListCopy, *cur, *next; if (clientList == NULL || *clientList == '\0') { return TRUE; } clientListCopy = str_dup(clientList); for (cur = clientListCopy; cur != NULL; cur = next) { next = strchr(cur, ','); if (next != NULL) { *next++ = '\0'; } if (inet_addr(cur) == saPtr->sin_addr.s_addr) { result = TRUE; break; } } free(clientListCopy); return result; } /* *---------------------------------------------------------------------- * * AcquireLock -- * * On platforms that implement concurrent calls to accept * on a shared listening ipcFd, returns 0. On other platforms, * acquires an exclusive lock across all processes sharing a * listening ipcFd, blocking until the lock has been acquired. * * Results: * 0 for successful call, -1 in case of system error (fatal). * * Side effects: * This process now has the exclusive lock. * *---------------------------------------------------------------------- */ static int AcquireLock(int sock, int fail_on_intr) { #ifdef USE_LOCKING do { struct flock lock; lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(sock, F_SETLKW, &lock) != -1) return 0; } while (errno == EINTR && ! fail_on_intr && ! shutdownPending); return -1; #else return 0; #endif } /* *---------------------------------------------------------------------- * * ReleaseLock -- * * On platforms that implement concurrent calls to accept * on a shared listening ipcFd, does nothing. On other platforms, * releases an exclusive lock acquired by AcquireLock. * * Results: * 0 for successful call, -1 in case of system error (fatal). * * Side effects: * This process no longer holds the lock. * *---------------------------------------------------------------------- */ static int ReleaseLock(int sock) { #ifdef USE_LOCKING do { struct flock lock; lock.l_type = F_UNLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(sock, F_SETLK, &lock) != -1) return 0; } while (errno == EINTR); return -1; #else return 0; #endif } /********************************************************************** * Determine if the errno resulting from a failed accept() warrants a * retry or exit(). Based on Apache's http_main.c accept() handling * and Stevens' Unix Network Programming Vol 1, 2nd Ed, para. 15.6. */ static int is_reasonable_accept_errno (const int error) { switch (error) { #ifdef EPROTO /* EPROTO on certain older kernels really means ECONNABORTED, so * we need to ignore it for them. See discussion in new-httpd * archives nh.9701 search for EPROTO. Also see nh.9603, search * for EPROTO: There is potentially a bug in Solaris 2.x x<6, and * other boxes that implement tcp sockets in userland (i.e. on top of * STREAMS). On these systems, EPROTO can actually result in a fatal * loop. See PR#981 for example. It's hard to handle both uses of * EPROTO. */ case EPROTO: #endif #ifdef ECONNABORTED case ECONNABORTED: #endif /* Linux generates the rest of these, other tcp stacks (i.e. * bsd) tend to hide them behind getsockopt() interfaces. They * occur when the net goes sour or the client disconnects after the * three-way handshake has been done in the kernel but before * userland has picked up the socket. */ #ifdef ECONNRESET case ECONNRESET: #endif #ifdef ETIMEDOUT case ETIMEDOUT: #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: #endif #ifdef ENETUNREACH case ENETUNREACH: #endif return 1; default: return 0; } } /********************************************************************** * This works around a problem on Linux 2.0.x and SCO Unixware (maybe * others?). When a connect() is made to a Unix Domain socket, but its * not accept()ed before the web server gets impatient and close()s, an * accept() results in a valid file descriptor, but no data to read. * This causes a block on the first read() - which never returns! * * Another approach to this is to write() to the socket to provoke a * SIGPIPE, but this is a pain because of the FastCGI protocol, the fact * that whatever is written has to be universally ignored by all FastCGI * web servers, and a SIGPIPE handler has to be installed which returns * (or SIGPIPE is ignored). * * READABLE_UNIX_FD_DROP_DEAD_TIMEVAL = 2,0 by default. * * Making it shorter is probably safe, but I'll leave that to you. Making * it 0,0 doesn't work reliably. The shorter you can reliably make it, * the faster your application will be able to recover (waiting 2 seconds * may _cause_ the problem when there is a very high demand). At any rate, * this is better than perma-blocking. */ static int is_af_unix_keeper(const int fd) { struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN; return poll(&pfd, 1, libfcgiIsAfUnixKeeperPollTimeout) >= 0 && (pfd.revents & POLLIN); } /* *---------------------------------------------------------------------- * * OS_Accept -- * * Accepts a new FastCGI connection. This routine knows whether * we're dealing with TCP based sockets or NT Named Pipes for IPC. * * Results: * -1 if the operation fails, otherwise this is a valid IPC fd. * * Side effects: * New IPC connection is accepted. * *---------------------------------------------------------------------- */ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { int socket = -1; union { struct sockaddr_un un; struct sockaddr_in in; } sa; for (;;) { if (AcquireLock(listen_sock, fail_on_intr)) return -1; for (;;) { do { #ifdef HAVE_SOCKLEN socklen_t len = sizeof(sa); #else unsigned int len = sizeof(sa); #endif if (shutdownPending) break; /* There's a window here */ socket = accept(listen_sock, (struct sockaddr *)&sa, &len); } while (socket < 0 && errno == EINTR && ! fail_on_intr && ! shutdownPending); if (socket < 0) { if (shutdownPending || ! is_reasonable_accept_errno(errno)) { int errnoSave = errno; ReleaseLock(listen_sock); if (! shutdownPending) { errno = errnoSave; } return (-1); } errno = 0; } else { /* socket >= 0 */ int set = 1; if (sa.in.sin_family != AF_INET) break; #ifdef TCP_NODELAY /* No replies to outgoing data, so disable Nagle */ setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); #endif /* Check that the client IP address is approved */ if (ClientAddrOK(&sa.in, webServerAddrs)) break; close(socket); socket = -1; } /* socket >= 0 */ } /* for(;;) */ if (ReleaseLock(listen_sock)) return (-1); if (sa.in.sin_family != AF_UNIX || is_af_unix_keeper(socket)) break; close(socket); } /* while(1) - lock */ return (socket); } /* *---------------------------------------------------------------------- * * OS_IpcClose * * OS IPC routine to close an IPC connection. * * Results: * * * Side effects: * IPC connection is closed. * *---------------------------------------------------------------------- */ int OS_IpcClose(int ipcFd, int shutdown) { return OS_Close(ipcFd, shutdown); } /* *---------------------------------------------------------------------- * * OS_IsFcgi -- * * Determines whether this process is a FastCGI process or not. * * Results: * Returns 1 if FastCGI, 0 if not. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_IsFcgi(int sock) { union { struct sockaddr_in in; struct sockaddr_un un; } sa; #ifdef HAVE_SOCKLEN socklen_t len = sizeof(sa); #else unsigned int len = sizeof(sa); #endif errno = 0; if (getpeername(sock, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { return TRUE; } else { return FALSE; } } /* *---------------------------------------------------------------------- * * OS_SetFlags -- * * Sets selected flag bits in an open file descriptor. * *---------------------------------------------------------------------- */ void OS_SetFlags(int fd, int flags) { int val; if((val = fcntl(fd, F_GETFL, 0)) < 0) { exit(errno); } val |= flags; if(fcntl(fd, F_SETFL, val) < 0) { exit(errno); } } fcgi2-2.4.5/libfcgi/os_win32.c000077500000000000000000001362161477724357700157650ustar00rootroot00000000000000/* * os_win32.c -- * * * Copyright (c) 1995 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com * * (Special thanks to Karen and Bill. They made my job much easier and * significantly more enjoyable.) */ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include "fcgimisc.h" #include "fcgios.h" #define WIN32_OPEN_MAX 128 /* XXX: Small hack */ /* * millisecs to wait for a client connection before checking the * shutdown flag (then go back to waiting for a connection, etc). */ #define ACCEPT_TIMEOUT 1000 #define MUTEX_VARNAME "_FCGI_MUTEX_" #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_" #define LOCALHOST "localhost" static HANDLE hIoCompPort = INVALID_HANDLE_VALUE; static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE; static HANDLE hStdinThread = INVALID_HANDLE_VALUE; static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; // This is a nail for listening to more than one port.. static HANDLE acceptMutex = INVALID_HANDLE_VALUE; static BOOLEAN shutdownPending = FALSE; static BOOLEAN shutdownNow = FALSE; /* * An enumeration of the file types * supported by the FD_TABLE structure. * * XXX: Not all currently supported. This allows for future * functionality. */ typedef enum { FD_UNUSED, FD_FILE_SYNC, FD_FILE_ASYNC, FD_SOCKET_SYNC, FD_SOCKET_ASYNC, FD_PIPE_SYNC, FD_PIPE_ASYNC } FILE_TYPE; typedef union { HANDLE fileHandle; SOCKET sock; unsigned int value; } DESCRIPTOR; /* * Structure used to map file handle and socket handle * values into values that can be used to create unix-like * select bitmaps, read/write for both sockets/files. */ struct FD_TABLE { DESCRIPTOR fid; FILE_TYPE type; char *path; DWORD Errno; unsigned long instance; int status; int offset; /* only valid for async file writes */ LPDWORD offsetHighPtr; /* pointers to offset high and low words */ LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */ HANDLE hMapMutex; /* mutex handle for multi-proc offset update */ LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */ }; /* * XXX Note there is no dyanmic sizing of this table, so if the * number of open file descriptors exceeds WIN32_OPEN_MAX the * app will blow up. */ static struct FD_TABLE fdTable[WIN32_OPEN_MAX]; static CRITICAL_SECTION fdTableCritical; struct OVERLAPPED_REQUEST { OVERLAPPED overlapped; unsigned long instance; /* file instance (won't match after a close) */ OS_AsyncProc procPtr; /* callback routine */ ClientData clientData; /* callback argument */ ClientData clientData1; /* additional clientData */ }; typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST; static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\"; static FILE_TYPE listenType = FD_UNUSED; // XXX This should be a DESCRIPTOR static HANDLE hListen = INVALID_HANDLE_VALUE; static BOOLEAN libInitialized = FALSE; /* *-------------------------------------------------------------- * * Win32NewDescriptor -- * * Set up for I/O descriptor masquerading. * * Results: * Returns "fake id" which masquerades as a UNIX-style "small * non-negative integer" file/socket descriptor. * Win32_* routine below will "do the right thing" based on the * descriptor's actual type. -1 indicates failure. * * Side effects: * Entry in fdTable is reserved to represent the socket/file. * *-------------------------------------------------------------- */ static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd) { int index = -1; EnterCriticalSection(&fdTableCritical); /* * If desiredFd is set, try to get this entry (this is used for * mapping stdio handles). Otherwise try to get the fd entry. * If this is not available, find a the first empty slot. . */ if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX) { if (fdTable[desiredFd].type == FD_UNUSED) { index = desiredFd; } } else if (fd > 0) { if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED) { index = fd; } else { int i; for (i = 1; i < WIN32_OPEN_MAX; ++i) { if (fdTable[i].type == FD_UNUSED) { index = i; break; } } } } if (index != -1) { fdTable[index].fid.value = fd; fdTable[index].type = type; fdTable[index].path = NULL; fdTable[index].Errno = NO_ERROR; fdTable[index].status = 0; fdTable[index].offset = -1; fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL; fdTable[index].hMapMutex = NULL; fdTable[index].ovList = NULL; } LeaveCriticalSection(&fdTableCritical); return index; } /* *-------------------------------------------------------------- * * StdinThread-- * * This thread performs I/O on stadard input. It is needed * because you can't guarantee that all applications will * create standard input with sufficient access to perform * asynchronous I/O. Since we don't want to block the app * reading from stdin we make it look like it's using I/O * completion ports to perform async I/O. * * Results: * Data is read from stdin and posted to the io completion * port. * * Side effects: * None. * *-------------------------------------------------------------- */ static void StdinThread(void * startup) { int doIo = TRUE; unsigned long fd; unsigned long bytesRead; POVERLAPPED_REQUEST pOv; // Touch the arg to prevent warning startup = NULL; while(doIo) { /* * Block until a request to read from stdin comes in or a * request to terminate the thread arrives (fd = -1). */ if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd, (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) { doIo = 0; break; } ASSERT((fd == STDIN_FILENO) || (fd == -1)); if(fd == -1) { doIo = 0; break; } ASSERT(pOv->clientData1 != NULL); if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead, &bytesRead, NULL)) { PostQueuedCompletionStatus(hIoCompPort, bytesRead, STDIN_FILENO, (LPOVERLAPPED)pOv); } else { doIo = 0; break; } } ExitThread(0); } void OS_ShutdownPending(void) { shutdownPending = TRUE; } static void ShutdownRequestThread(void * arg) { HANDLE shutdownEvent = (HANDLE) arg; WaitForSingleObject(shutdownEvent, INFINITE); shutdownPending = TRUE; // emulate the unix behaviour raise(SIGTERM); if (listenType == FD_PIPE_SYNC) { // Its a hassle to get ConnectNamedPipe to return early, // so just wack the whole process - yes, this will toast // any requests in progress, but at least its a clean // shutdown (its better than TerminateProcess()) exit(0); } // FD_SOCKET_SYNC: When in Accept(), select() is used to poll // the shutdownPending flag - yeah this isn't pretty either // but its only one process doing it if an Accept mutex is used. // This at least buys no toasted requests. } /* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Sockets initialized, pseudo file descriptors setup, etc. * *-------------------------------------------------------------- */ int OS_LibInit(int stdioFds[3]) { WORD wVersion; WSADATA wsaData; int err; int fakeFd; char *cLenPtr = NULL; char *val = NULL; if(libInitialized) return 0; InitializeCriticalSection(&fdTableCritical); /* * Initialize windows sockets library. */ wVersion = MAKEWORD(2,0); err = WSAStartup( wVersion, &wsaData ); if (err) { fprintf(stderr, "Error starting Windows Sockets. ERROR: %d", WSAGetLastError()); exit(111); } /* * Create the I/O completion port to be used for our I/O queue. */ if (hIoCompPort == INVALID_HANDLE_VALUE) { hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hIoCompPort == INVALID_HANDLE_VALUE) { printf("

OS_LibInit Failed CreateIoCompletionPort! ERROR: %lu

\r\n\r\n", GetLastError()); return -1; } } /* * If a shutdown event is in the env, save it (I don't see any to * remove it from the environment out from under the application). * Spawn a thread to wait on the shutdown request. */ val = getenv(SHUTDOWN_EVENT_NAME); if (val != NULL) { HANDLE shutdownEvent = (HANDLE) atoi(val); if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1) { return -1; } } if (acceptMutex == INVALID_HANDLE_VALUE) { /* If an accept mutex is in the env, use it */ val = getenv(MUTEX_VARNAME); if (val != NULL) { acceptMutex = (HANDLE) atoi(val); } } /* * Determine if this library is being used to listen for FastCGI * connections. This is communicated by STDIN containing a * valid handle to a listener object. In this case, both the * "stdout" and "stderr" handles will be INVALID (ie. closed) by * the starting process. * * The trick is determining if this is a pipe or a socket... * * XXX: Add the async accept test to determine socket or handle to a * pipe!!! */ if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) { DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT; HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE); // Move the handle to a "low" number if (! DuplicateHandle(GetCurrentProcess(), oldStdIn, GetCurrentProcess(), &hListen, 0, TRUE, DUPLICATE_SAME_ACCESS)) { return -1; } if (! SetStdHandle(STD_INPUT_HANDLE, hListen)) { return -1; } CloseHandle(oldStdIn); /* * Set the pipe handle state so that it operates in wait mode. * * NOTE: The listenFd is not mapped to a pseudo file descriptor * as all work done on it is contained to the OS library. * * XXX: Initial assumption is that SetNamedPipeHandleState will * fail if this is an IP socket... */ if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) { listenType = FD_PIPE_SYNC; } else { listenType = FD_SOCKET_SYNC; } } /* * If there are no stdioFds passed in, we're done. */ if(stdioFds == NULL) { libInitialized = 1; return 0; } /* * Setup standard input asynchronous I/O. There is actually a separate * thread spawned for this purpose. The reason for this is that some * web servers use anonymous pipes for the connection between itself * and a CGI application. Anonymous pipes can't perform asynchronous * I/O or use I/O completion ports. Therefore in order to present a * consistent I/O dispatch model to an application we emulate I/O * completion port behavior by having the standard input thread posting * messages to the hIoCompPort which look like a complete overlapped * I/O structure. This keeps the event dispatching simple from the * application perspective. */ stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDIN_FILENO], HANDLE_FLAG_INHERIT, 0)) { /* * XXX: Causes error when run from command line. Check KB err = GetLastError(); DebugBreak(); exit(99); */ } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDIN_FILENO], STDIN_FILENO)) == -1) { return -1; } else { /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ stdioFds[STDIN_FILENO] = fakeFd; } /* * Create the I/O completion port to be used for communicating with * the thread doing I/O on standard in. This port will carry read * and possibly thread termination requests to the StdinThread. */ if (hStdinCompPort == INVALID_HANDLE_VALUE) { hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hStdinCompPort == INVALID_HANDLE_VALUE) { printf("

OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %lu

\r\n\r\n", GetLastError()); return -1; } } /* * Create the thread that will read stdin if the CONTENT_LENGTH * is non-zero. */ if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL && atoi(cLenPtr) > 0) { hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL); if (hStdinThread == (HANDLE) -1) { printf("

OS_LibInit Failed to create STDIN thread! ERROR: %lu

\r\n\r\n", GetLastError()); return -1; } } /* * STDOUT will be used synchronously. * * XXX: May want to convert this so that it could be used for OVERLAPPED * I/O later. If so, model it after the Stdin I/O as stdout is * also incapable of async I/O on some servers. */ stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDOUT_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); exit(99); } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDOUT_FILENO], STDOUT_FILENO)) == -1) { return -1; } else { /* * Set stdout equal to our pseudo FD */ stdioFds[STDOUT_FILENO] = fakeFd; } stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE); if(!SetHandleInformation(stdioHandles[STDERR_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); exit(99); } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDERR_FILENO], STDERR_FILENO)) == -1) { return -1; } else { /* * Set stderr equal to our pseudo FD */ stdioFds[STDERR_FILENO] = fakeFd; } return 0; } /* *-------------------------------------------------------------- * * OS_LibShutdown -- * * Shutdown the OS library. * * Results: * None. * * Side effects: * Memory freed, handles closed. * *-------------------------------------------------------------- */ void OS_LibShutdown() { if (hIoCompPort != INVALID_HANDLE_VALUE) { CloseHandle(hIoCompPort); hIoCompPort = INVALID_HANDLE_VALUE; } if (hStdinCompPort != INVALID_HANDLE_VALUE) { CloseHandle(hStdinCompPort); hStdinCompPort = INVALID_HANDLE_VALUE; } if (acceptMutex != INVALID_HANDLE_VALUE) { ReleaseMutex(acceptMutex); } DisconnectNamedPipe(hListen); CancelIo(hListen); WSACleanup(); } /* *-------------------------------------------------------------- * * Win32FreeDescriptor -- * * Free I/O descriptor entry in fdTable. * * Results: * Frees I/O descriptor entry in fdTable. * * Side effects: * None. * *-------------------------------------------------------------- */ static void Win32FreeDescriptor(int fd) { /* Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); EnterCriticalSection(&fdTableCritical); if (fdTable[fd].type != FD_UNUSED) { switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: /* Free file path string */ ASSERT(fdTable[fd].path != NULL); free(fdTable[fd].path); fdTable[fd].path = NULL; break; default: break; } ASSERT(fdTable[fd].path == NULL); fdTable[fd].type = FD_UNUSED; fdTable[fd].path = NULL; fdTable[fd].Errno = NO_ERROR; fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL; if (fdTable[fd].hMapMutex != NULL) { CloseHandle(fdTable[fd].hMapMutex); fdTable[fd].hMapMutex = NULL; } } LeaveCriticalSection(&fdTableCritical); return; } static short getPort(const char * bindPath) { short port = 0; char * p = strchr(bindPath, ':'); if (p && *++p) { char buf[6]; strncpy(buf, p, 6); buf[5] = '\0'; port = (short) atoi(buf); } return port; } /* * OS_CreateLocalIpcFd -- * * This procedure is responsible for creating the listener pipe * on Windows NT for local process communication. It will create a * named pipe and return a file descriptor to it to the caller. * * Results: * Listener pipe created. This call returns either a valid * pseudo file descriptor or -1 on error. * * Side effects: * Listener pipe and IPC address are stored in the FCGI info * structure. * 'errno' will set on errors (-1 is returned). * *---------------------------------------------------------------------- */ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { int pseudoFd = -1; short port = getPort(bindPath); if (acceptMutex == INVALID_HANDLE_VALUE) { acceptMutex = CreateMutex(NULL, FALSE, NULL); if (acceptMutex == NULL) return -2; if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3; } // There's nothing to be gained (at the moment) by a shutdown Event if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST))) { fprintf(stderr, "To start a service on a TCP port can not " "specify a host name.\n" "You should either use \"localhost:\" or " " just use \":.\"\n"); exit(1); } listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC; if (port) { SOCKET listenSock; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); sockAddr.sin_port = htons(port); listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == INVALID_SOCKET) { return -4; } if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) ) { return -12; } if (listen(listenSock, backlog)) { return -5; } pseudoFd = Win32NewDescriptor(listenType, listenSock, -1); if (pseudoFd == -1) { closesocket(listenSock); return -6; } hListen = (HANDLE) listenSock; } else { HANDLE hListenPipe = INVALID_HANDLE_VALUE; char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); if (! pipePath) { return -7; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hListenPipe = CreateNamedPipe(pipePath, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); free(pipePath); if (hListenPipe == INVALID_HANDLE_VALUE) { return -8; } if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE)) { return -9; } pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1); if (pseudoFd == -1) { CloseHandle(hListenPipe); return -10; } hListen = (HANDLE) hListenPipe; } return pseudoFd; } /* *---------------------------------------------------------------------- * * OS_FcgiConnect -- * * Create the pipe pathname connect to the remote application if * possible. * * Results: * -1 if fail or a valid handle if connection succeeds. * * Side effects: * Remote connection established. * *---------------------------------------------------------------------- */ int OS_FcgiConnect(char *bindPath) { short port = getPort(bindPath); int pseudoFd = -1; if (port) { struct hostent *hp; char *host = NULL; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); SOCKET sock; if (*bindPath != ':') { char * p = strchr(bindPath, ':'); int len = p - bindPath; host = malloc(len + 1); memcpy(host, bindPath, len); host[len] = '\0'; } hp = gethostbyname(host ? host : LOCALHOST); if (host) { free(host); } if (hp == NULL) { fprintf(stderr, "Unknown host: %s\n", bindPath); return -1; } memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length); sockAddr.sin_port = htons(port); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { return -1; } if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen)) { closesocket(sock); return -1; } pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1); if (pseudoFd == -1) { closesocket(sock); return -1; } } else { char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); HANDLE hPipe; if (! pipePath) { return -1; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hPipe = CreateFile(pipePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); free(pipePath); if( hPipe == INVALID_HANDLE_VALUE) { return -1; } pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1); if (pseudoFd == -1) { CloseHandle(hPipe); return -1; } /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) { Win32FreeDescriptor(pseudoFd); CloseHandle(hPipe); return -1; } } return pseudoFd; } /* *-------------------------------------------------------------- * * OS_Read -- * * Pass through to the appropriate NT read function. * * Results: * Returns number of byes read. Mimics unix read:. * n bytes read, 0 or -1 failure: errno contains actual error * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Read(int fd, char * buf, size_t len) { DWORD bytesRead; int ret = -1; ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); if (shutdownNow) return -1; switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: case FD_PIPE_SYNC: case FD_PIPE_ASYNC: if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL)) { ret = bytesRead; } else { fdTable[fd].Errno = GetLastError(); } break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: ret = recv(fdTable[fd].fid.sock, buf, len, 0); if (ret == SOCKET_ERROR) { fdTable[fd].Errno = WSAGetLastError(); ret = -1; } break; default: ASSERT(0); } return ret; } /* *-------------------------------------------------------------- * * OS_Write -- * * Perform a synchronous OS write. * * Results: * Returns number of bytes written. Mimics unix write: * n bytes written, 0 or -1 failure (??? couldn't find man page). * * Side effects: * none. * *-------------------------------------------------------------- */ int OS_Write(int fd, char * buf, size_t len) { DWORD bytesWritten; int ret = -1; ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX); if (shutdownNow) return -1; switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: case FD_PIPE_SYNC: case FD_PIPE_ASYNC: if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL)) { ret = bytesWritten; } else { fdTable[fd].Errno = GetLastError(); } break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: ret = send(fdTable[fd].fid.sock, buf, len, 0); if (ret == SOCKET_ERROR) { fdTable[fd].Errno = WSAGetLastError(); ret = -1; } break; default: ASSERT(0); } return ret; } /* *---------------------------------------------------------------------- * * OS_SpawnChild -- * * Spawns a new server listener process, and stores the information * relating to the child in the supplied record. A wait handler is * registered on the child's completion. This involves creating * a process on NT and preparing a command line with the required * state (currently a -childproc flag and the server socket to use * for accepting connections). * * Results: * 0 if success, -1 if error. * * Side effects: * Child process spawned. * *---------------------------------------------------------------------- */ int OS_SpawnChild(char *execPath, int listenFd) { STARTUPINFO StartupInfo; PROCESS_INFORMATION pInfo; BOOL success; memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO)); StartupInfo.cb = sizeof (STARTUPINFO); StartupInfo.lpReserved = NULL; StartupInfo.lpReserved2 = NULL; StartupInfo.cbReserved2 = 0; StartupInfo.lpDesktop = NULL; /* * FastCGI on NT will set the listener pipe HANDLE in the stdin of * the new process. The fact that there is a stdin and NULL handles * for stdout and stderr tells the FastCGI process that this is a * FastCGI process and not a CGI process. */ StartupInfo.dwFlags = STARTF_USESTDHANDLES; /* * XXX: Do I have to dup the handle before spawning the process or is * it sufficient to use the handle as it's reference counted * by NT anyway? */ StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle; StartupInfo.hStdOutput = INVALID_HANDLE_VALUE; StartupInfo.hStdError = INVALID_HANDLE_VALUE; /* * Make the listener socket inheritable. */ success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT, TRUE); if(!success) { exit(99); } /* * XXX: Might want to apply some specific security attributes to the * processes. */ success = CreateProcess(execPath, /* LPCSTR address of module name */ NULL, /* LPCSTR address of command line */ NULL, /* Process security attributes */ NULL, /* Thread security attributes */ TRUE, /* Inheritable Handes inherited. */ 0, /* DWORD creation flags */ NULL, /* Use parent environment block */ NULL, /* Address of current directory name */ &StartupInfo, /* Address of STARTUPINFO */ &pInfo); /* Address of PROCESS_INFORMATION */ if(success) { return 0; } else { return -1; } } /* *-------------------------------------------------------------- * * OS_AsyncReadStdin -- * * This initiates an asynchronous read on the standard * input handle. This handle is not guaranteed to be * capable of performing asynchronous I/O so we send a * message to the StdinThread to do the synchronous read. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous message is queued to the StdinThread and an * overlapped structure is allocated/initialized. * *-------------------------------------------------------------- */ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { POVERLAPPED_REQUEST pOv; ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); pOv->clientData1 = (ClientData)buf; pOv->instance = fdTable[STDIN_FILENO].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO, (LPOVERLAPPED)pOv); return 0; } /* *-------------------------------------------------------------- * * OS_AsyncRead -- * * This initiates an asynchronous read on the file * handle which may be a socket or named pipe. * * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the ReadFile may * return data if it is cached) but do all completion * processing in OS_Select when we get the io completion * port done notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { DWORD bytesRead; POVERLAPPED_REQUEST pOv; /* * Catch any bogus fd values */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* * Confirm that this is an async fd */ ASSERT(fdTable[fd].type != FD_UNUSED); ASSERT(fdTable[fd].type != FD_FILE_SYNC); ASSERT(fdTable[fd].type != FD_PIPE_SYNC); ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); /* * Only file offsets should be non-zero, but make sure. */ if (fdTable[fd].type == FD_FILE_ASYNC) { if (fdTable[fd].offset >= 0) pOv->overlapped.Offset = fdTable[fd].offset; else pOv->overlapped.Offset = offset; } pOv->instance = fdTable[fd].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; bytesRead = fd; /* * ReadFile returns: TRUE success, FALSE failure */ if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, (LPOVERLAPPED)pOv)) { fdTable[fd].Errno = GetLastError(); if(fdTable[fd].Errno == ERROR_NO_DATA || fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return 0; } if(fdTable[fd].Errno != ERROR_IO_PENDING) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return -1; } fdTable[fd].Errno = 0; } return 0; } /* *-------------------------------------------------------------- * * OS_AsyncWrite -- * * This initiates an asynchronous write on the "fake" file * descriptor (which may be a file, socket, or named pipe). * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the WriteFile generally * completes immediately) but do all completion processing * in OS_DoIo when we get the io completion port done * notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { DWORD bytesWritten; POVERLAPPED_REQUEST pOv; /* * Catch any bogus fd values */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* * Confirm that this is an async fd */ ASSERT(fdTable[fd].type != FD_UNUSED); ASSERT(fdTable[fd].type != FD_FILE_SYNC); ASSERT(fdTable[fd].type != FD_PIPE_SYNC); ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); /* * Only file offsets should be non-zero, but make sure. */ if (fdTable[fd].type == FD_FILE_ASYNC) { /* * Only file opened via OS_AsyncWrite with * O_APPEND will have an offset != -1. */ if (fdTable[fd].offset >= 0) /* * If the descriptor has a memory mapped file * handle, take the offsets from there. */ if (fdTable[fd].hMapMutex != NULL) { /* * Wait infinitely; this *should* not cause problems. */ WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE); /* * Retrieve the shared offset values. */ pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr); pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr); /* * Update the shared offset values for the next write */ *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */ *(fdTable[fd].offsetLowPtr) += len; ReleaseMutex(fdTable[fd].hMapMutex); } else pOv->overlapped.Offset = fdTable[fd].offset; else pOv->overlapped.Offset = offset; } pOv->instance = fdTable[fd].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; bytesWritten = fd; /* * WriteFile returns: TRUE success, FALSE failure */ if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, (LPOVERLAPPED)pOv)) { fdTable[fd].Errno = GetLastError(); if(fdTable[fd].Errno != ERROR_IO_PENDING) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return -1; } fdTable[fd].Errno = 0; } if (fdTable[fd].offset >= 0) fdTable[fd].offset += len; return 0; } /* *-------------------------------------------------------------- * * OS_Close -- * * Closes the descriptor with routine appropriate for * descriptor's type. * * Results: * Socket or file is closed. Return values mimic Unix close: * 0 success, -1 failure * * Side effects: * Entry in fdTable is marked as free. * *-------------------------------------------------------------- */ int OS_Close(int fd, int shutdown_ok) { int ret = 0; /* * Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); ASSERT(fdTable[fd].type != FD_UNUSED); switch (fdTable[fd].type) { case FD_PIPE_SYNC: case FD_PIPE_ASYNC: case FD_FILE_SYNC: case FD_FILE_ASYNC: break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: /* * shutdown() the send side and then read() from client until EOF * or a timeout expires. This is done to minimize the potential * that a TCP RST will be sent by our TCP stack in response to * receipt of additional data from the client. The RST would * cause the client to discard potentially useful response data. */ if (shutdown_ok) { if (shutdown(fdTable[fd].fid.sock, SD_SEND) == 0) { struct timeval tv; fd_set rfds; int sock = fdTable[fd].fid.sock; int rv; char trash[1024]; FD_ZERO(&rfds); do { FD_SET((unsigned) sock, &rfds); tv.tv_sec = 2; tv.tv_usec = 0; rv = select(sock + 1, &rfds, NULL, NULL, &tv); } while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0); } } closesocket(fdTable[fd].fid.sock); break; default: ret = -1; /* fake failure */ } Win32FreeDescriptor(fd); return ret; } /* *-------------------------------------------------------------- * * OS_CloseRead -- * * Cancel outstanding asynchronous reads and prevent subsequent * reads from completing. * * Results: * Socket or file is shutdown. Return values mimic Unix shutdown: * 0 success, -1 failure * *-------------------------------------------------------------- */ int OS_CloseRead(int fd) { int ret = 0; /* * Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC || fdTable[fd].type == FD_SOCKET_SYNC); if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR) ret = -1; return ret; } /* *-------------------------------------------------------------- * * OS_DoIo -- * * This function was formerly OS_Select. It's purpose is * to pull I/O completion events off the queue and dispatch * them to the appropriate place. * * Results: * Returns 0. * * Side effects: * Handlers are called. * *-------------------------------------------------------------- */ int OS_DoIo(struct timeval *tmo) { unsigned long fd; unsigned long bytes; POVERLAPPED_REQUEST pOv; struct timeb tb; int ms; int ms_last; int err; /* XXX * We can loop in here, but not too long, as wait handlers * must run. * For cgi stdin, apparently select returns when io completion * ports don't, so don't wait the full timeout. */ if(tmo) ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2; else ms = 1000; ftime(&tb); ms_last = tb.time*1000 + tb.millitm; while (ms >= 0) { if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100) ms = 100; if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd, (LPOVERLAPPED *)&pOv, ms) && !pOv) { err = WSAGetLastError(); return 0; /* timeout */ } ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* call callback if descriptor still valid */ ASSERT(pOv); if(pOv->instance == fdTable[fd].instance) (*pOv->procPtr)(pOv->clientData, bytes); free(pOv); ftime(&tb); ms -= (tb.time*1000 + tb.millitm - ms_last); ms_last = tb.time*1000 + tb.millitm; } return 0; } static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs) { static const char *token = " ,;:\t"; char *ipaddr; char *p; if (okAddrs == NULL) return TRUE; ipaddr = inet_ntoa(inet_sockaddr->sin_addr); p = strstr(okAddrs, ipaddr); if (p == NULL) return FALSE; if (p == okAddrs) { p += strlen(ipaddr); return (strchr(token, *p) != NULL); } if (strchr(token, *--p) != NULL) { p += strlen(ipaddr) + 1; return (strchr(token, *p) != NULL); } return FALSE; } #ifndef NO_WSAACEPT static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId, LPWSABUF dc0, LPQOS dc1, LPQOS dc2, LPWSABUF dc3, LPWSABUF dc4, GROUP *dc5, DWORD data) { struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf; // Touch the args to avoid warnings dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; if ((void *) data == NULL) return CF_ACCEPT; if (sockaddr->sin_family != AF_INET) return CF_ACCEPT; return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT; } #endif static void printLastError(const char * text) { LPVOID buf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &buf, 0, NULL ); fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf); LocalFree(buf); } static int acceptNamedPipe() { int ipcFd = -1; if (! ConnectNamedPipe(hListen, NULL)) { switch (GetLastError()) { case ERROR_PIPE_CONNECTED: // A client connected after CreateNamedPipe but // before ConnectNamedPipe. Its a good connection. break; case ERROR_IO_PENDING: // The NamedPipe was opened with an Overlapped structure // and there is a pending io operation. mod_fastcgi // did this in 2.2.12 (fcgi_pm.c v1.52). case ERROR_PIPE_LISTENING: // The pipe handle is in nonblocking mode. case ERROR_NO_DATA: // The previous client closed its handle (and we failed // to call DisconnectNamedPipe) default: printLastError("unexpected ConnectNamedPipe() error"); } } ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1); if (ipcFd == -1) { DisconnectNamedPipe(hListen); } return ipcFd; } static int acceptSocket(const char *webServerAddrs) { SOCKET hSock; int ipcFd = -1; for (;;) { struct sockaddr sockaddr; int sockaddrLen = sizeof(sockaddr); for (;;) { struct timeval timeout = {1, 0}; fd_set readfds; FD_ZERO(&readfds); FD_SET((unsigned int) hListen, &readfds); if (select(0, &readfds, NULL, NULL, &timeout) == 0) { if (shutdownPending) { OS_LibShutdown(); return -1; } } else { break; } } #if NO_WSAACEPT hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen); if (hSock == INVALID_SOCKET) { break; } if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs)) { break; } closesocket(hSock); #else hSock = WSAAccept((unsigned int) hListen, &sockaddr, &sockaddrLen, isAddrOKCallback, (DWORD) webServerAddrs); if (hSock != INVALID_SOCKET) { break; } if (WSAGetLastError() != WSAECONNREFUSED) { break; } #endif } if (hSock == INVALID_SOCKET) { /* Use FormatMessage() */ fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError()); return -1; } ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1); if (ipcFd == -1) { closesocket(hSock); } return ipcFd; } /* *---------------------------------------------------------------------- * * OS_Accept -- * * Accepts a new FastCGI connection. This routine knows whether * we're dealing with TCP based sockets or NT Named Pipes for IPC. * * fail_on_intr is ignored in the Win lib. * * Results: * -1 if the operation fails, otherwise this is a valid IPC fd. * *---------------------------------------------------------------------- */ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { int ipcFd = -1; // Touch args to prevent warnings listen_sock = 0; fail_on_intr = 0; // @todo Muliple listen sockets and sockets other than 0 are not // supported due to the use of globals. if (shutdownPending) { OS_LibShutdown(); return -1; } // The mutex is to keep other processes (and threads, when supported) // from going into the accept cycle. The accept cycle needs to // periodically break out to check the state of the shutdown flag // and there's no point to having more than one thread do that. if (acceptMutex != INVALID_HANDLE_VALUE) { if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED) { printLastError("WaitForSingleObject() failed"); return -1; } } if (shutdownPending) { OS_LibShutdown(); } else if (listenType == FD_PIPE_SYNC) { ipcFd = acceptNamedPipe(); } else if (listenType == FD_SOCKET_SYNC) { ipcFd = acceptSocket(webServerAddrs); } else { fprintf(stderr, "unknown listenType (%d)\n", listenType); } if (acceptMutex != INVALID_HANDLE_VALUE) { ReleaseMutex(acceptMutex); } return ipcFd; } /* *---------------------------------------------------------------------- * * OS_IpcClose * * OS IPC routine to close an IPC connection. * * Results: * * * Side effects: * IPC connection is closed. * *---------------------------------------------------------------------- */ int OS_IpcClose(int ipcFd, int shutdown) { if (ipcFd == -1) return 0; /* * Catch it if fd is a bogus value */ ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX)); ASSERT(fdTable[ipcFd].type != FD_UNUSED); switch (listenType) { case FD_PIPE_SYNC: /* * Make sure that the client (ie. a Web Server in this case) has * read all data from the pipe before we disconnect. */ if (! FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1; if (! DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) return -1; /* fall through */ case FD_SOCKET_SYNC: OS_Close(ipcFd, shutdown); break; case FD_UNUSED: default: exit(106); break; } return 0; } /* *---------------------------------------------------------------------- * * OS_IsFcgi -- * * Determines whether this process is a FastCGI process or not. * * Results: * Returns 1 if FastCGI, 0 if not. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_IsFcgi(int sock) { // Touch args to prevent warnings sock = 0; /* XXX This is broken for sock */ return (listenType != FD_UNUSED); } /* *---------------------------------------------------------------------- * * OS_SetFlags -- * * Sets selected flag bits in an open file descriptor. Currently * this is only to put a SOCKET into non-blocking mode. * *---------------------------------------------------------------------- */ void OS_SetFlags(int fd, int flags) { unsigned long pLong = 1L; int err; if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) { if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) == SOCKET_ERROR) { exit(WSAGetLastError()); } if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock, hIoCompPort, fd, 1)) { err = GetLastError(); exit(err); } fdTable[fd].type = FD_SOCKET_ASYNC; } return; } fcgi2-2.4.5/libfcgi/strerror.c000066400000000000000000000054221477724357700161730ustar00rootroot00000000000000/* * The terms in the file "LICENSE" do not apply to this file. * See terms below. * * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 "fcgi_config.h" #if ! defined (HAVE_STRERROR) #include /* * Since perror() is not allowed to change the contents of strerror()'s * static buffer, both functions supply their own buffers to the * internal function __strerror(). */ char * __strerror(int num, char *buf) { #define UPREFIX "Unknown error: " extern char *sys_errlist[]; extern int sys_nerr; register unsigned int errnum; register char *p, *t; char tmp[40]; errnum = num; /* convert to unsigned */ if (errnum < sys_nerr) return(sys_errlist[errnum]); /* Do this by hand, so we don't include stdio(3). */ t = tmp; do { *t++ = "0123456789"[errnum % 10]; } while (errnum /= 10); strcpy (buf, UPREFIX); for (p = buf + sizeof(UPREFIX) -1;;) { *p++ = *--t; if (t <= tmp) break; } return buf; } char * strerror(int num) { static char buf[40]; /* 64-bit number + slop */ return __strerror(num, buf); } #endif fcgi2-2.4.5/m4/000077500000000000000000000000001477724357700130635ustar00rootroot00000000000000fcgi2-2.4.5/m4/.gitignore000066400000000000000000000000161477724357700150500ustar00rootroot00000000000000* !.gitignore fcgi2-2.4.5/perl/000077500000000000000000000000001477724357700135055ustar00rootroot00000000000000fcgi2-2.4.5/perl/.cvsignore000066400000000000000000000003151477724357700155040ustar00rootroot00000000000000*~ bak .bak *.bak blib *.old *.fpl FCGI.c FCGI.bs FCGI.cfg FCGI.def FCGI.pm FCGI.xs Makefile pm_to_blib *.obj *.o fcgi_config.* fcgi_config_x86.h configure config.cache config.status config.log MYMETA.* fcgi2-2.4.5/perl/.gitignore000066400000000000000000000002211477724357700154700ustar00rootroot00000000000000blib FCGI.c FCGI.bs FCGI.cfg FCGI.def FCGI.pm FCGI.xs pm_to_blib fcgi_config.* fcgi_config_x86.h Makefile config.log config.status MYMETA.* fcgi2-2.4.5/perl/ChangeLog000066400000000000000000000312251477724357700152620ustar00rootroot00000000000000Release history for FCGI Version 0.82 -- 2021-07-31 - fix failing skip tests on MSWin32 Version 0.81 -- 2021-07-31 - Add dependency to FCGI::Client 0.09 so as to not depend on Any::Moose, which is deprecated. (Michal Josef Špaček, PR #7) - fix test failures on MSWin32 ((Michal Josef Špaček, PR #8) Version 0.80 -- 2021-07-24 - Add test for FCGI over unix domain socket (Michal Josef Špaček, PR #6) Version 0.79 -- 2019-12-14 - Check socket path length in OS_CreateLocalIpcFd() and OS_FcgiConnect() to avoid buffer overrun (Petr Písař, pull request #5) - Fix a memory leak in ProcessManagementRecord() (Petr Písař, pull request #4) Version 0.78 -- 2015-03-07 - make copyright and license information more visible (RT#112535) Version 0.77 -- 2014-08-05 - re-release to remove build artifacts that should not have been shipped Version 0.76 -- 2014-08-05 - On Android, set TMPDIR before calling configure (RT#97680, Brian Fraser) Version 0.75 -- 2014-07-17 - deprecated APIs removed (chansen) - broken PP implementation removed (chansen) - retooled distribution so FCGI.pm and FCGI.xs exist as-is, rather than being generated by FCGI.PL and FCGI.XL (chansen) Version 0.74 -- 24 Sep 2011 - Stop leaking information across requests when using the deprecated and undocumented old FCGI interface. This is CVE-2011-2766. - Only discard input stream if FCGI_KEEP_CONN is set in FCGI_BeginRequestBody flags. Version 0.73 -- 19 May 2011 - Stop claiming we ship a file called -e in the MANIFEST. Version 0.72 -- 19 May 2011 - Clean up Makefile.PL and restore compatibility with recent ExtUtils::MakeMaker versions. Version 0.71_03 -- 28 Apr 2011 - Remove support for sfio which is an optional (and not enabled by default) compile option to perl that is never used. - Fix FCGI::Stream::READ() to warn() instead of croak() incase of wide characters which cannot be gracefully downgraded. - Fix warnings due to wide characters being mangled to note that accepting them is deprecated and will stop working at some point. - Various fixes to FCGI::Stream::READ() to improve handling of error and edge cases. - croak if called with invalid number of arguments - croak if length is negative - croak if offset is outside string - pad scalar if offset is greater than length - Fix in FCGX_Finish_r to discard any remaining data in input stream which otherwise ends up in next request. This fixes multiple requests being broken if something goes wrong whilst reading the initial request. This discarding is done silently, as RFC 3875 says a script is not obliged to read any of the data. - Fixed indent style and braces to be consistent, swapped tabs for spaces in indenting. Version 0.71_02 -- 28 Apr 2011 - Change the Request function to pass FAIL_ON_INTR into the XS RequestX function. This prevents the fcgi C client code from looping around their accept() call. This change means that when using CGI::Fast, and the process recieves SIGTERM or SIGHUP, the error statusis correctly passed back up, allowing process managers (such as FCGI::ProcManager) to correctly handle cleanly exiting. Version 0.71_01 -- 24 Aug 2010 - Restore old behavior when un-downgradeable uft8 is sent to FCGI. The first time this happens, a warning will be issued, but subsequently the bytes will be sent through raw (causing double encoding etc). If the character string can be downgraded safely, then it will still be. use warnings FATAL => 'utf8'; can be used to make undowngradeable strings throw an exception. - Fix PRINT retval (Closes: RT#57697). Version 0.71 -- 1 Apr 2010 Florian Ragwitz - Fix some more defined(%hash) warnings on perl 5.12. Version 0.70 -- 22 Mar 2010 Tomas Doran - Fix use of defined %hash which becomes deprecated in perl 5.12 Version 0.69 -- 15 Feb 2010 Matt S Trout - No changes since the previous development release. Version 0.68_02 -- 13 Jan 2010 Matt S Trout - Make the PRINT method return a boolean value rather than the number of bytes written, previous patch was incorrect. Version 0.68_01 -- 10 Jan 2010 Matt S Trout - Force signal handler installation so that we correctly install handlers for SIGPIPE. Fixes RT#5100 - Make the PRINT method return the number of bytes written rather than undef to be consistent with the IO:: interface. Fixes RT#24347 - Fix UTF-8 double encoding when FCGI is passed octets by downgrading them into bytes correctly. Fixes RT#52400 Version 0.68 -- 31 Dec 2009 Matt S Trout - No changes since the previous development release. Version 0.67_01 -- 20 Dec 2009 Matt S Trout - Add FILENO method which returns a defined but invalid value to placate things such as IPC::Run which call fileno to check if a filehandle is open. Closes bugs: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=544540 http://rt.cpan.org/Public/Bug/Display.html?id=50972 Removes need for upstream patch in OpenBSD - Call the fcgi lib's attach/detach Version 0.67 -- 22 December 2002 Sven Verdoolaege - Fixes for pure perl version based on report and patch from "Kurtis D. Rader" - FCGI_UndoBinding perl 5.8.0 compatibility Reported by Marko Asplund - Fix problem with fcgi_config.h on win32. Reported by Igor Franchuk - Add minimal tests Version 0.66 -- 5 September 2002 Sven Verdoolaege - perl 5.8.0 compatibility fix by Autrijus - library fixes from Rob Version 0.65 -- 19 February 2002 Sven Verdoolaege - fix perl 5.005 compatibility problem - fix strict warning Version 0.64 -- 25 September 2001 Sven Verdoolaege Version 0.63 -- 24 September 2001 Sven Verdoolaege - Update build process Version 0.62 -- 21 September 2001 Sven Verdoolaege - Move version number to separate file Version 0.61 -- 20 September 2001 Sven Verdoolaege - Fix refcounting bug - Add GetEnvironment for pure version - Add LastCall method - Allow filehandle for Request's socket parameter - library fixes ("Rob Saccoccio" ) Version 0.60 -- 8 July 2001 Sven Verdoolaege - Allow specification of purity on command line (suggested by Rob Brown ) - Fix bug in pure perl implementation - Don't try to compile anything on pure perl build - Add BINMODE method - Add comment on socket permissions Version 0.59 -- 31 December 2000 Sven Verdoolaege - preliminary pure perl implementation - copy win32 configuration file instead of moving it - convert echo.fpl to new interface Version 0.58 -- 15 November 2000 Sven Verdoolaege - fix bug introduced in 0.57 Version 0.57 -- 12 November 2000 Sven Verdoolaege - don't flush unbound request Version 0.56 -- 3 November 2000 Sven Verdoolaege - add example remote.fpl - provide access to the Request parameters - add IsFastCGI method - fix warn handler (Andrew Pimlott ) Version 0.55 -- 18 October 2000 Sven Verdoolaege - small documentation fix - compilation issues with older perls fixed - library initialization when using sockets fixed Version 0.54 -- 8 October 2000 Sven Verdoolaege - library fixes ("Rob Saccoccio" ) - compilation issues with newer gcc - completely untested OPEN and READLINE methods Version 0.53 -- 10 July 2000 Sven Verdoolaege - sfio version compiles again Version 0.52 -- 12 April 2000 Sven Verdoolaege Version 0.51 -- 12 April 2000 Sven Verdoolaege Version 0.50 -- 10 April 2000 Sven Verdoolaege Version 0.49 -- 9 April 2000 Sven Verdoolaege - General clean-ups - Allow attaching/detaching - Changed DESTROY behaviour - Fixed default warn/die handler of old interface - Document new interface Version 0.48 -- 27 August 1999 Sven Verdoolaege - perl 5.005_60 compatibility - locking on platforms that need it - support for remote connections Version 0.47 -- 31 July 1999 Sven Verdoolaege - move PRINTF into correct package - deprecated set_exit_status - general cleanup, moving old non thread safe interface from xs to perl Version 0.46 -- 30 July 1999 Sven Verdoolaege - new thread safe interface - new threaded example program Version 0.45 -- 8 March 1999 Sven Verdoolaege - FCGI.pm now part of the devel kit - library fixes ("Rob Saccoccio" ) - allow bypassing of installation of handlers - ActivePerl compatibility (Murray Nesbitt ) Version 0.43 -- 22 December 1998 Sven Verdoolaege - POST on bigendians (Paul GABORIT ) - Some win32 changes (Monty ) - library fixes ("Rob Saccoccio" ) Version 0.42 -- 28 August 1998 Sven Verdoolaege - environ fixes ? - print NULLs (Ken Alexander ) - PRINTF support - set version in FCGI.pm - library fixes ("Rob Saccoccio" ) Version 0.41 -- 29 July 1998 Sven Verdoolaege - Compiles with perl 5.005 Version 0.40 -- 15 July 1998 Sven Verdoolaege - Added default die hook - Minimal documentation Version 0.39 -- 3 July 1998 Sven Verdoolaege - Fixed read bug Version 0.38 -- 28 June 1998 Sven Verdoolaege - Fixed flush bug - Added default warn hook Version 0.37 -- 27 June 1998 Sven Verdoolaege - More support for tied handles - Added flush function Version 0.36 -- 23 June 1998 Sven Verdoolaege - More support for tied handles (GETC and autoflushing) Version 0.35 -- 22 June 1998 Sven Verdoolaege - Added forgotten typemap Version 0.34 -- 17 June 1998 Sven Verdoolaege - No longer force sfio less compile - Update os_unix.c from fcgi2.0b2.1 - Small documentation changes Version 0.33 -- 16 June 1998 Sven Verdoolaege - More support for tied handles Version 0.32 -- 16 June 1998 Sven Verdoolaege - Preliminary support for tied handles (doesn't require sfio) - Force sfio less compile - Changed protoype of set_exit_status Version 0.31 -- 13 July 1997 Sven Verdoolaege - Applied solaris accept patch from Chip Salzenberg - Preliminary support glibc's cookie mechanism Version 0.30 -- 24 June 1997 Sven Verdoolaege - Added forgotten library files Version 0.29 -- 10 June 1997 Sven Verdoolaege - Updated library files from fastcgi 2.02b - Use installed library/include file if found Version 0.28 -- 24 February 1997 Sven Verdoolaege - Intialization of %ENV did not change environ. Fixed. Problem reported by Jan Drehmer Version 0.26 -- 19 February 1997 Sven Verdoolaege - Flush output when $| is set to eliminate a problem reported by echo@echo.cica.fr Version 0.25 -- 13 October 1996 Sven Verdoolaege - Eliminate some warnings - Check whether perl is compiled with sfio support Version 0.25 -- 25 September 1996 Sven Verdoolaege - First public release - Additional bugfixes Version 0.21 -- 20 September 1996 Sven Verdoolaege - Bugfix Version 0.2 -- 19 September 1996 Sven Verdoolaege - First Version based on sfio Version 0.1 -- 12 June 1996 - Original version from Open Market's FastCGI Developer's Kit fcgi2-2.4.5/perl/FCGI.pm000066400000000000000000000133361477724357700145610ustar00rootroot00000000000000package FCGI; use strict; BEGIN { our $VERSION = '0.82'; require XSLoader; XSLoader::load(__PACKAGE__, $VERSION); } sub FAIL_ACCEPT_ON_INTR () { 1 }; sub Request(;***$*$) { my @defaults = (\*STDIN, \*STDOUT, \*STDERR, \%ENV, 0, FAIL_ACCEPT_ON_INTR); $_[4] = fileno($_[4]) if defined($_[4]) && defined(fileno($_[4])); splice @defaults,0,@_,@_; &RequestX(@defaults); } package FCGI::Stream; use strict; sub PRINTF { shift->PRINT(sprintf(shift, @_)); } sub BINMODE { } sub READLINE { my $stream = shift; my ($s, $c); my $rs = $/ eq '' ? "\n\n" : $/; my $l = substr $rs, -1; my $len = length $rs; $c = $stream->GETC(); if ($/ eq '') { while ($c eq "\n") { $c = $stream->GETC(); } } while (defined $c) { $s .= $c; last if $c eq $l and substr($s, -$len) eq $rs; $c = $stream->GETC(); } $s; } sub OPEN { require Carp; Carp::croak(q/Operation 'OPEN' not supported on FCGI::Stream handle/); } sub SEEK { require Carp; Carp::croak(q/Operation 'SEEK' not supported on FCGI::Stream handle/); } sub TELL { require Carp; Carp::croak(q/Operation 'TELL' not supported on FCGI::Stream handle/); } sub TIEHANDLE { require Carp; Carp::croak(q/Operation 'TIEHANDLE' not supported on FCGI::Stream handle/); } 1; =pod =head1 NAME FCGI - Fast CGI module =head1 SYNOPSIS use FCGI; my $count = 0; my $request = FCGI::Request(); while($request->Accept() >= 0) { print("Content-type: text/html\r\n\r\n", ++$count); } =head1 DESCRIPTION Functions: =over 4 =item FCGI::Request Creates a request handle. It has the following optional parameters: =over 8 =item input perl file handle (default: \*STDIN) =item output perl file handle (default: \*STDOUT) =item error perl file handle (default: \*STDERR) These filehandles will be setup to act as input/output/error on successful Accept. =item environment hash reference (default: \%ENV) The hash will be populated with the environment. =item socket (default: 0) Socket to communicate with the server. Can be the result of the OpenSocket function. For the moment, it's the file descriptor of the socket that should be passed. This may change in the future. You should only use your own socket if your program is not started by a process manager such as mod_fastcgi (except for the FastCgiExternalServer case) or cgi-fcgi. If you use the option, you have to let your FastCGI server know which port (and possibly server) your program is listening on. See remote.pl for an example. =item flags (default: FCGI::FAIL_ACCEPT_ON_INTR) Possible values: =over 12 =item FCGI::FAIL_ACCEPT_ON_INTR If set, Accept will fail if interrupted. It not set, it will just keep on waiting. =back =back Example usage: my $req = FCGI::Request; or: my %env; my $in = new IO::Handle; my $out = new IO::Handle; my $err = new IO::Handle; my $req = FCGI::Request($in, $out, $err, \%env); =item FCGI::OpenSocket(path, backlog) Creates a socket suitable to use as an argument to Request. =over 8 =item path Pathname of socket or colon followed by local tcp port. Note that some systems take file permissions into account on Unix domain sockets, so you'll have to make sure that the server can write to the created file, by changing the umask before the call and/or changing permissions and/or group of the file afterwards. =item backlog Maximum length of the queue of pending connections. If a connection request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED. =back =item FCGI::CloseSocket(socket) Close a socket opened with OpenSocket. =item $req->Accept() Accepts a connection on $req, attaching the filehandles and populating the environment hash. Returns 0 on success. If a connection has been accepted before, the old one will be finished first. Note that unlike with the old interface, no die and warn handlers are installed by default. This means that if you are not running an sfio enabled perl, any warn or die message will not end up in the server's log by default. It is advised you set up die and warn handlers yourself. FCGI.pm contains an example of die and warn handlers. =item $req->Finish() Finishes accepted connection. Also detaches filehandles. =item $req->Flush() Flushes accepted connection. =item $req->Detach() Temporarily detaches filehandles on an accepted connection. =item $req->Attach() Re-attaches filehandles on an accepted connection. =item $req->LastCall() Tells the library not to accept any more requests on this handle. It should be safe to call this method from signal handlers. Note that this method is still experimental and everything about it, including its name, is subject to change. =item $env = $req->GetEnvironment() Returns the environment parameter passed to FCGI::Request. =item ($in, $out, $err) = $req->GetHandles() Returns the file handle parameters passed to FCGI::Request. =item $isfcgi = $req->IsFastCGI() Returns whether or not the program was run as a FastCGI. =back =head1 LIMITATIONS FCGI.pm isn't Unicode aware, only characters within the range 0x00-0xFF are supported. Attempts to output strings containing characters above 0xFF results in a exception: (F) C. Users who wants the previous (FCGI.pm <= 0.68) incorrect behavior can disable the exception by using the C pragma. { use bytes; print "\x{263A}"; } =head1 AUTHOR Sven Verdoolaege =head1 COPYRIGHT AND LICENCE This software is copyrighted (c) 1996 by by Open Market, Inc. See the LICENSE file in this distribution for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. =cut __END__ fcgi2-2.4.5/perl/FCGI.xs000066400000000000000000000255121477724357700145760ustar00rootroot00000000000000/* $Id: FCGI.XL,v 1.10 2003/06/22 00:24:11 robs Exp $ */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "fcgi_config.h" #include "fcgiapp.h" #include "fastcgi.h" #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (1) #endif #ifndef dTHX #define dTHX #endif #ifndef INT2PTR #define INT2PTR(a,b) ((a) (b)) #endif /* Deprecation added 2010-10-05. The deprecated functionality should not be * removed for at least a year after that. */ #define WIDE_CHAR_DEPRECATION_MSG "Use of wide characters in %s is deprecated" \ " and will stop working in a future version of FCGI" #if defined(USE_ITHREADS) static perl_mutex accept_mutex; #endif typedef struct FCGP_Request { int accepted; int bound; SV* svin; SV* svout; SV* sverr; GV* gv[3]; HV* hvEnv; FCGX_Request* requestPtr; } FCGP_Request; static void FCGI_Finish(FCGP_Request* request); static void FCGI_Flush(FCGP_Request* request) { dTHX; if(!request->bound) return; FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->svout)))); FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->sverr)))); } static void FCGI_UndoBinding(FCGP_Request* request) { dTHX; #ifdef USE_PERLIO sv_unmagic((SV *)GvIOp(request->gv[0]), 'q'); sv_unmagic((SV *)GvIOp(request->gv[1]), 'q'); sv_unmagic((SV *)GvIOp(request->gv[2]), 'q'); #else sv_unmagic((SV *)request->gv[0], 'q'); sv_unmagic((SV *)request->gv[1], 'q'); sv_unmagic((SV *)request->gv[2], 'q'); #endif request->bound = FALSE; } static void FCGI_Bind(FCGP_Request* request) { dTHX; #ifdef USE_PERLIO /* For tied filehandles, we apply tiedscalar magic to the IO slot of the GP rather than the GV itself. */ if (!GvIOp(request->gv[1])) GvIOp(request->gv[1]) = newIO(); if (!GvIOp(request->gv[2])) GvIOp(request->gv[2]) = newIO(); if (!GvIOp(request->gv[0])) GvIOp(request->gv[0]) = newIO(); sv_magic((SV *)GvIOp(request->gv[1]), request->svout, 'q', Nullch, 0); sv_magic((SV *)GvIOp(request->gv[2]), request->sverr, 'q', Nullch, 0); sv_magic((SV *)GvIOp(request->gv[0]), request->svin, 'q', Nullch, 0); #else sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0); sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0); sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0); #endif request->bound = TRUE; } static void populate_env(char **envp, HV *hv) { int i; char *p, *p1; SV *sv; dTHX; hv_clear(hv); for(i = 0; ; i++) { if((p = envp[i]) == NULL) break; p1 = strchr(p, '='); assert(p1 != NULL); sv = newSVpv(p1 + 1, 0); /* call magic for this value ourselves */ hv_store(hv, p, p1 - p, sv, 0); SvSETMAGIC(sv); } } static int FCGI_IsFastCGI(FCGP_Request* request) { static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: CGI */ if (request->requestPtr->listen_sock == FCGI_LISTENSOCK_FILENO) { if (isCGI == -1) isCGI = FCGX_IsCGI(); return !isCGI; } /* A explicit socket is being used -> assume FastCGI */ return 1; } static int FCGI_Accept(FCGP_Request* request) { dTHX; if (!FCGI_IsFastCGI(request)) { static int been_here = 0; /* * Not first call to FCGI_Accept and running as CGI means * application is done. */ if (been_here) return EOF; been_here = 1; } else { FCGX_Request *fcgx_req = request->requestPtr; int acceptResult; FCGI_Finish(request); #if defined(USE_ITHREADS) MUTEX_LOCK(&accept_mutex); #endif acceptResult = FCGX_Accept_r(fcgx_req); #if defined(USE_ITHREADS) MUTEX_UNLOCK(&accept_mutex); #endif if(acceptResult < 0) { return acceptResult; } populate_env(fcgx_req->envp, request->hvEnv); if (!request->svout) { newSVrv(request->svout = newSV(0), "FCGI::Stream"); newSVrv(request->sverr = newSV(0), "FCGI::Stream"); newSVrv(request->svin = newSV(0), "FCGI::Stream"); } sv_setiv(SvRV(request->svout), INT2PTR(IV, fcgx_req->out)); sv_setiv(SvRV(request->sverr), INT2PTR(IV, fcgx_req->err)); sv_setiv(SvRV(request->svin), INT2PTR(IV, fcgx_req->in)); FCGI_Bind(request); request->accepted = TRUE; } return 0; } static void FCGI_Finish(FCGP_Request* request) { int was_bound; dTHX; if(!request->accepted) return; if (was_bound = request->bound) FCGI_UndoBinding(request); if (was_bound) FCGX_Finish_r(request->requestPtr); else FCGX_Free(request->requestPtr, 1); request->accepted = FALSE; } static int FCGI_StartFilterData(FCGP_Request* request) { return request->requestPtr->in ? FCGX_StartFilterData(request->requestPtr->in) : -1; } static FCGP_Request * FCGI_Request(GV *in, GV *out, GV *err, HV *env, int socket, int flags) { FCGX_Request* fcgx_req; FCGP_Request* req; Newz(551, fcgx_req, 1, FCGX_Request); FCGX_InitRequest(fcgx_req, socket, flags); Newz(551, req, 1, FCGP_Request); req->requestPtr = fcgx_req; SvREFCNT_inc(in); req->gv[0] = in; SvREFCNT_inc(out); req->gv[1] = out; SvREFCNT_inc(err); req->gv[2] = err; SvREFCNT_inc(env); req->hvEnv = env; return req; } static void FCGI_Release_Request(FCGP_Request *req) { SvREFCNT_dec(req->gv[0]); SvREFCNT_dec(req->gv[1]); SvREFCNT_dec(req->gv[2]); SvREFCNT_dec(req->hvEnv); FCGI_Finish(req); Safefree(req->requestPtr); Safefree(req); } static void FCGI_Init() { #if defined(USE_ITHREADS) dTHX; MUTEX_INIT(&accept_mutex); #endif FCGX_Init(); } typedef FCGX_Stream* FCGI__Stream; typedef FCGP_Request* FCGI; typedef GV* GLOBREF; typedef HV* HASHREF; MODULE = FCGI PACKAGE = FCGI PREFIX = FCGI_ BOOT: FCGI_Init(); SV * RequestX(in, out, err, env, socket, flags) GLOBREF in; GLOBREF out; GLOBREF err; HASHREF env; int socket; int flags; PROTOTYPE: ***$$$ CODE: RETVAL = sv_setref_pv(newSV(0), "FCGI", FCGI_Request(in, out, err, env, socket, flags)); OUTPUT: RETVAL int OpenSocket(path, backlog) char* path; int backlog; PROTOTYPE: $$ CODE: RETVAL = FCGX_OpenSocket(path, backlog); OUTPUT: RETVAL void CloseSocket(socket) int socket; PROTOTYPE: $ CODE: close(socket); int FCGI_Accept(request) FCGI request; PROTOTYPE: $ void FCGI_Finish(request) FCGI request; PROTOTYPE: $ void FCGI_Flush(request) FCGI request; PROTOTYPE: $ HV * GetEnvironment(request) FCGI request; PROTOTYPE: $ CODE: RETVAL = request->hvEnv; OUTPUT: RETVAL void GetHandles(request) FCGI request; PROTOTYPE: $ PREINIT: int i; PPCODE: EXTEND(sp,3); for (i = 0; i < 3; ++i) PUSHs(sv_2mortal(newRV((SV *) request->gv[i]))); int FCGI_IsFastCGI(request) FCGI request; PROTOTYPE: $ void Detach(request) FCGI request; PROTOTYPE: $ CODE: if (request->accepted && request->bound) { FCGI_UndoBinding(request); FCGX_Detach(request->requestPtr); } void Attach(request) FCGI request; PROTOTYPE: $ CODE: if (request->accepted && !request->bound) { FCGI_Bind(request); FCGX_Attach(request->requestPtr); } void LastCall(request) FCGI request; PROTOTYPE: $ CODE: FCGX_ShutdownPending(); int FCGI_StartFilterData(request) FCGI request; PROTOTYPE: $ void DESTROY(request) FCGI request; CODE: FCGI_Release_Request(request); MODULE = FCGI PACKAGE = FCGI::Stream SV * PRINT(stream, ...) FCGI::Stream stream; PREINIT: int n; STRLEN len; register char *str; bool ok = TRUE; CODE: for (n = 1; ok && n < items; ++n) { #ifdef DO_UTF8 if (DO_UTF8(ST(n)) && !sv_utf8_downgrade(ST(n), 1) && ckWARN_d(WARN_UTF8)) Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG, "FCGI::Stream::PRINT"); #endif str = (char *)SvPV(ST(n),len); if (FCGX_PutStr(str, len, stream) < 0) ok = FALSE; } if (ok && SvTRUEx(perl_get_sv("|", FALSE)) && FCGX_FFlush(stream) < 0) ok = FALSE; RETVAL = ok ? &PL_sv_yes : &PL_sv_undef; OUTPUT: RETVAL int WRITE(stream, bufsv, len, ...) FCGI::Stream stream; SV *bufsv; int len; PREINIT: int offset; char *buf; STRLEN blen; int n; CODE: offset = (items == 4) ? (int)SvIV(ST(3)) : 0; #ifdef DO_UTF8 if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1) && ckWARN_d(WARN_UTF8)) Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG, "FCGI::Stream::WRITE"); #endif buf = SvPV(bufsv, blen); if (offset < 0) offset += blen; if (len > blen - offset) len = blen - offset; if (offset < 0 || offset >= blen || (n = FCGX_PutStr(buf+offset, len, stream)) < 0) ST(0) = &PL_sv_undef; else { ST(0) = sv_newmortal(); sv_setiv(ST(0), n); } void READ(stream, bufsv, len, ...) FCGI::Stream stream; SV *bufsv; int len; PREINIT: int offset = 0; char *buf; STRLEN blen; CODE: if (items < 3 || items > 4) croak("Usage: FCGI::Stream::READ(STREAM, SCALAR, LENGTH [, OFFSET ])"); if (len < 0) croak("Negative length"); if (!SvOK(bufsv)) sv_setpvn(bufsv, "", 0); #ifdef DO_UTF8 if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1) && ckWARN_d(WARN_UTF8)) Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG, "FCGI::Stream::READ"); #endif buf = SvPV_force(bufsv, blen); if (items == 4) { offset = SvIV(ST(3)); if (offset < 0) { if (-offset > (int)blen) croak("Offset outside string"); offset += blen; } } buf = SvGROW(bufsv, len + offset + 1); if (offset > blen) Zero(buf + blen, offset - blen, char); len = FCGX_GetStr(buf + offset, len, stream); SvCUR_set(bufsv, len + offset); *SvEND(bufsv) = '\0'; (void)SvPOK_only(bufsv); SvSETMAGIC(bufsv); XSRETURN_IV(len); SV * GETC(stream) FCGI::Stream stream; PREINIT: int retval; CODE: if ((retval = FCGX_GetChar(stream)) != -1) { ST(0) = sv_newmortal(); sv_setpvf(ST(0), "%c", retval); } else ST(0) = &PL_sv_undef; SV * EOF(stream, called=0) FCGI::Stream stream; IV called; CODE: RETVAL = boolSV(FCGX_HasSeenEOF(stream)); OUTPUT: RETVAL void FILENO(stream) FCGI::Stream stream; CODE: if (FCGX_HasSeenEOF(stream) != 0) XSRETURN_UNDEF; else XSRETURN_IV(-1); bool CLOSE(stream) FCGI::Stream stream; CODE: RETVAL = FCGX_FClose(stream) != -1; OUTPUT: RETVAL fcgi2-2.4.5/perl/MANIFEST000066400000000000000000000003471477724357700146420ustar00rootroot00000000000000ChangeLog configure configure.ac configure.readme eg/echo.pl eg/remote.pl eg/threaded.pl FCGI.pm FCGI.xs fcgi_config.h.in Makefile.PL MANIFEST This list of files MANIFEST.SKIP README t/01-load.t t/02-unix_domain_socket.t typemap fcgi2-2.4.5/perl/MANIFEST.SKIP000066400000000000000000000002621477724357700154030ustar00rootroot00000000000000#!include_default ^\. \.bs$ \.c$ \.o$ ^FCGI- ^FCGI\.cfg$ ^aclocal\.m4$ ^autom4te\.cache ^config\. ^config\.log$ ^config\.status$ ^distrib$ ^fcgi_config\.h$ !^fcgi_config\.h\.in$ fcgi2-2.4.5/perl/Makefile.PL000066400000000000000000000125651477724357700154700ustar00rootroot00000000000000# $Id: Makefile.PL,v 1.33 2002/12/15 19:40:19 skimo Exp $ use 5.006; use ExtUtils::MakeMaker; use IO::File; use Config; use Cwd 'cwd'; use Getopt::Long; use File::Copy qw(copy); @h1 = qw(fastcgi.h fcgiapp.h fcgimisc.h fcgios.h); @h = (@h1, 'fcgi_config.h'); @o = qw(FCGI.o); @dist1 = qw(LICENSE); @dist2 = qw(fcgiapp.c os_unix.c os_win32.c); @dist3 = (@h1, qw(fcgi_config_x86.h)); GetOptions ("use-installed:s" => \$useinstalled); $libfound = 0; @libs = (); my $cwd = cwd(); my $devkit = "$cwd/.."; if (defined $useinstalled) { require ExtUtils::Liblist; my $libspec = $useinstalled ? "-L$useinstalled/lib " : ""; $libspec .= "-lfcgi"; my @l = MM->ext($libspec); if ($l[0] || $l[1] || $l[2]) { $prefix = "$useinstalled/include" if $useinstalled; $libfound = 1; push @libs, $libspec; } } if (!$libfound && -d "$devkit/libfcgi" && -d "$devkit/include") { # devkit if (grep { ! -f "$devkit/include/$_" } @dist3 or grep { ! -f "$devkit/libfcgi/$_" } @dist2) { warn "This appears to be a FastCGI devkit distribution, " . "but one or more FastCGI library files are missing. \n" . "Please check the integrity of the distribution.\n"; exit -1; } my $extrarules = join "\n", map { $b = $_; $b =~ s/\.c$//; my $s="$devkit/libfcgi/$b.c"; "$b\$(OBJ_EXT): $s\n\t". '$(CCCMD) $(CCCDLFLAGS) -I$(PERL_INC) $(DEFINE) '."$s\n"; } @dist2; eval 'package MY; sub postamble { $extrarules; }'; $prefix = $devkit; } $sys = $^O eq 'MSWin32' ? 'win32' : 'unix'; push @o, "fcgiapp.o", "os_$sys.o" unless $libfound; $inc = '-I.' unless $libfound; $inc .= " -I$prefix/include" if $prefix; push(@extras, CAPI => 'TRUE') if ($] >= 5.005 and $^O eq 'MSWin32' and $Config{archname} =~ /-object\b/i); push(@extras, ABSTRACT => 'Fast CGI module', AUTHOR => 'Sven Verdoolaege (skimo@kotnet.org)' ) if ($ExtUtils::MakeMaker::VERSION >= 5.4301); push @extras, META_MERGE => { 'meta-spec' => { version => 2 }, dynamic_config => 0, resources => { repository => { # this is the real repository # r/w: catagits@git.shadowcat.co.uk:fcgi2.git # r/o: git://git.shadowcat.co.uk/catagits/fcgi2.git # web: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits/fcgi2.git # this is a mirror, but can receive pull requests url => 'https://github.com/perl-catalyst/FCGI.git', web => 'https://github.com/perl-catalyst/FCGI', type => 'git', }, bugtracker => { mailto => 'bug-FCGI@rt.cpan.org', web => 'https://rt.cpan.org/Public/Dist/Display.html?Name=FCGI', }, }, } if $ExtUtils::MakeMaker::VERSION >= 6.46; push(@extras, MIN_PERL_VERSION => '5.006', ) if $ExtUtils::MakeMaker::VERSION >= 6.48; # not strictly necessary as everything is in core... #push(@extras, # CONFIGURE_REQUIRES => { # ... # }, #) if $ExtUtils::MakeMaker::VERSION >= 6.51_03; if ("$sys" eq "win32") { push @libs, ":nosearch -lws2_32"; push @extras, 'DEFINE' => '-DDLLAPI=__declspec(dllexport)'; } push @extras, 'LIBS' => [ "@libs" ], 'OBJECT' => "@o", 'INC' => $inc; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # Work around bug in previous versions of MakeMaker WriteMakefile( 'NAME' => 'FCGI', 'VERSION_FROM' => 'FCGI.pm', 'dist' => { 'COMPRESS' => 'gzip -9f', 'SUFFIX' => 'gz', 'PREOP' => '$(CP) '.join(' ', map {"../$_"} @dist1, (map {"libfcgi/$_"} @dist2), map {"include/$_"} @dist3).' $(DISTVNAME);'. '$(CP) MANIFEST MANIFEST.old;'. '$(ECHO) '. join('\\\n',@dist1,@dist2,@dist3) . '>> $(DISTVNAME)/MANIFEST', 'POSTOP' => '$(MV) MANIFEST.old MANIFEST', }, 'clean' => { FILES => 'config.cache fcgi_config.h fcgi_config.h.in fcgi_config.h.in~' . ' FCGI.c aclocal.m4 autom4te.cache config.log config.status' . ' FCGI.cfg' }, 'realclean' => { FILES => 'FCGI-*.tar.gz configure configure~ MANIFEST.SKIP.bak MANIFEST.bak Makefile.old' }, PM => {'FCGI.pm' => '$(INST_ARCHLIBDIR)/FCGI.pm'}, PREREQ_PM => {'XSLoader' => '0'}, TEST_REQUIRES => { 'Config' => 0, 'FCGI::Client' => 0.09, 'File::Temp' => 0, 'IO::Socket' => 0, 'Test::More' => 0, }, @extras, ); exit if -f 'fcgi_config.h' or $libfound; # CPAN and no installed lib found if ($sys eq "win32") { # configure will almost certainly not run on a normal NT install, # use the pregenerated configuration file print "Using prebuilt fcgi_config.h file for Windows\n"; unlink("fcgi_config.h"); my $confdir = $prefix ? "$prefix/include/" : ''; die $! unless copy("${confdir}fcgi_config_x86.h","fcgi_config.h"); } else { print "Running ./configure for you\n"; print "Please read configure.readme for information on how to run it yourself\n"; $ENV{'CC'} = $Config{'cc'}; if ( $^O eq 'android' && !$ENV{'TMPDIR'} ) { # See http://stackoverflow.com/a/15417261 require File::Spec; $ENV{'TMPDIR'} = File::Spec->tmpdir(); } system("$Config{sh} configure"); } fcgi2-2.4.5/perl/README000066400000000000000000000051541477724357700143720ustar00rootroot00000000000000$Id: README,v 1.7 2001/10/04 08:08:34 skimo Exp $ Copyright (c) 1996 Open Market, Inc. See the file "LICENSE" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. Copyright (c) 1996-1998 Sven Verdoolaege No additional restrictions/warranties. This is a Fast CGI module for perl. It's based on the FCGI module that comes with Open Market's FastCGI Developer's Kit, but does not require you to recompile perl. It even no longer requires perl to be compiled with sfio. To compile with sfio you'll need at least perl 5.003_02 and you'll have to have configured it with eg './Configure -Duseperlio -Dusesfio'. (See the INSTALL file that comes with the perl distribution.) To compile without sfio you'll need an even more recent perl version. (perl 5.004 and up should be fine.) See http://www.fastcgi.com/ for more information about fastcgi. Lincoln D. Stein's perl CGI module also contains some information about fastcgi programming. See echo.fpl for an example on how to use this module. You need to install gcc, gnu make, m4, aclocal, autoconf, automake and libtool packages. And to install, do the usual ./distrib perl Makefile.PL make make install If you want to use the (experimental) pure perl version, that doesn't require a compiler and currently only works on Unix, you have to pass the --pure-perl option as in "perl Makefile.PL --pure-perl". Note that the pure version does not support Window's Named Pipes. Support for Named Pipes is not a requirement of the FastCGI specification. Named Pipes are used by mod_fastcgi and the FastCGI application library as a replacement for Unix sockets. mod_fastcgi uses Named Pipes on Windows (Unix sockets on Unix) by default (see the mod_fastcgi docs for more information). If you want the module to use a previously installed fcgi library instead of the included files, use the --use-installed option, optionally followed by the name of the directory in which it can be found. To configure the library Makefile.PL will run ./configure . You may want to run it yourself beforehand because its findings may not always be correct. The configure.readme file describes how to run ./configure (and only that). If you're on a solaris system and your installed fcgi library is 2.02b or earlier, you'll probably want to use the included files. The old interface of the FCGI module installs die and warn handlers that merely print the error/warning to STDERR (the default handlers print directly to stderr, which isn't redirected in the non sfio case). I'm not very happy with the result. Suggestions welcome. Sven Verdoolaege skimo@kotnet.org fcgi2-2.4.5/perl/configure.ac000066400000000000000000000005621477724357700157760ustar00rootroot00000000000000dnl $Id: configure.ac,v 1.9 2001/09/22 09:30:45 skimo Exp $ dnl dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during the build dnl to configure the system for the local environment. AC_INIT AM_CONFIG_HEADER([fcgi_config.h]) AC_PROG_CC AC_PROG_CPP FCGI_COMMON_CHECKS AC_OUTPUT fcgi2-2.4.5/perl/configure.readme000066400000000000000000000173271477724357700166570ustar00rootroot00000000000000$Id: configure.readme,v 1.1 1999/02/13 05:26:47 roberts Exp $ Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. fcgi2-2.4.5/perl/distrib000077500000000000000000000001411477724357700150670ustar00rootroot00000000000000#!/bin/sh # $Id: distrib,v 1.1 2001/09/21 22:38:59 skimo Exp $ aclocal -I .. autoheader autoconf fcgi2-2.4.5/perl/eg/000077500000000000000000000000001477724357700141005ustar00rootroot00000000000000fcgi2-2.4.5/perl/eg/echo.pl000066400000000000000000000031111477724357700153470ustar00rootroot00000000000000#!/usr/bin/perl # echo-perl -- # # Produce a page containing all FastCGI inputs # # Copyright (c) 1996 Open Market, Inc. # # See the file "LICENSE" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # $Id: echo.PL,v 1.2 2000/12/14 13:46:23 skimo Exp $ # # Changed by skimo to demostrate autoflushing 1997/02/19 # use FCGI; use strict; sub print_env { my($label, $envp) = @_; print("$label:
\n
\n");
    my @keys = sort keys(%$envp);
    foreach my $key (@keys) {
        print("$key=$$envp{$key}\n");
    }
    print("

\n"); } my %env; my $req = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%env); my $count = 0; while($req->Accept() >= 0) { print("Content-type: text/html\r\n\r\n", "FastCGI echo (Perl)\n", "

FastCGI echo (Perl)

\n", "Request number ", ++$count, "

\n"); my $len = 0 + $env{'CONTENT_LENGTH'}; if($len == 0) { print("No data from standard input.

\n"); } else { print("Standard input:
\n

\n");
        for(my $i = 0; $i < $len; $i++) {
            my $ch = getc(STDIN);
            if($ch eq "") {
                print("Error: Not enough bytes received ",
                      "on standard input

\n"); last; } print($ch); } print("\n

\n"); } print_env("Request environment", \%env); print "More on its way ... wait a few seconds\n
\n
"; $req->Flush(); sleep(3); print_env("Initial environment", \%ENV); $req->Finish(); } fcgi2-2.4.5/perl/eg/remote.pl000066400000000000000000000014741477724357700157360ustar00rootroot00000000000000#!/usr/bin/perl # An example of using a remote script with an Apache webserver. # Run this Perl program on "otherhost" to bind port 8888 and wait # for FCGI requests from the webserver. ## Sample Apache configuration on the webserver to refer to the ## remote script on "otherhost" # # AddHandler fastcgi-script fcgi # FastCgiExternalServer /path-to/cgi-bin/external.fcgi -host otherhost:8888 # # Access the URL: http://webserver/cgi-bin/external.fcgi # Contributed by Don Bindner use FCGI; my $socket = FCGI::OpenSocket( ":8888", 5 ); my $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket ); my $count; while( $request->Accept() >= 0 ) { print "Content-type: text/html\r\n\r\n"; print ++$count; } FCGI::CloseSocket( $socket ); fcgi2-2.4.5/perl/eg/threaded.pl000066400000000000000000000021751477724357700162220ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use threads; use threads::shared; use FCGI qw[]; use IO::Handle qw[]; use constant THREAD_COUNT => 5; my @count : shared = (0, (0) x THREAD_COUNT); sub worker { my $k = shift; my %env; my $in = IO::Handle->new; my $out = IO::Handle->new; my $err = IO::Handle->new; my $request = FCGI::Request($in, $out, $err, \%env); while ($request->Accept >= 0) { print $out "Content-type: text/html\r\n", "\r\n", "FastCGI Hello! (multi-threaded perl, fcgiapp library)", "

FastCGI Hello! (multi-threaded perl, fcgiapp library)

", "Request counts for ", THREAD_COUNT ," threads ", "running on host $env{SERVER_NAME}"; { lock(@count); ++$count[$k]; for(my $i = 1; $i <= THREAD_COUNT; $i++) { print $out $count[$i]; print $out " "; } } $request->Flush; sleep(1); } } $_->join for map { threads->create(\&worker, $_) } 1..THREAD_COUNT; fcgi2-2.4.5/perl/t/000077500000000000000000000000001477724357700137505ustar00rootroot00000000000000fcgi2-2.4.5/perl/t/01-load.t000066400000000000000000000001321477724357700152660ustar00rootroot00000000000000use Test; BEGIN { plan tests => 1 }; use FCGI; ok(1); # If we made it this far, we're ok. fcgi2-2.4.5/perl/t/02-unix_domain_socket.t000066400000000000000000000050731477724357700202430ustar00rootroot00000000000000use strict; use warnings; use Config; use FCGI; use FCGI::Client; use File::Temp qw(tempfile); use IO::Socket; use Test::More 0.88; my $can_fork = $Config{d_fork} || ( ($^O eq 'MSWin32' || $^O eq 'NetWare') and $Config{useithreads} and $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/ ); if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bSocket\b/) { plan skip_all => 'Socket extension unavailable'; } elsif ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bIO\b/) { plan skip_all => 'IO extension unavailable'; } elsif ($^O eq 'os2') { eval { IO::Socket::pack_sockaddr_un('/foo/bar') || 1 }; if ($@ !~ /not implemented/) { plan skip_all => 'compiled without TCP/IP stack v4'; } } elsif ($^O =~ m/^(?:qnx|nto|vos)$/ ) { plan skip_all => "UNIX domain sockets not implemented on $^O"; } elsif (! $can_fork) { plan skip_all => 'no fork'; } elsif ($^O eq 'MSWin32') { if ($ENV{CONTINUOUS_INTEGRATION}) { # https://github.com/Perl/perl5/issues/17429 plan skip_all => 'Skipping on Windows CI'; } else { # https://github.com/Perl/perl5/issues/17575 if (! eval { socket(my $sock, PF_UNIX, SOCK_STREAM, 0) }) { plan skip_all => "AF_UNIX unavailable or disabled on this platform" } } } my (undef, $unix_socket_file) = tempfile(); my $fcgi_socket = FCGI::OpenSocket($unix_socket_file, 5); # Client if (my $pid = fork()) { my $right_ret = <<'END'; Content-Type: text/plain END my ($stdout, $stderr) = client_request($unix_socket_file); is($stdout, $right_ret."0\n", 'Test first round on stdout.'); is($stderr, undef, 'Test first round on stderr.'); ($stdout, $stderr) = client_request($unix_socket_file); is($stdout, $right_ret."1\n", 'Test second round on stdout.'); is($stderr, undef, 'Test second round on stderr.'); # Server } elsif (defined $pid) { my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $fcgi_socket); # Only two cycles. my $count = 0; while ($count < 2 && $request->Accept() >= 0) { print "Content-Type: text/plain\n\n"; print $count++."\n"; } exit; } else { die $!; } # Cleanup. FCGI::CloseSocket($fcgi_socket); unlink $unix_socket_file; done_testing; sub client_request { my $unix_socket_file = shift; my $sock = IO::Socket::UNIX->new( Peer => $unix_socket_file, ) or die $!; my $client = FCGI::Client::Connection->new(sock => $sock); my ($stdout, $stderr) = $client->request({ REQUEST_METHOD => 'GET', }, ''); return ($stdout, $stderr); } fcgi2-2.4.5/perl/typemap000066400000000000000000000005631477724357700151130ustar00rootroot00000000000000TYPEMAP FCGI T_PTROBJ FCGI::Stream T_PTROBJ GLOBREF T_GLOBREF HASHREF T_HASHREF INPUT T_GLOBREF if (SvROK($arg) && isGV(SvRV($arg))) { $var = (GV*)SvRV($arg); } else croak(\"$var is not a GLOB reference\"); T_HASHREF if (SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVHV) { $var = (HV*)SvRV($arg); } else croak(\"$var is not a reference to a hash\");