Browse Source

* Implemented ProtocolOptions and OnUnknownRecord to handle unknown FastCGI records

git-svn-id: trunk@15567 -
michael 15 years ago
parent
commit
2397fa8b40
1 changed files with 65 additions and 10 deletions
  1. 65 10
      packages/fcl-web/src/base/custfcgi.pp

+ 65 - 10
packages/fcl-web/src/base/custfcgi.pp

@@ -26,13 +26,22 @@ uses
 Type
 Type
   { TFCGIRequest }
   { TFCGIRequest }
   TCustomFCgiApplication = Class;
   TCustomFCgiApplication = Class;
+  TFCGIRequest = Class;
+  TFCGIResponse = Class;
+
+  TProtocolOption = (poNoPadding,poStripContentLength, poFailonUnknownRecord );
+  TProtocolOptions = Set of TProtocolOption;
+
+  TUnknownRecordEvent = Procedure (ARequest : TFCGIRequest; AFCGIRecord: PFCGI_Header) Of Object;
 
 
   TFCGIRequest = Class(TCGIRequest)
   TFCGIRequest = Class(TCGIRequest)
   Private
   Private
     FHandle: THandle;
     FHandle: THandle;
     FKeepConnectionAfterRequest: boolean;
     FKeepConnectionAfterRequest: boolean;
+    FPO: TProtoColOptions;
     FRequestID : Word;
     FRequestID : Word;
     FCGIParams : TSTrings;
     FCGIParams : TSTrings;
+    FUR: TUnknownRecordEvent;
     procedure GetNameValuePairsFromContentRecord(const ARecord : PFCGI_ContentRecord; NameValueList : TStrings);
     procedure GetNameValuePairsFromContentRecord(const ARecord : PFCGI_ContentRecord; NameValueList : TStrings);
   Protected
   Protected
     Function GetFieldValue(Index : Integer) : String; override;
     Function GetFieldValue(Index : Integer) : String; override;
@@ -43,16 +52,22 @@ Type
     property RequestID : word read FRequestID write FRequestID;
     property RequestID : word read FRequestID write FRequestID;
     property Handle : THandle read FHandle write FHandle;
     property Handle : THandle read FHandle write FHandle;
     property KeepConnectionAfterRequest : boolean read FKeepConnectionAfterRequest;
     property KeepConnectionAfterRequest : boolean read FKeepConnectionAfterRequest;
+    Property ProtocolOptions : TProtoColOptions read FPO Write FPO;
+    Property OnUnknownRecord : TUnknownRecordEvent Read FUR Write FUR;
   end;
   end;
 
 
   { TFCGIResponse }
   { TFCGIResponse }
 
 
   TFCGIResponse = Class(TCGIResponse)
   TFCGIResponse = Class(TCGIResponse)
   private
   private
+    FNoPadding: Boolean;
+    FPO: TProtoColOptions;
+    FStripCL: Boolean;
     procedure Write_FCGIRecord(ARecord : PFCGI_Header);
     procedure Write_FCGIRecord(ARecord : PFCGI_Header);
   Protected
   Protected
     Procedure DoSendHeaders(Headers : TStrings); override;
     Procedure DoSendHeaders(Headers : TStrings); override;
     Procedure DoSendContent; override;
     Procedure DoSendContent; override;
+    Property ProtocolOptions : TProtoColOptions Read FPO Write FPO;
   end;
   end;
 
 
   TReqResp = record
   TReqResp = record
@@ -64,6 +79,8 @@ Type
 
 
   TCustomFCgiApplication = Class(TCustomWebApplication)
   TCustomFCgiApplication = Class(TCustomWebApplication)
   Private
   Private
+    FOnUnknownRecord: TUnknownRecordEvent;
+    FPO: TProtoColOptions;
     FRequestsArray : Array of TReqResp;
     FRequestsArray : Array of TReqResp;
     FRequestsAvail : integer;
     FRequestsAvail : integer;
     FHandle : THandle;
     FHandle : THandle;
@@ -77,6 +94,8 @@ Type
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
     property Port: integer read FPort write FPort;
     property Port: integer read FPort write FPort;
+    Property ProtocolOptions : TProtoColOptions Read FPO Write FPO;
+    Property OnUnknownRecord : TUnknownRecordEvent Read FOnUnknownRecord Write FOnUnknownRecord;
   end;
   end;
 
 
 ResourceString
 ResourceString
@@ -146,6 +165,12 @@ begin
                           FContentRead:=True;
                           FContentRead:=True;
                           end;
                           end;
                         end;
                         end;
+  else
+    if Assigned(FUR) then
+      FUR(Self,AFCGIRecord)
+    else
+      if poFailonUnknownRecord in FPO then
+        Raise EFPWebError.CreateFmt('Unknown FASTCGI record type: %s',[AFCGIRecord^.reqtype]);
   end;
   end;
 end;
 end;
 
 
@@ -194,6 +219,7 @@ begin
     end;
     end;
 end;
 end;
 
 
+
 Function TFCGIRequest.GetFieldValue(Index : Integer) : String;
 Function TFCGIRequest.GetFieldValue(Index : Integer) : String;
 
 
 Type THttpToCGI = array[1..CGIVarCount] of byte;
 Type THttpToCGI = array[1..CGIVarCount] of byte;
@@ -270,14 +296,26 @@ var
   pl : byte;
   pl : byte;
   str : String;
   str : String;
   ARespRecord : PFCGI_ContentRecord;
   ARespRecord : PFCGI_ContentRecord;
+  I : Integer;
 
 
 begin
 begin
-  str := Headers.Text;
+  For I:=Headers.Count-1 downto 0 do
+    If (Headers[i]='') then
+      Headers.Delete(I);
+  // IndexOfName Does not work ?
+  If (poStripContentLength in ProtocolOptions) then
+    For I:=Headers.Count-1 downto 0 do
+      If (Pos('Content-Length',Headers[i])<>0)  then
+        Headers.Delete(i);
+  str := Headers.Text+sLineBreak;
   cl := length(str);
   cl := length(str);
-  pl := (cl mod 8);
-
+  if ((cl mod 8)=0) or (poNoPadding in ProtocolOptions) then
+    pl:=0
+  else
+    pl := 8-(cl mod 8);
   ARespRecord:=nil;
   ARespRecord:=nil;
   Getmem(ARespRecord,8+cl+pl);
   Getmem(ARespRecord,8+cl+pl);
+  FillChar(ARespRecord^,8+cl+pl,0);
   ARespRecord^.header.version:=FCGI_VERSION_1;
   ARespRecord^.header.version:=FCGI_VERSION_1;
   ARespRecord^.header.reqtype:=FCGI_STDOUT;
   ARespRecord^.header.reqtype:=FCGI_STDOUT;
   ARespRecord^.header.paddingLength:=pl;
   ARespRecord^.header.paddingLength:=pl;
@@ -305,10 +343,11 @@ begin
     end
     end
   else
   else
     str := Contents.Text;
     str := Contents.Text;
-
   cl := length(str);
   cl := length(str);
-  pl := (cl mod 8);
-
+  if ((cl mod 8)=0) or (poNoPadding in ProtocolOptions) then
+    pl:=0
+  else
+    pl := 8-(cl mod 8);
   ARespRecord:=Nil;
   ARespRecord:=Nil;
   Getmem(ARespRecord,8+cl+pl);
   Getmem(ARespRecord,8+cl+pl);
   ARespRecord^.header.version:=FCGI_VERSION_1;
   ARespRecord^.header.version:=FCGI_VERSION_1;
@@ -317,8 +356,10 @@ begin
   ARespRecord^.header.contentLength:=NtoBE(cl);
   ARespRecord^.header.contentLength:=NtoBE(cl);
   ARespRecord^.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
   ARespRecord^.header.requestId:=NToBE(TFCGIRequest(Request).RequestID);
   move(str[1],ARespRecord^.ContentData,cl);
   move(str[1],ARespRecord^.ContentData,cl);
+
   Write_FCGIRecord(PFCGI_Header(ARespRecord));
   Write_FCGIRecord(PFCGI_Header(ARespRecord));
   Freemem(ARespRecord);
   Freemem(ARespRecord);
+  FillChar(EndRequest,SizeOf(FCGI_EndRequestRecord),0);
 
 
   EndRequest.header.version:=FCGI_VERSION_1;
   EndRequest.header.version:=FCGI_VERSION_1;
   EndRequest.header.reqtype:=FCGI_END_REQUEST;
   EndRequest.header.reqtype:=FCGI_END_REQUEST;
@@ -342,8 +383,11 @@ end;
 destructor TCustomFCgiApplication.Destroy;
 destructor TCustomFCgiApplication.Destroy;
 begin
 begin
   SetLength(FRequestsArray,0);
   SetLength(FRequestsArray,0);
-  if port<>0 then
-    fpshutdown(Socket,2);
+  if (Socket<>0) then
+    begin
+    CloseSocket(Socket);
+    Socket:=0;
+    end;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -353,7 +397,7 @@ begin
     begin
     begin
     Assert(ARequest=Request);
     Assert(ARequest=Request);
     Assert(AResponse=Response);
     Assert(AResponse=Response);
-    if not TFCGIRequest(ARequest).KeepConnectionAfterRequest then
+    if (not TFCGIRequest(ARequest).KeepConnectionAfterRequest) then
       begin
       begin
       fpshutdown(FHandle,SHUT_RDWR);
       fpshutdown(FHandle,SHUT_RDWR);
       CloseSocket(FHandle);
       CloseSocket(FHandle);
@@ -425,14 +469,22 @@ begin
       begin
       begin
       Socket := fpsocket(AF_INET,SOCK_STREAM,0);
       Socket := fpsocket(AF_INET,SOCK_STREAM,0);
       if Socket=-1 then
       if Socket=-1 then
-        raise Exception.CreateFmt(SNoSocket,[socketerror]);
+        raise EFPWebError.CreateFmt(SNoSocket,[socketerror]);
       Address.sin_family:=AF_INET;
       Address.sin_family:=AF_INET;
       Address.sin_port:=htons(Port);
       Address.sin_port:=htons(Port);
       Address.sin_addr.s_addr:=0;
       Address.sin_addr.s_addr:=0;
       if fpbind(Socket,@Address,AddressLength)=-1 then
       if fpbind(Socket,@Address,AddressLength)=-1 then
+        begin
+        CloseSocket(socket);
+        Socket:=0;
         raise Exception.CreateFmt(SBindFailed,[port,socketerror]);
         raise Exception.CreateFmt(SBindFailed,[port,socketerror]);
+        end;
       if fplisten(Socket,1)=-1 then
       if fplisten(Socket,1)=-1 then
+        begin
+        CloseSocket(socket);
+        Socket:=0;
         raise Exception.CreateFmt(SListenFailed,[port,socketerror]);
         raise Exception.CreateFmt(SListenFailed,[port,socketerror]);
+        end;
       end
       end
     else
     else
       Socket:=StdInputHandle;
       Socket:=StdInputHandle;
@@ -463,12 +515,15 @@ begin
       ATempRequest:=TFCGIRequest.Create;
       ATempRequest:=TFCGIRequest.Create;
       ATempRequest.RequestID:=ARequestID;
       ATempRequest.RequestID:=ARequestID;
       ATempRequest.Handle:=FHandle;
       ATempRequest.Handle:=FHandle;
+      ATempRequest.ProtocolOptions:=Self.Protocoloptions;
+      ATempRequest.OnUnknownRecord:=Self.OnUnknownRecord;
       FRequestsArray[ARequestID].Request := ATempRequest;
       FRequestsArray[ARequestID].Request := ATempRequest;
       end;
       end;
     if FRequestsArray[ARequestID].Request.ProcessFCGIRecord(AFCGI_Record) then
     if FRequestsArray[ARequestID].Request.ProcessFCGIRecord(AFCGI_Record) then
       begin
       begin
       ARequest:=FRequestsArray[ARequestID].Request;
       ARequest:=FRequestsArray[ARequestID].Request;
       FRequestsArray[ARequestID].Response := TFCGIResponse.Create(ARequest);
       FRequestsArray[ARequestID].Response := TFCGIResponse.Create(ARequest);
+      FRequestsArray[ARequestID].Response.ProtocolOptions:=Self.ProtocolOptions;
       AResponse:=FRequestsArray[ARequestID].Response;
       AResponse:=FRequestsArray[ARequestID].Response;
       Result := True;
       Result := True;
       Break;
       Break;