FastCGI是對CGI的開放的擴展,它爲全部因特網應用提供高性能,且沒有Web服務器API的缺點(penalty)。數據庫
本規範具備有限的(narrow)目標:從應用的視角規定FastCGI應用和支持FastCGI的Web服務器之間的接口。Web服務器的不少特性涉及FastCGI,舉例來講,應用管理設施與應用到Web服務器的接口無關,所以不在這兒描述。編程
本規範適用於Unix(更確切地說,適用於支持伯克利socket的POSIX系統)。本規範大半是簡單的通訊協議,與字節序無關,而且將擴展到其餘系統。數組
咱們將經過與CGI/1.1的常規Unix實現的比較來介紹FastCGI。FastCGI被設計用來支持常駐(long-lived)應用進程,也就是應用服務器。那是與CGI/1.1的常規Unix實現的主要區別,後者構造應用進程,用它響應一個請求,以及讓它退出。緩存
FastCGI進程的初始狀態比CGI/1.1進程的初始狀態更簡潔,由於FastCGI進程開始不會鏈接任何東西。它沒有常規的打開的文件stdin、stdout和stderr,並且它不會經過環境變量接收大量的信息。FastCGI進程的初始狀態的關鍵部分是個正在監聽的socket,經過它來接收來自Web服務器的鏈接。服務器
FastCGI進程在其正在監聽的socket上收到一個鏈接以後,進程執行簡單的協議來接收和發送數據。協議服務於兩個目的。首先,協議在多個獨立的 FastCGI請求間多路複用單個傳輸線路。這可支持可以利用事件驅動或多線程編程技術處理併發請求的應用。第二,在每一個請求內部,協議在每一個方向上提供若干獨立的數據流。這種方式,例如,stdout和stderr數據經過從應用到Web服務器的單個傳輸線路傳遞,而不是像CGI/1.1那樣須要獨立的管道。多線程
一個FastCGI應用扮演幾個明肯定義的角色中的一個。最經常使用的是響應器(Responder)角色,其中應用接收全部與HTTP請求相關的信息,併產生一個HTTP響應;那是CGI/1.1程序扮演的角色。第二個角色是認證器(Authorizer),其中應用接收全部與HTTP請求相關的信息,併產生一個承認/未經承認的斷定。第三個角色是過濾器(Filter),其中應用接收全部與HTTP請求相關的信息,以及額外的來自存儲在Web服務器上的文件的數據流,併產生"已過濾"版的數據流做爲HTTP響應。框架是易擴展的,於是之後可定義更多的FastCGI。併發
在本規範的其他部分,只要不致引發混淆,術語"FastCGI應用"、"應用進程"或"應用服務器"簡寫爲"應用"。app
Web服務器缺省建立一個含有單個元素的參數表,該元素是應用的名字,用做可執行路徑名的最後一部分。Web服務器可提供某種方式來指定不一樣的應用名,或更詳細的參數表。
注意,被Web服務器執行的文件多是解釋程序文件(以字符#!開頭的文本文件),此情形中的應用參數表的構造在execve man頁中描述。
當應用開始執行時,Web服務器留下一個打開的文件描述符,FCGI_LISTENSOCK_FILENO。該描述符引用Web服務器建立的一個正在監聽的socket。
FCGI_LISTENSOCK_FILENO等於STDIN_FILENO。當應用開始執行時,標準的描述符STDOUT_FILENO和STDERR_FILENO被關閉。一個用於應用肯定它是用CGI調用的仍是用FastCGI調用的可靠方法是調用getpeername(FCGI_LISTENSOCK_FILENO),對於FastCGI應用,它返回-1,並設置errno爲ENOTCONN。
Web服務器對於可靠傳輸的選擇,Unix流式管道(AF_UNIX)或TCP/IP(AF_INET),是內含於FCGI_LISTENSOCK_FILENO socket的內部狀態中的。
Web服務器可用環境變量嚮應用傳參數。本規範定義了一個這樣的變量,FCGI_WEB_SERVER_ADDRS;咱們指望隨着規範的發展定義更多。Web服務器可提供某種方式綁定其餘環境變量,例如PATH變量。
Web服務器可提供某種方式指定應用的初始進程狀態的其餘組件,例如進程的優先級、用戶ID、組ID、根目錄和工做目錄。
咱們用C語言符號來定義協議消息格式。全部的結構元素按照unsigned char類型定義和排列,這樣ISO C編譯器以明確的方式將它們展開,不帶填充。結構中定義的第一字節第一個被傳送,第二字節排第二個,依次類推。
咱們用兩個約定來簡化咱們的定義。
首先,當兩個相鄰的結構組件除了後綴「B1」和「B0」以外命名相同時,它表示這兩個組件可視爲估值爲B1<<8 + B0的單個數字。該單個數字的名字是這些組件減去後綴的名字。這個約定概括了一個由超過兩個字節表示的數字的處理方式。
第二,咱們擴展C結構(struct)來容許形式
struct {
unsigned char mumbleLengthB1;
unsigned char mumbleLengthB0;
... /* 其餘東西 */
unsigned char mumbleData[mumbleLength];
};
表示一個變長結構,此處組件的長度由較早的一個或多個組件指示的值肯定。
FastCGI應用在文件描述符FCGI_LISTENSOCK_FILENO引用的socket上調用accept()來接收新的傳輸線路。若是accept()成功,並且也綁定了FCGI_WEB_SERVER_ADDRS環境變量,則應用馬上執行下列特殊處理:
FCGI_WEB_SERVER_ADDRS:值是一列有效的用於Web服務器的IP地址。
若是綁定了FCGI_WEB_SERVER_ADDRS,應用校驗新線路的同級IP地址是否列表中的成員。若是校驗失敗(包括線路不是用TCP/IP傳輸的可能性),應用關閉線路做爲響應。
FCGI_WEB_SERVER_ADDRS被表示成逗號分隔的IP地址列表。每一個IP地址寫成四個由小數點分隔的在區間[0..255]中的十進制數。因此該變量的一個合法綁定是FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71。
應用可接受若干個並行傳輸線路,但不是必須的。
應用利用簡單的協議執行來自Web服務器的請求。協議細節依賴應用的角色,可是大體說來,Web服務器首先發送參數和其餘數據到應用,而後應用發送結果數據到Web服務器,最後應用向Web服務器發送一個請求完成的指示。
經過傳輸線路流動的全部數據在FastCGI記錄中運載。FastCGI記錄實現兩件事。首先,記錄在多個獨立的FastCGI請求間多路複用傳輸線路。該多路複用技術支持可以利用事件驅動或多線程編程技術處理併發請求的應用。第二,在單個請求內部,記錄在每一個方向上提供若干獨立的數據流。這種方式,例如,stdout和stderr數據能經過從應用到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記錄由一個定長前綴後跟可變數量的內容和填充字節組成。記錄包含七個組件:
version: 標識FastCGI協議版本。本規範評述(document)FCGI_VERSION_1。
type: 標識FastCGI記錄類型,也就是記錄執行的通常職能。特定記錄類型和它們的功能在後面部分詳細說明。
requestId: 標識記錄所屬的FastCGI請求。
contentLength: 記錄的contentData組件的字節數。
paddingLength: 記錄的paddingData組件的字節數。
contentData: 在0和65535字節之間的數據,依據記錄類型進行解釋。
paddingData: 在0和255字節之間的數據,被忽略。
咱們用不嚴格的C結構初始化語法來指定常量FastCGI記錄。咱們省略version組件,忽略填充(Padding),而且把requestId視爲數字。於是{FCGI_END_REQUEST, 1, {FCGI_REQUEST_COMPLETE,0}}是個type == FCGI_END_REQUEST、requestId == 1且contentData == {FCGI_REQUEST_COMPLETE,0}的記錄。
填充(Padding)
協議容許發送者填充它們發送的記錄,而且要求接受者解釋paddingLength並跳過paddingData。填充容許發送者爲更有效地處理保持對齊的數據。X窗口系統協議上的經驗顯示了這種對齊方式的性能優點。
咱們建議記錄被放置在八字節倍數的邊界上。FCGI_Record的定長部分是八字節。
管理請求ID
Web服務器重用FastCGI請求ID;應用明瞭給定傳輸線路上的每一個請求ID的當前狀態。當應用收到一個記錄{FCGI_BEGIN_REQUEST, R, ...}時,請求ID R變成有效的,並且當應用向Web服務器發送記錄{FCGI_END_REQUEST, R, ...}時變成無效的。
當請求ID R無效時,應用會忽略requestId == R的記錄,除了剛纔描述的FCGI_BEGIN_REQUEST記錄。
Web服務器嘗試保持小的FastCGI請求ID。那種方式下應用能利用短數組而不是長數組或哈希表來明瞭請求ID的狀態。應用也有每次接受一個請求的選項。這種情形下,應用只是針對當前的請求ID檢查輸入的requestId值。
記錄類型的類型
有兩種有用的分類FastCGI記錄類型的方式。
第一個區別在管理(management)記錄和應用(application)記錄之間。管理記錄包含不特定於任何Web服務器請求的信息,例如關於應用的協議容量的信息。應用記錄包含關於特定請求的信息,由requestId組件標識。
管理記錄有0值的requestId,也稱爲null請求ID。應用記錄有非0的requestId。
第二個區別在離散和連續記錄之間。一個離散記錄包含一個本身的全部數據的有意義的單元。一個流記錄是stream的部分,也就是一連串流類型的0或更多非空記錄(length != 0),後跟一個流類型的空記錄(length == 0)。當鏈接流記錄的多個contentData組件時,造成一個字節序列;該字節序列是流的值。所以流的值獨立於它包含多少個記錄或它的字節如何在非空記錄間分配。
這兩種分類是獨立的。在本版的FastCGI協議定義的記錄類型中,全部管理記錄類型也是離散記錄類型,並且幾乎全部應用記錄類型都是流記錄類型。可是三種應用記錄類型是離散的,並且沒有什麼能防止在某些之後的協議版本中定義一個流式的管理記錄類型。
FastCGI應用的不少角色須要讀寫可變數量的可變長度的值。因此爲編碼名-值對提供標準格式頗有用。
FastCGI以名字長度,後跟值的長度,後跟名字,後跟值的形式傳送名-值對。127字節或更少的長度能在一字節中編碼,而更長的長度老是在四字節中編碼:
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;
長度的第一字節的高位指示長度的編碼方式。高位爲0意味着一個字節的編碼方式,1意味着四字節的編碼方式。
名-值對格式容許發送者不用額外的編碼方式就能傳輸二進制值,而且容許接收者馬上分配正確數量的內存,即便對於巨大的值。
Web服務器控制傳輸線路的生存期。當沒有活動的請求時Web服務器能關閉線路。或者Web服務器也能把關閉的職權委託給應用(見FCGI_BEGIN_REQUEST)。該情形下,應用在指定的請求結束時關閉線路。
這種靈活性提供了多種應用風格。簡單的應用會一次處理一個請求,而且爲每一個請求接受一個新的傳輸線路。更復雜的應用會經過一個或多個傳輸線路處理併發的請求,並且會長期保持傳輸線路爲打開狀態。
簡單的應用經過在寫入響應結束後關閉傳輸線路可獲得重大的性能提高。Web服務器須要控制常駐線路的生命期。
當應用關閉一個線路或發現一個線路關閉了,它就初始化一個新線路。
4. 管理(Management)記錄類型 4.1 FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT
Web服務器能查詢應用內部的具體的變量。典型地,服務器會在應用啓動上執行查詢以使系統配置的某些方面自動化。
應用把收到的查詢做爲記錄{FCGI_GET_VALUES, 0, ...}。FCGI_GET_VALUES記錄的contentData部分包含一系列值爲空的名-值對。
應用經過發送補充了值的{FCGI_GET_VALUES_RESULT, 0, ...}記錄來響應。若是應用不理解查詢中包含的一個變量名,它從響應中忽略那個名字。
FCGI_GET_VALUES被設計爲容許可擴充的變量集。初始集提供信息來幫助服務器執行應用和線路的管理:
FCGI_MAX_CONNS:該應用將接受的併發傳輸線路的最大值,例如"1"或"10"。
FCGI_MAX_REQS:該應用將接受的併發請求的最大值,例如"1"或"50"。
FCGI_MPXS_CONNS:若是應用很少路複用線路(也就是經過每一個線路處理併發請求)則爲 "0",其餘則爲"1"。
應用可在任什麼時候候收到FCGI_GET_VALUES記錄。除了FastCGI庫,應用的響應不能涉及應用固有的庫。
在本協議的將來版本中,管理記錄類型集可能會增加。爲了這種演變做準備,協議包含FCGI_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服務器發送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組件包含一個控制線路關閉的位:
flags & FCGI_KEEP_CONN:若是爲0,則應用在對本次請求響應後關閉線路。若是非0,應用在對本次請求響應後不會關閉線路;Web服務器爲線路保持響應性。
5.2 名-值對流:FCGI_PARAMS FCGI_PARAMS
是流記錄類型,用於從Web服務器嚮應用發送名-值對。名-值對被相繼地沿着流發送,沒有特定順序。
5.3 字節流:FCGI_STDIN, FCGI_DATA, FCGI_STDOUT, FCGI_STDERR FCGI_STDIN
是流記錄類型,用於從Web服務器嚮應用發送任意數據。FCGI_DATA是另外一種流記錄類型,用於嚮應用發送額外數據。
FCGI_STDOUT和FCGI_STDERR都是流記錄類型,分別用於從應用向Web服務器發送任意數據和錯誤數據。
Web服務器發送FCGI_ABORT_REQUEST記錄來停止請求。收到{FCGI_ABORT_REQUEST, R}後,應用盡快用{FCGI_END_REQUEST, R, {FCGI_REQUEST_COMPLETE, appStatus}}響應。這是真實的來自應用的響應,而不是來自FastCGI庫的低級確認。
當HTTP客戶端關閉了它的傳輸線路,但是受客戶端委託的FastCGI請求仍在運行時,Web服務器停止該FastCGI請求。這種狀況看似不太可能;多數FastCGI請求具備很短的響應時間,同時若是客戶端很慢則Web服務器提供輸出緩衝。可是FastCGI應用與其餘系統的通訊或執行服務器端進棧可能被延期。
當不是經過一個傳輸線路多路複用請求時,Web服務器能經過關閉請求的傳輸線路來停止請求。但使用多路複用請求時,關閉傳輸線路具備不幸的結果,停止線路上的全部請求。
不論已經處理了請求,仍是已經拒絕了請求,應用發送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組件是應用級別的狀態碼。每種角色說明其appStatus的用法。
protocolStatus組件是協議級別的狀態碼;可能的protocolStatus值是:
FCGI_REQUEST_COMPLETE:請求的正常結束。
FCGI_CANT_MPX_CONN:拒絕新請求。這發生在Web服務器經過一條線路嚮應用發送併發的請求時,後者被設計爲每條線路每次處理一個請求。
FCGI_OVERLOADED:拒絕新請求。這發生在應用用完某些資源時,例如數據庫鏈接。
FCGI_UNKNOWN_ROLE:拒絕新請求。這發生在Web服務器指定了一個應用不能識別的角色時。
6. 角色 6.1 角色協議
角色協議只包括帶應用記錄類型的記錄。它們本質上利用流傳輸全部數據。
爲了讓協議可靠以及簡化應用編程,角色協議被設計使用近似順序編組(nearly sequential marshalling)。在嚴格順序編組的協議中,應用接收其第一個輸入,而後是第二個,依次類推。直到收到所有。一樣地,應用發送其第一個輸出,而後是第二個,依次類推。直到發出所有。輸入不是相互交叉的,輸出也不是。
對於某些FastCGI角色,順序編組規則有太多限制,由於CGI程序能不受時限地(timing restriction)寫入stdout和stderr。因此用到了FCGI_STDOUT和FCGI_STDERR的角色協議容許交叉這兩個流。
全部角色協議使用FCGI_STDERR流的方式恰是stderr在傳統的應用編程中的使用方式:以易理解的方式報告應用級錯誤。FCGI_STDERR流的使用老是可選的。若是沒有錯誤要報告,應用要麼不發送FCGI_STDERR記錄,要麼發送一個0長度的FCGI_STDERR記錄。
當角色協議要求傳輸不一樣於FCGI_STDERR的流時,老是至少傳輸一個流類型的記錄,即便流是空的。
再次關注可靠的協議和簡化的應用編程技術,角色協議被設計爲近似請求-響應。在真正的請求-響應協議中,應用在發送其輸出記錄前接收其全部的輸入記錄。請求-響應協議不容許流水線技術(pipelining)。
對於某些FastCGI角色,請求響應規則約束太強;畢竟,CGI程序不限於在開始寫stdout前讀取所有stdin。因此某些角色協議容許特定的可能性。首先,除告終尾的流輸入,應用接收其全部輸入。當開始接收結尾的流輸入時,應用開始寫其輸出。
當角色協議用FCGI_PARAMS傳輸文本值時,例如CGI程序從環境變量獲得的值,其長度不包括結尾的null字節,並且它自己不包含null字節。須要提供environ(7)格式的名-值對的應用必須在名和值間插入等號,並在值後添加null字節。
角色協議不支持CGI的未解析的(non-parsed)報頭特性。FastCGI應用使用CGI報頭Status和Location設置響應狀態。
做爲響應器的FastCGI應用具備同CGI/1.1同樣的目的:它接收與HTTP請求關聯的全部信息併產生HTTP響應。
它足以解釋怎樣用響應器模擬CGI/1.1的每一個元素:
響應器應用經過FCGI_PARAMS接收來自Web服務器的CGI/1.1環境變量。
接下來響應器應用經過FCGI_STDIN接收來自Web服務器的CGI/1.1 stdin數據。在收到流尾指示前,應用從該流接收最多CONTENT_LENGTH字節。(只當HTTP客戶端未能提供時,例如由於客戶端崩潰了,應用才收到少於CONTENT_LENGTH的字節。)
響應器應用經過FCGI_STDOUT向Web服務器發送CGI/1.1 stdout數據,以及經過FCGI_STDERR發送CGI/1.1 stderr數據。應用同時發送這些,而非一個接一個。在開始寫FCGI_STDOUT和FCGI_STDERR前,應用必須等待讀取FCGI_PARAMS完成,可是不須要在開始寫這兩個流前完成從FCGI_STDIN讀取。
在發送其全部stdout和stderr數據後,響應器應用發送FCGI_END_REQUEST記錄。應用設置protocolStatus組件爲FCGI_REQUEST_COMPLETE,並設置appStatus組件爲CGI程序經過exit系統調用返回的狀態碼。
響應器執行更新,例如實現POST方法,應該比較在FCGI_STDIN上收到的字節數和CONTENT_LENGTH,而且若是兩數不等則停止更新。
做爲認證器的FastCGI應用接收全部與HTTP請求相關的信息,併產生一個承認/未經承認的斷定。對於承認的斷定,認證器也能把名-值對同HTTP請求相關聯;當給出未經承認的斷定時,認證器向HTTP客戶端發送結束響應。
因爲CGI/1.1定義了與HTTP請求相關聯的信息的極好的表示方式,認證器使用一樣的表示法:
認證器應用在FCGI_PARAMS流上接收來自Web服務器的HTTP信息,格式同響應器同樣。Web服務器不會發送報頭CONTENT_LENGTH、PATH_INFO、PATH_TRANSLATED和SCRIPT_NAME。
認證器應用以同響應器同樣的方式發送stdout和stderr數據。CGI/1.1響應狀態指定對結果的處理。若是應用發送狀態200(OK),Web服務器容許訪問。 依賴於其配置,Web服務器可繼續進行其餘的訪問檢查,包括對其餘認證器的請求。
認證器應用的200響應可包含以Variable-爲名字前綴的報頭。這些報頭從應用向Web服務器傳送名-值對。例如,響應報頭
Variable-AUTH_METHOD: database lookup
傳輸名爲AUTH-METHOD的值"database lookup"。服務器把這樣的名-值對同HTTP請求相關聯,而且把它們包含在後續的CGI或FastCGI請求中,這些請求在處理HTTP請求的過程當中執行。當應用給出200響應時,服務器忽略名字不以Variable-爲前綴的響應報頭,而且忽略任何響應內容。
對於「200」(OK)之外的認證器響應狀態值,Web服務器拒絕訪問並將響應狀態、報頭和內容發回HTTP客戶端。
做爲過濾器的FastCGI應用接收全部與HTTP請求相關聯的信息,以及額外的來自存儲在Web服務器上的文件的數據流,併產生數據流的「已過濾」版本做爲HTTP響應。
過濾器在功能上相似響應器,接受一個數據文件做爲參數。區別是,過濾器使得數據文件和過濾器自己都能用Web服務器的訪問控制機制進行訪問控制,而響應器接受數據文件名做爲參數,必須在數據文件上執行本身的訪問控制檢查。
過濾器採起的步驟與響應器的類似。服務器首先提供環境變量,而後是標準輸入(常規形式的POST數據),最後是數據文件輸入:
如同響應器,過濾器應用經過FCGI_PARAMS接收來自Web服務器的名-值對。過濾器應用接收兩個過濾器特定的變量:FCGI_DATA_LAST_MOD和FCGI_DATA_LENGTH。
接下來,過濾器應用經過FCGI_STDIN接收來自Web服務器的CGI/1.1 stdin數據。在收到流尾指示之前,應用從該流接收最多CONTENT_LENGTH字節。(只有HTTP客戶端未能提供時,應用收到的才少於CONTENT_LENGTH字節,例如由於客戶端崩潰了。)
下一步,過濾器應用經過FCGI_DATA接收來自Web服務器的文件數據。該文件的最後修改時間(表示成自UTC 1970年1月1日以來的整秒數)是FCGI_DATA_LAST_MOD;應用可能查閱該變量並從緩存做出響應,而不讀取文件數據。在收到流尾指示之前,應用從該流接收最多FCGI_DATA_LENGTH字節。
過濾器應用經過FCGI_STDOUT向Web服務器發送CGI/1.1 stdout數據,以及經過FCGI_STDERR的CGI/1.1 stderr數據。應用同時發送這些,而非相繼地。在開始寫入FCGI_STDOUT和FCGI_STDERR之前,應用必須等待讀取FCGI_STDIN完成,可是不須要在開始寫入這兩個流之前完成從FCGI_DATA的讀取。
在發送其全部的stdout和stderr數據以後,應用發送FCGI_END_REQUEST記錄。應用設定protocolStatus組件爲FCGI_REQUEST_COMPLETE,以及appStatus組件爲相似的CGI程序經過exit系統調用返回的狀態代碼。
過濾器應當把在FCGI_STDIN上收到的字節數同CONTENT_LENGTH比較,以及把FCGI_DATA上的同FCGI_DATA_LENGTH比較。若是數字不匹配且過濾器是個查詢,過濾器響應應當提供數據丟失的指示。若是數字不匹配且過濾器是個更新,過濾器應當停止更新。
FastCGI應用以0狀態退出來指出它故意結束了,例如,爲了執行原始形式的垃圾收集。FastCGI應用以非0狀態退出被假定爲崩潰了。以0或非0狀態退出的Web服務器或其餘的應用管理器如何響應應用超出了本規範的範圍。
Web服務器能經過向FastCGI應用發送SIGTERM來要求它退出。若是應用忽略SIGTERM,Web服務器能採用SIGKILL。
FastCGI應用使用FCGI_STDERR流和FCGI_END_REQUEST記錄的appStatus組件報告應用級別錯誤。在不少情形中,錯誤會經過FCGI_STDOUT流直接報告給用戶。
在Unix上,應用向syslog報告低級錯誤,包括FastCGI協議錯誤和FastCGI環境變量中的語法錯誤。依賴於錯誤的嚴重性,應用可能繼續或以非0狀態退出。
8. 類型和常量 /*
* 正在監聽的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中的字節數。協議的將來版本不會減小該數。
*/
#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.
下面的圖表列出了全部記錄類型,並指出各自的這些屬性:
WS->App:該類型的記錄只能由Web服務器發送到應用。其餘類型的記錄只能由應用發送到Web服務器。
management:該類型的記錄含有非特定於某個Web服務器請求的信息,並且使用null請求ID。其餘類型的記錄含有請求特定的信息,並且不能使用null請求ID。
stream:該類型的記錄組成一個由帶有空contentData的記錄結束的流。其餘類型的記錄是離散的;各自攜帶一個有意義的數據單元。
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. 典型的協議消息流程
用於示例的補充符號約定:
流記錄的contentData(FCGI_PARAMS、FCGI_STDIN、FCGI_STDOUT和FCGI_STDERR)被描述成一個字符串。以" ... "結束的字符串是太長而沒法顯示的,因此只顯示前綴。
發送到Web服務器的消息相對於收自Web服務器的消息縮進排版。
消息以應用經歷的時間順序顯示。
1. 在stdin上不帶數據的簡單請求,以及成功的響應:
{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有數據。Web服務器選擇用比以前更多的FCGI_PARAMS記錄發送參數:
{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,但此次應用發現了錯誤。應用把一條消息記錄到stderr,向客戶端返回一個頁面,而且向Web服務器返回非0退出狀態。應用選擇用更多FCGI_STDOUT記錄發送頁面:
{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}}