Browse Source

* Initial implementation of FastCgi support

git-svn-id: trunk@12970 -
joost 16 years ago
parent
commit
9938d57233
4 changed files with 742 additions and 0 deletions
  1. 3 0
      .gitattributes
  2. 438 0
      packages/fcl-web/src/custfcgi.pp
  3. 143 0
      packages/fcl-web/src/custweb.pp
  4. 158 0
      packages/fcl-web/src/fastcgi.pp

+ 3 - 0
.gitattributes

@@ -1635,7 +1635,10 @@ packages/fcl-web/fptemplate.txt svneol=native#text/plain
 packages/fcl-web/src/README.txt svneol=native#text/plain
 packages/fcl-web/src/README.txt svneol=native#text/plain
 packages/fcl-web/src/cgiapp.pp svneol=native#text/plain
 packages/fcl-web/src/cgiapp.pp svneol=native#text/plain
 packages/fcl-web/src/custcgi.pp svneol=native#text/plain
 packages/fcl-web/src/custcgi.pp svneol=native#text/plain
+packages/fcl-web/src/custfcgi.pp svneol=native#text/plain
+packages/fcl-web/src/custweb.pp svneol=native#text/plain
 packages/fcl-web/src/ezcgi.pp svneol=native#text/plain
 packages/fcl-web/src/ezcgi.pp svneol=native#text/plain
+packages/fcl-web/src/fastcgi.pp svneol=native#text/plain
 packages/fcl-web/src/fpapache.pp svneol=native#text/plain
 packages/fcl-web/src/fpapache.pp svneol=native#text/plain
 packages/fcl-web/src/fpcgi.pp svneol=native#text/plain
 packages/fcl-web/src/fpcgi.pp svneol=native#text/plain
 packages/fcl-web/src/fpdatasetform.pp svneol=native#text/plain
 packages/fcl-web/src/fpdatasetform.pp svneol=native#text/plain

+ 438 - 0
packages/fcl-web/src/custfcgi.pp

@@ -0,0 +1,438 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 1999-2009 by the Free Pascal development team
+
+    TFCgiApplication class.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{ $define CGIDEBUG}
+{$mode objfpc}
+{$H+}
+
+unit custfcgi;
+
+Interface
+
+uses
+  Classes,SysUtils, httpdefs,custweb, custcgi, fastcgi;
+
+Type
+  { TFCGIRequest }
+  TCustomFCgiApplication = Class;
+
+  TFCGIRequest = Class(TCGIRequest)
+  Private
+    FHandle: THandle;
+    FKeepConnectionAfterRequest: boolean;
+    FRequestID : Word;
+    FCGIParams : TSTrings;
+    procedure GetNameValuePairsFromContentRecord(const ARecord : PFCGI_ContentRecord; NameValueList : TStrings);
+  Protected
+    Function GetFieldValue(Index : Integer) : String; override;
+    procedure ReadContent; override;
+  Public
+    destructor Destroy; override;
+    function ProcessFCGIRecord(AFCGIRecord : PFCGI_Header) : boolean; virtual;
+    property RequestID : word read FRequestID write FRequestID;
+    property Handle : THandle read FHandle write FHandle;
+    property KeepConnectionAfterRequest : boolean read FKeepConnectionAfterRequest;
+  end;
+
+  { TFCGIResponse }
+
+  TFCGIResponse = Class(TCGIResponse)
+  private
+    procedure Write_FCGIRecord(ARecord : PFCGI_Header);
+  Protected
+    Procedure DoSendHeaders(Headers : TStrings); override;
+    Procedure DoSendContent; override;
+  end;
+
+  TReqResp = record
+             Request : TFCgiRequest;
+             Response : TFCgiResponse;
+             end;
+
+  { TCustomFCgiApplication }
+
+  TCustomFCgiApplication = Class(TCustomWebApplication)
+  Private
+    FRequestsArray : Array of TReqResp;
+    FRequestsAvail : integer;
+    FHandle : THandle;
+    function Read_FCGIRecord : PFCGI_Header;
+  protected
+    function WaitForRequest(var ARequest : TRequest; var AResponse : TResponse) : boolean; override;
+    procedure EndRequest(ARequest : TRequest;AResponse : TResponse); override;
+  Public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+  end;
+
+ResourceString
+  SNoInputHandle = 'Failed to open input-handle passed from server. Socket Error: %d';
+
+Implementation
+
+uses
+{$ifdef CGIDEBUG}
+  dbugintf,
+{$endif}
+  BaseUnix, Sockets;
+
+{ TFCGIHTTPRequest }
+
+procedure TFCGIRequest.ReadContent;
+begin
+  // Nothing has to be done. This should never be called
+end;
+
+destructor TFCGIRequest.Destroy;
+begin
+  FCGIParams.Free;
+  inherited Destroy;
+end;
+
+function TFCGIRequest.ProcessFCGIRecord(AFCGIRecord: PFCGI_Header): boolean;
+var cl,rcl : Integer;
+begin
+  Result := False;
+  case AFCGIRecord^.reqtype of
+    FCGI_BEGIN_REQUEST : FKeepConnectionAfterRequest := (PFCGI_BeginRequestRecord(AFCGIRecord)^.body.flags and FCGI_KEEP_CONN) = FCGI_KEEP_CONN;
+    FCGI_PARAMS :       begin
+                        if AFCGIRecord^.contentLength=0 then
+                          Result := False
+                        else
+                          begin
+                          if not assigned(FCGIParams) then
+                            FCGIParams := TStringList.Create;
+                          GetNameValuePairsFromContentRecord(PFCGI_ContentRecord(AFCGIRecord),FCGIParams);
+                          end;
+                        end;
+    FCGI_STDIN :        begin
+                        if AFCGIRecord^.contentLength=0 then
+                          begin
+                          Result := True;
+                          InitRequestVars;
+                          end
+                        else
+                          begin
+                          cl := length(FContent);
+                          rcl := BetoN(PFCGI_ContentRecord(AFCGIRecord)^.header.contentLength);
+                          SetLength(FContent, rcl+cl);
+                          move(PFCGI_ContentRecord(AFCGIRecord)^.ContentData[0],FContent[cl+1],rcl);
+                          FContentRead:=True;
+                          end;
+                        end;
+  end;
+end;
+
+procedure TFCGIRequest.GetNameValuePairsFromContentRecord(const ARecord: PFCGI_ContentRecord; NameValueList: TStrings);
+
+var
+  i : integer;
+
+  function GetVarLength : Integer;
+  begin
+    if (ARecord^.ContentData[i] and 128) = 0 then
+      Result:=ARecord^.ContentData[i]
+    else
+      begin
+      Result:=BEtoN(PWord(@(ARecord^.ContentData[i]))^);
+      // ((ARecord^.ContentData[i] and $7f) shl 24) + (ARecord^.ContentData[i+1] shl 16)
+      //             + (ARecord^.ContentData[i+2] shl 8) + (ARecord^.ContentData[i+3]);
+      inc(i,3);
+      end;
+    inc(i);
+  end;
+
+  function GetString(ALength : integer) : string;
+  begin
+    SetLength(Result,ALength);
+    move(ARecord^.ContentData[i],Result[1],ALength);
+    inc(i,ALength);
+  end;
+
+var
+  NameLength, ValueLength : Integer;
+  RecordLength : Integer;
+  Name,Value : String;
+
+begin
+  i := 0;
+  RecordLength:=BetoN(ARecord^.Header.contentLength);
+  while i < RecordLength do
+    begin
+    NameLength:=GetVarLength;
+    ValueLength:=GetVarLength;
+
+    Name:=GetString(NameLength);
+    Value:=GetString(ValueLength);
+    NameValueList.Add(Name+'='+Value);
+    end;
+end;
+
+Function TFCGIRequest.GetFieldValue(Index : Integer) : String;
+
+Type THttpToCGI = array[1..CGIVarCount] of byte;
+
+const HttpToCGI : THttpToCGI =
+   (
+     18,  //  1 'HTTP_ACCEPT'           - fieldAccept
+     19,  //  2 'HTTP_ACCEPT_CHARSET'   - fieldAcceptCharset
+     20,  //  3 'HTTP_ACCEPT_ENCODING'  - fieldAcceptEncoding
+      0,  //  4
+      0,  //  5
+      0,  //  6
+      0,  //  7
+      0,  //  8
+      2,  //  9 'CONTENT_LENGTH'
+      3,  // 10 'CONTENT_TYPE'          - fieldAcceptEncoding
+     24,  // 11 'HTTP_COOKIE'           - fieldCookie
+      0,  // 12
+      0,  // 13
+      0,  // 14
+     21,  // 15 'HTTP_IF_MODIFIED_SINCE'- fieldIfModifiedSince
+      0,  // 16
+      0,  // 17
+      0,  // 18
+     22,  // 19 'HTTP_REFERER'          - fieldReferer
+      0,  // 20
+      0,  // 21
+      0,  // 22
+     23,  // 23 'HTTP_USER_AGENT'       - fieldUserAgent
+      1,  // 24 'AUTH_TYPE'             - fieldWWWAuthenticate
+      5,  // 25 'PATH_INFO'
+      6,  // 26 'PATH_TRANSLATED'
+      8,  // 27 'REMOTE_ADDR'
+      9,  // 28 'REMOTE_HOST'
+     13,  // 29 'SCRIPT_NAME'
+     15,  // 30 'SERVER_PORT'
+     12,  // 31 'REQUEST_METHOD'
+      0,  // 32
+      7,  // 33 'QUERY_STRING'
+     27   // 34 'HTTP_HOST'
+    );
+
+var ACgiVarNr : Integer;
+
+begin
+  Result := '';
+  if assigned(FCGIParams) and (index < high(HttpToCGI)) and (index > 0) then
+    begin
+    ACgiVarNr:=HttpToCGI[Index];
+    if ACgiVarNr>0 then
+      Result:=FCGIParams.Values[CgiVarNames[ACgiVarNr]]
+    else
+      Result := '';
+    end
+  else
+    Result:=inherited GetFieldValue(Index);
+end;
+
+{ TCGIResponse }
+procedure TFCGIResponse.Write_FCGIRecord(ARecord : PFCGI_Header);
+var BytesToWrite : word;
+    BytesWritten  : Integer;
+begin
+  BytesToWrite := BEtoN(ARecord^.contentLength) + ARecord^.paddingLength+sizeof(FCGI_Header);
+  BytesWritten := sockets.fpsend(TFCGIRequest(Request).Handle, ARecord, BytesToWrite, MSG_NOSIGNAL);
+  Assert(BytesWritten=BytesToWrite);
+end;
+
+procedure TFCGIResponse.DoSendHeaders(Headers : TStrings);
+var
+  cl : word;
+  pl : byte;
+  str : String;
+  ARespRecord : PFCGI_ContentRecord;
+
+begin
+  str := Headers.Text;
+  cl := length(str);
+  pl := (cl mod 8);
+
+  ARespRecord:=nil;
+  Getmem(ARespRecord,8+cl+pl);
+  ARespRecord^.header.version:=FCGI_VERSION_1;
+  ARespRecord^.header.reqtype:=FCGI_STDOUT;
+  ARespRecord^.header.paddingLength:=pl;
+  ARespRecord^.header.contentLength:=NtoBE(cl);
+  ARespRecord^.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
+  move(str[1],ARespRecord^.ContentData,cl);
+  Write_FCGIRecord(PFCGI_Header(ARespRecord));
+  Freemem(ARespRecord);
+end;
+
+procedure TFCGIResponse.DoSendContent;
+var
+  cl : word;
+  pl : byte;
+  str : String;
+  ARespRecord : PFCGI_ContentRecord;
+  EndRequest : FCGI_EndRequestRecord;
+
+begin
+  If Assigned(ContentStream) then
+    begin
+    setlength(str,ContentStream.Size);
+    ContentStream.Position:=0;
+    ContentStream.Read(str[1],ContentStream.Size);
+    end
+  else
+    str := Contents.Text;
+
+  cl := length(str);
+  pl := (cl mod 8);
+
+  ARespRecord:=Nil;
+  Getmem(ARespRecord,8+cl+pl);
+  ARespRecord^.header.version:=FCGI_VERSION_1;
+  ARespRecord^.header.reqtype:=FCGI_STDOUT;
+  ARespRecord^.header.paddingLength:=pl;
+  ARespRecord^.header.contentLength:=NtoBE(cl);
+  ARespRecord^.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
+  move(str[1],ARespRecord^.ContentData,cl);
+  Write_FCGIRecord(PFCGI_Header(ARespRecord));
+  Freemem(ARespRecord);
+
+  EndRequest.header.version:=FCGI_VERSION_1;
+  EndRequest.header.reqtype:=FCGI_END_REQUEST;
+  EndRequest.header.contentLength:=NtoBE(8);
+  EndRequest.header.paddingLength:=0;
+  EndRequest.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
+  Write_FCGIRecord(PFCGI_Header(@EndRequest));
+end;
+
+{ TCustomFCgiApplication }
+
+constructor TCustomFCgiApplication.Create(AOwner: TComponent);
+begin
+  FRequestsAvail:=5;
+  SetLength(FRequestsArray,FRequestsAvail);
+  FHandle := -1;
+end;
+
+destructor TCustomFCgiApplication.Destroy;
+begin
+  SetLength(FRequestsArray,0);
+  inherited Destroy;
+end;
+
+procedure TCustomFCgiApplication.EndRequest(ARequest: TRequest; AResponse: TResponse);
+begin
+  with FRequestsArray[TFCGIRequest(ARequest).RequestID] do
+    begin
+    Assert(ARequest=Request);
+    Assert(AResponse=Response);
+    if not TFCGIRequest(ARequest).KeepConnectionAfterRequest then
+      begin
+      fpshutdown(FHandle,SHUT_RDWR);
+      FpClose(FHandle);
+      FHandle := -1;
+      end;
+    Request := Nil;
+    Response := Nil;
+    end;
+  Inherited;
+end;
+
+function TCustomFCgiApplication.Read_FCGIRecord : PFCGI_Header;
+
+var Header : FCGI_Header;
+    BytesRead : integer;
+    ContentLength : word;
+    PaddingLength : byte;
+    ResRecord : pointer;
+    ReadBuf : pointer;
+
+  function ReadBytes(ByteAmount : Word) : boolean;
+  begin
+   result := False;
+    if ByteAmount>0 then
+      begin
+      BytesRead := sockets.fpRecv(FHandle, ReadBuf, ByteAmount, MSG_NOSIGNAL);
+      if BytesRead<>ByteAmount then
+        begin
+//        SendDebug('FCGIRecord incomplete');
+//        SendDebug('BytesRead: '+inttostr(BytesRead)+', expected: '+inttostr(ByteAmount));
+        exit;
+        end
+      else
+        Result := True;
+      end;
+  end;
+
+begin
+  Result := Nil;
+  ResRecord:=Nil;
+  ReadBuf:=@Header;
+  if not ReadBytes(Sizeof(Header)) then exit;
+  ContentLength:=BetoN(Header.contentLength);
+  PaddingLength:=Header.paddingLength;
+  Getmem(ResRecord,BytesRead+ContentLength+PaddingLength);
+  PFCGI_Header(ResRecord)^:=Header;
+  ReadBuf:=ResRecord+BytesRead;
+  ReadBytes(ContentLength);
+  ReadBuf:=ReadBuf+BytesRead;
+  ReadBytes(PaddingLength);
+  Result := ResRecord;
+end;
+
+function TCustomFCgiApplication.WaitForRequest(var ARequest: TRequest; var AResponse: TResponse): boolean;
+var
+  Address       : TInetSockAddr;
+  AddressLength : tsocklen;
+  ARequestID    : word;
+  AFCGI_Record  : PFCGI_Header;
+  ATempRequest  : TFCGIRequest;
+begin
+  Result := False;
+  AddressLength:=Sizeof(Address);
+  if FHandle=-1 then
+    begin
+    FHandle:=fpaccept(StdInputHandle,psockaddr(@Address),@AddressLength);
+    if FHandle=-1 then
+      raise Exception.CreateFmt(SNoInputHandle,[socketerror]);
+    end;
+
+  repeat
+  AFCGI_Record:=Read_FCGIRecord;
+  if assigned(AFCGI_Record) then
+    begin
+    ARequestID:=BEtoN(AFCGI_Record^.requestID);
+    if AFCGI_Record^.reqtype = FCGI_BEGIN_REQUEST then
+      begin
+      if ARequestID>FRequestsAvail then
+        begin
+        inc(FRequestsAvail,10);
+        SetLength(FRequestsArray,FRequestsAvail);
+        end;
+      assert(not assigned(FRequestsArray[ARequestID].Request));
+      assert(not assigned(FRequestsArray[ARequestID].Response));
+
+      ATempRequest:=TFCGIRequest.Create;
+      ATempRequest.RequestID:=ARequestID;
+      ATempRequest.Handle:=FHandle;
+      FRequestsArray[ARequestID].Request := ATempRequest;
+      end;
+    if FRequestsArray[ARequestID].Request.ProcessFCGIRecord(AFCGI_Record) then
+      begin
+      ARequest:=FRequestsArray[ARequestID].Request;
+      FRequestsArray[ARequestID].Response := TFCGIResponse.Create(ARequest);
+      AResponse:=FRequestsArray[ARequestID].Response;
+      Result := True;
+      Break;
+      end;
+    end;
+  until Terminated;
+end;
+
+end.

+ 143 - 0
packages/fcl-web/src/custweb.pp

@@ -0,0 +1,143 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 1999-2009 by the Free Pascal development team
+
+    TWebApplication class.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{ $define CGIDEBUG}
+{$mode objfpc}
+{$H+}
+
+unit custweb;
+
+Interface
+
+uses
+  CustApp,Classes,SysUtils, httpdefs;
+
+Const
+  CGIVarCount = 34;
+
+Type
+  TCGIVarArray = Array[1..CGIVarCount] of String;
+
+Const
+  CgiVarNames : TCGIVarArray =
+   ({ 1  } 'AUTH_TYPE',
+    { 2  } 'CONTENT_LENGTH',
+    { 3  } 'CONTENT_TYPE',
+    { 4  } 'GATEWAY_INTERFACE',
+    { 5  } 'PATH_INFO',
+    { 6  } 'PATH_TRANSLATED',
+    { 7  } 'QUERY_STRING',
+    { 8  } 'REMOTE_ADDR',
+    { 9  } 'REMOTE_HOST',
+    { 10 } 'REMOTE_IDENT',
+    { 11 } 'REMOTE_USER',
+    { 12 } 'REQUEST_METHOD',
+    { 13 } 'SCRIPT_NAME',
+    { 14 } 'SERVER_NAME',
+    { 15 } 'SERVER_PORT',
+    { 16 } 'SERVER_PROTOCOL',
+    { 17 } 'SERVER_SOFTWARE',
+    { 18 } 'HTTP_ACCEPT',
+    { 19 } 'HTTP_ACCEPT_CHARSET',
+    { 20 } 'HTTP_ACCEPT_ENCODING',
+    { 21 } 'HTTP_IF_MODIFIED_SINCE',
+    { 22 } 'HTTP_REFERER',
+    { 23 } 'HTTP_USER_AGENT',
+    { 24 } 'HTTP_COOKIE',
+     // Additional Apache vars
+    { 25 } 'HTTP_CONNECTION',
+    { 26 } 'HTTP_ACCEPT_LANGUAGE',
+    { 27 } 'HTTP_HOST',
+    { 28 } 'SERVER_SIGNATURE',
+    { 29 } 'SERVER_ADDR',
+    { 30 } 'DOCUMENT_ROOT',
+    { 31 } 'SERVER_ADMIN',
+    { 32 } 'SCRIPT_FILENAME',
+    { 33 } 'REMOTE_PORT',
+    { 34 } 'REQUEST_URI'
+    );
+
+Type
+  { TCustomWebApplication }
+
+  TCustomWebApplication = Class(TCustomApplication)
+  Private
+    FRequest : TRequest;
+    FHandleGetOnPost : Boolean;
+    FRedirectOnError : Boolean;
+    FRedirectOnErrorURL : String;
+  protected
+    function WaitForRequest(var ARequest : TRequest; var AResponse : TResponse) : boolean; virtual; abstract;
+    procedure EndRequest(ARequest : TRequest;AResponse : TResponse); virtual;
+  Public
+    constructor Create(AOwner: TComponent); override;
+    Procedure Initialize; override;
+    Procedure DoRun; override;
+    Procedure handleRequest(ARequest : TRequest; AResponse : TResponse); virtual;
+    Property HandleGetOnPost : Boolean Read FHandleGetOnPost Write FHandleGetOnPost;
+    Property RedirectOnError : boolean Read FRedirectOnError Write FRedirectOnError;
+    Property RedirectOnErrorURL : string Read FRedirectOnErrorURL Write FRedirectOnErrorURL;
+    Property Request : TRequest read FRequest;
+  end;
+
+Implementation
+
+{$ifdef CGIDEBUG}
+uses
+  dbugintf;
+{$endif}
+
+procedure TCustomWebApplication.DoRun;
+var ARequest : TRequest;
+    AResponse : TResponse;
+begin
+  while not Terminated do
+    begin
+    if WaitForRequest(ARequest,AResponse) then
+      begin
+      HandleRequest(ARequest,AResponse);
+      If Not AResponse.ContentSent then
+        AResponse.SendContent;
+      EndRequest(ARequest,AResponse);
+      end;
+    end;
+end;
+
+procedure TCustomWebApplication.HandleRequest(ARequest: TRequest; AResponse: TResponse);
+begin
+  // Needs overriding;
+end;
+
+Procedure TCustomWebApplication.Initialize;
+
+begin
+  StopOnException:=True;
+  Inherited;
+end;
+
+procedure TCustomWebApplication.EndRequest(ARequest: TRequest; AResponse: TResponse);
+begin
+  AResponse.Free;
+  ARequest.Free;
+end;
+
+constructor TCustomWebApplication.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FHandleGetOnPost := True;
+  FRedirectOnError := False;
+  FRedirectOnErrorURL := '';
+end;
+
+end.

+ 158 - 0
packages/fcl-web/src/fastcgi.pp

@@ -0,0 +1,158 @@
+unit fastcgi;
+
+interface
+
+{
+  Automatically converted by H2Pas 0.99.16 from fastcgi.h
+  The following command line parameters were used:
+    fastcgi.h
+}
+
+{$IFDEF FPC}
+{$PACKRECORDS C}
+{$ENDIF}
+
+
+{
+ * Listening socket file number
+}
+
+const
+   FCGI_LISTENSOCK_FILENO = 0;     
+
+type
+
+   PFCGI_Header = ^FCGI_Header;
+   FCGI_Header = record
+      version : byte;
+      reqtype : byte;
+      // requestIdB1 : byte;
+      // requestIdB0 : byte;
+      requestID : word; // Stored as big-endian
+      //contentLengthB1 : byte;
+      //contentLengthB0 : byte;
+      contentLength : word; // Stored as big-endian
+      paddingLength : byte;
+      reserved : byte;
+   end;
+
+{
+ * Number of bytes in a FCGI_Header.  Future versions of the protocol
+ * will not reduce this number.
+}
+
+const
+   FCGI_HEADER_LEN = 8;     
+
+{
+ * Value for version component of FCGI_Header
+}
+   FCGI_VERSION_1 = 1;     
+
+{
+ * Values for type component of FCGI_Header
+}
+   FCGI_BEGIN_REQUEST = 1;     
+   FCGI_ABORT_REQUEST = 2;     
+   FCGI_END_REQUEST = 3;     
+   FCGI_PARAMS = 4;     
+   FCGI_STDIN = 5;     
+   FCGI_STDOUT = 6;     
+   FCGI_STDERR = 7;     
+   FCGI_DATA = 8;     
+   FCGI_GET_VALUES = 9;     
+   FCGI_GET_VALUES_RESULT = 10;     
+   FCGI_UNKNOWN_TYPE = 11;     
+   FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE;     
+   
+{
+ * Value for requestId component of FCGI_Header
+}
+   FCGI_NULL_REQUEST_ID = 0;     
+
+type
+   FCGI_BeginRequestBody = record
+      //roleB1 : byte;
+      //roleB0 : byte;
+      role : word; // Stored as big-endian
+      flags : byte;
+      reserved : array[0..4] of byte;
+   end;
+
+   PFCGI_BeginRequestRecord = ^FCGI_BeginRequestRecord;
+   FCGI_BeginRequestRecord = record
+      header : FCGI_Header;
+      body : FCGI_BeginRequestBody;
+   end;
+
+{
+ * Mask for flags component of FCGI_BeginRequestBody
+}
+
+const
+   FCGI_KEEP_CONN = 1;     
+   
+{
+ * Values for role component of FCGI_BeginRequestBody
+}
+
+   FCGI_RESPONDER = 1;     
+   FCGI_AUTHORIZER = 2;     
+   FCGI_FILTER = 3;     
+
+type
+
+   FCGI_EndRequestBody = record
+      appStatusB3 : byte;
+      appStatusB2 : byte;
+      appStatusB1 : byte;
+      appStatusB0 : byte;
+      protocolStatus : byte;
+      reserved : array[0..2] of byte;
+   end;
+
+   FCGI_EndRequestRecord = record
+      header : FCGI_Header;
+      body : FCGI_EndRequestBody;
+   end;
+   
+{
+ * Values for protocolStatus component of FCGI_EndRequestBody
+}
+
+const
+   FCGI_REQUEST_COMPLETE = 0;     
+   FCGI_CANT_MPX_CONN = 1;     
+   FCGI_OVERLOADED = 2;     
+   FCGI_UNKNOWN_ROLE = 3;     
+   
+{
+ * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
+}
+
+   FCGI_MAX_CONNS = 'FCGI_MAX_CONNS';     
+   FCGI_MAX_REQS = 'FCGI_MAX_REQS';
+   FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS';     
+
+type
+
+   FCGI_UnknownTypeBody = record
+      _type : byte;
+      reserved : array[0..6] of byte;
+   end;
+
+   FCGI_UnknownTypeRecord = record
+      header : FCGI_Header;
+      body : FCGI_UnknownTypeBody;
+   end;
+
+   PFCGI_ContentRecord = ^FCGI_ContentRecord;
+   FCGI_ContentRecord = record
+      header : FCGI_Header;
+      ContentData : array[0..1023] of byte;
+   end;
+
+
+implementation
+
+end.