randy 97/01/19 11:22:34
Modified: htdocs/manual/mod mod_fastcgi.html
Log:
Update docs. Note that I added a comment about the included patch
being reversed and added the <!--#include for our footer.
Submitted by: Stanley Gambarin
Revision Changes Path
1.5 +321 -76 apache/htdocs/manual/mod/mod_fastcgi.html
Index: mod_fastcgi.html
===================================================================
RCS file: /export/home/cvs/apache/htdocs/manual/mod/mod_fastcgi.html,v
retrieving revision 1.4
retrieving revision 1.5
diff -C3 -r1.4 -r1.5
*** mod_fastcgi.html 1996/12/02 18:14:05 1.4
--- mod_fastcgi.html 1997/01/19 19:21:51 1.5
***************
*** 12,84 ****
</head>
<body>
! <!--#include virtual="header.html" -->
! <h1>Module mod_fastcgi</h1>
- This module is contained in the <code>mod_fastcgi.c</code> file. It
- provides a high-performance alternative to CGI for writing Web server
- applications in a variety of languages, including Perl, C, C++,
- Java, and Python.<p>
-
- Any request for a file with the MIME type
- <code>application/x-httpd-fcgi</code> will be processed by
- <code>mod_fastcgi</code>. For the request to succeed, the server's
- configuration must have started the application (executable file)
- using the <code>AppClass</code> directive.
- <p>This module is included optionally in Apache 1.2 and later.
! <h2>Summary</h2>
! FastCGI is a high-performance alternative to CGI.
! FastCGI gets its speed by having
! the Web server keep the application processes running between requests. So,
! unlike CGI, you do not have the overhead of starting up a new process and
! doing application initialization (e.g. connecting
! to a database) each time somebody requests a document. The
! processes start up with the Web server and keep on running.<p>
FastCGI applications communicate with a Web server using a simple
communications protocol. A single full-duplex connection communicates
the environment variables and <code>stdin</code> data to the
application, and <code>stdout</code> and <code>stderr</code> data to
! the Web server.<p>
For more information on FastCGI, including freely available FastCGI
server modules and application libraries, go to the <A
! HREF="http://www.fastcgi.com/">FastCGI home page
(http://www.fastcgi.com/)</A>.<p>
<h2>Directives</h2>
<ul>
<li>
<a HREF="#AppClass">AppClass</a>
<li>
<a HREF="#FastCgiIpcDir">FastCgiIpcDir</a>
</ul>
<hr>
<A name="AppClass"><h2>AppClass</h2></A>
! <strong>Syntax:</strong> AppClass exec-path
<em>[-processes N] [-listen-queue-depth N]
[-restart-delay N] [-priority N]
[-initial-env name=value]<br></em>
<strong>Context:</strong> srm.conf<br>
<strong>Module:</strong> mod_fastcgi<p>
The <code>AppClass</code> directive starts one or more FastCGI
application processes, using the executable file
! <code>exec-path</code>. <code>mod_fastcgi</code> will restart these
! processes should they die.<p>
!
! When a client requests the file <code>exec-path</code>,
! the request is handled first by the <code>mod_fastcgi</code> module.
! <code>mod_fastcgi</code> communicates the request to a process
! in the application class, which generates the response.
! <code>mod_fastcgi</code> relays this response back to the client.<p>
The optional parameters to the <code>AppClass</code> directive
are as follows:
--- 12,165 ----
</head>
<body>
! <IMG SRC="http://www.apache.org/images/apache_sub.gif" ALT="">
! <!--/%hypertext -->
! <h1>Module mod_fastcgi</h1>
! This module is contained in the <code>mod_fastcgi.c</code> file, and
! is not compiled into the server by default.
! To use <code>mod_fastcgi</code> you first copy
! <code>src/mod_fastcgi.c</code> from this kit into your Apache server's
! source directory. Then you add the following line to the server build
! Configuration file:
! <pre>
! Module fastcgi_module mod_fastcgi.o
! </pre>
! <p>
! FastCGI provides a high-performance alternative to CGI for writing Web
! server applications in a variety of languages, including Perl, C, C++,
! Java, and Python. FastCGI gets its speed by having keeping
! application processes running between requests. So, unlike CGI, you do
! not have the overhead of starting up a new process and doing
! application initialization (e.g. connecting to a database) each time
! somebody requests a document.<p>
FastCGI applications communicate with a Web server using a simple
communications protocol. A single full-duplex connection communicates
the environment variables and <code>stdin</code> data to the
application, and <code>stdout</code> and <code>stderr</code> data to
! the Web server. An application can reside on a different
! machine from the Web server, allowing applications to scale beyond
! a single box and providing easier integration with existing systems.<p>
For more information on FastCGI, including freely available FastCGI
server modules and application libraries, go to the <A
! HREF="http://www.fastcgi.com/">FastCGI website
(http://www.fastcgi.com/)</A>.<p>
+
+ <h2>Summary</h2>
+
+ In order to configure FastCGI applications, you need a combination of
+ <code>mod_fastcgi</code> directives and other directives provided by
+ the Apache server.<p>
+
+ You use the
+ <code>AppClass</code> directive to start the FastCGI
+ applications that you want to be managed by this Web server. The
+ applications are managed in the sense that the server (a) logs an
+ error message when a managed process dies and (b) attempts to
+ restart managed processes that die.<p>
+
+ You use one or both of the
+ <code>AppClass</code> and <code>ExternalAppClass</code> directives
+ to define an association between a pathname and the connection
+ information for a FastCGI application. Connection
+ information is either the pathname of
+ a Unix domain socket or the IP address and port number of a
+ TCP port. The difference between the two directives
+ is that a single <code>AppClass</code>
+ directive both starts an application and sets up the association
+ for communicating with it, while <code>ExternalAppClass</code> only
+ defines the association. In the case of <code>AppClass</code>,
+ the pathname used in the association is always the pathname of the
+ application's executable; with <code>ExternalAppClass</code> the pathname
+ is arbitrary.<p>
+
+ In order for an HTTP request to be processed by <code>mod_fastcgi</code>
+ the request's handler must be <code>fastcgi-handler</code>
+ or the request's MIME type must be
+ <code>application/x-httpd-fcgi</code>. Apache
+ provides several ways to set the handler and MIME type of
+ a request:
+
+ <ul>
+ <li> <code>SetHandler</code> (in the context of a <code>Location</code>
+ or <code>Directory</code> section or <code>.htaccess</code>
+ file) can associate the handler
+ <code>fastcgi-handler</code> with
+ a specific file, or all the files in a directory.<p>
+ <li> <code>AddHandler</code> can associate the handler
+ <code>fastcgi-handler</code> with
+ files based on file extension.<p>
+ <li> <code>ForceType</code> (in the context of a <code>Location</code>
+ or <code>Directory</code> section or <code>.htaccess</code>
+ file) can associate the MIME type
+ <code>application/x-httpd-fcgi</code> with
+ a specific file, or all the files in a directory.<p>
+ <li> <code>AddType</code> can associate the MIME type
+ <code>application/x-httpd-fcgi</code> with
+ files based on file extension.<p>
+ </ul>
+
+ Refer to the documentation for <code>mod_mime</code>
+ for more information on these directives, and to the documentation
+ of the Apache core features for information on <code>Location</code>
+ and <code>Directory</code> sections.<p>
+
+ <code>mod_fastcgi</code> handles requests as follows:
+
+ <ul>
+ <li> <code>mod_fastcgi</code> retrieves the connection
+ information associated with the requested pathname.
+ <li> if no connection information is associated with the pathname,
+ the server returns <code>404 Not Found</code>.
+ <li> <code>mod_fastcgi</code> connects to the FastCGI application process.
+ <li> if the connection attempt fails,
+ the server returns <code>500 Server Error</code>.
+ <li> <code>mod_fastcgi</code> transmits the request to the
+ FastCGI application process, which generates a response.
+ <li> <code>mod_fastcgi</code> receives the application's
+ response and transforms
+ it into an HTTP response. The server sends
+ this response back to the client.
+ </ul>
+
+ The configuration examples below show some valid ways to configure
+ <code>mod_fastcgi</code>.
+
+
<h2>Directives</h2>
<ul>
<li>
<a HREF="#AppClass">AppClass</a>
<li>
+ <a HREF="#ExternalAppClass">ExternalAppClass</a>
+ <li>
<a HREF="#FastCgiIpcDir">FastCgiIpcDir</a>
</ul>
<hr>
<A name="AppClass"><h2>AppClass</h2></A>
! <strong>Syntax:</strong> AppClass path-name
<em>[-processes N] [-listen-queue-depth N]
[-restart-delay N] [-priority N]
+ [-port N] [-socket sock-name]
[-initial-env name=value]<br></em>
<strong>Context:</strong> srm.conf<br>
<strong>Module:</strong> mod_fastcgi<p>
The <code>AppClass</code> directive starts one or more FastCGI
application processes, using the executable file
! <code>path-name</code>. Should any of these processes die,
! <code>mod_fastcgi</code> will write an error log entry and restart
! the faulty process.<p>
The optional parameters to the <code>AppClass</code> directive
are as follows:
***************
*** 113,118 ****
--- 194,220 ----
values are not allowed.<p>
<li>
+ <B>port:</B> TCP port number that the FastCGI application
+ process will listen on. Using this
+ option makes the application accessible from the another
+ machine (as well as from this one.) An argument to the
+ <code>port</code> option should be a number
+ between 1 and 65535.<p>
+
+ <li>
+ <B>socket:</B> pathname of the Unix domain socket that
+ the FastCGI application process will listen on. The
+ module creates this socket within the directory
+ specified by <code>FastCgiIpcDir</code>. Using this option
+ makes the application accessible to other Web servers or
+ applications (e.g. <code>cgi-fcgi</code>) on the same machine
+ or via multiple pathnames on this Web server
+ (using <code>ExternalAppClass</code>.)
+ If neither the <code>-socket</code> nor the <code>-port</code>
+ options are given, the Web server generates a Unix domain
+ socket name itself.<p>
+
+ <li>
<B>initial-env:</B> a name-value pair in the initial environment
passed to the application processes. The argument has the form
<code>name=value</code>, with no whitespace allowed.
***************
*** 121,129 ****
initial environment is empty (no name-value pairs.)<p>
</ul>
<p>
! Errors possible in the <code>AppClass</code>
directive include syntax errors, arguments out of range, and the file
! <code>exec-path</code> being non-existent or not executable.<p>
<A name="FastCgiIpcDir"><h2>FastCgiIpcDir</h2></A>
--- 223,283 ----
initial environment is empty (no name-value pairs.)<p>
</ul>
<p>
! The <code>-socket</code> and <code>-port</code> options are
! mutually exclusive.
! <code>path-name</code>
! must not equal the <code>path-name</code> supplied to an
! earlier <code>AppClass</code> or <code>ExternalAppClass</code>
! directive. Other errors
! possible in the <code>AppClass</code>
directive include syntax errors, arguments out of range, and the file
! <code>path-name</code> being non-existent or not executable.
! <p>
!
!
! <A name="ExternalAppClass"><h2>ExternalAppClass</h2></A>
! <strong>Syntax:</strong> ExternalAppClass path-name
! <em>[-host host:port]
! [-socket sock-name]<br></em>
! <strong>Context:</strong> srm.conf<br>
! <strong>Module:</strong> mod_fastcgi<p>
!
! The <code>ExternalAppClass</code> directive provides a connection a
! FastCGI application process that is listening to a specified TCP port
! or a UNIX domain socket. <code>ExternalAppClass</code> is most commonly
! used to communicate with a FastCGI application running on a different
! machine. This directive implies nothing about how the FastCGI application
! is managed (started, restarted, etc.). <code>path-name</code>
! simply provides an identifier for the connection.<p>
!
! The optional parameters to the <code>ExternalAppClass</code> directive
! are as follows:
! <ul>
! <li>
! <B>host:</B> the host and port of the machine on which a FastCGI
! process is running. The argument should be a single string,
! containing the host IP address (either as
! a hostname to be resolved via DNS or as a "dotted quad"
! such as "123.96.248.182"), followed by a colon (":"),
! followed by a port number
! (an integer between 1 and 65535.)<p>
!
! <li>
! <B>socket:</B> full pathname of a Unix Domain socket accessible
! from this Web server. Most commonly, the FastCGI
! application listening on this socket would have been started by an
! <code>AppClass</code> directive with the <code>-socket</code>
! option.<p>
! </ul>
! <p>
! Exactly one of the <code>port</code> and <code>socket</code>
! parameters must be supplied. <code>path-name</code>
! must not equal the <code>path-name</code> supplied to an
! earlier <code>AppClass</code> or <code>ExternalAppClass</code>
! directive. Other
! errors possible in the <code>ExternalAppClass</code>
! directive include syntax errors and arguments out of range.
! <p>
<A name="FastCgiIpcDir"><h2>FastCgiIpcDir</h2></A>
***************
*** 145,151 ****
To avoid this problem place a <code>FastCgiIpcDir</code> directive
before the <code>AppClass</code> directives in your server
! configuration. Specify a directory that's readable, writable,
and searchable by the account you use for your Web server, but
otherwise not accessible to anyone.<p>
--- 299,305 ----
To avoid this problem place a <code>FastCgiIpcDir</code> directive
before the <code>AppClass</code> directives in your server
! configuration. Specify a directory that's readable, writeable,
and searchable by the account you use for your Web server, but
otherwise not accessible to anyone.<p>
***************
*** 177,183 ****
corruption problem. A corrupted error log makes it difficult to
debug problems on your Web server. You should apply the following
patch to Apache 1.1.1 in order to eliminate the possibility of
! this problem:
<pre>
% diff -c alloc.c alloc.c.orig
*** alloc.c Mon Sep 23 17:45:34 1996
--- 331,338 ----
corruption problem. A corrupted error log makes it difficult to
debug problems on your Web server. You should apply the following
patch to Apache 1.1.1 in order to eliminate the possibility of
! this problem: (Note: This patch is reversed and must be applied
! with a <em>-R</em> flag to <em>patch</em>)
<pre>
% diff -c alloc.c alloc.c.orig
*** alloc.c Mon Sep 23 17:45:34 1996
***************
*** 219,255 ****
<li>
The <code>ScriptAlias</code> directive takes priority over the
! <code>AddType</code> directive; a file located in a directory that
! is the target of <code>ScriptAlias</code>directive has type
! <code>application/x-httpd-cgi</code> and is handled by
! <code>mod_cgi</code>. So don't put FastCGI applications in your
! <code>/cgi-bin/</code> directory -- they won't work properly!<p>
!
! <li>
! <code>mod_fastcgi</code> becomes confused if you put a slash
! at the end of your <code>DocumentRoot</code>. The symptom
! is that the request handler won't find the applications that
! you have defined using <code>AppClass</code>.<p>
!
! <li>
! <code>mod_fastcgi</code> does not know about environment
! variables defined by the optional module <code>mod_env</code>.
! Use the <code>-initial-env</code> option to
<code>AppClass</code>.<p>
<li>
! <code>mod_fastcgi</code> does not implement TCP/IP
! connections to FastCGI applications, only Unix Domain socket
! connections. To connect to remote FastCGI applications
! run the <code>cgi-fcgi</code> program as a CGI script.
! See the
! <a
href="http://www.fastcgi.com/kit/doc/cgi-fcgi.1"><code>cgi-fcgi</code>
! manpage</a> for more information.<p>
</ul>
! <h2>Example</h2>
What follows is a minimal httpd.conf for Apache 1.1.1 and FastCGI
Developer's Kit 1.5. Use this configuration for initial testing
--- 374,474 ----
<li>
The <code>ScriptAlias</code> directive takes priority over the
! <code>AddType</code> directive: A file located in a directory that
! is the target of <code>ScriptAlias</code> is always
! handled by the handler <code>cgi-handler</code> (<code>mod_cgi</code>.)
! So don't put FastCGI applications in a <code>ScriptAlias</code>
! directory -- the applications won't work properly!<p>
!
! <li>
! The optional module <code>mod_env</code> provides two directives
! (<code>PassEnv</code> and <code>SetEnv</code>) that are designed
! for passing environment variables to CGI scripts. These
! directives also work for passing per-request environment variables
! to FastCGI applications. To pass initial environment variables
! you must use the <code>-initial-env</code> option to
<code>AppClass</code>.<p>
<li>
! <code>mod_fastcgi</code> does not implement the Authorizer or Filter
! roles described in the FastCGI specification. However, you can
! approximate the Filter role using Apache's <code>Action</code>
! directive to route requests to a FastCGI Responder.
! See the documentation for <code>mod_actions</code> for information
! on the <code>Action</code> directive.<p>
!
! <li>
! See the <a href="README"><code>README</code></a> file for a complete
! list of known bugs in this version of <code>mod_fastcgi</code>.<p>
</ul>
! <h2>Notes on CGI response headers</h2>
!
! You may have noticed that <code>mod_fastcgi</code> makes no
! provision for non-parsed-header scripts. There's a good reason for
! this: <code>mod_fastcgi</code> does not restrict the functionality
! of parsed-header scripts in any way. A parsed-header script
! can do anything that an "nph" script can do, and some things
! that an "nph" script can't do.<p>
!
! To exploit the power of parsed-header scripts you need
! to understand the three standard CGI/1.1
! response headers:<p>
!
! <ul>
! <li><code>Status:</code> an HTTP status line, e.g.
! <code>"206 Partial Content"</code>.<p>
! <li><code>Location:</code> a URL or absolute path to content.<p>
! <li><code>Content-type:</code> the media type of script-generated
! response content.<p>
! </ul>
!
! Only certain combinations of these headers make sense:<p>
!
! <ul>
! <li>Each header may appear at most once in a response.<p>
! <li>The <code>Status</code> and <code>Location</code> headers
! are mutually exclusive, i.e. at most one may appear
! in a response. If neither appears, the effect
! is as if <code>Status: 200 OK</code> appeared, and
! a <code>Content-type</code> header must appear.<p>
! <li>A <code>Content-type</code> header means that
! the script will generate response content (e.g.
! an HTML document), and the
! absence of a <code>Content-type</code> header means
! that the script will not generate response content.<p>
! <li>A <code>Location</code> header may only be generated
! in response to a <code>GET</code> or <code>HEAD</code>
! request.<p>
! </ul>
!
! The <code>Location</code> header specifies a redirect.
! There are two kinds. If the <code>Location</code> value
! is a full URL (e.g. <code>http://www.fastcgi.com/servers/apache</code>),
! the effect is to generate an HTTP 302 response. If the
! <code>Location</code> value is an absolute path
! (e.g. <code>/servers/apache</code>), the effect is to
! execute a recursive request for that path within the server,
! and return the response of that request.<p>
!
! <code>Location</code> is optionally accompanied by
! <code>Content-type</code>. If the <code>Location</code> value
! is a full URL and no <code>Content-type</code> is specified,
! the Web server provides standardized content (of type
! <code>text/html</code>); if a <code>Content-type</code> is specified,
! the script provides the content. If the <code>Location</code> value
! is an absolute path then script-generated <code>Content-type</code> and
! content are ignored.<p>
!
! <code>mod_fastcgi</code> performs buffering of script-provided
! content, but content is not allowed to linger in buffers
! for more than a fraction of a second. Therefore "server push"
! scripts work correctly.<p>
!
!
! <h2>Configuration example</h2>
What follows is a minimal httpd.conf for Apache 1.1.1 and FastCGI
Developer's Kit 1.5. Use this configuration for initial testing
***************
*** 275,288 ****
Save the resulting file as <code>$APACHE/conf/httpd.conf</code>.<p>
<li>
! Build Apache 1.1.1 with mod_fastcgi. This creates the
<code>httpd</code> executable.<p>
Build the FastCGI Developer's Kit 1.5. This creates the
<code>echo</code> executable that you are going to run as a
! FastCGI application, and makes the <code>echo.fcg</code> link
! to this application. This link gives it a distinctive MIME type
! so that <code>mod_fastcgi</code> will handle it.<p>
<li>
In a shell, cd to <code>$APACHE</code> and start httpd:
--- 494,505 ----
Save the resulting file as <code>$APACHE/conf/httpd.conf</code>.<p>
<li>
! Build Apache 1.1.1 with <code>mod_fastcgi</code>. This creates the
<code>httpd</code> executable.<p>
Build the FastCGI Developer's Kit 1.5. This creates the
<code>echo</code> executable that you are going to run as a
! FastCGI application.<p>
<li>
In a shell, cd to <code>$APACHE</code> and start httpd:
***************
*** 293,303 ****
<li>
Use a browser to access the URL
<pre>
! http://$YOUR_HOST:5556/examples/echo.fcg
</pre>
where <code>$YOUR_HOST</code> is the IP address of the host
running httpd. Look for <code>STATE=TEXAS</code> in the
! initial environment that <code>echo.fcg</code> displays.<p>
</ol>
<pre>
--- 510,521 ----
<li>
Use a browser to access the URL
<pre>
! http://$YOUR_HOST:5556/examples/echo
</pre>
where <code>$YOUR_HOST</code> is the IP address of the host
running httpd. Look for <code>STATE=TEXAS</code> in the
! initial environment that <code>echo</code> displays. The
! request counter should increment each time you reload the page.<p>
</ol>
<pre>
***************
*** 310,315 ****
--- 528,540 ----
# Not starting httpd as root, so Port must be larger than 1023
Port 5556
+ # This is what you'd add to the config if the server is to be
+ # started as root. Don't do this until you've verified that the
+ # server works when started as non-root! Don't use user/group nobody;
+ # define a new user and group specifically for running the server.
+ # User httpd
+ # Group httpd
+
# Configure just one idle httpd child process, to simplify debugging
StartServers 1
MinSpareServers 1
***************
*** 322,342 ****
ScoreBoardFile logs/httpd.scoreboard
# Tell httpd where to get documents
- # XXX: No slash allowed at the end of DocumentRoot
DocumentRoot $FASTCGI
- # Tell Apache that mod_fastcgi should handle files ending in .fcg
- AddType application/x-httpd-fcgi .fcg
-
# This is how you'd place the Unix-domain socket files in the logs
# directory (you'd probably want to create a subdirectory for them.)
! # Don't do this until you've verified that the server works with
! # the socket files stored locally, in /tmp.
# FastCgiIpcDir $APACHE/logs
! # Start the echo.fcg application (echo.fcg is a sym-link to echo,
! # created by $FASTCGI/examples/Makefile.)
! AppClass $FASTCGI/examples/echo.fcg -initial-env STATE=TEXAS
# End of httpd.conf
</pre>
--- 547,588 ----
ScoreBoardFile logs/httpd.scoreboard
# Tell httpd where to get documents
DocumentRoot $FASTCGI
# This is how you'd place the Unix-domain socket files in the logs
# directory (you'd probably want to create a subdirectory for them.)
! # Don't do this until you've verified that everything works with
! # the socket files stored locally, in /tmp!
# FastCgiIpcDir $APACHE/logs
! # Start the echo app
! AppClass $FASTCGI/examples/echo -initial-env STATE=TEXAS
!
! # Have mod_fastcgi handle requests for the echo app
! # (otherwise the server will return the app's binary as a file!)
! <Location /examples/echo>
! SetHandler fastcgi-script
! </Location>
!
! # Start a FastCGI application that's accessible from other machines
! AppClass $FastCGI/examples/echo.fcg -port 8978
! <Location /examples/echo.fcg>
! SetHandler fastcgi-script
! </Location>
!
! # Connect to "remote" app started above. Since the app is actually
! # local, communication will take place using TCP loopback.
! # To test true remote operation, start one copy of this
! # Web server on one machine, then start another copy with
! # "localhost" in the line below changed to the host name of the first
machine.
! ExternalAppClass $FASTCGI/examples/remote-echo -host localhost:8978
! <Location /examples/remote-echo>
! SetHandler fastcgi-script
! </Location>
!
! # This is how you'd have mod_fastcgi handle any request for a file
! # whose name ends in .fcg:
! # AddHandler fastcgi-script fcg
# End of httpd.conf
</pre>
***************
*** 344,347 ****
<!--#include virtual="footer.html" -->
</BODY>
</HTML>
-
--- 590,592 ----
Modified: src CHANGES mod_fastcgi.c
Log:
Update the supplied mod_fastcgi.c to the current 1.4.3 version.
Submitted by: Stanley Gambarin
Revision Changes Path
1.124 +3 -0 apache/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.123
retrieving revision 1.124
diff -C3 -r1.123 -r1.124
*** CHANGES 1997/01/16 17:22:54 1.123
--- CHANGES 1997/01/19 19:22:31 1.124
***************
*** 1,5 ****
--- 1,8 ----
Changes with Apache 1.2b5
+ *) Update mod_fastcgi.c to version 1.4.3 as distributed by
+ Open Market.
+
*) Fixed bug in modules/Makefile that wouldn't allow building in more
than one subdirectory (or cleaning, either). [Jeremy Laidman]
1.5 +1188 -502 apache/src/mod_fastcgi.c
Index: mod_fastcgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_fastcgi.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C3 -r1.4 -r1.5
*** mod_fastcgi.c 1996/12/28 00:04:52 1.4
--- mod_fastcgi.c 1997/01/19 19:22:31 1.5
***************
*** 1,8 ****
/*
* mod_fastcgi.c --
*
! * Apache server module for FastCGI
! *
*
*
* Copyright (c) 1995-1996 Open Market, Inc.
--- 1,7 ----
/*
* mod_fastcgi.c --
*
! * Apache server module for FastCGI.
*
*
* Copyright (c) 1995-1996 Open Market, Inc.
***************
*** 21,56 ****
*/
/*
- * TODO list
- *
- * * Test cleanup of FastCGI processes more thoroughly.
- * We still have a problem getting them all killed off
- * for the config file re-read. How many processes can
- * be killed off (reliably) in 3 seconds? This would
- * work much better if the process manager had its own
- * process group.
- *
- * * On Solaris, the process manager gets killed when you re-link
- * the server, so you always need to move the executable out of the
- * build directory before you use it. Is there any way the process
- * manager can handle this exception?
- *
- * * Implement application timeouts. Request timeouts are done.
- *
- * * Investigate using more Apache core code for response header
- * generation, etc.
- *
- * * Investigate using the optional module mod_env for setting
- * initial environment variables (suggested by Michael Smith).
- *
- * * [Major] Support running FastCGI apps without AppClass.
- * Design notes below. A side-effect of implementing
- * this feature is that the module will correctly handle
- * a DocumentRoot with a slash at the end.
- *
- */
-
- /*
* Module design notes.
*
* 1. Restart cleanup.
--- 20,25 ----
***************
*** 225,230 ****
--- 194,219 ----
free(ptr);
}
}
+
+ static char *StringCopy(char *str)
+ {
+ int strLen = strlen(str);
+ char *newString = Malloc(strLen + 1);
+ memcpy(newString, str, strLen);
+ newString[strLen] = '\000';
+ return newString;
+ }
+
+ static char *StrError(int errorCode)
+ {
+ /* No strerror prototype on SunOS? */
+ char *msg = (char *) strerror(errno);
+ if(msg == NULL) {
+ msg = "errno out of range";
+ }
+ ASSERT(strlen(msg) < 100);
+ return msg;
+ }
/*-----------------------Include fastcgi.h definitions ----------*/
***************
*** 262,268 ****
* The length of the FastCGI packet header.
*/
#define FCGI_HEADER_LEN 8
-
#define FCGI_MAX_LENGTH 0xffff
--- 251,256 ----
***************
*** 567,572 ****
--- 555,561 ----
* Change the length of a dynamic string. This can cause the
* string to either grow or shrink, depending on the value of
* length.
+ *
* Input:
* Tcl_DString *dsPtr; Structure describing dynamic string
* int length; New length for dynamic string.
***************
*** 618,623 ****
--- 607,613 ----
*
* Frees up any memory allocated for the dynamic string and
* reinitializes the string to an empty state.
+ *
* Input:
* Tcl_DString *dsPtr; Structure describing dynamic string
*
***************
*** 728,738 ****
--- 718,736 ----
typedef void *OS_IpcAddress;
typedef struct _OS_IpcAddr {
+ int addrType; /* one of TYPE_* below */
+ int port; /* port used for TCP connections */
DString bindPath; /* Path used for the socket bind point */
struct sockaddr *serverAddr; /* server address (for connect) */
int addrLen; /* length of server address (for
connect) */
} OS_IpcAddr;
+ /*
+ * Values of addrType field.
+ */
+ #define TYPE_UNKNOWN 0 /* Uninitialized address type */
+ #define TYPE_LOCAL 1 /* Local IPC: UNIX domain stream socket
*/
+ #define TYPE_TCP 2 /* TCP stream socket */
/*
*----------------------------------------------------------------------
***************
*** 749,757 ****
--- 747,758 ----
*
*----------------------------------------------------------------------
*/
+
OS_IpcAddress OS_InitIpcAddr(void)
{
OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *)Malloc(sizeof(OS_IpcAddr));
+ ipcAddrPtr->addrType = TYPE_UNKNOWN;
+ ipcAddrPtr->port = -1;
DStringInit(&ipcAddrPtr->bindPath);
ipcAddrPtr->serverAddr = NULL;
ipcAddrPtr->addrLen = 0;
***************
*** 775,796 ****
*----------------------------------------------------------------------
*/
! static int OS_BuildSockAddrUn(char *bindPath,
! struct sockaddr_un *servAddrPtr,
! int *servAddrLen)
{
int bindPathLen = strlen(bindPath);
#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI */
! if(bindPathLen >= sizeof(servAddrPtr->sun_path))
return -1;
#else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */
! if(bindPathLen > 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 */
*servAddrLen = sizeof(servAddrPtr->sun_len)
+ sizeof(servAddrPtr->sun_family)
--- 776,801 ----
*----------------------------------------------------------------------
*/
! static int OS_BuildSockAddrUn(
! char *bindPath,
! struct sockaddr_un *servAddrPtr,
! int *servAddrLen)
{
int bindPathLen = strlen(bindPath);
#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI */
! if(bindPathLen >= sizeof(servAddrPtr->sun_path)) {
return -1;
+ }
#else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */
! if(bindPathLen > 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 */
*servAddrLen = sizeof(servAddrPtr->sun_len)
+ sizeof(servAddrPtr->sun_family)
***************
*** 807,844 ****
*
* OS_CreateLocalIpcFd --
*
! * This procedure is responsible for creating the listener socket
! * on Unix for local process communication. It will create a Unix
! * domain socket, bind it, 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.
*
*----------------------------------------------------------------------
*/
! typedef char *MakeSocketNameProc(Tcl_DString *dsPtr);
! int OS_CreateLocalIpcFd(OS_IpcAddress ipcAddress, int listenQueueDepth,
! uid_t uid, gid_t gid, MakeSocketNameProc makeSocketName)
{
OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *)ipcAddress;
- char bindPathExt[32];
struct sockaddr_un *addrPtr = NULL;
int listenSock = -1;
- makeSocketName(&ipcAddrPtr->bindPath);
/*
* Build the domain socket address.
*/
addrPtr = (struct sockaddr_un *) Malloc(sizeof(struct sockaddr_un));
ipcAddrPtr->serverAddr = (struct sockaddr *) addrPtr;
!
! if (OS_BuildSockAddrUn(DStringValue(&ipcAddrPtr->bindPath), addrPtr,
! &ipcAddrPtr->addrLen)) {
goto GET_IPC_ERROR_EXIT;
}
/*
* Create the listening socket to be used by the fcgi server.
--- 812,856 ----
*
* OS_CreateLocalIpcFd --
*
! * This procedure is responsible for creating the listener socket
! * on Unix for local process communication. It will create a Unix
! * domain socket, bind it, and return a file descriptor to it to the
! * caller.
*
* Results:
! * Valid file descriptor or -1 on error.
! *
! * Side effects:
! * *ipcAddress initialized.
*
*----------------------------------------------------------------------
*/
! typedef char *MakeSocketNameProc(char *name, int extension, Tcl_DString
*dsPtr);
! int OS_CreateLocalIpcFd(
! OS_IpcAddress ipcAddress,
! int listenQueueDepth,
! uid_t uid,
! gid_t gid,
! MakeSocketNameProc makeSocketName,
! char *name,
! int extension)
{
OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *)ipcAddress;
struct sockaddr_un *addrPtr = NULL;
int listenSock = -1;
+ ASSERT(ipcAddrPtr->addrType == TYPE_UNKNOWN);
/*
* Build the domain socket address.
*/
addrPtr = (struct sockaddr_un *) Malloc(sizeof(struct sockaddr_un));
ipcAddrPtr->serverAddr = (struct sockaddr *) addrPtr;
! if (OS_BuildSockAddrUn(makeSocketName(name, extension,
&ipcAddrPtr->bindPath),
! addrPtr, &ipcAddrPtr->addrLen)) {
goto GET_IPC_ERROR_EXIT;
}
+ ipcAddrPtr->addrType = TYPE_LOCAL;
/*
* Create the listening socket to be used by the fcgi server.
***************
*** 851,864 ****
/*
* Bind the listening socket and set it to listen.
*/
if(OS_Bind(listenSock, ipcAddrPtr->serverAddr, ipcAddrPtr->addrLen) < 0
|| OS_Listen(listenSock, listenQueueDepth) < 0) {
goto GET_IPC_ERROR_EXIT;
}
#ifndef __EMX__
! /* OS/2 dosen't support changing ownership. */
chown(DStringValue(&ipcAddrPtr->bindPath), uid, gid);
#endif
chmod(DStringValue(&ipcAddrPtr->bindPath), S_IRUSR | S_IWUSR);
return listenSock;
--- 863,882 ----
/*
* Bind the listening socket and set it to listen.
*/
+ if((unlink(DStringValue(&ipcAddrPtr->bindPath)) < 0)
+ && (errno != ENOENT)) {
+ goto GET_IPC_ERROR_EXIT;
+ }
if(OS_Bind(listenSock, ipcAddrPtr->serverAddr, ipcAddrPtr->addrLen) < 0
|| OS_Listen(listenSock, listenQueueDepth) < 0) {
goto GET_IPC_ERROR_EXIT;
}
+
#ifndef __EMX__
! /* OS/2 dosen't support changing ownership. */
chown(DStringValue(&ipcAddrPtr->bindPath), uid, gid);
#endif
+
chmod(DStringValue(&ipcAddrPtr->bindPath), S_IRUSR | S_IWUSR);
return listenSock;
***************
*** 868,873 ****
--- 886,893 ----
if(addrPtr != NULL) {
free(addrPtr);
ipcAddrPtr->serverAddr = NULL;
+ ipcAddrPtr->addrType = TYPE_UNKNOWN;
+ ipcAddrPtr->addrLen = 0;
}
return -1;
}
***************
*** 900,923 ****
/*
*----------------------------------------------------------------------
*
! * OS_CleanupFcgiProgram --
*
! * Perform OS cleanup on a server managed process.
! * XXX: odd name, really more like inverse of OS_CreateLocalIpcFd
*
* Results:
* None.
*
! * Side effects:
! * Domain socket bindPath is unlinked.
*
*----------------------------------------------------------------------
! */
! void OS_CleanupFcgiProgram(OS_IpcAddress ipcAddress)
{
! OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *)ipcAddress;
! unlink(DStringValue(&ipcAddrPtr->bindPath));
}
/* XXX: where does this number come from? */
#define ht_openmax (128)
--- 920,1124 ----
/*
*----------------------------------------------------------------------
*
! * OS_CreateRemoteIpcFd --
! *
! * This procedure is responsible for creating a listener socket
! * for remote process communication. It will create a TCP socket,
! * bind it, and return a file descriptor to the caller.
! *
! * Results:
! * Valid file descriptor or -1 on error.
! *
! *----------------------------------------------------------------------
! */
!
! int OS_CreateRemoteIpcFd(
! OS_IpcAddress ipcAddress,
! int portIn,
! int listenQueueDepth)
! {
! OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *) ipcAddress;
! struct sockaddr_in *addrPtr = (struct sockaddr_in *)
! Malloc(sizeof(struct sockaddr_in));
! int resultSock = -1;
! int flag = 1;
!
! ASSERT(ipcAddrPtr->addrType == TYPE_UNKNOWN);
! ipcAddrPtr->addrType = TYPE_TCP;
! ipcAddrPtr->port = portIn;
! ipcAddrPtr->addrLen = sizeof(struct sockaddr_in);
!
! memset((char *) addrPtr, 0, sizeof(struct sockaddr_in));
! addrPtr->sin_family = AF_INET;
! addrPtr->sin_addr.s_addr = htonl(INADDR_ANY);
! addrPtr->sin_port = htons(portIn);
! ipcAddrPtr->serverAddr = (struct sockaddr *) addrPtr;
!
! if((resultSock = OS_Socket(ipcAddrPtr->serverAddr->sa_family,
! SOCK_STREAM, 0)) < 0) {
! goto GET_IPC_ERROR_EXIT;
! }
!
! if(setsockopt(resultSock, SOL_SOCKET, SO_REUSEADDR,
! (char *) &flag, sizeof(flag)) < 0) {
! goto GET_IPC_ERROR_EXIT;
! }
!
! /*
! * Bind the listening socket and set it to listen
! */
! if(OS_Bind(resultSock, ipcAddrPtr->serverAddr, ipcAddrPtr->addrLen) < 0
! || OS_Listen(resultSock, listenQueueDepth) < 0) {
! goto GET_IPC_ERROR_EXIT;
! }
!
! return resultSock;
!
! GET_IPC_ERROR_EXIT:
! if(resultSock != -1) {
! OS_Close(resultSock);
! }
! if(addrPtr != NULL) {
! Free(addrPtr);
! ipcAddrPtr->serverAddr = NULL;
! ipcAddrPtr->port = -1;
! ipcAddrPtr->addrType = TYPE_UNKNOWN;
! ipcAddrPtr->addrLen = 0;
! }
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * ResolveHostname --
! *
! * Given a hostname string (aegaen.openmarket.com) or an ASCII
! * "dotted decimal" IP address (199.170.183.5), convert to
! * IP address.
*
! * NOTE: This routine will block as the hostname is resolved, and
! * should only be used in startup or debugging code.
*
* Results:
+ * Returns -1 if error, and the number of resolved addresses (one
+ * or more), if success.
+ *
+ * Side effects:
* None.
*
! *----------------------------------------------------------------------
! */
!
! int ResolveHostname(char *hostname, struct in_addr *addr)
! {
! struct hostent *hp;
! int count;
!
! addr->s_addr = inet_addr(hostname);
! if(addr->s_addr == (unsigned int) -1) {
! if((hp = gethostbyname(hostname)) == NULL) {
! return -1;
! }
!
! memcpy((char *) addr, hp->h_addr, hp->h_length);
! count = 0;
! while(hp->h_addr_list[count] != 0) {
! count++;
! }
!
! return count;
! }
! return 1;
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * OS_CreateLocalIpcAddr --
! *
! * This procedure is responsible for creating a Unix domain address
! * to be used to connect to a fcgi server not managed by the Web
! * server.
! *
! * Results:
! * Unix Domain socket is created. This call returns 0 on success
! * or -1 on error.
! *
! * Side effects:
! * OS_IpcAddress structure is allocated and returned to the caller.
! * 'errno' will set on errors (-1 is returned).
*
*----------------------------------------------------------------------
! */
!
! int OS_CreateLocalIpcAddr(
! OS_IpcAddress ipcAddress,
! MakeSocketNameProc makeSocketName,
! char *name,
! int extension)
{
! OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *) ipcAddress;
! struct sockaddr_un* addrPtr = NULL;
! ASSERT(ipcAddrPtr->addrType == TYPE_UNKNOWN);
! ASSERT(name != NULL);
!
! /*
! * Build the domain socket address.
! */
! addrPtr = (struct sockaddr_un *) Malloc(sizeof(struct sockaddr_un));
! ipcAddrPtr->serverAddr = (struct sockaddr *) addrPtr;
! if(OS_BuildSockAddrUn(makeSocketName(name, extension,
&ipcAddrPtr->bindPath),
! addrPtr, &ipcAddrPtr->addrLen)) {
! goto GET_IPC_ADDR_ERROR;
! }
! ipcAddrPtr->addrType = TYPE_LOCAL;
! return 0;
!
! GET_IPC_ADDR_ERROR:
! if(addrPtr != NULL) {
! free(addrPtr);
! ipcAddrPtr->serverAddr = NULL;
! }
! return -1;
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * OS_CreateInetIpc --
! *
! * This procedure is responsible for creating an OS_IpcAddr version
! * of hostname:port to be used for communications via TCP.
! *
! * Results:
! * AF_INET socket created.
! *
! * Side effects:
! * OS_IpcAddress structure is allocated and returned to the caller.
! *
! *----------------------------------------------------------------------
! */
! void OS_CreateInetIpc(
! OS_IpcAddress ipcAddress,
! struct in_addr *hostIn,
! int portIn)
! {
! OS_IpcAddr *ipcAddrPtr = (OS_IpcAddr *) ipcAddress;
! struct sockaddr_in *addrPtr;
!
! ASSERT(ipcAddrPtr->addrType == TYPE_UNKNOWN);
! ipcAddrPtr->addrType = TYPE_TCP;
! ipcAddrPtr->port = portIn;
!
! addrPtr = (struct sockaddr_in *) Malloc(sizeof(struct sockaddr_in));
! memset(addrPtr, 0, sizeof(struct sockaddr_in));
! ipcAddrPtr->addrLen = sizeof(struct sockaddr_in);
! addrPtr->sin_family = AF_INET;
! addrPtr->sin_port = htons(portIn);
! memcpy(&addrPtr->sin_addr.s_addr, hostIn, sizeof(struct in_addr));
! ipcAddrPtr->serverAddr = (struct sockaddr *) addrPtr;
}
+
/* XXX: where does this number come from? */
#define ht_openmax (128)
***************
*** 931,950 ****
*
* Results:
* 0 for successful fork, -1 for failed fork.
! * Child process exits in case of failure, with exit
! * status = errno of failed system call.
*
* Side effects:
* Child process created.
*
*----------------------------------------------------------------------
*/
! static int OS_ExecFcgiProgram(pid_t *childPid, int listenFd, int priority,
! char *programName, char **envPtr)
{
int i;
DString dirName;
! char *dnEnd;
/*
* Fork the fcgi process.
*/
--- 1132,1163 ----
*
* Results:
* 0 for successful fork, -1 for failed fork.
! *
! * In case the child fails before or in the exec, the child
! * obtains the error log by calling getErrLog, logs
! * the error, and exits with exit status = errno of
! * the failed system call.
*
* Side effects:
* Child process created.
*
*----------------------------------------------------------------------
*/
! typedef FILE *GetErrLog(void);
!
! static int OS_ExecFcgiProgram(
! pid_t *childPid,
! int listenFd,
! int priority,
! char *programName,
! char **envPtr,
! GetErrLog *getErrLog)
{
int i;
DString dirName;
! char *dnEnd, *failedSysCall;
! FILE *errorLogFile;
!
/*
* Fork the fcgi process.
*/
***************
*** 954,969 ****
} else if(*childPid != 0) {
return 0;
}
/*
* We're the child; no return.
*/
if(!geteuid() && setuid(user_id) == -1) {
! exit(errno);
}
if(listenFd != FCGI_LISTENSOCK_FILENO) {
OS_Dup2(listenFd, FCGI_LISTENSOCK_FILENO);
OS_Close(listenFd);
}
DStringInit(&dirName);
dnEnd = strrchr(programName, '/');
if(dnEnd == NULL) {
--- 1167,1185 ----
} else if(*childPid != 0) {
return 0;
}
+
/*
* We're the child; no return.
*/
if(!geteuid() && setuid(user_id) == -1) {
! failedSysCall = "setuid";
! goto ErrorExit;
}
if(listenFd != FCGI_LISTENSOCK_FILENO) {
OS_Dup2(listenFd, FCGI_LISTENSOCK_FILENO);
OS_Close(listenFd);
}
+
DStringInit(&dirName);
dnEnd = strrchr(programName, '/');
if(dnEnd == NULL) {
***************
*** 972,1004 ****
DStringAppend(&dirName, programName, dnEnd - programName);
}
if(chdir(DStringValue(&dirName)) < 0) {
! exit(errno);
}
DStringFree(&dirName);
#ifndef __EMX__
! /* OS/2 dosen't support nice() */
if(priority != 0) {
! if(nice (priority) == -1) {
! exit(errno);
}
}
! #endif
/*
* Close any file descriptors we may have gotten from the parent
* process. The only FD left open is the FCGI listener socket.
*/
for(i=0; i < ht_openmax; i++) {
! if(i != FCGI_LISTENSOCK_FILENO)
OS_Close(i);
}
! if(envPtr != NULL) {
! execle(programName, programName, NULL, envPtr);
! } else {
! execl(programName, programName, NULL);
! }
/*
! * exec failed
! */
exit(errno);
}
--- 1188,1240 ----
DStringAppend(&dirName, programName, dnEnd - programName);
}
if(chdir(DStringValue(&dirName)) < 0) {
! failedSysCall = "chdir";
! goto ErrorExit;
}
DStringFree(&dirName);
+
#ifndef __EMX__
! /* OS/2 dosen't support nice() */
if(priority != 0) {
! if(nice(priority) == -1) {
! failedSysCall = "nice";
! goto ErrorExit;
}
}
! #endif
!
/*
* Close any file descriptors we may have gotten from the parent
* process. The only FD left open is the FCGI listener socket.
*/
for(i=0; i < ht_openmax; i++) {
! if(i != FCGI_LISTENSOCK_FILENO) {
OS_Close(i);
+ }
}
! do {
! if(envPtr != NULL) {
! execle(programName, programName, NULL, envPtr);
! failedSysCall = "execle";
! } else {
! execl(programName, programName, NULL);
! failedSysCall = "execl";
! }
! } while(errno == EINTR);
!
! ErrorExit:
/*
! * We had to close all files but the FCGI listener socket in order to
! * exec the application. So if we want to report exec errors (we do!)
! * we must wait until now to open the log file.
! */
! errorLogFile = getErrLog();
! fprintf(errorLogFile,
! "[%s] mod_fastcgi: %s pid %d syscall %s failed"
! " before entering app, errno = %s.\n",
! get_time(), programName, getpid(), failedSysCall,
! strerror(errno));
! fflush(errorLogFile);
exit(errno);
}
***************
*** 1061,1066 ****
--- 1297,1303 ----
*----------------------------------------------------------------------
*/
#define WS_SET_errno(x) errno = x
+
int WS_Access(const char *path, int mode, uid_t uid, gid_t gid)
{
struct stat statBuf;
***************
*** 1068,1090 ****
struct group *grp;
struct passwd *usr;
! if(stat(path, &statBuf) < 0)
return -1;
/*
* If the user owns this file, check the owner bits.
*/
if(uid == statBuf.st_uid) {
WS_SET_errno(EACCES);
! if((mode & R_OK) && !(statBuf.st_mode & S_IRUSR))
goto no_access;
!
! if((mode & W_OK) && !(statBuf.st_mode & S_IWUSR))
goto no_access;
!
! if((mode & X_OK) && !(statBuf.st_mode & S_IXUSR))
goto no_access;
!
return 0;
}
--- 1305,1328 ----
struct group *grp;
struct passwd *usr;
! if(stat(path, &statBuf) < 0) {
return -1;
+ }
/*
* If the user owns this file, check the owner bits.
*/
if(uid == statBuf.st_uid) {
WS_SET_errno(EACCES);
! if((mode & R_OK) && !(statBuf.st_mode & S_IRUSR)) {
goto no_access;
! }
! if((mode & W_OK) && !(statBuf.st_mode & S_IWUSR)) {
goto no_access;
! }
! if((mode & X_OK) && !(statBuf.st_mode & S_IXUSR)) {
goto no_access;
! }
return 0;
}
***************
*** 1110,1134 ****
* user is a member of that group, apply the group permissions.
*/
grp = getgrgid(statBuf.st_gid);
! if(grp == NULL)
return -1;
usr = getpwuid(uid);
! if(usr == NULL)
return -1;
for(names = grp->gr_mem; *names != NULL; names++) {
if(!strcmp(*names, usr->pw_name)) {
WS_SET_errno(EACCES);
! if((mode & R_OK) && !(statBuf.st_mode & S_IRGRP))
goto no_access;
!
! if((mode & W_OK) && !(statBuf.st_mode & S_IWGRP))
goto no_access;
!
! if((mode & X_OK) && !(statBuf.st_mode & S_IXGRP))
goto no_access;
!
return 0;
}
}
--- 1348,1374 ----
* user is a member of that group, apply the group permissions.
*/
grp = getgrgid(statBuf.st_gid);
! if(grp == NULL) {
return -1;
+ }
usr = getpwuid(uid);
! if(usr == NULL) {
return -1;
+ }
for(names = grp->gr_mem; *names != NULL; names++) {
if(!strcmp(*names, usr->pw_name)) {
WS_SET_errno(EACCES);
! if((mode & R_OK) && !(statBuf.st_mode & S_IRGRP)) {
goto no_access;
! }
! if((mode & W_OK) && !(statBuf.st_mode & S_IWGRP)) {
goto no_access;
! }
! if((mode & X_OK) && !(statBuf.st_mode & S_IXGRP)) {
goto no_access;
! }
return 0;
}
}
***************
*** 1173,1178 ****
--- 1413,1419 ----
*
*----------------------------------------------------------------------
*/
+
void BufferCheck(Buffer *bufPtr)
{
ASSERT(bufPtr->size > 0);
***************
*** 1203,1208 ****
--- 1444,1450 ----
*
*----------------------------------------------------------------------
*/
+
void BufferReset(Buffer *bufPtr)
{
bufPtr->length = 0;
***************
*** 1224,1229 ****
--- 1466,1472 ----
*
*----------------------------------------------------------------------
*/
+
Buffer *BufferCreate(int size)
{
Buffer *bufPtr;
***************
*** 1249,1254 ****
--- 1492,1498 ----
*
*----------------------------------------------------------------------
*/
+
void BufferDelete(Buffer *bufPtr)
{
BufferCheck(bufPtr);
***************
*** 1270,1275 ****
--- 1514,1520 ----
*
*----------------------------------------------------------------------
*/
+
int BufferRead(Buffer *bufPtr, int fd)
{
int len;
***************
*** 1283,1290 ****
len = OS_Read(fd, bufPtr->end, len);
if(len > 0) {
bufPtr->end += len;
! if(bufPtr->end >= (bufPtr->data + bufPtr->size))
bufPtr->end -= bufPtr->size;
bufPtr->length += len;
}
}
--- 1528,1536 ----
len = OS_Read(fd, bufPtr->end, len);
if(len > 0) {
bufPtr->end += len;
! if(bufPtr->end >= (bufPtr->data + bufPtr->size)) {
bufPtr->end -= bufPtr->size;
+ }
bufPtr->length += len;
}
}
***************
*** 1325,1336 ****
if(len > MAX_WRITE) {
len = MAX_WRITE;
}
if(len > 0) {
len = OS_Write(fd, bufPtr->begin, len);
if(len > 0) {
bufPtr->begin += len;
! if(bufPtr->begin >= (bufPtr->data + bufPtr->size))
bufPtr->begin -= bufPtr->size;
bufPtr->length -= len;
}
}
--- 1571,1584 ----
if(len > MAX_WRITE) {
len = MAX_WRITE;
}
+
if(len > 0) {
len = OS_Write(fd, bufPtr->begin, len);
if(len > 0) {
bufPtr->begin += len;
! if(bufPtr->begin >= (bufPtr->data + bufPtr->size)) {
bufPtr->begin -= bufPtr->size;
+ }
bufPtr->length -= len;
}
}
***************
*** 1356,1361 ****
--- 1604,1610 ----
*
*----------------------------------------------------------------------
*/
+
void BufferPeekToss(Buffer *bufPtr, char **beginPtr, int *countPtr)
{
BufferCheck(bufPtr);
***************
*** 1380,1385 ****
--- 1629,1635 ----
*
*----------------------------------------------------------------------
*/
+
void BufferToss(Buffer *bufPtr, int count)
{
BufferCheck(bufPtr);
***************
*** 1387,1394 ****
bufPtr->length -= count;
bufPtr->begin += count;
! if(bufPtr->begin >= bufPtr->data + bufPtr->size)
bufPtr->begin -= bufPtr->size;
}
/*
--- 1637,1645 ----
bufPtr->length -= count;
bufPtr->begin += count;
! if(bufPtr->begin >= bufPtr->data + bufPtr->size) {
bufPtr->begin -= bufPtr->size;
+ }
}
/*
***************
*** 1410,1415 ****
--- 1661,1667 ----
*
*----------------------------------------------------------------------
*/
+
void BufferPeekExpand(Buffer *bufPtr, char **endPtr, int *countPtr)
{
BufferCheck(bufPtr);
***************
*** 1436,1441 ****
--- 1688,1694 ----
*
*----------------------------------------------------------------------
*/
+
void BufferExpand(Buffer *bufPtr, int count)
{
BufferCheck(bufPtr);
***************
*** 1443,1450 ****
bufPtr->length += count;
bufPtr->end += count;
! if(bufPtr->end >= bufPtr->data + bufPtr->size)
bufPtr->end -= bufPtr->size;
BufferCheck(bufPtr);
}
--- 1696,1704 ----
bufPtr->length += count;
bufPtr->end += count;
! if(bufPtr->end >= bufPtr->data + bufPtr->size) {
bufPtr->end -= bufPtr->size;
+ }
BufferCheck(bufPtr);
}
***************
*** 1464,1469 ****
--- 1718,1724 ----
*
*----------------------------------------------------------------------
*/
+
int BufferAddData(Buffer *bufPtr, char *data, int datalen)
{
char *end;
***************
*** 1471,1481 ****
int canCopy; /* Number of bytes to copy in a given op. */
ASSERT(data != NULL);
! if(datalen == 0)
return 0;
ASSERT(datalen > 0);
-
BufferCheck(bufPtr);
end = bufPtr->data + bufPtr->size;
--- 1726,1736 ----
int canCopy; /* Number of bytes to copy in a given op. */
ASSERT(data != NULL);
! if(datalen == 0) {
return 0;
+ }
ASSERT(datalen > 0);
BufferCheck(bufPtr);
end = bufPtr->data + bufPtr->size;
***************
*** 1489,1496 ****
bufPtr->length += canCopy;
bufPtr->end += canCopy;
copied += canCopy;
! if (bufPtr->end >= end)
bufPtr->end = bufPtr->data;
datalen -= canCopy;
/*
--- 1744,1752 ----
bufPtr->length += canCopy;
bufPtr->end += canCopy;
copied += canCopy;
! if (bufPtr->end >= end) {
bufPtr->end = bufPtr->data;
+ }
datalen -= canCopy;
/*
***************
*** 1522,1527 ****
--- 1778,1784 ----
*
*----------------------------------------------------------------------
*/
+
int BufferAdd(Buffer *bufPtr, char *str)
{
return BufferAddData(bufPtr, str, strlen(str));
***************
*** 1542,1547 ****
--- 1799,1805 ----
*
*----------------------------------------------------------------------
*/
+
int BufferGetData(Buffer *bufPtr, char *data, int datalen)
{
char *end;
***************
*** 1563,1570 ****
bufPtr->length -= canCopy;
bufPtr->begin += canCopy;
copied += canCopy;
! if (bufPtr->begin >= end)
bufPtr->begin = bufPtr->data;
/*
* If there's more to go, copy the second part starting from the
--- 1821,1829 ----
bufPtr->length -= canCopy;
bufPtr->begin += canCopy;
copied += canCopy;
! if (bufPtr->begin >= end) {
bufPtr->begin = bufPtr->data;
+ }
/*
* If there's more to go, copy the second part starting from the
***************
*** 1599,1604 ****
--- 1858,1864 ----
*
*----------------------------------------------------------------------
*/
+
void BufferMove(Buffer *toPtr, Buffer *fromPtr, int len)
{
int fromLen, toLen, toMove;
***************
*** 1621,1628 ****
toMove = min(toMove, len);
ASSERT(toMove >= 0);
! if(toMove == 0)
return;
memcpy(toPtr->end, fromPtr->begin, toMove);
BufferToss(fromPtr, toMove);
--- 1881,1889 ----
toMove = min(toMove, len);
ASSERT(toMove >= 0);
! if(toMove == 0) {
return;
+ }
memcpy(toPtr->end, fromPtr->begin, toMove);
BufferToss(fromPtr, toMove);
***************
*** 1647,1652 ****
--- 1908,1914 ----
*
*----------------------------------------------------------------------
*/
+
void BufferDStringAppend(DString *strPtr, Buffer *bufPtr, int len)
{
int fromLen;
***************
*** 1668,1675 ****
/*
* Done with the generic stuff. Here starts the FastCGI stuff.
*/
-
-
typedef request_rec WS_Request;
#define FCGI_MAGIC_TYPE "application/x-httpd-fcgi"
--- 1930,1935 ----
***************
*** 1677,1682 ****
--- 1937,1943 ----
#define FCGI_DEFAULT_RESTART_DELAY 5
#define FCGI_MAX_PROCESSES 20
#define FCGI_ERRMSG_LEN 200
+
/*
* If the exec of a FastCGI app fails, this is the minimum number
* of seconds to wait before retrying the exec.
***************
*** 1722,1727 ****
--- 1983,1996 ----
int numFailures; /* num restarts due to exit failure */
OS_IpcAddress ipcAddr; /* IPC Address of FCGI app server class.
* Used to connect to an app server. */
+ int directive; /* AppClass or ExternalAppClass */
+ DString bindname; /* Name used to create a socket */
+ DString host; /* Hostname for externally managed
+ * FastCGI application processes */
+ int port; /* Port number either for externally
+ * managed FastCGI applications or for
+ * server managed FastCGI applications,
+ * where server became application
mngr. */
int listenFd; /* Listener socket of FCGI app server
* class. Passed to app server process
* at process creation. */
***************
*** 1736,1741 ****
--- 2005,2012 ----
int freeOnZero; /* Deferred free; free this structure
* when refCount = 0. Not used
* by Apache. */
+ int affinity; /* Session affinity. Not used by
+ * Apache server. */
int restartTimerQueued; /* = TRUE = restart timer queued.
* Not used by Apache. */
int keepConnection; /* = 1 = maintain connection to app. */
***************
*** 1745,1750 ****
--- 2016,2028 ----
struct _FastCgiServerInfo *next;
} FastCgiServerInfo;
+ /*
+ * Value of directive field.
+ */
+ #define APP_CLASS_UNKNOWN 0
+ #define APP_CLASS_STANDARD 1
+ #define APP_CLASS_EXTERNAL 2
+
/*
* FastCgiInfo holds the state of a particular FastCGI request.
*/
***************
*** 1773,1778 ****
--- 2051,2065 ----
int requestId;
int eofSent;
} FastCgiInfo;
+
+ /*
+ * Values of parseHeader field
+ */
+ #define SCAN_CGI_READING_HEADERS 1
+ #define SCAN_CGI_FINISHED 0
+ #define SCAN_CGI_BAD_HEADER -1
+ #define SCAN_CGI_INT_REDIRECT -2
+ #define SCAN_CGI_SRV_REDIRECT -3
/*
* Global variables
***************
*** 1797,1821 ****
*
* FastCgiIpcDirCmd --
*
*----------------------------------------------------------------------
*/
! static char *FastCgiIpcDirCmd(cmd_parms *cmd, void *dummy, char *arg)
{
uid_t uid;
gid_t gid;
int len;
ASSERT(arg != NULL);
len = strlen(arg);
ASSERT(len > 0);
if(*arg != '/') {
return "FastCgiIpcDir: Directory path must be absolute\n";
}
uid = (user_id == (uid_t) -1) ? geteuid() : user_id;
gid = (group_id == (gid_t) -1) ? getegid() : group_id;
if(WS_Access(arg, R_OK | W_OK | X_OK, uid, gid)) {
return
"FastCgiIpcDir: Need read/write/exec permission on directory\n";
}
ipcDir = Malloc(len + 1);
strcpy(ipcDir, arg);
while(len > 1 && ipcDir[len-1] == '/') {
--- 2084,2118 ----
*
* FastCgiIpcDirCmd --
*
+ * Sets up the directory into which Unix domain sockets
+ * that are used for local communication will be deposited.
+ *
+ * Results:
+ * NULL or an error message
+ *
*----------------------------------------------------------------------
*/
!
! const char *FastCgiIpcDirCmd(cmd_parms *cmd, void *dummy, char *arg)
{
uid_t uid;
gid_t gid;
int len;
+
ASSERT(arg != NULL);
len = strlen(arg);
ASSERT(len > 0);
if(*arg != '/') {
return "FastCgiIpcDir: Directory path must be absolute\n";
}
+
uid = (user_id == (uid_t) -1) ? geteuid() : user_id;
gid = (group_id == (gid_t) -1) ? getegid() : group_id;
if(WS_Access(arg, R_OK | W_OK | X_OK, uid, gid)) {
return
"FastCgiIpcDir: Need read/write/exec permission on directory\n";
}
+
ipcDir = Malloc(len + 1);
strcpy(ipcDir, arg);
while(len > 1 && ipcDir[len-1] == '/') {
***************
*** 1832,1861 ****
*
* Appends a socket path name to an empty DString.
* The name is formed from the directory specified to
! * the FastCgiIpcDir directive, followed by
! * "OM_WS_N.pid" where N is a sequence number and pid
! * is the current process ID.
*
* Results:
* The value of the socket path name.
*
* Side effects:
! * Appends to the DString, increments the sequence number.
*
*----------------------------------------------------------------------
*/
static int bindPathExtInt = 1;
! char *MakeSocketName(Tcl_DString *dsPtr)
{
char bindPathExt[32];
ASSERT(DStringLength(dsPtr) == 0);
DStringAppend(dsPtr, ipcDir, -1);
! DStringAppend(dsPtr, "/OM_WS_", -1);
! sprintf(bindPathExt, "%d.%d", bindPathExtInt, (int)getpid());
! bindPathExtInt++;
! return DStringAppend(dsPtr, bindPathExt, -1);
! }
/*
*----------------------------------------------------------------------
--- 2129,2170 ----
*
* Appends a socket path name to an empty DString.
* The name is formed from the directory specified to
! * the FastCgiIpcDir directive, followed by either
! * (1) the name parameter, if it is not NULL
! * (2) "OM_WS_N.pid" where N is a sequence number and pid
! * is the current process ID.
*
* Results:
* The value of the socket path name.
*
* Side effects:
! * Appends to the DString. If name != NULL, increments the
! * sequence number.
*
*----------------------------------------------------------------------
*/
static int bindPathExtInt = 1;
! char *MakeSocketName(char *name, int extension, Tcl_DString *dsPtr)
{
char bindPathExt[32];
ASSERT(DStringLength(dsPtr) == 0);
DStringAppend(dsPtr, ipcDir, -1);
! DStringAppend(dsPtr, "/", -1);
! if(name != NULL) {
! DStringAppend(dsPtr, name, -1);
! } else {
! DStringAppend(dsPtr, "OM_WS_", -1);
! sprintf(bindPathExt, "%d.%d", bindPathExtInt, (int)getpid());
! DStringAppend(dsPtr, bindPathExt, -1);
! bindPathExtInt++;
! }
! if(extension != -1) {
! sprintf(bindPathExt, ".%d", extension);
! DStringAppend(dsPtr, bindPathExt, -1);
! }
! return DStringValue(dsPtr);
! }
/*
*----------------------------------------------------------------------
***************
*** 1873,1881 ****
--- 2182,2192 ----
*
*----------------------------------------------------------------------
*/
+
FastCgiServerInfo *LookupFcgiServerInfo(char *ePath)
{
FastCgiServerInfo *info;
+
for(info = fastCgiServers; info != NULL; info = info->next) {
const char *execPath = DStringValue(&info->execPath);
if(execPath != NULL && strcmp(execPath, ePath) == 0) {
***************
*** 1906,1911 ****
--- 2217,2223 ----
*
*----------------------------------------------------------------------
*/
+
static FastCgiServerInfo *CreateFcgiServerInfo(int numInstances, char
*ePath)
{
FastCgiServerInfo *serverInfoPtr = NULL;
***************
*** 1929,1938 ****
--- 2241,2255 ----
serverInfoPtr->numRestarts = 0;
serverInfoPtr->numFailures = 0;
serverInfoPtr->ipcAddr = OS_InitIpcAddr();
+ serverInfoPtr->directive = APP_CLASS_UNKNOWN;
+ DStringInit(&serverInfoPtr->host);
+ DStringInit(&serverInfoPtr->bindname);
+ serverInfoPtr->port = -1;
serverInfoPtr->processPriority = 0;
serverInfoPtr->listenFd = -1;
serverInfoPtr->reqRefCount = 0;
serverInfoPtr->freeOnZero = FALSE;
+ serverInfoPtr->affinity = FALSE;
serverInfoPtr->restartTimerQueued = FALSE;
serverInfoPtr->keepConnection = FALSE;
serverInfoPtr->fcgiFd = -1;
***************
*** 1990,1998 ****
/*
* Clean up server info structure resources.
*/
- OS_CleanupFcgiProgram(serverInfoPtr->ipcAddr);
OS_FreeIpcAddr(serverInfoPtr->ipcAddr);
DStringFree(&serverInfoPtr->execPath);
if(serverInfoPtr->listenFd != -1) {
OS_Close(serverInfoPtr->listenFd);
serverInfoPtr->listenFd = -1;
--- 2307,2318 ----
/*
* Clean up server info structure resources.
*/
OS_FreeIpcAddr(serverInfoPtr->ipcAddr);
DStringFree(&serverInfoPtr->execPath);
+ DStringFree(&serverInfoPtr->host);
+ DStringFree(&serverInfoPtr->bindname);
+ serverInfoPtr->port = -1;
+ serverInfoPtr->directive = APP_CLASS_UNKNOWN;
if(serverInfoPtr->listenFd != -1) {
OS_Close(serverInfoPtr->listenFd);
serverInfoPtr->listenFd = -1;
***************
*** 2023,2028 ****
--- 2343,2371 ----
/*
*----------------------------------------------------------------------
*
+ * CleanupPreviousConfig --
+ *
+ * This routine is called by each directive in the module.
+ * If the directive is the first directive in the reading of
+ * a new configuration, the routine cleans up from any previous
+ * reading of a configuration by this process.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ static void CleanupPreviousConfig(void)
+ {
+ if(!readingConfig) {
+ while(fastCgiServers != NULL) {
+ FreeFcgiServerInfo(fastCgiServers);
+ }
+ readingConfig = TRUE;
+ }
+ }
+
+ /*
+ *----------------------------------------------------------------------
+ *
* ParseApacheRawArgs --
*
* Turns an Apache RAW_ARGS input into argc and argv.
***************
*** 2042,2047 ****
--- 2385,2391 ----
*
*----------------------------------------------------------------------
*/
+
char **ParseApacheRawArgs(char *rawArgs, int *argcPtr)
{
char *input, *p;
***************
*** 2059,2064 ****
--- 2403,2409 ----
}
input = Malloc(strlen(rawArgs) + 1);
strcpy(input, rawArgs);
+
/*
* Make one pass over the input, null-terminating each argument and
* computing argc. Then allocate argv, with argc entries. argc
***************
*** 2076,2081 ****
--- 2421,2427 ----
break;
}
*p++ = '\0';
+
/*
* Look for a non-whitespace character.
*/
***************
*** 2085,2090 ****
--- 2431,2437 ----
}
}
argv = Malloc(sizeof(char *) * argc);
+
/*
* Make a second pass over the input to fill in argv.
*/
***************
*** 2106,2154 ****
/*
*----------------------------------------------------------------------
*
! * AppClassCmd --
! *
! * Implements the FastCGI AppClass configuration directive. This
! * command adds a fast cgi program class which will be used by the
! * httpd parent to start/stop/maintain fast cgi server apps.
! *
! * AppClass <exec-path> [-processes N] \
! * [-restart-delay N] [-priority N] \
! * [-initial-env name1=value1] \
! * [-initial-env name2=value2]
! *
! * Default values:
*
! * o numProcesses will be set to 1
! * o restartDelay will be set to 5 which means the application will not
! * be restarted any earlier than 5 seconds from when it was last
! * invoked. If the application has been up for longer than 5 seconds
! * and it fails, a single copy will be restarted immediately. Other
! * restarts within that group will be inhibited until the restart-delay
! * has elapsed.
! * o affinity will be set to FALSE (ie. no process affinity) if not
! * specified.
*
* Results:
! * TCL_OK or TCL_ERROR.
*
* Side effects:
! * Registers a new AppClass handler for FastCGI.
*
*----------------------------------------------------------------------
*/
! static char *AppClassCmd(cmd_parms *cmd, void *dummy, char *arg)
{
! int argc;
! char **argv = NULL;
! char *execPath;
! FastCgiServerInfo *serverInfoPtr = NULL;
! int i, n;
! uid_t uid;
! gid_t gid;
! char *cvtPtr;
! char **envHead = NULL;
! char **envPtr;
int envCount;
char *namePtr;
char *valuePtr;
--- 2453,2633 ----
/*
*----------------------------------------------------------------------
*
! * ConfigureLocalServer --
*
! * Configure a FastCGI server for local communication using
! * Unix domain sockets. This is used by ExternalAppClass directive
! * to configure connection point for "-socket" option.
*
* Results:
! * 0 on successful configure or -1 if there was an error
*
* Side effects:
! * New FastCGI structure is allocated.
*
*----------------------------------------------------------------------
*/
!
! static int ConfigureLocalServer(
! char *path,
! int affinity,
! int numInstances,
! FastCgiServerInfo *serverInfoPtr)
{
! FcgiProcessInfo *processInfoPtr;
! int i;
!
! serverInfoPtr->affinity = affinity;
! serverInfoPtr->maxProcesses = numInstances;
!
! if(serverInfoPtr->affinity == FALSE) {
! if(OS_CreateLocalIpcAddr(serverInfoPtr->ipcAddr, MakeSocketName,
path, -1) != 0) {
! return -1;
! }
! } else {
! processInfoPtr = serverInfoPtr->procInfo;
! for(i = 0; i < numInstances; i++) {
! if(OS_CreateLocalIpcAddr(processInfoPtr->ipcAddr,
MakeSocketName, path, i+1) != 0) {
! return -1;
! }
! processInfoPtr++;
! }
! }
! return 0;
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * ConfigureTCPServer --
! *
! * Configure a FastCGI server for communication using TCP. This is
! * used by ExternalAppClass directive to configure connection point
! * for "-host" option. The remote host is specified as 'host:port',
! * as in 'aegean.openmarket.com:666'.
! *
! * Results:
! * 0 on successful configure or -1 if there was an error
! *
! * Side effects:
! * New FastCGI structure is allocated and modifies hostSpec.
! *
! *----------------------------------------------------------------------
! */
!
! static int ConfigureTCPServer(
! char *hostSpec,
! int affinity,
! int numInstances,
! FastCgiServerInfo *serverInfoPtr)
! {
! FcgiProcessInfo *processInfoPtr;
! struct in_addr host;
! long port;
! char *p, *cvptr;
! int i, numHosts;
!
! /*
! * Parse the host specification string into host and port components.
! */
! p = strchr(hostSpec, ':');
! if(p == NULL) {
! return -1;
! }
! *p = '\0';
! *p++;
!
! if((numHosts = ResolveHostname(hostSpec, &host)) < 0) {
! return -1;
! }
!
! /*
! * If the address lookup resolves to more than one host, this is
! * an error. The proper way to handle this is for the creator of
! * the server configuration file to specify the IP address in dotted
! * decimal notation. This will insure the proper host routing (as
! * long as someone doesn't have multiple machines with the same IP
! * address which is not legal and we can't do anything about that).
! */
! if(numHosts > 1) {
! return -1;
! }
!
! port = strtol(p, &cvptr, 10);
! if(*cvptr != '\0' || port < 1 || port > 65535) {
! return -1;
! }
!
! /*
! * Create an info structure for the Fast CGI server (TCP type).
! */
! DStringAppend(&serverInfoPtr->host, hostSpec, -1);
! serverInfoPtr->port = (int)port;
! serverInfoPtr->affinity = affinity;
! serverInfoPtr->maxProcesses = numInstances;
!
! if(serverInfoPtr->affinity == FALSE) {
! OS_CreateInetIpc(serverInfoPtr->ipcAddr, &host, (int)port);
! } else {
! processInfoPtr = serverInfoPtr->procInfo;
! for(i = 0; i < numInstances; i++) {
! OS_CreateInetIpc(processInfoPtr->ipcAddr, &host, (int)(port +
i));
! processInfoPtr++;
! }
! }
! return 0;
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * AppClassCmd --
! *
! * Implements the FastCGI AppClass configuration directive. This
! * command adds a fast cgi program class which will be used by the
! * httpd parent to start/stop/maintain fast cgi server apps.
! *
! * AppClass <exec-path> [-processes N] \
! * [-restart-delay N] [-priority N] \
! * [-port N] [-socket sock-name] \
! * [-initial-env name1=value1] \
! * [-initial-env name2=value2]
! *
! * Default values:
! *
! * o numProcesses will be set to 1
! * o restartDelay will be set to 5 which means the application will not
! * be restarted any earlier than 5 seconds from when it was last
! * invoked. If the application has been up for longer than 5 seconds
! * and it fails, a single copy will be restarted immediately. Other
! * restarts within that group will be inhibited until the restart-delay
! * has elapsed.
! * o affinity will be set to FALSE (ie. no process affinity) if not
! * specified.
! * o if both -socket and -port are omitted, server generates a name for the
! * socket used in connection.
! *
! * Results:
! * NULL or an error message.
! *
! * Side effects:
! * Registers a new AppClass handler for FastCGI.
! *
! *----------------------------------------------------------------------
! */
!
! const char *AppClassCmd(cmd_parms *cmd, void *dummy, char *arg)
! {
! int argc;
! char **argv = NULL;
! char *execPath;
! FastCgiServerInfo *serverInfoPtr = NULL;
! int i, n;
! uid_t uid;
! gid_t gid;
! char *cvtPtr;
! char **envHead = NULL;
! char **envPtr;
int envCount;
char *namePtr;
char *valuePtr;
***************
*** 2156,2161 ****
--- 2635,2642 ----
int restartDelay = FCGI_DEFAULT_RESTART_DELAY;
int processPriority = 0;
int listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
+ char *bindname = NULL;
+ int portNumber = -1;
int affinity = FALSE;
char *errMsg = Malloc(1024);
***************
*** 2164,2188 ****
* server restart, clean up structures created by the previous
* sequence of AppClassCmds.
*/
! if(!readingConfig) {
! while(fastCgiServers != NULL) {
! /*
! * Let FreeFcgiServerInfo know that it should not try
! * to kill the application processes (they were killed
! * by SIGHUP).
! */
! for(i = 0; i < fastCgiServers->maxProcesses; i++) {
! fastCgiServers->procInfo[i].pid = -1;
! }
! FreeFcgiServerInfo(fastCgiServers);
! }
! readingConfig = TRUE;
! }
! /*
! * Parse the raw arguments into tokens.
! * argv[0] is empty and argv[1] is the exec path.
! * Validate the exec path.
! */
argv = ParseApacheRawArgs(arg, &argc);
if(argc < 2) {
sprintf(errMsg, "AppClass: Too few args\n");
--- 2645,2657 ----
* server restart, clean up structures created by the previous
* sequence of AppClassCmds.
*/
! CleanupPreviousConfig();
!
! /*
! * Parse the raw arguments into tokens.
! * argv[0] is empty and argv[1] is the exec path.
! * Validate the exec path.
! */
argv = ParseApacheRawArgs(arg, &argc);
if(argc < 2) {
sprintf(errMsg, "AppClass: Too few args\n");
***************
*** 2202,2207 ****
--- 2671,2677 ----
sprintf(errMsg, "AppClass: Could not access file %s\n", execPath);
goto ErrorReturn;
}
+
/*
* You'd like to create the server info structure now, but
* you can't because you don't know numProcesses. So
***************
*** 2257,2262 ****
--- 2727,2750 ----
}
listenQueueDepth = n;
continue;
+ } else if((strcmp(argv[i], "-port") == 0)) {
+ if((i + 1) == argc) {
+ goto MissingValueReturn;
+ }
+ i++;
+ n = strtol(argv[i], &cvtPtr, 10);
+ if(*cvtPtr != '\0' || n < 1) {
+ goto BadValueReturn;
+ }
+ portNumber = n;
+ continue;
+ } else if((strcmp(argv[i], "-socket") == 0)) {
+ if((i + 1) == argc) {
+ goto MissingValueReturn;
+ }
+ i++;
+ bindname = argv[i];
+ continue;
} else if((strcmp(argv[i], "-initial-env") == 0)) {
if((i + 1) == argc) {
goto MissingValueReturn;
***************
*** 2280,2285 ****
--- 2768,2779 ----
goto ErrorReturn;
}
} /* for */
+
+ if((bindname != NULL) && (portNumber != -1)) {
+ sprintf(errMsg,
+ "AppClass: -port and -socket options are mutually
exclusive");
+ goto ErrorReturn;
+ }
serverInfoPtr = CreateFcgiServerInfo(numProcesses, execPath);
ASSERT(serverInfoPtr != NULL);
DStringAppend(&serverInfoPtr->execPath, execPath, -1);
***************
*** 2287,2329 ****
serverInfoPtr->restartDelay = restartDelay;
serverInfoPtr->processPriority = processPriority;
serverInfoPtr->listenQueueDepth = listenQueueDepth;
serverInfoPtr->envp = envHead;
/*
* Set envHead to NULL so that if there is an error below we don't
* free the environment structure twice.
*/
envHead = NULL;
/*
* Create an IPC path for the AppClass.
*/
if(affinity == FALSE) {
! int listenFd = OS_CreateLocalIpcFd(serverInfoPtr->ipcAddr,
! serverInfoPtr->listenQueueDepth, uid, gid, MakeSocketName);
if(listenFd < 0) {
! sprintf(errMsg, "AppClass: could not create local IPC
socket\n");
goto ErrorReturn;
! } else {
! serverInfoPtr->listenFd = listenFd;
! /*
! * Propagate listenFd to each process so that process manager
! * doesn't have to understand affinity.
! */
! for(i = 0; i < serverInfoPtr->maxProcesses; i++) {
! serverInfoPtr->procInfo[i].listenFd = listenFd;
! }
}
}
Free(argv[1]);
Free(argv);
Free(errMsg);
return NULL;
! MissingValueReturn:
sprintf(errMsg, "AppClass: missing value for %s\n", argv[i]);
goto ErrorReturn;
! BadValueReturn:
sprintf(errMsg, "AppClass: bad value \"%s\" for %s\n", argv[i],
argv[i-1]);
goto ErrorReturn;
! ErrorReturn:
if(serverInfoPtr != NULL) {
FreeFcgiServerInfo(serverInfoPtr);
}
--- 2781,2840 ----
serverInfoPtr->restartDelay = restartDelay;
serverInfoPtr->processPriority = processPriority;
serverInfoPtr->listenQueueDepth = listenQueueDepth;
+ if(bindname != NULL) {
+ DStringAppend(&serverInfoPtr->bindname, bindname, -1);
+ }
+ serverInfoPtr->port = portNumber;
serverInfoPtr->envp = envHead;
+ serverInfoPtr->directive = APP_CLASS_STANDARD;
+
/*
* Set envHead to NULL so that if there is an error below we don't
* free the environment structure twice.
*/
envHead = NULL;
+
/*
* Create an IPC path for the AppClass.
*/
if(affinity == FALSE) {
! int listenFd;
! if(serverInfoPtr->port == -1) {
! /* local IPC */
! listenFd = OS_CreateLocalIpcFd(serverInfoPtr->ipcAddr,
! serverInfoPtr->listenQueueDepth, uid, gid,
! MakeSocketName, bindname, -1);
! } else {
! /* TCP/IP */
! listenFd = OS_CreateRemoteIpcFd(serverInfoPtr->ipcAddr,
! serverInfoPtr->port, serverInfoPtr->listenQueueDepth);
! }
!
if(listenFd < 0) {
! sprintf(errMsg, "AppClass: could not create IPC socket\n");
goto ErrorReturn;
! }
! serverInfoPtr->listenFd = listenFd;
! /*
! * Propagate listenFd to each process so that process manager
! * doesn't have to understand affinity.
! */
! for(i = 0; i < serverInfoPtr->maxProcesses; i++) {
! serverInfoPtr->procInfo[i].listenFd = listenFd;
}
}
Free(argv[1]);
Free(argv);
Free(errMsg);
return NULL;
!
! MissingValueReturn:
sprintf(errMsg, "AppClass: missing value for %s\n", argv[i]);
goto ErrorReturn;
! BadValueReturn:
sprintf(errMsg, "AppClass: bad value \"%s\" for %s\n", argv[i],
argv[i-1]);
goto ErrorReturn;
! ErrorReturn:
if(serverInfoPtr != NULL) {
FreeFcgiServerInfo(serverInfoPtr);
}
***************
*** 2340,2345 ****
--- 2851,3001 ----
/*
*----------------------------------------------------------------------
*
+ * ExternalAppClassCmd --
+ *
+ * Implements the FastCGI ExternalAppClass configuration directive.
+ * This command adds a fast cgi program class which will be used by the
+ * httpd parent to connect to the fastcgi process which is not managed
+ * by the web server and may be running on the local or remote machine.
+ *
+ * ExternalAppClass <name> [-host hostname:port] \
+ * [-socket socket_path]
+ *
+ *
+ * Results:
+ * NULL or an error message.
+ *
+ * Side effects:
+ * Registers a new ExternalAppClass handler for FastCGI.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ const char *ExternalAppClassCmd(cmd_parms *cmd, void *dummy, char *arg)
+ {
+ int argc;
+ char **argv = NULL;
+ char *className = NULL;
+ char *hostPort = NULL;
+ char *localPath = NULL;
+ FastCgiServerInfo *serverInfoPtr = NULL;
+ int configResult = -1;
+ int i;
+ char *errMsg = Malloc(1024);
+
+ /*
+ * If this is the first call to ExternalAppClassCmd since a
+ * server restart, clean up structures created by the previous
+ * sequence of ExternalAppClassCmds.
+ */
+ CleanupPreviousConfig();
+
+ /*
+ * Parse the raw arguments into tokens.
+ * argv[0] is empty and argv[1] is the symbolic
+ * name of the connection. Note that this name
+ * is not used for anything but the lookup of the
+ * proper server.
+ */
+ argv = ParseApacheRawArgs(arg, &argc);
+ if(argc < 3) {
+ sprintf(errMsg, "ExternalAppClass: Too few args\n");
+ goto ErrorReturn;
+ }
+ className = argv[1];
+ serverInfoPtr = LookupFcgiServerInfo(className);
+ if(serverInfoPtr != NULL) {
+ sprintf(errMsg,
+ "ExternalAppClass: Redefinition of previously \
+ defined class %s\n",
+ className);
+ goto ErrorReturn;
+ }
+
+ /*
+ * Parse out the command line arguments.
+ */
+ for(i = 2; i < argc; i++) {
+ if((strcmp(argv[i], "-host") == 0)) {
+ if((i + 1) == argc) {
+ goto MissingValueReturn;
+ }
+ i++;
+ hostPort = argv[i];
+ continue;
+ } else if((strcmp(argv[i], "-socket") == 0)) {
+ if((i+1) == argc) {
+ goto MissingValueReturn;
+ }
+ i++;
+ localPath = argv[i];
+ continue;
+ } else {
+ sprintf(errMsg, "ExternalAppClass: Unknown option %s\n",
argv[i]);
+ goto ErrorReturn;
+ }
+ } /* for */
+
+ /*
+ * Check out that we do not have any conflicts
+ */
+ if(((hostPort != NULL) && (localPath != NULL)) ||
+ ((hostPort == NULL) && (localPath == NULL))) {
+ sprintf(errMsg, "ExternalAppClass: Conflict of arguments -port \
+ and -socket.\n");
+ goto ErrorReturn;
+ }
+
+ /*
+ * The following code will have to change when Apache will
+ * begin to support connections with affinity. Note that the
+ * className becomes an execPath member of the serverInfo
+ * structure and it used just for lookups. I also put in values
+ * for affinity and numInstances in order to keep most of the
+ * common code in sync.
+ */
+ serverInfoPtr = CreateFcgiServerInfo(1, className);
+ ASSERT(serverInfoPtr != NULL);
+ DStringAppend(&serverInfoPtr->execPath, className, -1);
+ serverInfoPtr->directive = APP_CLASS_EXTERNAL;
+
+ if(hostPort != NULL) {
+ configResult = ConfigureTCPServer(hostPort, FALSE,
+ 1, serverInfoPtr);
+ } else {
+ configResult = ConfigureLocalServer(localPath, FALSE,
+ 1, serverInfoPtr);
+ }
+
+ if(configResult == 0) {
+ return NULL;
+ } else {
+ sprintf(errMsg, "ExternalAppClass: Unable to configure server\n");
+ goto ErrorReturn;
+ }
+
+ MissingValueReturn:
+ sprintf(errMsg, "ExternalAppClass: missing value for %s\n", argv[i]);
+ goto ErrorReturn;
+ BadValueReturn:
+ sprintf(errMsg, "ExternalAppClass: bad value \"%s\" for %s\n",
+ argv[i], argv[i-1]);
+ goto ErrorReturn;
+ ErrorReturn:
+ if(serverInfoPtr != NULL) {
+ FreeFcgiServerInfo(serverInfoPtr);
+ }
+ if(argv != NULL) {
+ Free(argv[1]);
+ Free(argv);
+ }
+ return errMsg;
+ }
+
+
+ /*
+ *----------------------------------------------------------------------
+ *
* Code related to the FastCGI process manager.
*
*----------------------------------------------------------------------
***************
*** 2374,2379 ****
--- 3030,3055 ----
static char *errorLogPathname = NULL;
static sigset_t signalsToBlock;
+ static FILE *FastCgiProcMgrGetErrLog(void)
+ {
+ FILE *errorLogFile = NULL;
+ if(errorLogPathname != NULL) {
+ /*
+ * errorLogFile = fopen(errorLogPathname, "a"),
+ * but work around faulty implementations of fopen (SunOS)
+ */
+ int fd = open(errorLogPathname, O_WRONLY | O_APPEND | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if(fd >= 0) {
+ errorLogFile = fdopen(fd, "a");
+ }
+ }
+ if(errorLogFile == NULL) {
+ errorLogFile = fopen("/dev/null", "a");
+ }
+ return errorLogFile;
+ }
+
static void FastCgiProcMgrSignalHander(int signo)
{
if(signo == SIGTERM) {
***************
*** 2386,2397 ****
--- 3062,3075 ----
static int CaughtSigTerm(void)
{
int result;
+
/*
* Start of critical region for caughtSigTerm
*/
sigprocmask(SIG_BLOCK, &signalsToBlock, NULL);
result = caughtSigTerm;
sigprocmask(SIG_UNBLOCK, &signalsToBlock, NULL);
+
/*
* End of critical region for caughtSigTerm
*/
***************
*** 2408,2429 ****
int waitStatus, status, callWaitPid;
sigset_t sigMask;
pid_t myPid = getpid();
! FILE *errorLogFile = NULL;
- if(errorLogPathname != NULL) {
- /*
- * errorLogFile = fopen(errorLogPathname, "a"),
- * but work around faulty implementations of fopen (SunOS)
- */
- int fd = open(errorLogPathname, O_WRONLY | O_APPEND | O_CREAT,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
- if (fd >= 0) {
- errorLogFile = fdopen(fd, "a");
- }
- }
- if(errorLogFile == NULL) {
- errorLogFile = fopen("/dev/null", "a");
- }
/*
* If the Apache parent process is running as root,
* consider reducing privileges now.
--- 3086,3093 ----
int waitStatus, status, callWaitPid;
sigset_t sigMask;
pid_t myPid = getpid();
! FILE *errorLogFile = FastCgiProcMgrGetErrLog();
/*
* If the Apache parent process is running as root,
* consider reducing privileges now.
***************
*** 2431,2440 ****
if(geteuid() == 0 && setuid(user_id) == -1) {
fprintf(errorLogFile,
"[%s] mod_fastcgi: Unable to change uid\n",
! get_time());
fflush(errorLogFile);
exit(1);
}
/*
* Set up to handle SIGTERM, SIGCHLD, and SIGALRM.
*/
--- 3095,3106 ----
if(geteuid() == 0 && setuid(user_id) == -1) {
fprintf(errorLogFile,
"[%s] mod_fastcgi: Unable to change uid\n",
! " exiting\n",
! get_time());
fflush(errorLogFile);
exit(1);
}
+
/*
* Set up to handle SIGTERM, SIGCHLD, and SIGALRM.
*/
***************
*** 2449,2454 ****
--- 3115,3121 ----
ASSERT(OS_Signal(SIGTERM, FastCgiProcMgrSignalHander) != SIG_ERR);
ASSERT(OS_Signal(SIGCHLD, FastCgiProcMgrSignalHander) != SIG_ERR);
ASSERT(OS_Signal(SIGALRM, FastCgiProcMgrSignalHander) != SIG_ERR);
+
/*
* s->procInfo[i].pid == 0 means we've never tried to start this one.
*/
***************
*** 2458,2463 ****
--- 3125,3131 ----
s->procInfo[i].pid = 0;
}
}
+
/*
* Loop until SIGTERM
*/
***************
*** 2466,2477 ****
int sleepSeconds = INT_MAX;
pid_t childPid;
int waitStatus;
/*
* Examine each configured AppClass for a process that needs
* starting. Compute the earliest time when the start should
! * be attempted, starting it now if the time has passed.
*/
for(s = fastCgiServers; s != NULL; s = s->next) {
for(i = 0; i < s->maxProcesses; i++) {
if(s->procInfo[i].pid <= 0) {
time_t restartTime = s->restartTime + s->restartDelay;
--- 3134,3151 ----
int sleepSeconds = INT_MAX;
pid_t childPid;
int waitStatus;
+
/*
* Examine each configured AppClass for a process that needs
* starting. Compute the earliest time when the start should
! * be attempted, starting it now if the time has passed. Also,
! * remember that we do NOT need to restart externally managed
! * FastCGI applications.
*/
for(s = fastCgiServers; s != NULL; s = s->next) {
+ if(s->directive == APP_CLASS_EXTERNAL) {
+ continue;
+ }
for(i = 0; i < s->maxProcesses; i++) {
if(s->procInfo[i].pid <= 0) {
time_t restartTime = s->restartTime + s->restartDelay;
***************
*** 2490,2496 ****
s->procInfo[i].listenFd,
s->processPriority,
DStringValue(&s->execPath),
! s->envp);
if(status != 0) {
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s"
--- 3164,3171 ----
s->procInfo[i].listenFd,
s->processPriority,
DStringValue(&s->execPath),
! s->envp,
! FastCgiProcMgrGetErrLog);
if(status != 0) {
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s"
***************
*** 2511,2517 ****
" restarted with pid %d.\n",
get_time(),
DStringValue(&s->execPath),
! s->procInfo[i].pid);
fflush(errorLogFile);
}
ASSERT(s->procInfo[i].pid > 0);
--- 3186,3192 ----
" restarted with pid %d.\n",
get_time(),
DStringValue(&s->execPath),
! (int)s->procInfo[i].pid);
fflush(errorLogFile);
}
ASSERT(s->procInfo[i].pid > 0);
***************
*** 2521,2527 ****
}
}
}
! /*
* Start of critical region for caughtSigChld and caughtSigTerm.
*/
sigprocmask(SIG_BLOCK, &signalsToBlock, NULL);
--- 3196,3203 ----
}
}
}
!
! /*
* Start of critical region for caughtSigChld and caughtSigTerm.
*/
sigprocmask(SIG_BLOCK, &signalsToBlock, NULL);
***************
*** 2541,2546 ****
--- 3217,3223 ----
callWaitPid = caughtSigChld;
caughtSigChld = FALSE;
sigprocmask(SIG_UNBLOCK, &signalsToBlock, NULL);
+
/*
* End of critical region for caughtSigChld and caughtSigTerm.
*/
***************
*** 2550,2555 ****
--- 3227,3233 ----
*/
continue;
}
+
/*
* We've caught SIGCHLD, so poll for signal notifications
* using waitpid. If a child has died, write a log message
***************
*** 2564,2569 ****
--- 3242,3250 ----
break;
}
for(s = fastCgiServers; s != NULL; s = s->next) {
+ if(s->directive == APP_CLASS_EXTERNAL) {
+ continue;
+ }
for(i = 0; i < s->maxProcesses; i++) {
if(s->procInfo[i].pid == childPid) {
goto ChildFound;
***************
*** 2576,2589 ****
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s pid %d terminated"
" by calling exit with status = %d.\n",
! get_time(), DStringValue(&s->execPath), childPid,
WEXITSTATUS(waitStatus));
} else {
ASSERT(WIFSIGNALED(waitStatus));
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s pid %d terminated"
" due to uncaught signal %d.\n",
! get_time(), DStringValue(&s->execPath), childPid,
WTERMSIG(waitStatus));
}
s->procInfo[i].pid = -1;
--- 3257,3270 ----
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s pid %d terminated"
" by calling exit with status = %d.\n",
! get_time(), DStringValue(&s->execPath),
(int)childPid,
WEXITSTATUS(waitStatus));
} else {
ASSERT(WIFSIGNALED(waitStatus));
fprintf(errorLogFile,
"[%s] mod_fastcgi: AppClass %s pid %d terminated"
" due to uncaught signal %d.\n",
! get_time(), DStringValue(&s->execPath),
(int)childPid,
WTERMSIG(waitStatus));
}
s->procInfo[i].pid = -1;
***************
*** 2591,2601 ****
fflush(errorLogFile);
} /* for (;;) */
} /* for (;;) */
! ProcessSigTerm:
/*
* Kill off the children, then exit.
*/
for(s = fastCgiServers; s != NULL; s = s->next) {
for(i = 0; i < s->maxProcesses; i++) {
if(s->procInfo[i].pid > 0) {
kill(s->procInfo[i].pid, SIGTERM);
--- 3272,3286 ----
fflush(errorLogFile);
} /* for (;;) */
} /* for (;;) */
!
! ProcessSigTerm:
/*
* Kill off the children, then exit.
*/
for(s = fastCgiServers; s != NULL; s = s->next) {
+ if(s->directive == APP_CLASS_EXTERNAL) {
+ continue;
+ }
for(i = 0; i < s->maxProcesses; i++) {
if(s->procInfo[i].pid > 0) {
kill(s->procInfo[i].pid, SIGTERM);
***************
*** 2624,2630 ****
void ModFastCgiInit(server_rec *s, pool *p)
{
if(s->error_fname != NULL) {
! errorLogPathname = server_root_relative(p, s->error_fname);
}
if(fastCgiServers != NULL) {
ASSERT(readingConfig);
--- 3309,3315 ----
void ModFastCgiInit(server_rec *s, pool *p)
{
if(s->error_fname != NULL) {
! errorLogPathname = StringCopy(server_root_relative(p,
s->error_fname));
}
if(fastCgiServers != NULL) {
ASSERT(readingConfig);
***************
*** 2696,2704 ****
*
*----------------------------------------------------------------------
*/
! static void MakeBeginRequestBody(int role,
! int keepConnection,
! FCGI_BeginRequestBody *body)
{
ASSERT((role >> 16) == 0);
body->roleB1 = (role >> 8) & 0xff;
--- 3381,3391 ----
*
*----------------------------------------------------------------------
*/
!
! static void MakeBeginRequestBody(
! int role,
! int keepConnection,
! FCGI_BeginRequestBody *body)
{
ASSERT((role >> 16) == 0);
body->roleB1 = (role >> 8) & 0xff;
***************
*** 2721,2726 ****
--- 3408,3414 ----
*
*----------------------------------------------------------------------
*/
+
static void SendBeginRequest(FastCgiInfo *infoPtr)
{
FCGI_BeginRequestBody body;
***************
*** 2756,2766 ****
*
*----------------------------------------------------------------------
*/
static void FCGIUtil_BuildNameValueHeader(
int nameLen,
int valueLen,
unsigned char *headerBuffPtr,
! int *headerLenPtr) {
unsigned char *startHeaderBuffPtr = headerBuffPtr;
ASSERT(nameLen >= 0);
--- 3444,3456 ----
*
*----------------------------------------------------------------------
*/
+
static void FCGIUtil_BuildNameValueHeader(
int nameLen,
int valueLen,
unsigned char *headerBuffPtr,
! int *headerLenPtr)
! {
unsigned char *startHeaderBuffPtr = headerBuffPtr;
ASSERT(nameLen >= 0);
***************
*** 2800,2805 ****
--- 3490,3496 ----
*
*----------------------------------------------------------------------
*/
+
static void SendEnvironment(WS_Request *reqPtr, FastCgiInfo *infoPtr)
{
int headerLen, nameLen, valueLen;
***************
*** 2837,2843 ****
}
SendPacketHeader(infoPtr, FCGI_PARAMS, 0);
}
-
/*
*----------------------------------------------------------------------
--- 3528,3533 ----
***************
*** 2859,2864 ****
--- 3549,3555 ----
*
*----------------------------------------------------------------------
*/
+
static void ClientToCgiBuffer(FastCgiInfo *infoPtr)
{
int movelen;
***************
*** 2871,2876 ****
--- 3562,3568 ----
if(infoPtr->eofSent) {
return;
}
+
/*
* If there's some client data and room for at least one byte
* of data in the output buffer (after protocol overhead), then
***************
*** 2883,2888 ****
--- 3575,3581 ----
SendPacketHeader(infoPtr, FCGI_STDIN, movelen);
BufferMove(infoPtr->outbufPtr, infoPtr->reqInbufPtr, movelen);
}
+
/*
* If all the client data has been sent, and there's room
* in the output buffer, indicate EOF.
***************
*** 2914,2919 ****
--- 3607,3613 ----
*
*----------------------------------------------------------------------
*/
+
static int CgiToClientBuffer(FastCgiInfo *infoPtr)
{
FCGI_Header header;
***************
*** 2924,2931 ****
* State #1: looking for the next complete packet header.
*/
if(infoPtr->gotHeader == FALSE) {
! if(BufferLength(infoPtr->inbufPtr) < sizeof(FCGI_Header))
return OK;
BufferGetData(infoPtr->inbufPtr, (char *) &header,
sizeof(FCGI_Header));
/*
--- 3618,3626 ----
* State #1: looking for the next complete packet header.
*/
if(infoPtr->gotHeader == FALSE) {
! if(BufferLength(infoPtr->inbufPtr) < sizeof(FCGI_Header)) {
return OK;
+ }
BufferGetData(infoPtr->inbufPtr, (char *) &header,
sizeof(FCGI_Header));
/*
***************
*** 2941,2946 ****
--- 3636,3642 ----
infoPtr->gotHeader = TRUE;
infoPtr->paddingLen = header.paddingLength;
}
+
/*
* State #2: got a header, and processing packet bytes.
*/
***************
*** 2949,2964 ****
switch(infoPtr->packetType) {
case FCGI_STDOUT:
if(len > 0) {
! if(infoPtr->parseHeader)
! BufferDStringAppend(infoPtr->header,
! infoPtr->inbufPtr, len);
! else {
! len = min(BufferFree(infoPtr->reqOutbufPtr), len);
! if (len > 0)
! BufferMove(infoPtr->reqOutbufPtr,
! infoPtr->inbufPtr, len);
! else
! return OK;
}
infoPtr->dataLen -= len;
}
--- 3645,3667 ----
switch(infoPtr->packetType) {
case FCGI_STDOUT:
if(len > 0) {
! switch(infoPtr->parseHeader) {
! case SCAN_CGI_READING_HEADERS:
! BufferDStringAppend(infoPtr->header,
! infoPtr->inbufPtr, len);
! break;
! case SCAN_CGI_FINISHED:
! len = min(BufferFree(infoPtr->reqOutbufPtr),
len);
! if(len > 0) {
! BufferMove(infoPtr->reqOutbufPtr,
! infoPtr->inbufPtr, len);
! } else {
! return OK;
! }
! break;
! default:
! /* Toss data on the floor */
! break;
}
infoPtr->dataLen -= len;
}
***************
*** 2973,2979 ****
case FCGI_END_REQUEST:
if(!infoPtr->readingEndRequestBody) {
if(infoPtr->dataLen != sizeof(FCGI_EndRequestBody)) {
- infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN);
sprintf(infoPtr->errorMsg,
"mod_fastcgi: FastCGI protocol error -"
" FCGI_END_REQUEST record body size %d !="
--- 3676,3681 ----
***************
*** 2993,2999 ****
/*
* XXX: What to do with FCGI_OVERLOADED?
*/
- infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN);
sprintf(infoPtr->errorMsg,
"mod_fastcgi: FastCGI protocol error -"
" FCGI_END_REQUEST record protocolStatus %d !="
--- 3695,3700 ----
***************
*** 3021,3026 ****
--- 3722,3728 ----
infoPtr->dataLen -= len;
break;
} /* switch */
+
/*
* Discard padding, then start looking for
* the next header.
***************
*** 3052,3057 ****
--- 3754,3764 ----
* If the end of the string is reached, return a pointer to the
* end of the string.
*
+ * If the FIRST character(s) in the line are '\n' or "\r\n", the
+ * first character is replaced with a NULL and next character
+ * past the newline is returned. NOTE: this condition supercedes
+ * the processing of RFC-822 continuation lines.
+ *
* If continuation is set to 'TRUE', then it parses a (possible)
* sequence of RFC-822 continuation lines.
*
***************
*** 3063,3074 ****
*
*----------------------------------------------------------------------
*/
char *ScanLine(char *start, int continuation)
{
char *p = start;
char *end = start;
! if(*p != '\n') {
if(continuation) {
while(*p != '\0') {
if(*p == '\n' && p[1] != ' ' && p[1] != '\t')
--- 3770,3784 ----
*
*----------------------------------------------------------------------
*/
+
char *ScanLine(char *start, int continuation)
{
char *p = start;
char *end = start;
! if(p[0] == '\r' && p[1] == '\n') { /* If EOL in 1st 2 chars */
! p++; /* point to \n and stop */
! } else if(*p != '\n') {
if(continuation) {
while(*p != '\0') {
if(*p == '\n' && p[1] != ' ' && p[1] != '\t')
***************
*** 3076,3290 ****
p++;
}
} else {
! while(*p != '\0' && *p != '\n')
p++;
}
}
end = p;
! if(*end != '\0')
end++;
/*
* Trim any trailing whitespace.
*/
! while(isspace(p[-1]) && p > start)
p--;
*p = '\0';
return end;
}
- /*
- *----------------------------------------------------------------------
- *
- * HTTPTime --
- *
- * Return the current time, in HTTP time format.
- *
- * Results:
- * Returns pointer to time string, in static allocated array.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- #define TIMEBUFSIZE 256
- char *HTTPTime(struct tm *when)
- {
- static char str[TIMEBUFSIZE];
-
- strftime(str, TIMEBUFSIZE-1, "%A, %d-%b-%y %T GMT", when);
- return str;
- }
- #undef TIMEBUFSIZE
-
/*
*----------------------------------------------------------------------
*
! * SendHeader --
! *
! * Queue an HTTP header line for sending back to the client.
! * If this is an old HTTP request (HTTP/0.9) or an inner (nested)
! * request, ignore and return.
! *
! * NOTE: This routine assumes that the sent data will fit in
! * remaining space in the output buffer.
! *
! * Results:
! * None.
*
! * Side effects:
! * Header information queued to be sent to client.
*
! *----------------------------------------------------------------------
! */
! void SendHeader(FastCgiInfo *reqPtr, ...)
! {
! va_list argList;
! char *str;
!
! va_start(argList, reqPtr);
! while (TRUE) {
! str = va_arg(argList, char *);
! if(str == NULL)
! break;
! BufferAdd(reqPtr->reqOutbufPtr, str);
! ASSERT(BufferFree(reqPtr->reqOutbufPtr) > 0);
! }
! BufferAdd(reqPtr->reqOutbufPtr, "\r\n"); /* terminate */
! ASSERT(BufferFree(reqPtr->reqOutbufPtr) > 0);
! va_end(argList);
! }
!
! /*
! *----------------------------------------------------------------------
*
! * AddHeaders --
*
* Results:
! * None.
*
* Side effects:
! * None.
*
*----------------------------------------------------------------------
*/
- void AddHeaders(FastCgiInfo *reqPtr, char *str)
- {
- BufferAdd(reqPtr->reqOutbufPtr, str);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * BeginHeader --
- *
- * Begin a standard HTTP header: emits opening message
- * (i.e. "200 OK") and standard header matter.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- void BeginHeader(FastCgiInfo *reqPtr, char *msg)
- {
- time_t now;
-
- ASSERT(BufferLength(reqPtr->reqOutbufPtr) == 0);
-
- now = time(NULL);
- SendHeader(reqPtr, SERVER_PROTOCOL, " ", msg, NULL);
- SendHeader(reqPtr, "Date: ", HTTPTime(gmtime(&now)), NULL);
- SendHeader(reqPtr, "Server: ", SERVER_VERSION, NULL);
- SendHeader(reqPtr, "MIME-version: 1.0", NULL);
! /*
! * We shouldn't have run out of space in the output buffer.
! */
! ASSERT(BufferFree(reqPtr->reqOutbufPtr) > 0);
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * EndHeader --
! *
! * Marks the end of the HTTP header: sends a blank line.
! *
! * Results:
! * None.
! *
! * Side effects:
! * None.
! *
! *----------------------------------------------------------------------
! */
! void EndHeader(FastCgiInfo *reqPtr)
{
! SendHeader(reqPtr, "", NULL);
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * ComposeURL --
! *
! * XXX
! *
! *----------------------------------------------------------------------
! */
! #define HostInfo(x) x->server
! #define HostName(x) x->server->server_hostname
! #define HostPort(x) x->server->port
!
! void ComposeURL(WS_Request *reqPtr, char *url, DString *result)
! {
! if(url[0] == '/') {
! char portStr[10];
! if (HostInfo(reqPtr) && HostName(reqPtr)) {
! sprintf(portStr, "%d", HostPort(reqPtr));
! DStringAppend(result, "http://", -1);
! DStringAppend(result, HostName(reqPtr), -1);
! DStringAppend(result, ":", -1);
! DStringAppend(result, portStr, -1);
! }
! }
! DStringAppend(result, url, -1);
! }
!
! /*
! *----------------------------------------------------------------------
! *
! * ScanCGIHeader --
! *
! * Scans the CGI header data to see if the CGI program has sent a
! * complete header. If it has, then parse the header and queue
! * the HTTP response header information back to the client.
! *
! * Results:
! * TRUE if completed without error, FALSE otherwise.
! *
! * Side effects:
! * Sets 'reqPtr->parseHeader' to TRUE if the header was parsed
! * successfully.
! *
! *----------------------------------------------------------------------
! */
! int ScanCGIHeader(WS_Request *reqPtr, FastCgiInfo *infoPtr)
! {
! DString status;
! DString headers;
! char *line, *next, *p, *data, *name;
! int len, flag, sent;
! int bufFree;
! ASSERT(infoPtr->parseHeader == TRUE);
/*
* Do we have the entire header? Scan for the blank line that
--- 3786,3854 ----
p++;
}
} else {
! while(*p != '\0' && *p != '\n') {
p++;
+ }
}
}
end = p;
! if(*end != '\0') {
end++;
+ }
/*
* Trim any trailing whitespace.
*/
! while(isspace(p[-1]) && p > start) {
p--;
+ }
*p = '\0';
return end;
}
/*
*----------------------------------------------------------------------
*
! * ScanCGIHeader --
*
! * Call with reqPtr->parseHeader == SCAN_CGI_READING_HEADERS
! * and initial script output in infoPtr->header.
*
! * If the initial script output does not include the header
! * terminator ("\r\n\r\n") ScanCGIHeader returns with no side
! * effects, to be called again when more script output
! * has been appended to infoPtr->header.
*
! * If the initial script output includes the header terminator,
! * ScanCGIHeader parses the headers and determines whether or
! * not the remaining script output will be sent to the client.
! * If so, ScanCGIHeader sends the HTTP response headers to the
! * client and copies any non-header script output to the output
! * buffer reqOutbuf.
*
* Results:
! * none.
*
* Side effects:
! * May set reqPtr->parseHeader to:
! * SCAN_CGI_FINISHED -- headers parsed, returning script response
! * SCAN_CGI_BAD_HEADER -- malformed header from script
! * (specific message placed in infoPtr->errorMsg.)
! * SCAN_CGI_INT_REDIRECT -- handler should perform internal redirect
! * SCAN_CGI_SRV_REDIRECT -- handler should return REDIRECT
*
*----------------------------------------------------------------------
*/
! void ScanCGIHeader(WS_Request *reqPtr, FastCgiInfo *infoPtr)
{
! char *p, *next, *name, *value, *location;
! int len, flag;
! int hasContentType, hasStatus, hasLocation;
! ASSERT(infoPtr->parseHeader == SCAN_CGI_READING_HEADERS);
/*
* Do we have the entire header? Scan for the blank line that
***************
*** 3308,3416 ****
}
/*
! * Return (to be called later when we have more data) if we don't have
! * and entire header.
*/
! if(flag < 2)
! return TRUE;
!
! infoPtr->parseHeader = FALSE;
! DStringInit(&status);
! DStringAppend(&status, "200 OK", -1);
! DStringInit(&headers);
next = DStringValue(infoPtr->header);
for(;;) {
! next = ScanLine(line = next, TRUE);
! if(*line == '\0') {
break;
}
! if((p = strpbrk(line, " \t")) != NULL) {
! data = p + 1;
! *p = '\0';
! } else {
! data = "";
}
! while(isspace(*data)) {
! data++;
}
/*
! * Handle "Location:" and "Status:" specially.
! * All other headers get passed through unmodified.
*/
! if(!strcasecmp(line, "Location:")) {
! DStringTrunc(&status, 0);
! DStringAppend(&status, "302 Redirect", -1);
! DStringAppend(&headers, "Location: ", -1);
/*
! * This is a deviation from the CGI/1.1 spec.
! *
! * The spec says that "virtual paths" should be treated
! * as if the client had accessed the file in the first
! * place. This usually breaks relative references in the
! * referenced document.
! *
! * XXX: do more research on this?
*/
! ComposeURL(reqPtr, data, &headers);
! DStringAppend(&headers, "\r\n", -1);
! } else if(!strcasecmp(line, "Status:")) {
! DStringTrunc(&status, 0);
! DStringAppend(&status, data, -1);
} else {
! if(data != NULL) {
! DStringAppend(&headers, line, -1);
! DStringAppend(&headers, " ", 1);
! DStringAppend(&headers, data, -1);
! DStringAppend(&headers, "\r\n", -1);
}
}
}
-
/*
! * We're done scanning the CGI script's header output. Now
! * we have to write to the client: status, CGI header, and
! * any over-read CGI output.
*/
! BeginHeader(infoPtr, DStringValue(&status));
! DStringFree(&status);
!
! AddHeaders(infoPtr, DStringValue(&headers));
! DStringFree(&headers);
! EndHeader(infoPtr);
!
len = next - DStringValue(infoPtr->header);
len = DStringLength(infoPtr->header) - len;
ASSERT(len >= 0);
! bufFree = BufferFree(infoPtr->reqOutbufPtr);
!
! if (bufFree < len) {
! /* should the same fix be made in core server? */
! int bufLen = BufferLength(infoPtr->reqOutbufPtr);
! Buffer *newBuf = BufferCreate(len + bufLen);
! BufferMove(newBuf, infoPtr->reqOutbufPtr, bufLen);
! BufferDelete(infoPtr->reqOutbufPtr);
! infoPtr->reqOutbufPtr = newBuf;
! }
! bufFree = BufferFree(infoPtr->reqOutbufPtr);
! if(bufFree == 0)
! goto fail;
!
/*
! * Only send the body for methods other than HEAD.
*/
! if(!infoPtr->reqPtr->header_only) {
! if(len > 0) {
! sent = BufferAddData(infoPtr->reqOutbufPtr, next, len);
! if(sent != len)
! goto fail;
! }
}
! return TRUE;
! fail:
! return FALSE;
}
/*
--- 3872,4041 ----
}
/*
! * Return (to be called later when we have more data)
! * if we don't have an entire header.
*/
! if(flag < 2) {
! return;
! }
! /*
! * Parse all the headers.
! */
! infoPtr->parseHeader = SCAN_CGI_FINISHED;
! hasContentType = hasStatus = hasLocation = FALSE;
next = DStringValue(infoPtr->header);
for(;;) {
! next = ScanLine(name = next, TRUE);
! if(*name == '\0') {
break;
}
! if((p = strchr(name, ':')) == NULL) {
! goto BadHeader;
! }
! value = p + 1;
! while(p != name && isspace(*(p - 1))) {
! p--;
}
! if(p == name) {
! goto BadHeader;
}
+ *p = '\0';
+ if(strpbrk(name, " \t") != NULL) {
+ *p = ' ';
+ goto BadHeader;
+ }
+ while(isspace(*value)) {
+ value++;
+ }
+
/*
! * name is the trimmed header name and value the
! * trimmed header value. Perform checks, then record value
! * in the request data structure.
*/
! if(!strcasecmp(name, "Content-type")) {
! if(hasContentType) {
! goto DuplicateNotAllowed;
! }
! hasContentType = TRUE;
! reqPtr->content_type = pstrdup(reqPtr->pool, value);
! } else if(!strcasecmp(name, "Status")) {
! int statusValue = strtol(value, NULL, 10);
! if(hasStatus) {
! goto DuplicateNotAllowed;
! } else if(statusValue < 0) {
! goto BadStatusValue;
! }
! hasStatus = TRUE;
! reqPtr->status = statusValue;
! reqPtr->status_line = pstrdup(reqPtr->pool, value);
! } else if(!strcasecmp(name, "Location")) {
! if(hasLocation) {
! goto DuplicateNotAllowed;
! }
! hasLocation = TRUE;
! table_set(reqPtr->headers_out, "Location", value);
! } else {
/*
! * Don't merge headers. If the script wants them
! * merged, the script can do the merging.
*/
! table_add(reqPtr->err_headers_out, name, value);
! }
! }
! /*
! * Who responds, this handler or Apache?
! */
! if(hasLocation) {
! location = table_get(reqPtr->headers_out, "Location");
! if(location[0] == '/') {
! /*
! * Location is an absolute path. This handler will
! * consume all script output, then have Apache perform an
! * internal redirect.
! */
! infoPtr->parseHeader = SCAN_CGI_INT_REDIRECT;
! return;
} else {
! /*
! * Location is an absolute URL. If the script didn't
! * produce a Content-type header, this handler will
! * consume all script output and then have Apache generate
! * its standard redirect response. Otherwise this handler
! * will transmit the script's response.
! */
! if(!hasContentType) {
! infoPtr->parseHeader = SCAN_CGI_SRV_REDIRECT;
! return;
! } else {
! reqPtr->status = REDIRECT;
! if (!hasStatus) {
! reqPtr->status_line =
! pstrdup(reqPtr->pool, "302 Moved Temporarily");
! }
}
}
}
/*
! * We're responding. Send headers, buffer excess script output.
*/
! send_http_header(reqPtr);
! if(reqPtr->header_only) {
! return;
! }
len = next - DStringValue(infoPtr->header);
len = DStringLength(infoPtr->header) - len;
ASSERT(len >= 0);
+ if(BufferFree(infoPtr->reqOutbufPtr) < len) {
+ /*
+ * XXX: Since headers don't pass through reqOutbuf anymore,
+ * the following code appears unnecessary. But does Open Market
+ * server have a lurking problem here?
+ */
+ int bufLen = BufferLength(infoPtr->reqOutbufPtr);
+ Buffer *newBuf = BufferCreate(len + bufLen);
+ BufferMove(newBuf, infoPtr->reqOutbufPtr, bufLen);
+ BufferDelete(infoPtr->reqOutbufPtr);
+ infoPtr->reqOutbufPtr = newBuf;
+ }
+ ASSERT(BufferFree(infoPtr->reqOutbufPtr) >= len);
+ if(len > 0) {
+ int sent = BufferAddData(infoPtr->reqOutbufPtr, next, len);
+ ASSERT(sent == len);
+ }
+ return;
! BadHeader:
/*
! * Log an informative message, but only log first line of
! * a multi-line header
*/
! if((p = strpbrk(name, "\r\n")) != NULL) {
! *p = '\0';
}
! Free(infoPtr->errorMsg);
! infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(name));
! sprintf(infoPtr->errorMsg,
! "mod_fastcgi: Malformed response header from app: '%s'", name);
! goto ErrorReturn;
!
! DuplicateNotAllowed:
! sprintf(infoPtr->errorMsg,
! "mod_fastcgi: Duplicate CGI response header '%s'"
! " not allowed", name);
! goto ErrorReturn;
!
! BadStatusValue:
! Free(infoPtr->errorMsg);
! infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(value));
! sprintf(infoPtr->errorMsg,
! "mod_fastcgi: Invalid Status value '%s'", value);
! goto ErrorReturn;
! ErrorReturn:
! infoPtr->parseHeader = SCAN_CGI_BAD_HEADER;
! return;
}
/*
***************
*** 3437,3442 ****
--- 4062,4068 ----
*
*----------------------------------------------------------------------
*/
+
static void FillOutbuf(WS_Request *reqPtr, FastCgiInfo *infoPtr)
{
char *end;
***************
*** 3480,3489 ****
--- 4106,4117 ----
*
*----------------------------------------------------------------------
*/
+
static void DrainReqOutbuf(WS_Request *reqPtr, FastCgiInfo *infoPtr)
{
char *begin;
int count;
+
BufferPeekToss(infoPtr->reqOutbufPtr, &begin, &count);
if(count == 0) {
return;
***************
*** 3507,3512 ****
--- 4135,4149 ----
* done with the request. This avoids the FastCGI application
* receiving SIGPIPE.
*
+ * If the FastCGI application sends a bad header, FastCGIDoWork
+ * continues reading from the application but sends no response
+ * to the client (returns SERVER_ERROR.)
+ *
+ * If the FastCGI application requests an internal redirect,
+ * or requests a redirect response without returning content,
+ * FastCGIDoWork sends no response and returns OK; the variable
+ * infoPtr->parseHeader tells the story.
+ *
* Results:
* OK or SERVER_ERROR
*
***************
*** 3515,3520 ****
--- 4152,4158 ----
*
*----------------------------------------------------------------------
*/
+
static int FastCgiDoWork(WS_Request *reqPtr, FastCgiInfo *infoPtr)
{
struct timeval timeOut, *timeOutPtr;
***************
*** 3535,3540 ****
--- 4173,4179 ----
if(!infoPtr->eofSent) {
FillOutbuf(reqPtr, infoPtr);
}
+
/*
* To avoid deadlock, don't do a blocking select to write to
* the FastCGI application without selecting to read from the
***************
*** 3597,3626 ****
if(infoPtr->exitStatusSet) {
keepReadingFromFcgiApp = FALSE;
}
! if(infoPtr->parseHeader) {
! if(!ScanCGIHeader(reqPtr, infoPtr)) {
! goto BadHeader;
! }
}
} /* while */
! if(infoPtr->parseHeader) {
! goto BadHeader;
}
! bflush(reqPtr->connection->client);
! bgetopt(reqPtr->connection->client, BO_BYTECT, &reqPtr->bytes_sent);
! /*
! * XXX: This would be the place to check the error status
! * of the connection to the client, if indeed we care.
! */
! return OK;
! BadHeader:
! infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN);
sprintf(infoPtr->errorMsg,
! "mod_fastcgi: Malformed CGI or HTTP header from FastCGI app");
return SERVER_ERROR;
! AppIoError:
/* No strerror prototype on SunOS? */
fromStrerror = (char *) strerror(errno);
infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(fromStrerror));
sprintf(infoPtr->errorMsg,
"mod_fastcgi: OS error '%s' while communicating with app",
--- 4236,4273 ----
if(infoPtr->exitStatusSet) {
keepReadingFromFcgiApp = FALSE;
}
! if(infoPtr->parseHeader == SCAN_CGI_READING_HEADERS) {
! ScanCGIHeader(reqPtr, infoPtr);
}
} /* while */
! switch(infoPtr->parseHeader) {
! case SCAN_CGI_FINISHED:
! bflush(reqPtr->connection->client);
! bgetopt(reqPtr->connection->client,
! BO_BYTECT, &reqPtr->bytes_sent);
! return OK;
! case SCAN_CGI_READING_HEADERS:
! goto UnterminatedHeader;
! case SCAN_CGI_BAD_HEADER:
! return SERVER_ERROR;
! case SCAN_CGI_INT_REDIRECT:
! case SCAN_CGI_SRV_REDIRECT:
! return OK;
! default:
! ASSERT(FALSE);
}
!
! UnterminatedHeader:
sprintf(infoPtr->errorMsg,
! "mod_fastcgi: Unterminated CGI response headers,"
! " %d bytes received from app",
! DStringLength(infoPtr->header));
return SERVER_ERROR;
!
! AppIoError:
/* No strerror prototype on SunOS? */
fromStrerror = (char *) strerror(errno);
+ Free(infoPtr->errorMsg);
infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(fromStrerror));
sprintf(infoPtr->errorMsg,
"mod_fastcgi: OS error '%s' while communicating with app",
***************
*** 3629,3642 ****
}
/*
! * Cleans up data associated with a request.
*/
void FcgiCleanUp(FastCgiInfo *infoPtr)
{
if(infoPtr == NULL) {
return;
}
if(DStringLength(infoPtr->errorOut) > 0) {
fprintf(infoPtr->reqPtr->server->error_log,
"[%s] mod_fastcgi: stderr output from %s: '%s'\n",
get_time(), infoPtr->reqPtr->filename,
--- 4276,4306 ----
}
/*
! *----------------------------------------------------------------------
! *
! * FcgiCleanUp --
! *
! * Cleanup the resources
! *
! * Results:
! * none.
! *
! * Side effects:
! * Free memory.
! *
! *----------------------------------------------------------------------
*/
+
void FcgiCleanUp(FastCgiInfo *infoPtr)
{
if(infoPtr == NULL) {
return;
}
if(DStringLength(infoPtr->errorOut) > 0) {
+ /*
+ * Would like to call log_reason here, but log_reason
+ * says "access failed" which isn't necessarily so.
+ */
fprintf(infoPtr->reqPtr->server->error_log,
"[%s] mod_fastcgi: stderr output from %s: '%s'\n",
get_time(), infoPtr->reqPtr->filename,
***************
*** 3671,3676 ****
--- 4335,4341 ----
*
*----------------------------------------------------------------------
*/
+
static int FastCgiHandler(WS_Request *reqPtr)
{
FastCgiServerInfo *serverInfoPtr;
***************
*** 3679,3694 ****
--- 4344,4362 ----
char *msg = NULL;
int status;
+ no2slash(reqPtr->filename);
serverInfoPtr = LookupFcgiServerInfo(reqPtr->filename);
if (serverInfoPtr == NULL) {
log_reason("mod_fastcgi: No AppClass directive for requested file",
reqPtr->filename, reqPtr);
return NOT_FOUND;
}
+
status = setup_client_block(reqPtr, REQUEST_CHUNKED_ERROR);
if(status != OK) {
return status;
}
+
/*
* Allocate and initialize FastCGI private data to augment the request
* structure.
***************
*** 3700,3707 ****
infoPtr->gotHeader = FALSE;
infoPtr->reqInbufPtr = BufferCreate(SERVER_BUFSIZE);
infoPtr->reqOutbufPtr = BufferCreate(SERVER_BUFSIZE);
! infoPtr->errorMsg = NULL;
! infoPtr->parseHeader = TRUE;
infoPtr->header = (DString *) malloc(sizeof(DString));
infoPtr->errorOut = (DString *) malloc(sizeof(DString));
infoPtr->reqPtr = reqPtr;
--- 4368,4375 ----
infoPtr->gotHeader = FALSE;
infoPtr->reqInbufPtr = BufferCreate(SERVER_BUFSIZE);
infoPtr->reqOutbufPtr = BufferCreate(SERVER_BUFSIZE);
! infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN);
! infoPtr->parseHeader = SCAN_CGI_READING_HEADERS;
infoPtr->header = (DString *) malloc(sizeof(DString));
infoPtr->errorOut = (DString *) malloc(sizeof(DString));
infoPtr->reqPtr = reqPtr;
***************
*** 3718,3729 ****
--- 4386,4399 ----
SendBeginRequest(infoPtr);
SendEnvironment(reqPtr, infoPtr);
+
/*
* Read as much as possible from the client now, before connecting
* to the FastCGI application.
*/
soft_timeout("read script input or send script output", reqPtr);
FillOutbuf(reqPtr, infoPtr);
+
/*
* Open a connection to the FastCGI application.
*/
***************
*** 3736,3774 ****
ipcAddrPtr->addrLen) < 0) {
goto ConnectionErrorReturn;
}
! if((status = FastCgiDoWork(reqPtr, infoPtr)) != OK) {
goto ErrorReturn;
}
! kill_timeout(reqPtr);
! FcgiCleanUp(infoPtr);
! return OK;
! ConnectionErrorReturn:
msg = (char *) strerror(errno);
if (msg == NULL) {
msg = "errno out of range";
}
infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(msg));
sprintf(infoPtr->errorMsg,
"mod_fastcgi: Could not connect to application,"
" OS error '%s'", msg);
! ErrorReturn:
log_reason(infoPtr->errorMsg, reqPtr->filename, reqPtr);
FcgiCleanUp(infoPtr);
return SERVER_ERROR;
}
command_rec fastcgi_cmds[] = {
{ "FastCgiIpcDir", FastCgiIpcDirCmd, NULL, RSRC_CONF, TAKE1,
NULL },
! { "AppClass", AppClassCmd, NULL, RSRC_CONF, RAW_ARGS,
! NULL },
{ NULL }
};
handler_rec fastcgi_handlers[] = {
{ FCGI_MAGIC_TYPE, FastCgiHandler },
{ NULL }
};
--- 4406,4460 ----
ipcAddrPtr->addrLen) < 0) {
goto ConnectionErrorReturn;
}
! status = FastCgiDoWork(reqPtr, infoPtr);
! kill_timeout(reqPtr);
! if(status != OK) {
goto ErrorReturn;
+ };
+ switch(infoPtr->parseHeader) {
+ case SCAN_CGI_INT_REDIRECT:
+ internal_redirect_handler(
+ table_get(reqPtr->headers_out, "Location"), reqPtr);
+ break;
+ case SCAN_CGI_SRV_REDIRECT:
+ status = REDIRECT;
+ break;
}
! goto CleanupReturn;
!
! ConnectionErrorReturn:
msg = (char *) strerror(errno);
if (msg == NULL) {
msg = "errno out of range";
}
+ Free(infoPtr->errorMsg);
infoPtr->errorMsg = Malloc(FCGI_ERRMSG_LEN + strlen(msg));
sprintf(infoPtr->errorMsg,
"mod_fastcgi: Could not connect to application,"
" OS error '%s'", msg);
! ErrorReturn:
log_reason(infoPtr->errorMsg, reqPtr->filename, reqPtr);
FcgiCleanUp(infoPtr);
return SERVER_ERROR;
+
+ CleanupReturn:
+ FcgiCleanUp(infoPtr);
+ return status;
}
command_rec fastcgi_cmds[] = {
{ "FastCgiIpcDir", FastCgiIpcDirCmd, NULL, RSRC_CONF, TAKE1,
NULL },
! { "AppClass", AppClassCmd, NULL, RSRC_CONF, RAW_ARGS, NULL },
! { "ExternalAppClass", ExternalAppClassCmd, NULL, RSRC_CONF, RAW_ARGS, NULL
},
{ NULL }
};
handler_rec fastcgi_handlers[] = {
{ FCGI_MAGIC_TYPE, FastCgiHandler },
+ { "fastcgi-script", FastCgiHandler },
{ NULL }
};