FastCGI中文规范
?
http://fuzhong1983.blog.163.com/blog/static/1684705201051002951763/
.?介紹
FastCGI是對CGI的開放的擴展,它為所有因特網(wǎng)應用提供高性能,且沒有Web服務器API的缺點(penalty)。
本規(guī)范具有有限的(narrow)目標:從應用的視角規(guī)定FastCGI應用和支持FastCGI的Web服務器之間的接口。Web服務器的很多特性涉及FastCGI,舉例來說,應用管理設施與應用到Web服務器的接口無關(guān),因此不在這兒描述。
本規(guī)范適用于Unix(更確切地說,適用于支持伯克利socket的POSIX系統(tǒng))。本規(guī)范大半是簡單的通信協(xié)議,與字節(jié)序無關(guān),并且將擴展到其他系統(tǒng)。
我們將通過與CGI/1.1的常規(guī)Unix實現(xiàn)的比較來介紹FastCGI。FastCGI被設計用來支持常駐(long-lived)應用進程,也就是應用服務器。那是與CGI/1.1的常規(guī)Unix實現(xiàn)的主要區(qū)別,后者構(gòu)造應用進程,用它響應一個請求,以及讓它退出。
FastCGI進程的初始狀態(tài)比CGI/1.1進程的初始狀態(tài)更簡潔,因為FastCGI進程開始不會連接任何東西。它沒有常規(guī)的打開的文件stdin、stdout和stderr,而且它不會通過環(huán)境變量接收大量的信息。FastCGI進程的初始狀態(tài)的關(guān)鍵部分是個正在監(jiān)聽的socket,通過它來接收來自Web服務器的連接。
FastCGI進程在其正在監(jiān)聽的socket上收到一個連接之后,進程執(zhí)行簡單的協(xié)議來接收和發(fā)送數(shù)據(jù)。協(xié)議服務于兩個目的。首先,協(xié)議在多個獨立的?FastCGI請求間多路復用單個傳輸線路。這可支持能夠利用事件驅(qū)動或多線程編程技術(shù)處理并發(fā)請求的應用。第二,在每個請求內(nèi)部,協(xié)議在每個方向上提供若干獨立的數(shù)據(jù)流。這種方式,例如,stdout和stderr數(shù)據(jù)通過從應用到Web服務器的單個傳輸線路傳遞,而不是像CGI/1.1那樣需要獨立的管道。
一個FastCGI應用扮演幾個明確定義的角色中的一個。最常用的是響應器(Responder)角色,其中應用接收所有與HTTP請求相關(guān)的信息,并產(chǎn)生一個HTTP響應;那是CGI/1.1程序扮演的角色。第二個角色是認證器(Authorizer),其中應用接收所有與HTTP請求相關(guān)的信息,并產(chǎn)生一個認可/未經(jīng)認可的判定。第三個角色是過濾器(Filter),其中應用接收所有與HTTP請求相關(guān)的信息,以及額外的來自存儲在Web服務器上的文件的數(shù)據(jù)流,并產(chǎn)生"已過濾"版的數(shù)據(jù)流作為HTTP響應。框架是易擴展的,因而以后可定義更多的FastCGI。
在本規(guī)范的其余部分,只要不致引起混淆,術(shù)語"FastCGI應用"、"應用進程"或"應用服務器"簡寫為"應用"。
2.?初始進程狀態(tài)?2.1?參數(shù)表
Web服務器缺省創(chuàng)建一個含有單個元素的參數(shù)表,該元素是應用的名字,用作可執(zhí)行路徑名的最后一部分。Web服務器可提供某種方式來指定不同的應用名,或更詳細的參數(shù)表。
注意,被Web服務器執(zhí)行的文件可能是解釋程序文件(以字符#!開頭的文本文件),此情形中的應用參數(shù)表的構(gòu)造在execve man頁中描述。
2.2?文件描述符
當應用開始執(zhí)行時,Web服務器留下一個打開的文件描述符,FCGI_LISTENSOCK_FILENO。該描述符引用Web服務器創(chuàng)建的一個正在監(jiān)聽的socket。
FCGI_LISTENSOCK_FILENO等于STDIN_FILENO。當應用開始執(zhí)行時,標準的描述符STDOUT_FILENO和STDERR_FILENO被關(guān)閉。一個用于應用確定它是用CGI調(diào)用的還是用FastCGI調(diào)用的可靠方法是調(diào)用getpeername(FCGI_LISTENSOCK_FILENO),對于FastCGI應用,它返回-1,并設置errno為ENOTCONN。
Web服務器對于可靠傳輸?shù)倪x擇,Unix流式管道(AF_UNIX)或TCP/IP(AF_INET),是內(nèi)含于FCGI_LISTENSOCK_FILENO socket的內(nèi)部狀態(tài)中的。
2.3?環(huán)境變量
Web服務器可用環(huán)境變量向應用傳參數(shù)。本規(guī)范定義了一個這樣的變量,FCGI_WEB_SERVER_ADDRS;我們期望隨著規(guī)范的發(fā)展定義更多。Web服務器可提供某種方式綁定其他環(huán)境變量,例如PATH變量。
2.4?其他狀態(tài)
Web服務器可提供某種方式指定應用的初始進程狀態(tài)的其他組件,例如進程的優(yōu)先級、用戶ID、組ID、根目錄和工作目錄。
3.?協(xié)議基礎?3.1?符號(Notation)
我們用C語言符號來定義協(xié)議消息格式。所有的結(jié)構(gòu)元素按照unsigned char類型定義和排列,這樣ISO C編譯器以明確的方式將它們展開,不帶填充。結(jié)構(gòu)中定義的第一字節(jié)第一個被傳送,第二字節(jié)排第二個,依次類推。
我們用兩個約定來簡化我們的定義。
首先,當兩個相鄰的結(jié)構(gòu)組件除了后綴“B1”和“B0”之外命名相同時,它表示這兩個組件可視為估值為B1<<8 + B0的單個數(shù)字。該單個數(shù)字的名字是這些組件減去后綴的名字。這個約定歸納了一個由超過兩個字節(jié)表示的數(shù)字的處理方式。
第二,我們擴展C結(jié)構(gòu)(struct)來允許形式
struct {
unsigned char mumbleLengthB1;
unsigned char mumbleLengthB0;
... /*?其他東西?*/
unsigned char mumbleData[mumbleLength];
};
表示一個變長結(jié)構(gòu),此處組件的長度由較早的一個或多個組件指示的值確定。
3.2?接受傳輸線路
FastCGI應用在文件描述符FCGI_LISTENSOCK_FILENO引用的socket上調(diào)用accept()來接收新的傳輸線路。如果accept()成功,而且也綁定了FCGI_WEB_SERVER_ADDRS環(huán)境變量,則應用立刻執(zhí)行下列特殊處理:
FCGI_WEB_SERVER_ADDRS:值是一列有效的用于Web服務器的IP地址。
如果綁定了FCGI_WEB_SERVER_ADDRS,應用校驗新線路的同級IP地址是否列表中的成員。如果校驗失敗(包括線路不是用TCP/IP傳輸?shù)目赡苄?#xff09;,應用關(guān)閉線路作為響應。
FCGI_WEB_SERVER_ADDRS被表示成逗號分隔的IP地址列表。每個IP地址寫成四個由小數(shù)點分隔的在區(qū)間[0..255]中的十進制數(shù)。所以該變量的一個合法綁定是FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71。
?
應用可接受若干個并行傳輸線路,但不是必須的。
3.3?記錄
應用利用簡單的協(xié)議執(zhí)行來自Web服務器的請求。協(xié)議細節(jié)依賴應用的角色,但是大致說來,Web服務器首先發(fā)送參數(shù)和其他數(shù)據(jù)到應用,然后應用發(fā)送結(jié)果數(shù)據(jù)到Web服務器,最后應用向Web服務器發(fā)送一個請求完成的指示。
通過傳輸線路流動的所有數(shù)據(jù)在FastCGI記錄中運載。FastCGI記錄實現(xiàn)兩件事。首先,記錄在多個獨立的FastCGI請求間多路復用傳輸線路。該多路復用技術(shù)支持能夠利用事件驅(qū)動或多線程編程技術(shù)處理并發(fā)請求的應用。第二,在單個請求內(nèi)部,記錄在每個方向上提供若干獨立的數(shù)據(jù)流。這種方式,例如,stdout和stderr數(shù)據(jù)能通過從應用到Web服務器的單個傳輸線路傳遞,而不需要獨立的管道。
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;
FastCGI記錄由一個定長前綴后跟可變數(shù)量的內(nèi)容和填充字節(jié)組成。記錄包含七個組件:
version:?標識FastCGI協(xié)議版本。本規(guī)范評述(document)FCGI_VERSION_1。?
type:?標識FastCGI記錄類型,也就是記錄執(zhí)行的一般職能。特定記錄類型和它們的功能在后面部分詳細說明。?
requestId:?標識記錄所屬的FastCGI請求。?
contentLength:?記錄的contentData組件的字節(jié)數(shù)。?
paddingLength:?記錄的paddingData組件的字節(jié)數(shù)。?
contentData:?在0和65535字節(jié)之間的數(shù)據(jù),依據(jù)記錄類型進行解釋。?
paddingData:?在0和255字節(jié)之間的數(shù)據(jù),被忽略。
我們用不嚴格的C結(jié)構(gòu)初始化語法來指定常量FastCGI記錄。我們省略version組件,忽略填充(Padding),并且把requestId視為數(shù)字。因而{FCGI_END_REQUEST, 1, {FCGI_REQUEST_COMPLETE,0}}是個type == FCGI_END_REQUEST、requestId == 1且contentData == {FCGI_REQUEST_COMPLETE,0}的記錄。
填充(Padding)
協(xié)議允許發(fā)送者填充它們發(fā)送的記錄,并且要求接受者解釋paddingLength并跳過paddingData。填充允許發(fā)送者為更有效地處理保持對齊的數(shù)據(jù)。X窗口系統(tǒng)協(xié)議上的經(jīng)驗顯示了這種對齊方式的性能優(yōu)勢。
我們建議記錄被放置在八字節(jié)倍數(shù)的邊界上。FCGI_Record的定長部分是八字節(jié)。
管理請求ID
Web服務器重用FastCGI請求ID;應用明了給定傳輸線路上的每個請求ID的當前狀態(tài)。當應用收到一個記錄{FCGI_BEGIN_REQUEST, R, ...}時,請求ID R變成有效的,而且當應用向Web服務器發(fā)送記錄{FCGI_END_REQUEST, R, ...}時變成無效的。
當請求ID R無效時,應用會忽略requestId == R的記錄,除了剛才描述的FCGI_BEGIN_REQUEST記錄。
Web服務器嘗試保持小的FastCGI請求ID。那種方式下應用能利用短數(shù)組而不是長數(shù)組或哈希表來明了請求ID的狀態(tài)。應用也有每次接受一個請求的選項。這種情形下,應用只是針對當前的請求ID檢查輸入的requestId值。
記錄類型的類型
有兩種有用的分類FastCGI記錄類型的方式。
第一個區(qū)別在管理(management)記錄和應用(application)記錄之間。管理記錄包含不特定于任何Web服務器請求的信息,例如關(guān)于應用的協(xié)議容量的信息。應用記錄包含關(guān)于特定請求的信息,由requestId組件標識。
管理記錄有0值的requestId,也稱為null請求ID。應用記錄有非0的requestId。
第二個區(qū)別在離散和連續(xù)記錄之間。一個離散記錄包含一個自己的所有數(shù)據(jù)的有意義的單元。一個流記錄是stream的部分,也就是一連串流類型的0或更多非空記錄(length != 0),后跟一個流類型的空記錄(length == 0)。當連接流記錄的多個contentData組件時,形成一個字節(jié)序列;該字節(jié)序列是流的值。因此流的值獨立于它包含多少個記錄或它的字節(jié)如何在非空記錄間分配。
這兩種分類是獨立的。在本版的FastCGI協(xié)議定義的記錄類型中,所有管理記錄類型也是離散記錄類型,而且?guī)缀跛袘糜涗涱愋投际橇饔涗涱愋汀5侨N應用記錄類型是離散的,而且沒有什么能防止在某些以后的協(xié)議版本中定義一個流式的管理記錄類型。
3.4?名-值對
FastCGI應用的很多角色需要讀寫可變數(shù)量的可變長度的值。所以為編碼名-值對提供標準格式很有用。
FastCGI以名字長度,后跟值的長度,后跟名字,后跟值的形式傳送名-值對。127字節(jié)或更少的長度能在一字節(jié)中編碼,而更長的長度總是在四字節(jié)中編碼:
typedef struct {
unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */
unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
unsigned char nameData[nameLength];
unsigned char valueData[valueLength];
} FCGI_NameValuePair11;
typedef struct {
unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */
unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
unsigned char valueLengthB2;
unsigned char valueLengthB1;
unsigned char valueLengthB0;
unsigned char nameData[nameLength];
unsigned char valueData[valueLength
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair14;
typedef struct {
unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */
unsigned char nameLengthB2;
unsigned char nameLengthB1;
unsigned char nameLengthB0;
unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
unsigned char nameData[nameLength
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
unsigned char valueData[valueLength];
} FCGI_NameValuePair41;
typedef struct {
unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */
unsigned char nameLengthB2;
unsigned char nameLengthB1;
unsigned char nameLengthB0;
unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
unsigned char valueLengthB2;
unsigned char valueLengthB1;
unsigned char valueLengthB0;
unsigned char nameData[nameLength
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
unsigned char valueData[valueLength
((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair44;
長度的第一字節(jié)的高位指示長度的編碼方式。高位為0意味著一個字節(jié)的編碼方式,1意味著四字節(jié)的編碼方式。
名-值對格式允許發(fā)送者不用額外的編碼方式就能傳輸二進制值,并且允許接收者立刻分配正確數(shù)量的內(nèi)存,即使對于巨大的值。
3.5?關(guān)閉傳輸線路
Web服務器控制傳輸線路的生存期。當沒有活動的請求時Web服務器能關(guān)閉線路。或者Web服務器也能把關(guān)閉的職權(quán)委托給應用(見FCGI_BEGIN_REQUEST)。該情形下,應用在指定的請求結(jié)束時關(guān)閉線路。
這種靈活性提供了多種應用風格。簡單的應用會一次處理一個請求,并且為每個請求接受一個新的傳輸線路。更復雜的應用會通過一個或多個傳輸線路處理并發(fā)的請求,而且會長期保持傳輸線路為打開狀態(tài)。
簡單的應用通過在寫入響應結(jié)束后關(guān)閉傳輸線路可得到重大的性能提升。Web服務器需要控制常駐線路的生命期。
當應用關(guān)閉一個線路或發(fā)現(xiàn)一個線路關(guān)閉了,它就初始化一個新線路。
4.?管理(Management)記錄類型?4.1 FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT
Web服務器能查詢應用內(nèi)部的具體的變量。典型地,服務器會在應用啟動上執(zhí)行查詢以使系統(tǒng)配置的某些方面自動化。
應用把收到的查詢作為記錄{FCGI_GET_VALUES, 0, ...}。FCGI_GET_VALUES記錄的contentData部分包含一系列值為空的名-值對。
應用通過發(fā)送補充了值的{FCGI_GET_VALUES_RESULT, 0, ...}記錄來響應。如果應用不理解查詢中包含的一個變量名,它從響應中忽略那個名字。
FCGI_GET_VALUES被設計為允許可擴充的變量集。初始集提供信息來幫助服務器執(zhí)行應用和線路的管理:
FCGI_MAX_CONNS:該應用將接受的并發(fā)傳輸線路的最大值,例如"1"或"10"。?
FCGI_MAX_REQS:該應用將接受的并發(fā)請求的最大值,例如"1"或"50"。?
FCGI_MPXS_CONNS:如果應用不多路復用線路(也就是通過每個線路處理并發(fā)請求)則為?"0",其他則為"1"。
應用可在任何時候收到FCGI_GET_VALUES記錄。除了FastCGI庫,應用的響應不能涉及應用固有的庫。
4.2 FCGI_UNKNOWN_TYPE
在本協(xié)議的未來版本中,管理記錄類型集可能會增長。為了這種演變作準備,協(xié)議包含F(xiàn)CGI_UNKNOWN_TYPE管理記錄。當應用收到無法理解的類型為T的管理記錄時,它用{FCGI_UNKNOWN_TYPE, 0, {T}}響應。
FCGI_UNKNOWN_TYPE記錄的contentData組件具有形式:
typedef struct {
unsigned char type;?
unsigned char reserved[7];
} FCGI_UnknownTypeBody;
type組件是無法識別的管理記錄的類型。
5.?應用(Application)記錄類型?5.1 FCGI_BEGIN_REQUEST
Web服務器發(fā)送FCGI_BEGIN_REQUEST記錄開始一個請求。
FCGI_BEGIN_REQUEST記錄的contentData組件具有形式:
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
role組件設置Web服務器期望應用扮演的角色。當前定義的角色有:
FCGI_RESPONDER?
FCGI_AUTHORIZER?
FCGI_FILTER
角色在下面的第6章中作更詳細地描述。
flags組件包含一個控制線路關(guān)閉的位:
flags & FCGI_KEEP_CONN:如果為0,則應用在對本次請求響應后關(guān)閉線路。如果非0,應用在對本次請求響應后不會關(guān)閉線路;Web服務器為線路保持響應性。
5.2?名-值對流:FCGI_PARAMS?FCGI_PARAMS
是流記錄類型,用于從Web服務器向應用發(fā)送名-值對。名-值對被相繼地沿著流發(fā)送,沒有特定順序。
5.3?字節(jié)流:FCGI_STDIN, FCGI_DATA, FCGI_STDOUT, FCGI_STDERR?FCGI_STDIN
是流記錄類型,用于從Web服務器向應用發(fā)送任意數(shù)據(jù)。FCGI_DATA是另一種流記錄類型,用于向應用發(fā)送額外數(shù)據(jù)。
FCGI_STDOUT和FCGI_STDERR都是流記錄類型,分別用于從應用向Web服務器發(fā)送任意數(shù)據(jù)和錯誤數(shù)據(jù)。
5.4 FCGI_ABORT_REQUEST
Web服務器發(fā)送FCGI_ABORT_REQUEST記錄來中止請求。收到{FCGI_ABORT_REQUEST, R}后,應用盡快用{FCGI_END_REQUEST, R, {FCGI_REQUEST_COMPLETE, appStatus}}響應。這是真實的來自應用的響應,而不是來自FastCGI庫的低級確認。
當HTTP客戶端關(guān)閉了它的傳輸線路,可是受客戶端委托的FastCGI請求仍在運行時,Web服務器中止該FastCGI請求。這種情況看似不太可能;多數(shù)FastCGI請求具有很短的響應時間,同時如果客戶端很慢則Web服務器提供輸出緩沖。但是FastCGI應用與其他系統(tǒng)的通信或執(zhí)行服務器端進棧可能被延期。
當不是通過一個傳輸線路多路復用請求時,Web服務器能通過關(guān)閉請求的傳輸線路來中止請求。但使用多路復用請求時,關(guān)閉傳輸線路具有不幸的結(jié)果,中止線路上的所有請求。
5.5 FCGI_END_REQUEST
不論已經(jīng)處理了請求,還是已經(jīng)拒絕了請求,應用發(fā)送FCGI_END_REQUEST記錄來終止請求。
FCGI_END_REQUEST記錄的contentData組件具有形式:
typedef struct {
unsigned char appStatusB3;
unsigned char appStatusB2;
unsigned char appStatusB1;
unsigned char appStatusB0;
unsigned char protocolStatus;
unsigned char reserved[3];
} FCGI_EndRequestBody;
appStatus組件是應用級別的狀態(tài)碼。每種角色說明其appStatus的用法。
protocolStatus組件是協(xié)議級別的狀態(tài)碼;可能的protocolStatus值是:
FCGI_REQUEST_COMPLETE:請求的正常結(jié)束。?
FCGI_CANT_MPX_CONN:拒絕新請求。這發(fā)生在Web服務器通過一條線路向應用發(fā)送并發(fā)的請求時,后者被設計為每條線路每次處理一個請求。?
FCGI_OVERLOADED:拒絕新請求。這發(fā)生在應用用完某些資源時,例如數(shù)據(jù)庫連接。?
FCGI_UNKNOWN_ROLE:拒絕新請求。這發(fā)生在Web服務器指定了一個應用不能識別的角色時。
6.?角色?6.1?角色協(xié)議
角色協(xié)議只包括帶應用記錄類型的記錄。它們本質(zhì)上利用流傳輸所有數(shù)據(jù)。
為了讓協(xié)議可靠以及簡化應用編程,角色協(xié)議被設計使用近似順序編組(nearly sequential marshalling)。在嚴格順序編組的協(xié)議中,應用接收其第一個輸入,然后是第二個,依次類推。直到收到全部。同樣地,應用發(fā)送其第一個輸出,然后是第二個,依次類推。直到發(fā)出全部。輸入不是相互交叉的,輸出也不是。
對于某些FastCGI角色,順序編組規(guī)則有太多限制,因為CGI程序能不受時限地(timing restriction)寫入stdout和stderr。所以用到了FCGI_STDOUT和FCGI_STDERR的角色協(xié)議允許交叉這兩個流。
所有角色協(xié)議使用FCGI_STDERR流的方式恰是stderr在傳統(tǒng)的應用編程中的使用方式:以易理解的方式報告應用級錯誤。FCGI_STDERR流的使用總是可選的。如果沒有錯誤要報告,應用要么不發(fā)送FCGI_STDERR記錄,要么發(fā)送一個0長度的FCGI_STDERR記錄。
當角色協(xié)議要求傳輸不同于FCGI_STDERR的流時,總是至少傳輸一個流類型的記錄,即使流是空的。
再次關(guān)注可靠的協(xié)議和簡化的應用編程技術(shù),角色協(xié)議被設計為近似請求-響應。在真正的請求-響應協(xié)議中,應用在發(fā)送其輸出記錄前接收其所有的輸入記錄。請求-響應協(xié)議不允許流水線技術(shù)(pipelining)。
對于某些FastCGI角色,請求響應規(guī)則約束太強;畢竟,CGI程序不限于在開始寫stdout前讀取全部stdin。所以某些角色協(xié)議允許特定的可能性。首先,除了結(jié)尾的流輸入,應用接收其所有輸入。當開始接收結(jié)尾的流輸入時,應用開始寫其輸出。
當角色協(xié)議用FCGI_PARAMS傳輸文本值時,例如CGI程序從環(huán)境變量得到的值,其長度不包括結(jié)尾的null字節(jié),而且它本身不包含null字節(jié)。需要提供environ(7)格式的名-值對的應用必須在名和值間插入等號,并在值后添加null字節(jié)。
角色協(xié)議不支持CGI的未解析的(non-parsed)報頭特性。FastCGI應用使用CGI報頭Status和Location設置響應狀態(tài)。
6.2?響應器(Responder)
作為響應器的FastCGI應用具有同CGI/1.1一樣的目的:它接收與HTTP請求關(guān)聯(lián)的所有信息并產(chǎn)生HTTP響應。
它足以解釋怎樣用響應器模擬CGI/1.1的每個元素:
響應器應用通過FCGI_PARAMS接收來自Web服務器的CGI/1.1環(huán)境變量。?
接下來響應器應用通過FCGI_STDIN接收來自Web服務器的CGI/1.1 stdin數(shù)據(jù)。在收到流尾指示前,應用從該流接收最多CONTENT_LENGTH字節(jié)。(只當HTTP客戶端未能提供時,例如因為客戶端崩潰了,應用才收到少于CONTENT_LENGTH的字節(jié)。)?
響應器應用通過FCGI_STDOUT向Web服務器發(fā)送CGI/1.1 stdout數(shù)據(jù),以及通過FCGI_STDERR發(fā)送CGI/1.1 stderr數(shù)據(jù)。應用同時發(fā)送這些,而非一個接一個。在開始寫FCGI_STDOUT和FCGI_STDERR前,應用必須等待讀取FCGI_PARAMS完成,但是不需要在開始寫這兩個流前完成從FCGI_STDIN讀取。?
在發(fā)送其所有stdout和stderr數(shù)據(jù)后,響應器應用發(fā)送FCGI_END_REQUEST記錄。應用設置protocolStatus組件為FCGI_REQUEST_COMPLETE,并設置appStatus組件為CGI程序通過exit系統(tǒng)調(diào)用返回的狀態(tài)碼。
響應器執(zhí)行更新,例如實現(xiàn)POST方法,應該比較在FCGI_STDIN上收到的字節(jié)數(shù)和CONTENT_LENGTH,并且如果兩數(shù)不等則中止更新。
6.3?認證器(Authorizer)
作為認證器的FastCGI應用接收所有與HTTP請求相關(guān)的信息,并產(chǎn)生一個認可/未經(jīng)認可的判定。對于認可的判定,認證器也能把名-值對同HTTP請求相關(guān)聯(lián);當給出未經(jīng)認可的判定時,認證器向HTTP客戶端發(fā)送結(jié)束響應。
由于CGI/1.1定義了與HTTP請求相關(guān)聯(lián)的信息的極好的表示方式,認證器使用同樣的表示法:
認證器應用在FCGI_PARAMS流上接收來自Web服務器的HTTP信息,格式同響應器一樣。Web服務器不會發(fā)送報頭CONTENT_LENGTH、PATH_INFO、PATH_TRANSLATED和SCRIPT_NAME。?
認證器應用以同響應器一樣的方式發(fā)送stdout和stderr數(shù)據(jù)。CGI/1.1響應狀態(tài)指定對結(jié)果的處理。如果應用發(fā)送狀態(tài)200(OK),Web服務器允許訪問。 依賴于其配置,Web服務器可繼續(xù)進行其他的訪問檢查,包括對其他認證器的請求。
認證器應用的200響應可包含以Variable-為名字前綴的報頭。這些報頭從應用向Web服務器傳送名-值對。例如,響應報頭
Variable-AUTH_METHOD: database lookup
傳輸名為AUTH-METHOD的值"database lookup"。服務器把這樣的名-值對同HTTP請求相關(guān)聯(lián),并且把它們包含在后續(xù)的CGI或FastCGI請求中,這些請求在處理HTTP請求的過程中執(zhí)行。當應用給出200響應時,服務器忽略名字不以Variable-為前綴的響應報頭,并且忽略任何響應內(nèi)容。
對于“200”(OK)以外的認證器響應狀態(tài)值,Web服務器拒絕訪問并將響應狀態(tài)、報頭和內(nèi)容發(fā)回HTTP客戶端。
6.4?過濾器(Filter)
作為過濾器的FastCGI應用接收所有與HTTP請求相關(guān)聯(lián)的信息,以及額外的來自存儲在Web服務器上的文件的數(shù)據(jù)流,并產(chǎn)生數(shù)據(jù)流的“已過濾”版本作為HTTP響應。
過濾器在功能上類似響應器,接受一個數(shù)據(jù)文件作為參數(shù)。區(qū)別是,過濾器使得數(shù)據(jù)文件和過濾器本身都能用Web服務器的訪問控制機制進行訪問控制,而響應器接受數(shù)據(jù)文件名作為參數(shù),必須在數(shù)據(jù)文件上執(zhí)行自己的訪問控制檢查。
過濾器采取的步驟與響應器的相似。服務器首先提供環(huán)境變量,然后是標準輸入(常規(guī)形式的POST數(shù)據(jù)),最后是數(shù)據(jù)文件輸入:
如同響應器,過濾器應用通過FCGI_PARAMS接收來自Web服務器的名-值對。過濾器應用接收兩個過濾器特定的變量:FCGI_DATA_LAST_MOD和FCGI_DATA_LENGTH。?
接下來,過濾器應用通過FCGI_STDIN接收來自Web服務器的CGI/1.1 stdin數(shù)據(jù)。在收到流尾指示以前,應用從該流接收最多CONTENT_LENGTH字節(jié)。(只有HTTP客戶端未能提供時,應用收到的才少于CONTENT_LENGTH字節(jié),例如因為客戶端崩潰了。)?
下一步,過濾器應用通過FCGI_DATA接收來自Web服務器的文件數(shù)據(jù)。該文件的最后修改時間(表示成自UTC?1970年1月1日以來的整秒數(shù))是FCGI_DATA_LAST_MOD;應用可能查閱該變量并從緩存作出響應,而不讀取文件數(shù)據(jù)。在收到流尾指示以前,應用從該流接收最多FCGI_DATA_LENGTH字節(jié)。?
過濾器應用通過FCGI_STDOUT向Web服務器發(fā)送CGI/1.1 stdout數(shù)據(jù),以及通過FCGI_STDERR的CGI/1.1 stderr數(shù)據(jù)。應用同時發(fā)送這些,而非相繼地。在開始寫入FCGI_STDOUT和FCGI_STDERR以前,應用必須等待讀取FCGI_STDIN完成,但是不需要在開始寫入這兩個流以前完成從FCGI_DATA的讀取。?
在發(fā)送其所有的stdout和stderr數(shù)據(jù)之后,應用發(fā)送FCGI_END_REQUEST記錄。應用設定protocolStatus組件為FCGI_REQUEST_COMPLETE,以及appStatus組件為類似的CGI程序通過exit系統(tǒng)調(diào)用返回的狀態(tài)代碼。
過濾器應當把在FCGI_STDIN上收到的字節(jié)數(shù)同CONTENT_LENGTH比較,以及把FCGI_DATA上的同F(xiàn)CGI_DATA_LENGTH比較。如果數(shù)字不匹配且過濾器是個查詢,過濾器響應應當提供數(shù)據(jù)丟失的指示。如果數(shù)字不匹配且過濾器是個更新,過濾器應當中止更新。
7.?錯誤
FastCGI應用以0狀態(tài)退出來指出它故意結(jié)束了,例如,為了執(zhí)行原始形式的垃圾收集。FastCGI應用以非0狀態(tài)退出被假定為崩潰了。以0或非0狀態(tài)退出的Web服務器或其他的應用管理器如何響應應用超出了本規(guī)范的范圍。
Web服務器能通過向FastCGI應用發(fā)送SIGTERM來要求它退出。如果應用忽略SIGTERM,Web服務器能采用SIGKILL。
FastCGI應用使用FCGI_STDERR流和FCGI_END_REQUEST記錄的appStatus組件報告應用級別錯誤。在很多情形中,錯誤會通過FCGI_STDOUT流直接報告給用戶。
在Unix上,應用向syslog報告低級錯誤,包括FastCGI協(xié)議錯誤和FastCGI環(huán)境變量中的語法錯誤。依賴于錯誤的嚴重性,應用可能繼續(xù)或以非0狀態(tài)退出。
8.?類型和常量?/*
*?正在監(jiān)聽的socket文件編號
*/
#define FCGI_LISTENSOCK_FILENO 0
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
} FCGI_Header;
/*
* FCGI_Header中的字節(jié)數(shù)。協(xié)議的未來版本不會減少該數(shù)。
*/
#define FCGI_HEADER_LEN 8
/*
*?可用于FCGI_Header的version組件的值
*/
#define FCGI_VERSION_1 1
/*
*?可用于FCGI_Header的type組件的值
*/
#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)
/*
*?可用于FCGI_Header的requestId組件的值
*/
#define FCGI_NULL_REQUEST_ID 0
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
typedef struct {
FCGI_Header header;
FCGI_BeginRequestBody body;
} FCGI_BeginRequestRecord;
/*
*?可用于FCGI_BeginRequestBody的flags組件的掩碼
*/
#define FCGI_KEEP_CONN 1
/*
*?可用于FCGI_BeginRequestBody的role組件的值
*/
#define FCGI_RESPONDER 1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER 3
typedef struct {
unsigned char appStatusB3;
unsigned char appStatusB2;
unsigned char appStatusB1;
unsigned char appStatusB0;
unsigned char protocolStatus;
unsigned char reserved[3];
} FCGI_EndRequestBody;
typedef struct {
FCGI_Header header;
FCGI_EndRequestBody body;
} FCGI_EndRequestRecord;
/*
*?可用于FCGI_EndRequestBody的protocolStatus組件的值
*/
#define FCGI_REQUEST_COMPLETE 0
#define FCGI_CANT_MPX_CONN 1
#define FCGI_OVERLOADED 2
#define FCGI_UNKNOWN_ROLE 3
/*
*?可用于FCGI_GET_VALUES/FCGI_GET_VALUES_RESULT記錄的變量名
*/
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
typedef struct {
unsigned char type;?
unsigned char reserved[7];
} FCGI_UnknownTypeBody;
typedef struct {
FCGI_Header header;
FCGI_UnknownTypeBody body;
} FCGI_UnknownTypeRecord;
9.?參考
National?Center?for Supercomputer Applications,?The Common Gateway Interface, version CGI/1.1.
D.R.T. Robinson,?The WWW Common Gateway Interface Version 1.1, Internet-Draft, 15 February 1996.
A.?表:記錄類型的屬性
下面的圖表列出了所有記錄類型,并指出各自的這些屬性:
WS->App:該類型的記錄只能由Web服務器發(fā)送到應用。其他類型的記錄只能由應用發(fā)送到Web服務器。?
management:該類型的記錄含有非特定于某個Web服務器請求的信息,而且使用null請求ID。其他類型的記錄含有請求特定的信息,而且不能使用null請求ID。?
stream:該類型的記錄組成一個由帶有空contentData的記錄結(jié)束的流。其他類型的記錄是離散的;各自攜帶一個有意義的數(shù)據(jù)單元。
WS->App management stream
FCGI_GET_VALUES x x
FCGI_GET_VALUES_RESULT x
FCGI_UNKNOWN_TYPE x
FCGI_BEGIN_REQUEST x
FCGI_ABORT_REQUEST x
FCGI_END_REQUEST
FCGI_PARAMS x x
FCGI_STDIN x x
FCGI_DATA x x
FCGI_STDOUT x?
FCGI_STDERR x?
B.?典型的協(xié)議消息流程
用于示例的補充符號約定:
流記錄的contentData(FCGI_PARAMS、FCGI_STDIN、FCGI_STDOUT和FCGI_STDERR)被描述成一個字符串。以" ... "結(jié)束的字符串是太長而無法顯示的,所以只顯示前綴。?
發(fā)送到Web服務器的消息相對于收自Web服務器的消息縮進排版。?
消息以應用經(jīng)歷的時間順序顯示。
1.?在stdin上不帶數(shù)據(jù)的簡單請求,以及成功的響應:
{FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS, 1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS, 1, ""}
{FCGI_STDIN, 1, ""}
{FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
{FCGI_STDOUT, 1, ""}
{FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
2.?類似例1,但這次在stdin有數(shù)據(jù)。Web服務器選擇用比之前更多的FCGI_PARAMS記錄發(fā)送參數(shù):
{FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS, 1, "\013\002SERVER_PORT80\013\016SER"}
{FCGI_PARAMS, 1, "VER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS, 1, ""}
{FCGI_STDIN, 1, "quantity=100&item=3047936"}
{FCGI_STDIN, 1, ""}
{FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
{FCGI_STDOUT, 1, ""}
{FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
3.?類似例1,但這次應用發(fā)現(xiàn)了錯誤。應用把一條消息記錄到stderr,向客戶端返回一個頁面,并且向Web服務器返回非0退出狀態(tài)。應用選擇用更多FCGI_STDOUT記錄發(fā)送頁面:
{FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS, 1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS, 1, ""}
{FCGI_STDIN, 1, ""}
{FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n<ht"}
{FCGI_STDERR, 1, "config error: missing SI_UID\n"}
{FCGI_STDOUT, 1, "ml>\n<head> ... "}
{FCGI_STDOUT, 1, ""}
{FCGI_STDERR, 1, ""}
{FCGI_END_REQUEST, 1, {938, FCGI_REQUEST_COMPLETE}}
4.?在單條線路上多路復用的兩個例1實例。第一個請求比第二個難,所以應用顛倒次序完成這些請求:
{FCGI_BEGIN_REQUEST, 1, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
{FCGI_PARAMS, 1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_PARAMS, 1, ""}
{FCGI_BEGIN_REQUEST, 2, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
{FCGI_PARAMS, 2, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_STDIN, 1, ""}
{FCGI_STDOUT, 1, "Content-type: text/html\r\n\r\n"}
{FCGI_PARAMS, 2, ""}
{FCGI_STDIN, 2, ""}
{FCGI_STDOUT, 2, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
{FCGI_STDOUT, 2, ""}
{FCGI_END_REQUEST, 2, {0, FCGI_REQUEST_COMPLETE}}
{FCGI_STDOUT, 1, "<html>\n<head> ... "}
{FCGI_STDOUT, 1, ""}
{FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
轉(zhuǎn)載于:https://www.cnblogs.com/DjangoBlog/p/4151248.html
總結(jié)
以上是生活随笔為你收集整理的FastCGI中文规范的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 进入redis 数据库,Li
- 下一篇: java 实现 sql join_Sql