mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 01:09:06 +02:00
* Initial implementation of FastCgi support
git-svn-id: trunk@12970 -
This commit is contained in:
parent
b4a8ae3637
commit
9938d57233
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -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
packages/fcl-web/src/custfcgi.pp
Normal file
438
packages/fcl-web/src/custfcgi.pp
Normal file
@ -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
packages/fcl-web/src/custweb.pp
Normal file
143
packages/fcl-web/src/custweb.pp
Normal file
@ -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
packages/fcl-web/src/fastcgi.pp
Normal file
158
packages/fcl-web/src/fastcgi.pp
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user