FastCGI协议分析
不知道什么时候,就开始有了让HomeServer支持PHP的念头。于是分析起了FastCGI协议。FastCGI用于WebServer与WebApplication之间的通讯,例如Apache与PHP程序。
FastCGI协议数据包是8字节对齐的,由包头(Header)和包体(Body)组成。例如要请求一个index.php的页面,WebServer首先向WebApp发送一个Request数据包。包头有个请求ID用于并行工作时,区别不同的请求。
包头
[版本:1][类型:1][请求ID:2][数据长度:2][填充字节数:1][保留:1]
包体
[角色:2][参数:1][保留:5]
接着,再发送一个Params数据包,用于传递执行页面所需要的参数和环境变量。
包头
[版本:1][类型:1][请求ID:2][数据长度:2][填充字节数:1][保留:1]
包体
[名称长度:1或4][值长度:1或4][名称:变长][值:变长] …
其中,名称和值的长度占用的字节数是可变,取决于第一个字节(高位)的最高位是否为1,为1则长度是4个字节,否则为1个字节。即如果长度不超过128字节,就用一个字节来保存长度足够了。
参数发送后还要发送一个没有包体,只有包头的空的Params数据包,用来表示参数发送结束。
如果请求页面时POST方式,还会发送表单数据。这就要用到Stdin数据包了。
包头
[版本:1][类型:1][请求ID:2][数据长度:2][填充字节数:1][保留:1]
包体
[数据内容:长度在包头中设置,8字节对齐]
有时候POST的数据大于或等于64KB,就不能使用一个Stdin数据包发送完毕了,需要使用多次Stdin数据包来完成所有数据的传输。与Params数据包一样,结尾要发送一个没有包体,只有包头的空的Stdin数据包,用来表示参数发送结束。
至此,WebServer要提供给WebApplication的数据已经发送完毕。接着就接收来自WebApplication的数据了。
数据接收包Stdout与Stdin是差不多的,这里不再描述。不过接收到的数据由HTTP头和网页数据两部分组成,WebServer要对其做一定的处理后才能发送到浏览器。同Stdin数据包一样,WebServer会接收到一个来自WebApplication的Stdout的空数据包,表示接收的Stdout数据已经完毕。
最后,WebApplication会发送一个包含状态的EndRequest数据包,至此,一次页面请求处理完毕。
下面给出一些相关结构参考。
通用包头:
typedef struct {
    unsigned char version;
    unsigned char type;
    unsigned char requestIdB1;
    unsigned char requestIdB0;
    unsigned char contentLengthB1;
    unsigned char contentLengthB0;
    unsigned char paddingLength;
    unsigned char reserved;
}FCGI_Header;
typedef struct {
    unsigned char roleB1;
    unsigned char roleB0;
    unsigned char flags;
    unsigned char reserved[5];
} FCGI_BeginRequestBody;
typedef struct {
    FCGI_Header header;
    FCGI_BeginRequestBody body;
} FCGI_BeginRequestRecord;
typedef struct {
    unsigned char appStatusB3;
    unsigned char appStatusB2;
    unsigned char appStatusB1;
    unsigned char appStatusB0;
    unsigned char protocolStatus;
    unsigned char reserved[3];
} FCGI_EndRequestBody;
每次请求页面时,传递给PHP程序的参数:
	SCRIPT_FILENAME,
	QUERY_STRING,
	REQUEST_METHOD,
	CONTENT_TYPE,
	CONTENT_LENGTH,
	SCRIPT_NAME,
	REQUEST_URI,
	DOCUMENT_URI,
	DOCUMENT_ROOT,
	SERVER_PROTOCOL,
	GATEWAY_INTERFACE,
	SERVER_SOFTWARE,
	REMOTE_ADDR,
	REMOTE_PORT,
	SERVER_ADDR,
	SERVER_PORT,
	SERVER_NAME,
	REDIRECT_STATUS,
	HTTP_ACCEPT,
	HTTP_ACCEPT_LANGUAGE,
	HTTP_ACCEPT_ENCODING,
	HTTP_USER_AGENT,
	HTTP_HOST,
	HTTP_CONNECTION,
	HTTP_CONTENT_TYPE,
	HTTP_CONTENT_LENGTH,
	HTTP_CACHE_CONTROL,
	HTTP_COOKIE,
	HTTP_FCGI_PARAMS_MAX
好像很多,但是很多空值的,可以省去,不发送之,即可。
参考文献:
FastCGI Specification
感谢狙击手同志提供相关帮助。
呵呵,这篇文章专业性太强了,有点像教科书似的。估计新手还是很难理解。
呃~~ 仅个人理解,最好还是自己去实践一次呢!
学习了,谢谢xiaoxia无私的共享精神。
网上唯一的中文解释. 高兴死我了。
Enjoy it!
有问题的话可以共同探讨:-)
小弟最近再看linux下写web server 但是目前不知道如何使用fastcgi来让自己的server去支持php。能给点建议么`?
Pingback引用通告: fastcgi | Wandai Blog
最好有个简单的实际例子
看我之前写的源代码:
http://xiaoxia.org/2009/11/05/homeserver-publishing/
具体自己的web server 如何与php环境进行交互呢。当broswer请求一个index.php时,如何让php环境去执行呢?
通过FastCGI协议通信,具体可以查一下FCGI标准。
Pingback引用通告: web容器与PHP间fastcgi协议(1) – 囧升
你好小虾,你的讲解非常透彻而且易懂。
但是我按照协议写好了相关数据让WEBSERVER发送给PHP,结果收到了 “No input file specified.” 的错误。
Params已经传递了,不知道为何这样,请赐教!
抓包看看是不是有不一样的地方