http://wangnow.com/article/28-fastcgi-protocol-specification
首先介紹響應的數據,比較簡單,再者我們對返回的數據比較敏感……
1 響應格式
如(十六進制方式顯示)
序列 0 1 2 3 4 5 6 7 ...
數值 01 06 00 01 01 1D 03 00...
序列0(值01)為version,固定取1即可
序列1(值06)為type,代表FCGI_STDOUT,表示應用的輸出
序列2 3(00 01)代表2字節的請求id,默認取1即可(準確說應該是和請求應用時發送的id一致,這里假設請求和響應的id都是1)
序列4 5(01 1D)代表2字節的輸出長度,最大為65535,例如當前內容長度為(0x01 << 8) + 0x1D = 285
序列6(03)代表填充padding字節數(填充為滿8字節的整數倍),例如當前填充(以0填充)長度為8 - 285 % 8 = 3,即獲取輸出長度(285)的內容后要跳過的字節數,當然如果為8就無需填充了
序列7(00)為保留字節
8字節(序列7)之后為具體內容(contentData)和填充內容(paddingData)
最后為通知web服務器的請求結束記錄,具體內容如下
序列 0 1 2 3 4 5 6 7 ...
數值 01 03 00 01 00 08 00 00...
其中序列1(03)type代表FCGI_END_REQUEST,即請求結束,8字節之后為contentData(EndRequestBody)和paddingData
EndRequestBody的內容也比較個性,是單獨定義的
typedef struct {
unsigned char appStatusB3;
unsigned char appStatusB2;
unsigned char appStatusB1;
unsigned char appStatusB0;
unsigned char protocolStatus;
unsigned char reserved[3];
} FCGI_EndRequestBody;
appStatus占了四個字節,定義為cgi通過調用系統退出返回的狀態碼(The application sets the protocolStatus component to FCGI_REQUEST_COMPLETE and the appStatus component to the status code that the CGI program would have returned via the exit system call.)Linux正常的程序退出默認是返回0(應該是吧?我記著是……)
protocolStatus的值可以是
#define FCGI_REQUEST_COMPLETE 0
#define FCGI_CANT_MPX_CONN 1
#define FCGI_OVERLOADED 2
#define FCGI_UNKNOWN_ROLE 3
因此最后FCGI_END_REQUEST的contentData為
序列 0 1 2 3 4 5 6 7
數值 00 00 00 00 00 00 00 00
0-3序列為appStatus
4序列protocolStatus為0(FCGI_REQUEST_COMPLETE)
5-7序列為保留的3字節reserved[3]
2 請求格式
序列 0 1 2 3 4 5 6 7 ...
數值 01 01 00 01 00 08 00 00...
序列0(值01)為version
序列1(值01)為type,代表FCGI_BEGIN_REQUES,表示開始發送請求
序列2 3(00 01)代表2字節的請求id,默認取1即可
請求開始的記錄稍微特殊,發送的內容(contentData)如下格式
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
#role的可以取如下三個值
#define FCGI_RESPONDER 1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER 3
我們取1(FCGI_RESPONDER)為啥?說是和經典的CGI/1.1作用一樣(http那些東西)
flags取0表示本次請求完畢后即關閉鏈接。
序列 0 1 2 3 4 5 6 7
數值 00 01 00 00 00 00 00 00
0和1序列代表role為1(FCGI_RESPONDER)
2序列為flags 0
3-7序列為reserved[5]
再說下協議中FCGI_PARAMS中的Name-Value Pairs,目的是提供應用層一些必要的變量(類似http中的header:headerName-headerValue,當然可以為多個),詳細定義見http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S3.4
其中一種定義格式如下:
typedef struct {
unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */
unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
unsigned char valueLengthB2;
unsigned char valueLengthB1;
unsigned char valueLengthB0;
unsigned char nameData[nameLength];
unsigned char valueData[valueLength
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair14;
結合實例說明下
序列 0 1 2 3 4 5 6 7 ...
數值 00 04 00 01 04 EB 05 00...
序列1(04)代表FCGI_PARAMS
序列7之后的為相應的名字(Name)長度(nameLength)、值(Value)長度(valueLength)、名字(nameData)、值(valueData)
其中規定名字或者值的長度如果大于127字節,則要以4字節存儲,如下
序列 0 1 2 3 4 5 6 7 ............
數值 0F 80 00 00 91 S C R IPT_FILENAME/data/www/......
序列0的0F即十進制的15(SCRIPT_FILENAME的長度),不大于127所以占一個字節
序列1的80即十進制的128,大于127,說明要占用4字節(80 00 00 91),長度為
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0
算算等于多少呢?如果對位移、與等操作符號不熟悉的話,更詳細的介紹見之前的文章
3 其他說明
各個值的詳細定義參見http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S8
以下做一些概要說明
記錄(Records,可以順序發送或者接受多個記錄)的格式具體定義如下
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
unsigned char contentData[contentLength];
unsigned char paddingData[paddingLength];
} FCGI_Record;
#前八字節定義為Header(可以這么理解,頭信息+響應內容,想想htpp協議中的header+body就明白了)
#協議說明中把這部分定義為FCGI_Header(以上紅色字體部分),即:
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;
#version定義為1
#define FCGI_VERSION_1 1
#type具體值定義,主要關注FCGI_BEGIN_REQUEST(請求開始) FCGI_END_REQUEST(請求結束) FCGI_PARAMS(fastcgi參數,即一些服務器變量,如HTTP_USER_AGENT) FCGI_STDOUT(fastcgi標準輸出,即請求后返回的內容)
#define FCGI_BEGIN_REQUEST 1
#define FCGI_ABORT_REQUEST 2
#define FCGI_END_REQUEST 3
#define FCGI_PARAMS 4
#define FCGI_STDIN 5
#define FCGI_STDOUT 6
#define FCGI_STDERR 7
#define FCGI_DATA 8
#define FCGI_GET_VALUES 9
#define FCGI_GET_VALUES_RESULT 10
#define FCGI_UNKNOWN_TYPE 11
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
Fastcgi官方文檔:http://www.fastcgi.com/devkit/doc/fcgi-spec.html
中文版:http://fuzhong1983.blog.163.com/blog/static/1684705201051002951763/
FAQ:
1 如何查看web服務器發送給fastcgi應用的頭信息呢?
我采用的是用python監聽一個端口,然后把nginx中的fastcgi配置改為此端口,這樣python中就可以把接受的信息存為文件。當然你還可以直接改nginx的代碼……
2 那請求后對應的輸出內容如何查看呢?
既然發送的信息都有了,那就直接發送給fastcgi應用(如php-fpm)吧,然后輸出隨你處理
3 如何查看請求或者響應信息呢?
Linux下可通過xxd命令查看這種二進制輸出文件,Windows下UltraEdit也可以(我用的是未注冊版的,剩余日子21,注冊要$59.95),免費的還可以試試PSPad(想起了游戲機)