Sun Java System Web Server 7.0 管理员指南

附录 B FastCGI 插件

简介

FastCGI 是对现有 CGI(Common Gateway Interface,公共网关接口)的增强,CGI 是一种连接外部应用程序与 Web Server 的标准。与 CGI 一样,FastCGI 应用程序在单独、隔离的进程中运行。以下是使用 FastCGI 的一些优点:

通过使用 FastCGI 插件,Web Server 能够以可伸缩的方式安全地使用流行的第三方动态内容生成技术(如 Perl 和 Python)。

有关 FastCGI 的更多信息,请参阅 http://www.fastcgi.com/devkit/doc/fcgi-spec.html 中的规范。

插件函数 (SAF)

FastCGI 插件提供以下服务器应用程序函数 (Server Application Function, SAF):

以下各节介绍了 FastCGI SAF 的各种参数和“错误原因”字符串:

auth-fastcgi

auth-fastcgi 是修补程序检查函数。此函数用于将请求转发到“授权者”FastCGI 应用程序。如果授权成功,则发送返回码 200。否则,将“授权者”FastCGI 应用程序的响应发送回至用户代理。

可以在 http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6 中找到有关 FastCGI 角色的更多信息。

以下位置提供了 auth-fastcgi SAF 接受的参数:FastCGI SAF 参数

以下 obj.conf 代码示例说明了 auth-fastcgi 的用法:

PathCheck fn="auth-fastcgi" app-path="/usr/bin/perl" app-args="/fastcgi/apps/auth/SimpleAuth.pl" bind-path="localhost:3432"

responder-fastcgi

responder-fastcgi 是服务函数。此函数用于将请求转发到充当“响应者”的 FastCGI 应用程序。“响应者”应用程序的响应将被发送到用户代理。http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6 中提供了有关 FastCGI 角色的更多信息。

以下位置提供了 responder-fastcgi SAF 接受的参数列表:FastCGI SAF 参数

以下 obj.conf 代码示例说明了 responder-fastcgi 的用法:

Service fn="responder-fastcgi" app-path="/fastcgi-enabled-php-installation/bin/php" bind-path="localhost:3433" app-env="PHP_FCGI_CHILDREN=8" app-env="PHP_FCGI_MAX_REQUEST=500"

filter-fastcgi

filter-fastcgi 是服务函数。此函数用于将请求转发到“过滤器”类型的 FastCGI 应用程序。“过滤器”应用程序接收与 HTTP 请求关联的信息,还接收存储在服务器上的文件中的数据。然后,“过滤器”应用程序生成“已过滤”版本的数据流作为响应。该响应将被发送到用户代理。http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6 中提供了有关 FastCGI 角色的更多信息。

以下位置提供了 filter-fastcgi SAF 接受的参数列表:FastCGI SAF 参数

以下 obj.conf 代码示例说明了 filter-fastcgi 的用法:

Service fn="filter-fastcgi" app-path="/fastcgi/apps/filter/SimpleFilter" bind-path="localhost:3434" app-env="LD_LIBRARY_PATH=/fastcgi/fcgi-2.4/libfcgi/.libs" min-procs=2

error-fastcgi

error-fastcgi 是错误函数。error-fastcgi SAF 处理特定于 FastCGI 插件的错误。但是,此函数不处理 HTTP 错误。发生错误时,可以将 FastCGI 插件配置为显示特定的页面或将请求重定向至特定的 URL。

以下位置提供了 error-fastcgi SAF 接受的参数列表:FastCGI SAF 参数

以下 obj.conf 代码片段说明了 error-fastcgi 的用法:

Error fn="error-fastcgi" error-reason="Invalid Parameters" error-url="http://www.foo.com/errorPage.html"

有关 error-fastcgi 参数的信息,请参见FastCGI SAF 参数

FastCGI SAF 参数

FastCGI 插件 SAF "auth-fastcgi"、" responder-fastcgi" 和 "filter-fastcgi" 全都接受下列参数,除非另外显式说明:

请注意,参数 chrootusergroupnice 只适用于 UNIX 平台。在 Windows 平台上,这些参数都将被忽略。

error-fastcgi 服务器应用程序函数 (Server Application Function, SAF) 接受以下参数:

error-fastcgi SAF 错误原因字符串

本节提供了所有有效“错误原因”字符串及其说明:

在 Web Server 上配置 FastCGI 插件

FastCGI 插件与 Web Server 7.0 捆绑在一起。该插件安装在以下位置:

32 位 FastCGI 插件二进制文件安装在 <install_dir>/plugins/fastcgi 目录中。

64 位 Solaris SPARC FastCGI 插件二进制文件安装在 <install_dir>/lib/plugins/fastcgi/64 目录中。

将安装以下 FastCGI 二进制文件:

libfastcgi.so(适用于 Solaris/Linux)

fastcgi.dll(适用于 Windows)

Fastcgistub.exe(适用于 Windows)

libfastcgi.sl(适用于 HP-UX)

Fastcgistub(可执行文件)

可以使用位于 <instance-dir>/config 目录中的 Web Server 配置文件来配置 FastCGI 插件。要配置 FastCGI 插件,请执行以下步骤:

修改 magnus.conf

使用 "load-modules" 初始化函数加载 FastCGI 插件共享库。

Init fn=flex-init access="access" format.access="%Ses->client.ip%
- %Req->vars.auth-user% [%SYSDATE%] \"%Req->reqpb.clf-request%\"
%Req->srvhdrs.clf-status% %Req->srvhdrs.content-length%"

Init fn="load-modules" shlib="libJava EEplugin.so" shlib_flags="(global|now)"

Init fn="load-modules" shlib="libfastcgi.so" shlib_flags="(global|now)"

修改 MIME 类型(可选)

编辑 mime.types 文件以指定 MIME 映射。修改 MIME 类型映射为可选步骤。

例如,

#--Sun Microsystems Inc. MIME Information

# Do not delete the above line. It is used to identify the file type.

#

# Copyright 2006 Sun Microsystems, Inc. All rights reserved.

# Use is subject to license terms.

#


type=application/octet-stream exts=bin

type=application/astound exts=asd,asn

...

...

type=magnus-internal/fastcgi exts=php

...

...

修改 obj.conf

编辑 obj.conf 文件以使用前面几节中介绍的插件 SAF 配置特定于 FastCGI 的请求。

以下是修改的 obj.conf 文件示例:

#

# Copyright 2006 Sun Microsystems, Inc. All rights reserved.

# Use is subject to license terms.

#


# You can edit this file, but comments and formatting changes

# might be lost when you use the administration GUI or CLI.



<object name = "default">

		AuthTrans fn="match-browser" browser="*MSIE*" 
						ssl-unclean-shutdown="true"
		NameTrans fn="ntrans-Java EE" name="Java EE"
		NameTrans fn="pfx2dir" from="/mc-icons" 
						dir="/ws7/lib/icons" name="es-internal"
		NameTrans fn="assign-name" from="/fcgi/*" name="fcgi.config"

</object>

<Object name="fcgi.config">

		AuthTrans fn="auth-fastcgi" app-path="/fastcgi/apps/c/simpleAuth" 
				bind-path="localhost:2111"
		Service fn="responder-fastcgi" 
						app-path="/fastcgi_enabled_php_installation_dir/bin/php" 
				app-env="name1=abc"

</object>
...

请注意,通过为不同 URL 模式定义不同对象或将 SAF 映射至不同 MIME 类型,可以采用不同方法调用 FastCGI SAF。

有关 obj.conf 配置和语法的更多信息,请参阅管理配置文件参考指南

排除 FastCGI 插件的故障

Fastcgistub 是一个管理 FastCGI 应用程序进程生命周期的进程管理器。Fastcgistub 将消息记录在 Web Server 临时目录下的 Fastcgistub.log 文件中。如果发生任何错误,检查此文件可以帮助调试问题。

问题:未满足 FastCGI 请求

可能的原因和解决方法如下:

  1. 检查是否加载了 FastCGI 插件。如果在 Web Server 启动过程中显示以下消息,则表示已成功加载该插件。否则,请在 magnus.conf 中检查插件库的路径: FCGI1000: Sun Java System Web Server 7.0 FastCGI NSAPI Plugin < build info>

  2. 检查是否在 obj.conf 中正确指定了请求映射。有关 obj.conf 文件的更多信息,请参见 Sun Java System Web Server 管理员配置参考文件

  3. 检查错误日志以找出任何可能的错误消息。

  4. 检查存根二进制文件和 FastCGI 应用程序的权限。如果未授予插件足够的权限,则插件将无法启动存根或应用程序。

  5. 检查 Fastcgistub.log 文件以找出存根端任何可能的错误。

  6. 如果可能的话,以单机模式运行 FastCGI 应用程序并检查它在运行时是否出现任何问题。

如果抛出任何与库相关的错误,请在 obj.conf 中将 LD_LIBRARY_PATH 指定为具有 LD_LIBRARY_PATH=<dependency library paths> 值的 app-env 参数。

问题:FastCGI 应用程序无法启动。

可能的原因和解决方法如下:

检查 Fastcgistub.log 文件以找出以下日志消息:

..
<pid> process startup failure, trying to restart
...
Even after trying <n> time(s), <application path> process failed to start...no more retries

启动失败的原因之一可能是加载相关库失败。可以通过将相应的库路径指定为 obj.conf 文件中配置的 FastCGI 应用程序的 app-env 参数值来解决此问题。例如:


Service fn="responder_fastcgi" app-path="/fastcgi/c/tux-app" bind-path="localhost:2112" 
app-env="LD_LIBRARY_PATH=/tuxedo/lib"

开发 FastCGI 应用程序

可以使用 Perl、PHP、C 和 Java 开发 FastCGI 应用程序。以下各节简要介绍了使用几种常见编程语言开发此类应用程序的过程。

Procedure执行 FastCGI 应用程序

  1. 停止 Web Server。

  2. 重新启动 Web Server。

  3. 访问应用程序根目录为 "fcgi" 的应用程序。

    例如:http://localhost/fcgi/ListDir.php

FastCGI 应用程序的结构

典型的 FastCGI 应用程序具有以下代码结构:

Initialization code

Start of response loop
		body of response loop
End of response loop

初始化代码只在应用程序初始化时运行一次。初始化代码执行的操作通常比较耗时,例如打开数据库或计算表或位图的值。将 CGI 程序转换为 FastCGI 程序的主要任务是将初始化代码与需要针对每个请求运行的代码分开。

响应循环连续运行,等待客户机请求到达。该循环以对 FCGI_Accept(FastCGI 库中的一个例程)的调用开始。FCGI_Accept 例程将阻止程序执行,直到客户机请求 FastCGI 应用程序为止。客户机请求到达后,FCGI_Accept 将解除阻止并运行一次响应循环主体,然后重新阻止,等待另一个客户机请求。只有在系统管理员或 Web Server 中止 FastCGI 应用程序时,该循环才会终止。

使用 Perl

从 CPAN 下载并安装最新的 FCGI 模块。对于 ActivePerl,可以从 http://aspn.activestate.com/ASPN/Downloads/ActivePerl/PPM/Zips 下载模块。

有关使用 Perl 编写 FastCGI 应用程序的更多信息,请访问 http://www.fastcgi.com/devkit/fastcgi-prog-guide/ch3perl.htm#3659

使用 PHP

从 PHP 4.3.0 开始,FastCGI 成为 PHP 引擎支持的配置。要编译支持 FastCGI 的 PHP 4.3.x 或更高版本的引擎,请在构建进程中包括配置开关 --enable-fastcgi,例如:


./configure <other-options> --enable-fastcgi
gmake

编译完成后,php 二进制文件将启用 FastCGI。

使用 PHP 版本 5.1.2 或早期版本(包括 PHP 4.x)时,应配置 FastCGI 插件且 bind-path 的格式为“主机:端口”。例如,bind-path = "localhost:3333"。

对于 PHP 版本 5.1.3 和更高版本,bind-path 是可选的。如果指定了此项,其格式应为“主机:端口”。它可以是字符串。例如,bind-path = "myphpbindpath"。

使用 C/Java

FastCGI 开发工具包提供了用于编写 FastCGI C/Java 应用程序的 API。您可以从 http://www.fastcgi.com/devkit/doc/fcgi-devel-kit.htm 下载该工具包。

要构建下载的 FastCGI 开发工具包,请执行以下步骤:

  1. 解压缩 tar 文件。此操作将创建一个名为 fcgi-devel-kit 的新目录。

  2. fcgi-devel-kit 目录中执行以下命令序列:

    1. ./configure

    2. make

有关使用 C 编写 FastCGI 应用程序的更多信息,请访问 http://www.fastcgi.com/devkit/doc/fcgi-devel-kit.htm#S3

有关使用 Java 编写 FastCGI 应用程序的更多信息,请访问 http://www.fastcgi.com/devkit/doc/fcgi-java.htm

FastCGI 应用程序样例

本节包含使用 PHP、Perl 和 C 编写的 FastCGI 应用程序样例。

使用 PHP 编写的响应者应用程序 (ListDir.php)

<?php
 		$dir = "/tmp/";

		// Open a known directory, and proceed to read its contents
		if (is_dir($dir)) {
			if ($dh = opendir($dir)) {
 				while (($file = readdir($dh)) !== false) {
 					echo "filename: $file : filetype: " . filetype($dir . $file) . "\n";
 				}
				closedir($dh);
			}

		}
?>

以上示例的 obj.conf 代码片段为:

<Object name="default">
		NameTrans fn="assign-name" from="/fcgi/*" name="responder.fcgi"
</Object>
<Object name="responder.fcgi">
		Service fn="responder-fastcgi" app-path="/foo/fastcgi-enabled-php-installation/bin/php" 
			bind-path="localhost:3431"  min-procs=3
</Object>

使用 Perl 编写的授权者应用程序 (SimpleAuth.pl)

#!/usr/bin/perl

use FCGI;

while (FCGI::accept >= 0) {
		if( $ENV{'HTTP_AUTHORIZATION'} ) { 
        # This value can be further decoded to get the actual username and password and then
        # perform some kind of user validation. This program only checks for the presence of
        # of this environment param and is not really bothered about its value

        print( "Status: 200\r\n" );
        print( "\r\n" );

    } else {
       
        print( "Status: 401\r\n" );
        print( "WWW-Authenticate: basic realm=\"foo\"\r\n" );
        print( "\r\n" );  

   }

}
Example obj.conf settings for the above example:

以上示例的 obj.conf 代码片段为:

<Object name="responder.fcgi">
		AuthTrans fn="auth-fastcgi" app-path="/fastcgi/apps/auth/SimpleAuth.pl" 
		bind-path="localhost:3432"
		Service fn="responder-fastcgi" app-path="/foo/fastcgi-enabled-php-installation/bin/php" 
		bind-path="localhost:3433" app-env="PHP_FCGI_CHILDREN=8" min-procs=1
</Object>

第一次请求 http://localhost/fcgi/php/ListDir.php 时,浏览器将显示验证对话框。在用户输入用户名和密码后,将列出 "/tmp" 目录的内容。

使用 C 编写的过滤器应用程序 (SimpleFilter.c)

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

		void main(void) {
			size_t PageSize = 1024 * 3;
    		char *page;
    		FCGX_Stream *in, *out, *err;
    		FCGX_ParamArray envp;

		 	int count=0;
    		page = (char *)malloc(PageSize);

		 	if (page == NULL) {

				printf("Content-type: text/x-server-parsed-html\r\n");
				printf("<title>malloc failure</title>");
				printf("<h1>Cannot allocate memory to run filter. exiting</h1>");
				printf("\r\n\r\n");
				exit(2);
			}

			while(FCGI_Accept() >= 0) {

         	char *tmp;
         	char *execcgi;      
         	char *dataLenStr = NULL;
         	int numchars = 0;
         	int stdinDataSize = 0;
         	int filterDataLen = 0;
         	int dataToBeRead = 0;
         	int x = 0;
         	int loopCount = 0;   
				

			  	count++;
         	dataLenStr = getenv("FCGI_DATA_LENGTH");
        
         	if(dataLenStr)
             	filterDataLen = atoi(dataLenStr);
   
				/* clear out stdin */
         	while (EOF != getc(stdin)) {
             	stdinDataSize++;
         	}

				dataToBeRead = filterDataLen;
        	FCGI_StartFilterData();
        	tmp = page; /** just in case fread or fwrite moves our pointer **/


				//start responding
				printf("Content-type: text/plain\r\n");
				printf("\r\n"); /** send a new line at the beginning **/
				printf("<title>SIMPLE FILTER</title>");
				printf(<h1>This page was Filtered by SimpleFilter FastCGI filter</h1>");
				printf("file size=%d<br>", filterDatalen);
				printf("stdin size=%d<br>, stdinDataSize);


				while(dataToBeRead > 0 ) {
           		x = 0;
           		page = tmp;
            
           		if(dataToBeRead > PageSize)
             		 x = PageSize;
           		else
              		 x = dataToBeRead;
					numchars = fread((void *)(page), 1, x, stdin);
				
					if( numchars == 0 )
						continue; 
					/** at this point your data is in page pointer, so do 
					whatever you want 
               with it before sending it back to the server.
					In this example, no data is manipulated. Only the count of number of 
               times the filter data is read and the total bytes read 
					at the end of every 
               loop is printed. **/	

					dataToBeRead -= numchars;
					loopCount++;
					printf("loop count = %d ... so far read %d bytes <br>", loopCount, 
					(filterDatalen - dataToBeRead));
				}
				printf("\r\n\r\n"); /** send a new line at the end of transfer **/

				fflush(stdout);

				page = tmp; /** restore page pointer **/
				memset(page,NULL,numchars);
		}

		free(page);
}

以上示例的 obj.conf 设置示例。

如果该 FastCGI 应用程序是在运行 Web Server 的计算机上运行,则

<Object name=<"filter.fcgi">
						Service fn="filter-fastcgi" app-path="/fastcgi/apps/filter/SimpleFilter.exe" 
             bind-path="localhost:3434" app-env="LD_LIBRARY_PATH=/fastcgi/fcgi-2.4/libfcgi/.libs"
</Object>

如果该应用程序正在远程计算机上运行,则必须在 obj.conf 文件中包含以下代码行:

<Object name="filter.fcgi">
			Service fn="filter-fastcgi" bind-path="<remote-host>:<remote-port>"
</Object>

如果要过滤 Web Server 实例根目录下的 fcgi 目录中大小为 "26868" 字节的 "FilterThisFile" 文件,则对 "http://localhost/fcgi/filter/FilterThisFile" 的请求将生成以下输出:

This page was Filtered by SimpleFilter FastCGI filter

file size = 26868

stdin size = 0

loop count = 1... so far read 3072 bytes

loop count = 2... so far read 6144 bytes

loop count = 3... so far read 9216 bytes

loop count = 4... so far read 12288 bytes

loop count = 5... so far read 15360 bytes

loop count = 6... so far read 18432 bytes

loop count = 7... so far read 21504 bytes

loop count = 8... so far read 24576 bytes

loop count = 9... so far read 26868 bytes