mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-12 14:29:14 +02:00
* Rework header treatment, deprecated some calls/properties
git-svn-id: trunk@30550 -
This commit is contained in:
parent
9559dabe51
commit
4769a5407c
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -3149,6 +3149,7 @@ packages/fcl-web/src/base/Makefile svneol=native#text/plain
|
|||||||
packages/fcl-web/src/base/Makefile.fpc svneol=native#text/plain
|
packages/fcl-web/src/base/Makefile.fpc svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/README.txt svneol=native#text/plain
|
packages/fcl-web/src/base/README.txt svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/cgiapp.pp svneol=native#text/plain
|
packages/fcl-web/src/base/cgiapp.pp svneol=native#text/plain
|
||||||
|
packages/fcl-web/src/base/cgiprotocol.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/custcgi.pp svneol=native#text/plain
|
packages/fcl-web/src/base/custcgi.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/custfcgi.pp svneol=native#text/plain
|
packages/fcl-web/src/base/custfcgi.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/custhttpapp.pp svneol=native#text/plain
|
packages/fcl-web/src/base/custhttpapp.pp svneol=native#text/plain
|
||||||
@ -3169,6 +3170,7 @@ packages/fcl-web/src/base/fphttpstatus.pas svneol=native#text/plain
|
|||||||
packages/fcl-web/src/base/fpweb.pp svneol=native#text/plain
|
packages/fcl-web/src/base/fpweb.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/fpwebfile.pp svneol=native#text/plain
|
packages/fcl-web/src/base/fpwebfile.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/httpdefs.pp svneol=native#text/plain
|
packages/fcl-web/src/base/httpdefs.pp svneol=native#text/plain
|
||||||
|
packages/fcl-web/src/base/httpprotocol.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/iniwebsession.pp svneol=native#text/plain
|
packages/fcl-web/src/base/iniwebsession.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/webpage.pp svneol=native#text/plain
|
packages/fcl-web/src/base/webpage.pp svneol=native#text/plain
|
||||||
packages/fcl-web/src/base/websession.pp svneol=native#text/plain
|
packages/fcl-web/src/base/websession.pp svneol=native#text/plain
|
||||||
|
@ -48,6 +48,8 @@ begin
|
|||||||
T.ResourceStrings:=true;
|
T.ResourceStrings:=true;
|
||||||
with T.Dependencies do
|
with T.Dependencies do
|
||||||
begin
|
begin
|
||||||
|
AddUnit('httpprotocol');
|
||||||
|
AddUnit('cgiprotocol');
|
||||||
AddUnit('httpdefs');
|
AddUnit('httpdefs');
|
||||||
end;
|
end;
|
||||||
T:=P.Targets.AddUnit('ezcgi.pp');
|
T:=P.Targets.AddUnit('ezcgi.pp');
|
||||||
@ -83,7 +85,12 @@ begin
|
|||||||
AddUnit('fphttp');
|
AddUnit('fphttp');
|
||||||
AddUnit('websession');
|
AddUnit('websession');
|
||||||
end;
|
end;
|
||||||
|
T:=P.Targets.AddUnit('httpprotocol.pp');
|
||||||
|
T:=P.Targets.AddUnit('cgiprotocol.pp');
|
||||||
|
|
||||||
T:=P.Targets.AddUnit('httpdefs.pp');
|
T:=P.Targets.AddUnit('httpdefs.pp');
|
||||||
|
T.Dependencies.AddUnit('httpprotocol');
|
||||||
|
|
||||||
T.ResourceStrings:=true;
|
T.ResourceStrings:=true;
|
||||||
T:=P.Targets.AddUnit('iniwebsession.pp');
|
T:=P.Targets.AddUnit('iniwebsession.pp');
|
||||||
T.ResourceStrings:=true;
|
T.ResourceStrings:=true;
|
||||||
@ -122,6 +129,9 @@ begin
|
|||||||
with P.Targets.AddUnit('custfcgi.pp') do
|
with P.Targets.AddUnit('custfcgi.pp') do
|
||||||
begin
|
begin
|
||||||
OSes:=AllOses-[wince,darwin,iphonesim,aix,amiga,aros];
|
OSes:=AllOses-[wince,darwin,iphonesim,aix,amiga,aros];
|
||||||
|
Dependencies.AddUnit('httpprotocol');
|
||||||
|
Dependencies.AddUnit('cgiprotocol');
|
||||||
|
Dependencies.AddUnit('custcgi');
|
||||||
Dependencies.AddUnit('httpdefs');
|
Dependencies.AddUnit('httpdefs');
|
||||||
Dependencies.AddUnit('custweb');
|
Dependencies.AddUnit('custweb');
|
||||||
ResourceStrings:=true;
|
ResourceStrings:=true;
|
||||||
@ -129,6 +139,7 @@ begin
|
|||||||
with P.Targets.AddUnit('fpapache.pp') do
|
with P.Targets.AddUnit('fpapache.pp') do
|
||||||
begin
|
begin
|
||||||
OSes:=AllOses-[amiga,aros];
|
OSes:=AllOses-[amiga,aros];
|
||||||
|
Dependencies.AddUnit('httpprotocol');
|
||||||
Dependencies.AddUnit('fphttp');
|
Dependencies.AddUnit('fphttp');
|
||||||
Dependencies.AddUnit('custweb');
|
Dependencies.AddUnit('custweb');
|
||||||
ResourceStrings:=true;
|
ResourceStrings:=true;
|
||||||
|
80
packages/fcl-web/src/base/cgiprotocol.pp
Normal file
80
packages/fcl-web/src/base/cgiprotocol.pp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
unit cgiprotocol;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
Const
|
||||||
|
CGIVarCount = 44 ;
|
||||||
|
|
||||||
|
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',
|
||||||
|
{ 35 } 'CONTENT',
|
||||||
|
{ 36 } 'HTTP_X_REQUESTED_WITH',
|
||||||
|
{ 37 } 'HTTP_AUTHORIZATION',
|
||||||
|
{ 38 } 'SCRIPT_URI',
|
||||||
|
{ 39 } 'SCRIPT_URL',
|
||||||
|
{ 40 } 'CONTEXT_DOCUMENT_ROOT',
|
||||||
|
{ 41 } 'CONTEXT_PREFIX',
|
||||||
|
{ 42 } 'HTTP_CACHE_CONTROL',
|
||||||
|
{ 43 } 'HTTP_PRAGMA',
|
||||||
|
{ 44 } 'REQUEST_SCHEME'
|
||||||
|
);
|
||||||
|
|
||||||
|
Function IndexOfCGIVar(AVarName: String): Integer;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses sysutils;
|
||||||
|
|
||||||
|
Function IndexOfCGIVar(AVarName: String): Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=CGIVarCount;
|
||||||
|
While (Result>0) and (CompareText(AVarName,CgiVarNames[Result])<>0) do
|
||||||
|
Dec(Result);
|
||||||
|
If Result<1 then
|
||||||
|
Result:=-1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
@ -21,7 +21,7 @@ unit custcgi;
|
|||||||
Interface
|
Interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
CustWeb,Classes,SysUtils, httpdefs;
|
CustWeb, Classes,SysUtils, httpdefs, cgiprotocol, httpprotocol;
|
||||||
|
|
||||||
Type
|
Type
|
||||||
{ TCGIRequest }
|
{ TCGIRequest }
|
||||||
@ -36,7 +36,8 @@ Type
|
|||||||
FOnContentRead: TCGIContentReadEvent;
|
FOnContentRead: TCGIContentReadEvent;
|
||||||
function GetCGIVar(Index: integer): String;
|
function GetCGIVar(Index: integer): String;
|
||||||
Protected
|
Protected
|
||||||
Function GetFieldValue(Index : Integer) : String; override;
|
Function DoMapCgiToHTTP(Const AVariableName : String; Out AHeaderType : THeader; Out AVariableType : THTTPVariableType) : Boolean;
|
||||||
|
function DoGetCGIVar(AVarName: String): String; virtual;
|
||||||
Procedure InitFromEnvironment; virtual;
|
Procedure InitFromEnvironment; virtual;
|
||||||
// Read content from stdin. Calls DoContentRead to see if reading must be aborted.
|
// Read content from stdin. Calls DoContentRead to see if reading must be aborted.
|
||||||
procedure ReadContent; override;
|
procedure ReadContent; override;
|
||||||
@ -45,16 +46,30 @@ Type
|
|||||||
Function DoContentRead(B : PByte; Len : Integer) : Boolean; virtual;
|
Function DoContentRead(B : PByte; Len : Integer) : Boolean; virtual;
|
||||||
Public
|
Public
|
||||||
Constructor CreateCGI(ACGI : TCGIHandler);
|
Constructor CreateCGI(ACGI : TCGIHandler);
|
||||||
|
Function GetCustomHeader(const Name: String) : String; override;
|
||||||
Property OnContentRead : TCGIContentReadEvent Read FOnContentRead Write FOnContentRead;
|
Property OnContentRead : TCGIContentReadEvent Read FOnContentRead Write FOnContentRead;
|
||||||
Property GatewayInterface : String Index 1 Read GetCGIVar;
|
// Index is index in CGIVarnames array.
|
||||||
Property RemoteIdent : String Index 2 read GetCGIVar;
|
Property GatewayInterface : String Index 4 Read GetCGIVar;
|
||||||
Property RemoteUser : String Index 3 read GetCGIVar;
|
Property RemoteIdent : String Index 10 read GetCGIVar;
|
||||||
Property RequestMethod : String Index 4 read GetCGIVar;
|
Property RemoteUser : String Index 11 read GetCGIVar;
|
||||||
Property ServerName : String Index 5 read GetCGIVar;
|
Property RequestMethod : String Index 12 read GetCGIVar;
|
||||||
Property ServerProtocol : String Index 6 read GetCGIVar;
|
Property ServerName : String Index 14 read GetCGIVar;
|
||||||
Property ServerSoftware : String Index 7 read GetCGIVar;
|
Property ServerProtocol : String Index 16 read GetCGIVar;
|
||||||
|
Property ServerSoftware : String Index 17 read GetCGIVar;
|
||||||
|
Property ServerSignature : String Index 28 Read GetCGIVar;
|
||||||
|
Property ServerAddr : String Index 29 Read GetCGIVar;
|
||||||
|
Property DocumentRoot : String Index 30 Read GetCGIVar;
|
||||||
|
Property ServerAdmin : String Index 31 Read GetCGIVar;
|
||||||
|
Property ScriptFileName : String Index 32 Read GetCGIVar;
|
||||||
|
Property RemotePort : String Index 33 Read GetCGIVar;
|
||||||
|
Property RequestURI : String Index 34 Read GetCGIVar;
|
||||||
|
Property ScriptURI : String Index 38 Read GetCGIVar;
|
||||||
|
Property ContextDocumentRoot : String Index 40 Read GetCGIVar;
|
||||||
|
Property ContextPrefix : String Index 41 Read GetCGIVar;
|
||||||
|
Property RequestScheme : String Index 44 Read GetCGIVar;
|
||||||
end;
|
end;
|
||||||
TCGIRequestClass = Class of TCGIRequest;
|
TCGIRequestClass = Class of TCGIRequest;
|
||||||
|
|
||||||
{ TCGIResponse }
|
{ TCGIResponse }
|
||||||
|
|
||||||
TCGIResponse = Class(TResponse)
|
TCGIResponse = Class(TResponse)
|
||||||
@ -139,53 +154,61 @@ uses
|
|||||||
{$endif}
|
{$endif}
|
||||||
iostream;
|
iostream;
|
||||||
|
|
||||||
|
Type
|
||||||
|
TMap = record
|
||||||
|
h : THeader;
|
||||||
|
v : THTTPVariableType;
|
||||||
|
end;
|
||||||
|
TCGIHeaderMap = Array[1..CGIVarCount] of TMap;
|
||||||
|
|
||||||
Const
|
Const
|
||||||
MapCgiToHTTP : TCGIVarArray =
|
|
||||||
({ 1: 'AUTH_TYPE' } fieldWWWAuthenticate, // ?
|
MapCgiToHTTP : TCGIHeaderMap =
|
||||||
{ 2: 'CONTENT_LENGTH' } FieldContentLength,
|
({ 1: 'AUTH_TYPE' } (h : hhWWWAuthenticate; v : hvUnknown), // ?
|
||||||
{ 3: 'CONTENT_TYPE' } FieldContentType,
|
{ 2: 'CONTENT_LENGTH' } (h : hhContentLength; v : hvUnknown),
|
||||||
{ 4: 'GATEWAY_INTERFACE' } '',
|
{ 3: 'CONTENT_TYPE' } (h : hhContentType; v : hvUnknown),
|
||||||
{ 5: 'PATH_INFO' } '',
|
{ 4: 'GATEWAY_INTERFACE' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 6: 'PATH_TRANSLATED' } '',
|
{ 5: 'PATH_INFO' } (h:hhUnknown; v : hvPathInfo),
|
||||||
{ 7: 'QUERY_STRING' } '',
|
{ 6: 'PATH_TRANSLATED' } (h:hhUnknown; v : hvPathTranslated),
|
||||||
{ 8: 'REMOTE_ADDR' } '',
|
{ 7: 'QUERY_STRING' } (h:hhUnknown; v : hvQuery),
|
||||||
{ 9: 'REMOTE_HOST' } '',
|
{ 8: 'REMOTE_ADDR' } (h:hhUnknown; v : hvRemoteAddress),
|
||||||
{ 10: 'REMOTE_IDENT' } '',
|
{ 9: 'REMOTE_HOST' } (h:hhUnknown; v : hvRemoteHost),
|
||||||
{ 11: 'REMOTE_USER' } '',
|
{ 10: 'REMOTE_IDENT' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 12: 'REQUEST_METHOD' } '',
|
{ 11: 'REMOTE_USER' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 13: 'SCRIPT_NAME' } '',
|
{ 12: 'REQUEST_METHOD' } (h:hhUnknown; v : hvMethod),
|
||||||
{ 14: 'SERVER_NAME' } '',
|
{ 13: 'SCRIPT_NAME' } (h:hhUnknown; v : hvScriptName),
|
||||||
{ 15: 'SERVER_PORT' } '',
|
{ 14: 'SERVER_NAME' } (h:hhServer; v : hvUnknown),
|
||||||
{ 16: 'SERVER_PROTOCOL' } '',
|
{ 15: 'SERVER_PORT' } (h:hhUnknown; v : hvServerPort),
|
||||||
{ 17: 'SERVER_SOFTWARE' } '',
|
{ 16: 'SERVER_PROTOCOL' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 18: 'HTTP_ACCEPT' } FieldAccept,
|
{ 17: 'SERVER_SOFTWARE' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 19: 'HTTP_ACCEPT_CHARSET' } FieldAcceptCharset,
|
{ 18: 'HTTP_ACCEPT' } (h:hhAccept; v : hvUnknown),
|
||||||
{ 20: 'HTTP_ACCEPT_ENCODING' } FieldAcceptEncoding,
|
{ 19: 'HTTP_ACCEPT_CHARSET' } (h:hhAcceptCharset; v : hvUnknown),
|
||||||
{ 21: 'HTTP_IF_MODIFIED_SINCE' } FieldIfModifiedSince,
|
{ 20: 'HTTP_ACCEPT_ENCODING' } (h:hhAcceptEncoding; v : hvUnknown),
|
||||||
{ 22: 'HTTP_REFERER' } FieldReferer,
|
{ 21: 'HTTP_IF_MODIFIED_SINCE' } (h:hhIfModifiedSince; v : hvUnknown),
|
||||||
{ 23: 'HTTP_USER_AGENT' } FieldUserAgent,
|
{ 22: 'HTTP_REFERER' } (h:hhReferer; v : hvUnknown),
|
||||||
{ 24: 'HTTP_COOKIE' } FieldCookie,
|
{ 23: 'HTTP_USER_AGENT' } (h:hhUserAgent; v : hvUnknown),
|
||||||
|
{ 24: 'HTTP_COOKIE' } (h:hhUnknown; v : hvCookie),
|
||||||
// Additional Apache vars
|
// Additional Apache vars
|
||||||
{ 25: 'HTTP_CONNECTION' } FieldConnection,
|
{ 25: 'HTTP_CONNECTION' } (h:hhConnection; v : hvUnknown),
|
||||||
{ 26: 'HTTP_ACCEPT_LANGUAGE' } FieldAcceptLanguage,
|
{ 26: 'HTTP_ACCEPT_LANGUAGE' } (h:hhAcceptLanguage; v : hvUnknown),
|
||||||
{ 27: 'HTTP_HOST' } '',
|
{ 27: 'HTTP_HOST' } (h:hhHost; v : hvUnknown),
|
||||||
{ 28: 'SERVER_SIGNATURE' } '',
|
{ 28: 'SERVER_SIGNATURE' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 29: 'SERVER_ADDR' } '',
|
{ 29: 'SERVER_ADDR' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 30: 'DOCUMENT_ROOT' } '',
|
{ 30: 'DOCUMENT_ROOT' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 31: 'SERVER_ADMIN' } '',
|
{ 31: 'SERVER_ADMIN' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 32: 'SCRIPT_FILENAME' } '',
|
{ 32: 'SCRIPT_FILENAME' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 33: 'REMOTE_PORT' } '',
|
{ 33: 'REMOTE_PORT' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 34: 'REQUEST_URI' } '',
|
{ 34: 'REQUEST_URI' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 35: 'CONTENT' } '',
|
{ 35: 'CONTENT' } (h:hhUnknown; v : hvContent),
|
||||||
{ 36: 'XHTTPREQUESTEDWITH' } '',
|
{ 36: 'XHTTPREQUESTEDWITH' } (h:hhUnknown; v : hvXRequestedWith),
|
||||||
{ 37: 'HTTP_AUTHORIZATION' } FieldAuthorization,
|
{ 37: 'HTTP_AUTHORIZATION' } (h:hhAuthorization; v : hvUnknown),
|
||||||
{ 38: 'SCRIPT_URI' } '',
|
{ 38: 'SCRIPT_URI' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 39: 'SCRIPT_URL' } '',
|
{ 39: 'SCRIPT_URL' } (h:hhUnknown; v : hvURL),
|
||||||
{ 40: 'CONTEXT_DOCUMENT_ROOT' } '',
|
{ 40: 'CONTEXT_DOCUMENT_ROOT' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 41: 'CONTEXT_PREFIX' } '',
|
{ 41: 'CONTEXT_PREFIX' } (h:hhUnknown; v : hvUnknown),
|
||||||
{ 42: 'HTTP_CACHE_CONTROL' } '',
|
{ 42: 'HTTP_CACHE_CONTROL' } (h:hhCacheControl; v : hvUnknown),
|
||||||
{ 43: 'HTTP_PRAGMA' } '',
|
{ 43: 'HTTP_PRAGMA' } (h:hhPragma; v : hvUnknown),
|
||||||
{ 44: 'REQUEST_SCHEME' } ''
|
{ 44: 'REQUEST_SCHEME' } (h:hhUnknown; v : hvUnknown)
|
||||||
);
|
);
|
||||||
|
|
||||||
procedure TCgiHandler.GetCGIVarList(List: TStrings);
|
procedure TCgiHandler.GetCGIVarList(List: TStrings);
|
||||||
@ -282,7 +305,20 @@ begin
|
|||||||
FCGI:=ACGI;
|
FCGI:=ACGI;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCGIRequest.GetCustomHeader(const Name: String): String;
|
||||||
|
begin
|
||||||
|
Result:=inherited GetCustomHeader(Name);
|
||||||
|
// Check environment
|
||||||
|
if (Result='') then
|
||||||
|
Result:=DoGetCGIVAr('HTTP_'+StringReplace(Uppercase(Name),'-','_',[rfReplaceAll]));
|
||||||
|
end;
|
||||||
|
|
||||||
{ TCGIHTTPRequest }
|
{ TCGIHTTPRequest }
|
||||||
|
function TCGIRequest.DoGetCGIVar(AVarName : String) : String;
|
||||||
|
|
||||||
|
begin
|
||||||
|
GetEnvironmentVariable(AVarName);
|
||||||
|
end;
|
||||||
|
|
||||||
function TCGIRequest.GetCGIVar(Index: integer): String;
|
function TCGIRequest.GetCGIVar(Index: integer): String;
|
||||||
|
|
||||||
@ -290,41 +326,51 @@ Var
|
|||||||
R : String;
|
R : String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Case Index of
|
if Index in [1..CGIVarCount] then
|
||||||
1 : R:=GetEnvironmentVariable(CGIVarNames[4]); // Property GatewayInterface : String Index 1 Read GetCGIVar;
|
R:=DoGetCGIVar(CGIVarNames[Index])
|
||||||
2 : R:=GetEnvironmentVariable(CGIVarNames[10]); // Property RemoteIdent : String Index 2 read GetCGIVar;
|
else
|
||||||
3 : R:=GetEnvironmentVariable(CGIVarNames[11]); // Property RemoteUser : String Index 3 read GetCGIVar;
|
R:='';
|
||||||
4 : R:=GetEnvironmentVariable(CGIVarNames[12]); // Property RequestMethod : String Index 4 read GetCGIVar;
|
|
||||||
5 : R:=GetEnvironmentVariable(CGIVarNames[14]); // Property ServerName : String Index 5 read GetCGIVar;
|
|
||||||
6 : R:=GetEnvironmentVariable(CGIVarNames[16]); // Property ServerProtocol : String Index 6 read GetCGIVar;
|
|
||||||
7 : R:=GetEnvironmentVariable(CGIVarNames[17]); // Property ServerSoftware : String Index 7 read GetCGIVar;
|
|
||||||
end;
|
|
||||||
Result:=HTTPDecode(R);
|
Result:=HTTPDecode(R);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCGIRequest.DoMapCgiToHTTP(const AVariableName: String; out
|
||||||
|
AHeaderType: THeader; Out AVariableType: THTTPVariableType): Boolean;
|
||||||
|
Var
|
||||||
|
I : Integer;
|
||||||
|
begin
|
||||||
|
I:=IndexOfCGIVar(AVariableName);
|
||||||
|
Result:=I<>-1;
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
AHeaderType:=MapCgiToHTTP[i].H;
|
||||||
|
AVariableType:=MapCgiToHTTP[i].V;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCGIRequest.InitFromEnvironment;
|
procedure TCGIRequest.InitFromEnvironment;
|
||||||
|
|
||||||
|
|
||||||
Var
|
Var
|
||||||
I : Integer;
|
I : Integer;
|
||||||
N,V,OV : String;
|
V,OV : String;
|
||||||
|
M : TMap;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
For I:=1 to CGIVarCount do
|
For I:=1 to CGIVarCount do
|
||||||
begin
|
begin
|
||||||
N:=MapCgiToHTTP[i];
|
V:=GetEnvironmentVariable(CGIVarNames[I]);
|
||||||
if (N<>'') then
|
if (V<>'') then
|
||||||
begin
|
begin
|
||||||
OV:=GetFieldByName(N);
|
M:=MapCgiToHTTP[i];
|
||||||
V:=GetEnvironmentVariable(CGIVarNames[I]);
|
if M.H<>hhUnknown then
|
||||||
If (OV='') or (V<>'') then
|
SetHeader(M.H,HTTPDecode(V))
|
||||||
|
else if M.V<>hvUnknown then
|
||||||
begin
|
begin
|
||||||
if (N<>'QUERY_STRING') then
|
if M.V<>hvQuery then
|
||||||
V:=HTTPDecode(V);
|
V:=HTTPDecode(V);
|
||||||
SetFieldByName(N,V);
|
SetHTTPVariable(M.V,V)
|
||||||
end;
|
end;
|
||||||
end;
|
end
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -390,35 +436,6 @@ begin
|
|||||||
FOnContentRead(Self,B,Len,Result);
|
FOnContentRead(Self,B,Len,Result);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TCGIRequest.GetFieldValue(Index: Integer): String;
|
|
||||||
|
|
||||||
Function DecodeVar(I : Integer; DoDecode : Boolean = true) : String;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Result:=GetEnvironmentVariable(CGIVarNames[I]);
|
|
||||||
if DoDecode then
|
|
||||||
Result:=HttpDecode(Result)
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Case Index of
|
|
||||||
21,
|
|
||||||
34 : Result:=DecodeVar(14); // Property ServerName and Host
|
|
||||||
25 : Result:=Decodevar(5); // Property PathInfo
|
|
||||||
26 : Result:=DecodeVar(6); // Property PathTranslated
|
|
||||||
27 : Result:=DecodeVar(8); // Property RemoteAddress
|
|
||||||
28 : Result:=DecodeVar(9); // Property RemoteHost
|
|
||||||
29 : Result:=DecodeVar(13); // Property ScriptName
|
|
||||||
30 : Result:=DecodeVar(15); // Property ServerPort
|
|
||||||
31 : Result:=DecodeVar(12); // Property RequestMethod
|
|
||||||
32 : Result:=DecodeVar(34); // Property URI
|
|
||||||
33 : Result:=DecodeVar(7,False); // Property QueryString
|
|
||||||
36 : Result:=DecodeVar(36); // Property XRequestedWith
|
|
||||||
else
|
|
||||||
Result:=Inherited GetFieldValue(Index);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{ TCGIResponse }
|
{ TCGIResponse }
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ uses
|
|||||||
{$else}
|
{$else}
|
||||||
winsock2, windows,
|
winsock2, windows,
|
||||||
{$endif}
|
{$endif}
|
||||||
Sockets, custweb, custcgi, fastcgi;
|
Sockets, custweb, cgiprotocol, httpprotocol, custcgi, fastcgi;
|
||||||
|
|
||||||
Type
|
Type
|
||||||
{ TFCGIRequest }
|
{ TFCGIRequest }
|
||||||
@ -62,11 +62,10 @@ Type
|
|||||||
FUR: TUnknownRecordEvent;
|
FUR: TUnknownRecordEvent;
|
||||||
FLog : TLogEvent;
|
FLog : TLogEvent;
|
||||||
FSTDin : String;
|
FSTDin : String;
|
||||||
procedure GetNameValuePairsFromContentRecord(const ARecord : PFCGI_ContentRecord; NameValueList : TStrings);
|
|
||||||
Protected
|
Protected
|
||||||
|
function DoGetCGIVar(AVarName: String): String; override;
|
||||||
|
procedure GetNameValuePairsFromContentRecord(const ARecord : PFCGI_ContentRecord; NameValueList : TStrings); virtual;
|
||||||
Procedure Log(EventType : TEventType; Const Msg : String);
|
Procedure Log(EventType : TEventType; Const Msg : String);
|
||||||
Function GetFieldValue(Index : Integer) : String; override;
|
|
||||||
procedure ReadContent; override;
|
|
||||||
Public
|
Public
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function ProcessFCGIRecord(AFCGIRecord : PFCGI_Header) : boolean; virtual;
|
function ProcessFCGIRecord(AFCGIRecord : PFCGI_Header) : boolean; virtual;
|
||||||
@ -240,11 +239,6 @@ end;
|
|||||||
|
|
||||||
{ TFCGIHTTPRequest }
|
{ TFCGIHTTPRequest }
|
||||||
|
|
||||||
procedure TFCGIRequest.ReadContent;
|
|
||||||
begin
|
|
||||||
// Nothing has to be done. This should never be called
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TFCGIRequest.Destroy;
|
destructor TFCGIRequest.Destroy;
|
||||||
begin
|
begin
|
||||||
FCGIParams.Free;
|
FCGIParams.Free;
|
||||||
@ -297,6 +291,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TFCGIRequest.DoGetCGIVar(AVarName: String): String;
|
||||||
|
begin
|
||||||
|
Result:=FCGIParams.Values[AVarName];
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFCGIRequest.GetNameValuePairsFromContentRecord(const ARecord: PFCGI_ContentRecord; NameValueList: TStrings);
|
procedure TFCGIRequest.GetNameValuePairsFromContentRecord(const ARecord: PFCGI_ContentRecord; NameValueList: TStrings);
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -327,21 +326,44 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
NameLength, ValueLength : Integer;
|
VarNo,NameLength, ValueLength : Integer;
|
||||||
RecordLength : Integer;
|
RecordLength : Integer;
|
||||||
Name,Value : String;
|
Name,Value : String;
|
||||||
|
h : THeader;
|
||||||
|
v : THTTPVariableType;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
Touch('pairs-enter');
|
||||||
i := 0;
|
i := 0;
|
||||||
RecordLength:=BetoN(ARecord^.Header.contentLength);
|
RecordLength:=BetoN(ARecord^.Header.contentLength);
|
||||||
while i < RecordLength do
|
while i < RecordLength do
|
||||||
begin
|
begin
|
||||||
NameLength:=GetVarLength;
|
NameLength:=GetVarLength;
|
||||||
ValueLength:=GetVarLength;
|
ValueLength:=GetVarLength;
|
||||||
|
|
||||||
Name:=GetString(NameLength);
|
Name:=GetString(NameLength);
|
||||||
Value:=GetString(ValueLength);
|
Value:=GetString(ValueLength);
|
||||||
NameValueList.Add(Name+'='+Value);
|
VarNo:=IndexOfCGIVar(Name);
|
||||||
|
Touch('pairs_'+Name+'__'+Value);
|
||||||
|
if Not DoMapCgiToHTTP(Name,H,V) then
|
||||||
|
NameValueList.Add(Name+'='+Value)
|
||||||
|
else if (H<>hhUnknown) then
|
||||||
|
SetHeader(H,Value)
|
||||||
|
else if (v<>hvUnknown) then
|
||||||
|
begin
|
||||||
|
Touch('pairs_var_'+Name+'__'+Value);
|
||||||
|
if (V=hvPathInfo) and (Copy(Value,1,2)='//') then //mod_proxy_fcgi gives double slashes at the beginning for some reason
|
||||||
|
Delete(Value,1,3);
|
||||||
|
if (V<>hvQuery) then
|
||||||
|
Value:=HTTPDecode(Value);
|
||||||
|
SetHTTPVariable(v,Value);
|
||||||
|
Touch('pairs_var_done_'+Name+'__'+Value);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Touch('pairs_other_'+Name+'__'+Value);
|
||||||
|
NameValueList.Add(Name+'='+Value)
|
||||||
|
end;
|
||||||
|
Inc(I);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -351,74 +373,8 @@ begin
|
|||||||
FLog(EventType,Msg);
|
FLog(EventType,Msg);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
Function TFCGIRequest.GetFieldValue(Index : Integer) : String;
|
|
||||||
|
|
||||||
Type THttpToCGI = array[1..37] of byte;
|
|
||||||
|
|
||||||
const HttpToCGI : THttpToCGI =
|
|
||||||
(
|
|
||||||
18, // 1 'HTTP_ACCEPT' - field Accept
|
|
||||||
19, // 2 'HTTP_ACCEPT_CHARSET' - field AcceptCharset
|
|
||||||
20, // 3 'HTTP_ACCEPT_ENCODING' - field AcceptEncoding
|
|
||||||
26, // 4 'HTTP_ACCEPT_LANGUAGE' - field AcceptLanguage
|
|
||||||
37, // 5 HTTP_AUTHORIZATION - field Authorization
|
|
||||||
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'
|
|
||||||
0, // 35 'CONTENT'
|
|
||||||
36, // 36 'XHTTPREQUESTEDWITH'
|
|
||||||
37 // 37 'HTTP_AUTHORIZATION'
|
|
||||||
);
|
|
||||||
|
|
||||||
var ACgiVarNr : Integer;
|
|
||||||
|
|
||||||
begin
|
|
||||||
|
|
||||||
Result := '';
|
|
||||||
if assigned(FCGIParams) and (index <= high(HttpToCGI)) and (index > 0) and (index<>35) then
|
|
||||||
begin
|
|
||||||
ACgiVarNr:=HttpToCGI[Index];
|
|
||||||
if ACgiVarNr>0 then
|
|
||||||
begin
|
|
||||||
Result:=FCGIParams.Values[CgiVarNames[ACgiVarNr]];
|
|
||||||
if (ACgiVarNr = 5) and //PATH_INFO
|
|
||||||
(length(Result)>=2)and(word(Pointer(@Result[1])^)=$2F2F)then //mod_proxy_fcgi gives double slashes at the beginning for some reason
|
|
||||||
Delete(Result, 1, 1); //Remove the extra first one
|
|
||||||
end else
|
|
||||||
Result := '';
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Result:=inherited GetFieldValue(Index);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TCGIResponse }
|
{ TCGIResponse }
|
||||||
|
|
||||||
procedure TFCGIResponse.Write_FCGIRecord(ARecord : PFCGI_Header);
|
procedure TFCGIResponse.Write_FCGIRecord(ARecord : PFCGI_Header);
|
||||||
|
|
||||||
var ErrorCode,
|
var ErrorCode,
|
||||||
|
@ -23,65 +23,7 @@ Interface
|
|||||||
uses
|
uses
|
||||||
CustApp,Classes,SysUtils, httpdefs, fphttp, eventlog;
|
CustApp,Classes,SysUtils, httpdefs, fphttp, eventlog;
|
||||||
|
|
||||||
Const
|
|
||||||
CGIVarCount = 44 ;
|
|
||||||
|
|
||||||
Type
|
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',
|
|
||||||
{ 35 } 'CONTENT',
|
|
||||||
{ 36 } 'HTTP_X_REQUESTED_WITH',
|
|
||||||
{ 37 } 'HTTP_AUTHORIZATION',
|
|
||||||
{ 38 } 'SCRIPT_URI',
|
|
||||||
{ 39 } 'SCRIPT_URL',
|
|
||||||
{ 40 } 'CONTEXT_DOCUMENT_ROOT',
|
|
||||||
{ 41 } 'CONTEXT_PREFIX',
|
|
||||||
{ 42 } 'HTTP_CACHE_CONTROL',
|
|
||||||
{ 43 } 'HTTP_PRAGMA',
|
|
||||||
{ 44 } 'REQUEST_SCHEME'
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
Type
|
|
||||||
|
|
||||||
{ TCustomWebApplication }
|
{ TCustomWebApplication }
|
||||||
|
|
||||||
TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
|
TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
|
||||||
|
@ -18,7 +18,7 @@ unit fpapache;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
SysUtils,Classes,CustWeb,httpDefs,fpHTTP,httpd, apr, SyncObjs;
|
SysUtils,Classes,CustWeb,httpDefs,fpHTTP,httpd,httpprotocol, apr, SyncObjs;
|
||||||
|
|
||||||
Type
|
Type
|
||||||
|
|
||||||
@ -31,11 +31,11 @@ Type
|
|||||||
FApache : TApacheHandler;
|
FApache : TApacheHandler;
|
||||||
FRequest : PRequest_rec;
|
FRequest : PRequest_rec;
|
||||||
Protected
|
Protected
|
||||||
Function GetFieldValue(Index : Integer) : String; override;
|
|
||||||
Procedure InitFromRequest;
|
Procedure InitFromRequest;
|
||||||
procedure ReadContent; override;
|
procedure ReadContent; override;
|
||||||
Public
|
Public
|
||||||
Constructor CreateReq(App : TApacheHandler; ARequest : PRequest_rec);
|
Constructor CreateReq(App : TApacheHandler; ARequest : PRequest_rec);
|
||||||
|
Function GetCustomHeader(const Name: String) : String; override;
|
||||||
Property ApacheRequest : Prequest_rec Read FRequest;
|
Property ApacheRequest : Prequest_rec Read FRequest;
|
||||||
Property ApacheApp : TApacheHandler Read FApache;
|
Property ApacheApp : TApacheHandler Read FApache;
|
||||||
end;
|
end;
|
||||||
@ -179,6 +179,12 @@ const
|
|||||||
HPRIO : Array[THandlerPriority] of Integer
|
HPRIO : Array[THandlerPriority] of Integer
|
||||||
= (APR_HOOK_FIRST,APR_HOOK_MIDDLE,APR_HOOK_LAST);
|
= (APR_HOOK_FIRST,APR_HOOK_MIDDLE,APR_HOOK_LAST);
|
||||||
|
|
||||||
|
Function MaybeP(P : Pchar) : String;
|
||||||
|
|
||||||
|
begin
|
||||||
|
If (P<>Nil) then
|
||||||
|
Result:=StrPas(P);
|
||||||
|
end;
|
||||||
|
|
||||||
Procedure InitApache;
|
Procedure InitApache;
|
||||||
|
|
||||||
@ -448,60 +454,6 @@ end;
|
|||||||
|
|
||||||
{ TApacheRequest }
|
{ TApacheRequest }
|
||||||
|
|
||||||
function TApacheRequest.GetFieldValue(Index: Integer): String;
|
|
||||||
|
|
||||||
Function MaybeP(P : Pchar) : String;
|
|
||||||
|
|
||||||
begin
|
|
||||||
If (P<>Nil) then
|
|
||||||
Result:=StrPas(P);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
FN : String;
|
|
||||||
I : Integer;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Result:='';
|
|
||||||
If (Index in [1..NoHTTPFields]) then
|
|
||||||
begin
|
|
||||||
FN:=HTTPFieldNames[Index];
|
|
||||||
Result:=MaybeP(apr_table_get(FRequest^.headers_in,pchar(FN)));
|
|
||||||
end;
|
|
||||||
if (Result='') and Assigned(FRequest) then
|
|
||||||
case Index of
|
|
||||||
0 : Result:=MaybeP(FRequest^.protocol); // ProtocolVersion
|
|
||||||
7 : Result:=MaybeP(FRequest^.content_encoding); //ContentEncoding
|
|
||||||
25 : Result:=MaybeP(FRequest^.path_info); // PathInfo
|
|
||||||
26 : Result:=MaybeP(FRequest^.filename); // PathTranslated
|
|
||||||
27 : // RemoteAddr
|
|
||||||
If (FRequest^.Connection<>Nil) then
|
|
||||||
Result:=MaybeP(FRequest^.Connection^.remote_ip);
|
|
||||||
28 : // RemoteHost
|
|
||||||
If (FRequest^.Connection<>Nil) then
|
|
||||||
begin
|
|
||||||
Result:=MaybeP(ap_get_remote_host(FRequest^.Connection,
|
|
||||||
FRequest^.per_dir_config,
|
|
||||||
// nil,
|
|
||||||
REMOTE_NAME,@i));
|
|
||||||
end;
|
|
||||||
29 : begin // ScriptName
|
|
||||||
Result:=MaybeP(FRequest^.unparsed_uri);
|
|
||||||
I:=Pos('?',Result)-1;
|
|
||||||
If (I=-1) then
|
|
||||||
I:=Length(Result);
|
|
||||||
Result:=Copy(Result,1,I-Length(PathInfo));
|
|
||||||
end;
|
|
||||||
30 : Result:=IntToStr(ap_get_server_port(FRequest)); // ServerPort
|
|
||||||
31 : Result:=MaybeP(FRequest^.method); // Method
|
|
||||||
32 : Result:=MaybeP(FRequest^.unparsed_uri); // URL
|
|
||||||
33 : Result:=MaybeP(FRequest^.args); // Query
|
|
||||||
34 : Result:=MaybeP(FRequest^.HostName); // Host
|
|
||||||
else
|
|
||||||
Result:=inherited GetFieldValue(Index);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TApacheRequest.ReadContent;
|
procedure TApacheRequest.ReadContent;
|
||||||
|
|
||||||
Function MinS(A,B : Integer) : Integer;
|
Function MinS(A,B : Integer) : Integer;
|
||||||
@ -542,11 +494,46 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TApacheRequest.InitFromRequest;
|
procedure TApacheRequest.InitFromRequest;
|
||||||
|
|
||||||
|
|
||||||
|
Var
|
||||||
|
H : THeader;
|
||||||
|
V : String;
|
||||||
|
I : Integer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
ParseCookies;
|
ParseCookies;
|
||||||
|
For H in THeader do
|
||||||
|
begin
|
||||||
|
V:=MaybeP(apr_table_get(FRequest^.headers_in,PAnsiChar(HTTPHeaderNames[h])));
|
||||||
|
If (V<>'') then
|
||||||
|
SetHeader(H,V);
|
||||||
|
end;
|
||||||
|
// Some Specials;
|
||||||
|
SetHeader(hhContentEncoding,MaybeP(FRequest^.content_encoding));
|
||||||
|
SetHTTPVariable(hvHTTPVersion,MaybeP(FRequest^.protocol));
|
||||||
|
SetHTTPVariable(hvPathInfo,MaybeP(FRequest^.path_info));
|
||||||
|
SetHTTPVariable(hvPathTranslated,MaybeP(FRequest^.filename));
|
||||||
|
If (FRequest^.Connection<>Nil) then
|
||||||
|
begin
|
||||||
|
SetHTTPVariable(hvRemoteAddress,MaybeP(FRequest^.Connection^.remote_ip));
|
||||||
|
SetHTTPVariable(hvRemoteHost,MaybeP(ap_get_remote_host(FRequest^.Connection,
|
||||||
|
FRequest^.per_dir_config, REMOTE_NAME,@i)));
|
||||||
|
end;
|
||||||
|
V:=MaybeP(FRequest^.unparsed_uri);
|
||||||
|
I:=Pos('?',V)-1;
|
||||||
|
If (I=-1) then
|
||||||
|
I:=Length(V);
|
||||||
|
SetHTTPVariable(hvScriptName,Copy(V,1,I-Length(PathInfo)));
|
||||||
|
SetHTTPVariable(hvServerPort,IntToStr(ap_get_server_port(FRequest)));
|
||||||
|
SetHTTPVariable(hvMethod,MaybeP(FRequest^.method));
|
||||||
|
SetHTTPVariable(hvURL,FRequest^.unparsed_uri);
|
||||||
|
SetHTTPVariable(hvQuery,MaybeP(FRequest^.args));
|
||||||
|
SetHeader(hhHost,MaybeP(FRequest^.HostName));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Constructor TApacheRequest.CreateReq(App : TApacheHandler; ARequest : PRequest_rec);
|
constructor TApacheRequest.CreateReq(App: TApacheHandler; ARequest: PRequest_rec
|
||||||
|
);
|
||||||
|
|
||||||
begin
|
begin
|
||||||
FApache:=App;
|
FApache:=App;
|
||||||
@ -556,6 +543,13 @@ begin
|
|||||||
InitFromRequest;
|
InitFromRequest;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TApacheRequest.GetCustomHeader(const Name: String): String;
|
||||||
|
begin
|
||||||
|
Result:=inherited GetCustomHeader(Name);
|
||||||
|
if Result='' then
|
||||||
|
Result:=MaybeP(apr_table_get(FRequest^.headers_in,pchar(Name)));
|
||||||
|
end;
|
||||||
|
|
||||||
{ TApacheResponse }
|
{ TApacheResponse }
|
||||||
|
|
||||||
procedure TApacheResponse.DoSendHeaders(Headers: TStrings);
|
procedure TApacheResponse.DoSendHeaders(Headers: TStrings);
|
||||||
|
File diff suppressed because it is too large
Load Diff
269
packages/fcl-web/src/base/httpprotocol.pp
Normal file
269
packages/fcl-web/src/base/httpprotocol.pp
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
unit httpprotocol;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils;
|
||||||
|
|
||||||
|
Type
|
||||||
|
// HTTP 1.1 defined headers.
|
||||||
|
THeader = (hhUnknown,
|
||||||
|
hhAccept,hhAcceptCharset,hhAcceptEncoding, hhAcceptLanguage,
|
||||||
|
hhAcceptRanges, hhAge, hhAllow, hhAuthorization, hhCacheControl,
|
||||||
|
hhConnection, hhContentEncoding, hhContentLanguage,
|
||||||
|
hhContentLength,hhContentLocation, hhContentMD5, hhContentRange,
|
||||||
|
hhContentType, hhDate, hhETag, hhExpires, hhExpect,
|
||||||
|
hhFrom, hhHost, hhIfMatch, hhIfModifiedSince, hhIfNoneMatch,
|
||||||
|
hhIfRange, hhIfUnModifiedSince, hhLastModified, hhLocation, hhMaxForwards,
|
||||||
|
hhPragma, hhProxyAuthenticate, hhProxyAuthorization, hhRange, hhReferer,
|
||||||
|
hhRetryAfter, hhServer, hhTE, hhTrailer,
|
||||||
|
hhTransferEncoding, hhUpgrade , hhUserAgent, hhVary,
|
||||||
|
hhVia, hhWarning, hhWWWAuthenticate);
|
||||||
|
THeaders = Set of THeader;
|
||||||
|
THeaderDirection = (hdRequest,hdResponse);
|
||||||
|
THeaderDirections = Set of THeaderDirection;
|
||||||
|
|
||||||
|
THeadersArray = Array[THeader] of string;
|
||||||
|
|
||||||
|
Const
|
||||||
|
HeaderAccept = 'Accept';
|
||||||
|
HeaderAcceptCharset = 'Accept-Charset';
|
||||||
|
HeaderAcceptEncoding = 'Accept-Encoding';
|
||||||
|
HeaderAcceptLanguage = 'Accept-Language';
|
||||||
|
HeaderAcceptRanges = 'Accept-Ranges';
|
||||||
|
HeaderAge = 'Age';
|
||||||
|
HeaderAllow = 'Allow';
|
||||||
|
HeaderAuthorization = 'Authorization';
|
||||||
|
HeaderCacheControl = 'Cache-Control';
|
||||||
|
HeaderConnection = 'Connection';
|
||||||
|
HeaderContentEncoding = 'Content-Encoding';
|
||||||
|
HeaderContentLanguage = 'Content-Language';
|
||||||
|
HeaderContentLength = 'Content-Length';
|
||||||
|
HeaderContentLocation = 'Content-Location';
|
||||||
|
HeaderContentMD5 = 'Content-MD5';
|
||||||
|
HeaderContentRange = 'Content-Range';
|
||||||
|
HeaderContentType = 'Content-Type';
|
||||||
|
HeaderDate = 'Date';
|
||||||
|
HeaderETag = 'ETag';
|
||||||
|
HeaderExpires = 'Expires';
|
||||||
|
HeaderExpect = 'Expect';
|
||||||
|
HeaderFrom = 'From';
|
||||||
|
HeaderHost = 'Host';
|
||||||
|
HeaderIfMatch = 'If-Match';
|
||||||
|
HeaderIfModifiedSince = 'If-Modified-Since';
|
||||||
|
HeaderIfNoneMatch = 'If-None-Match';
|
||||||
|
HeaderIfRange = 'If-Range';
|
||||||
|
HeaderIfUnModifiedSince = 'If-Unmodified-Since';
|
||||||
|
HeaderLastModified = 'Last-Modified';
|
||||||
|
HeaderLocation = 'Location';
|
||||||
|
HeaderMaxForwards = 'Max-Forwards';
|
||||||
|
HeaderPragma = 'Pragma';
|
||||||
|
HeaderProxyAuthenticate = 'Proxy-Authenticate';
|
||||||
|
HeaderProxyAuthorization = 'Proxy-Authorization';
|
||||||
|
HeaderRange = 'Range';
|
||||||
|
HeaderReferer = 'Referer';
|
||||||
|
HeaderRetryAfter = 'Retry-After';
|
||||||
|
HeaderServer = 'Server';
|
||||||
|
HeaderTE = 'TE';
|
||||||
|
HeaderTrailer = 'Trailer';
|
||||||
|
HeaderTransferEncoding = 'Transfer-Encoding';
|
||||||
|
HeaderUpgrade = 'Upgrade';
|
||||||
|
HeaderUserAgent = 'User-Agent';
|
||||||
|
HeaderVary = 'Vary';
|
||||||
|
HeaderVia = 'Via';
|
||||||
|
HeaderWarning = 'Warning';
|
||||||
|
HeaderWWWAuthenticate = 'WWW-Authenticate';
|
||||||
|
|
||||||
|
// These Headers are NOT in the HTTP 1.1 definition.
|
||||||
|
HeaderXRequestedWith = 'X-Requested-With';
|
||||||
|
HeaderCookie = 'Cookie';
|
||||||
|
HeaderSetCookie = 'Set-Cookie';
|
||||||
|
|
||||||
|
HTTPDateFmt = '"%s", dd "%s" yyyy hh:mm:ss'; // For use in FormatDateTime
|
||||||
|
SCookieExpire = ' "Expires="'+HTTPDateFmt+' "GMT"';
|
||||||
|
SCookieDomain = ' Domain=%s';
|
||||||
|
SCookiePath = ' Path=%s';
|
||||||
|
SCookieSecure = ' Secure';
|
||||||
|
SCookieHttpOnly = ' HttpOnly';
|
||||||
|
|
||||||
|
HTTPMonths: array[1..12] of string[3] = (
|
||||||
|
'Jan', 'Feb', 'Mar', 'Apr',
|
||||||
|
'May', 'Jun', 'Jul', 'Aug',
|
||||||
|
'Sep', 'Oct', 'Nov', 'Dec');
|
||||||
|
HTTPDays: array[1..7] of string[3] = (
|
||||||
|
'Sun', 'Mon', 'Tue', 'Wed',
|
||||||
|
'Thu', 'Fri', 'Sat');
|
||||||
|
|
||||||
|
|
||||||
|
Const
|
||||||
|
HTTPHeaderDirections : Array[THeader] of THeaderDirections = (
|
||||||
|
[],
|
||||||
|
[hdRequest],[hdRequest],[hdRequest], [hdRequest],
|
||||||
|
[hdResponse], [hdResponse], [hdResponse], [hdRequest], [hdRequest,hdResponse],
|
||||||
|
[hdRequest,hdResponse], [hdRequest,hdResponse], [hdRequest,hdResponse],
|
||||||
|
[hdRequest,hdResponse],[hdRequest,hdResponse], [hdRequest,hdResponse], [hdRequest,hdResponse],
|
||||||
|
[hdRequest,hdResponse], [hdRequest,hdResponse], [hdResponse], [hdRequest,hdResponse], [hdRequest],
|
||||||
|
[hdRequest], [hdRequest], [hdRequest], [hdRequest], [hdRequest],
|
||||||
|
[hdRequest], [hdRequest], [hdRequest,hdResponse], [hdResponse], [hdRequest],
|
||||||
|
[hdRequest, hdResponse] , [hdResponse], [hdRequest], [hdRequest,hdResponse], [hdRequest],
|
||||||
|
[hdResponse], [hdResponse], [hdRequest], [hdRequest,hdResponse],
|
||||||
|
[hdRequest,hdResponse], [hdRequest,hdResponse], [hdRequest], [hdRequest,hdResponse],
|
||||||
|
[hdRequest,hdResponse], [hdRequest,hdResponse], [hdResponse]);
|
||||||
|
|
||||||
|
HTTPHeaderNames : THeadersArray
|
||||||
|
= ('',
|
||||||
|
HeaderAccept,HeaderAcceptCharset,HeaderAcceptEncoding, HeaderAcceptLanguage,
|
||||||
|
HeaderAcceptRanges, HeaderAge, HeaderAllow, HeaderAuthorization, HeaderCacheControl,
|
||||||
|
HeaderConnection, HeaderContentEncoding, HeaderContentLanguage,
|
||||||
|
HeaderContentLength,HeaderContentLocation, HeaderContentMD5, HeaderContentRange,
|
||||||
|
HeaderContentType, HeaderDate, HeaderETag, HeaderExpires, HeaderExpect,
|
||||||
|
HeaderFrom, HeaderHost, HeaderIfMatch, HeaderIfModifiedSince, HeaderIfNoneMatch,
|
||||||
|
HeaderIfRange, HeaderIfModifiedSince, HeaderLastModified, HeaderLocation, HeaderMaxForwards ,
|
||||||
|
HeaderPragma, HeaderProxyAuthenticate, HeaderProxyAuthorization, HeaderRange, HeaderReferer,
|
||||||
|
HeaderRetryAfter, HeaderServer, HeaderTE, HeaderTrailer,
|
||||||
|
HeaderTransferEncoding, HeaderUpgrade , HeaderUserAgent, HeaderVary,
|
||||||
|
HeaderVia, HeaderWarning, HeaderWWWAuthenticate);
|
||||||
|
|
||||||
|
Function HeaderName(AHeader : THeader) : String;
|
||||||
|
Function HeaderType(AHeader : String) : THeader;
|
||||||
|
Function HTTPDecode(const AStr: String): String;
|
||||||
|
Function HTTPEncode(const AStr: String): String;
|
||||||
|
Function IncludeHTTPPathDelimiter(const AStr: String): String;
|
||||||
|
Function ExcludeHTTPPathDelimiter(const AStr: String): String;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
function HeaderName(AHeader: THeader): String;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=HTTPHeaderNames[AHeader];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function HeaderType(AHeader: String): THeader;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=High(THeader);
|
||||||
|
While (Result>hhUnknown) and (CompareText(HTTPHeaderNames[Result],AHeader)<>0) do
|
||||||
|
Result:=Pred(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function HTTPDecode(const AStr: String): String;
|
||||||
|
|
||||||
|
var
|
||||||
|
S,SS, R : PChar;
|
||||||
|
H : String[3];
|
||||||
|
L,C : Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
L:=Length(Astr);
|
||||||
|
SetLength(Result,L);
|
||||||
|
If (L=0) then
|
||||||
|
exit;
|
||||||
|
S:=PChar(AStr);
|
||||||
|
SS:=S;
|
||||||
|
R:=PChar(Result);
|
||||||
|
while (S-SS)<L do
|
||||||
|
begin
|
||||||
|
case S^ of
|
||||||
|
'+': R^ := ' ';
|
||||||
|
'%': begin
|
||||||
|
Inc(S);
|
||||||
|
if ((S-SS)<L) then
|
||||||
|
begin
|
||||||
|
if (S^='%') then
|
||||||
|
R^:='%'
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
H:='$00';
|
||||||
|
H[2]:=S^;
|
||||||
|
Inc(S);
|
||||||
|
If (S-SS)<L then
|
||||||
|
begin
|
||||||
|
H[3]:=S^;
|
||||||
|
Val(H,PByte(R)^,C);
|
||||||
|
If (C<>0) then
|
||||||
|
R^:=' ';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
R^ := S^;
|
||||||
|
end;
|
||||||
|
Inc(R);
|
||||||
|
Inc(S);
|
||||||
|
end;
|
||||||
|
SetLength(Result,R-PChar(Result));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function HTTPEncode(const AStr: String): String;
|
||||||
|
|
||||||
|
const
|
||||||
|
HTTPAllowed = ['A'..'Z','a'..'z',
|
||||||
|
'*','@','.','_','-',
|
||||||
|
'0'..'9',
|
||||||
|
'$','!','''','(',')'];
|
||||||
|
|
||||||
|
var
|
||||||
|
SS,S,R: PChar;
|
||||||
|
H : String[2];
|
||||||
|
L : Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
L:=Length(AStr);
|
||||||
|
SetLength(Result,L*3); // Worst case scenario
|
||||||
|
if (L=0) then
|
||||||
|
exit;
|
||||||
|
R:=PChar(Result);
|
||||||
|
S:=PChar(AStr);
|
||||||
|
SS:=S; // Avoid #0 limit !!
|
||||||
|
while ((S-SS)<L) do
|
||||||
|
begin
|
||||||
|
if S^ in HTTPAllowed then
|
||||||
|
R^:=S^
|
||||||
|
else if (S^=' ') then
|
||||||
|
R^:='+'
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
R^:='%';
|
||||||
|
H:=HexStr(Ord(S^),2);
|
||||||
|
Inc(R);
|
||||||
|
R^:=H[1];
|
||||||
|
Inc(R);
|
||||||
|
R^:=H[2];
|
||||||
|
end;
|
||||||
|
Inc(R);
|
||||||
|
Inc(S);
|
||||||
|
end;
|
||||||
|
SetLength(Result,R-PChar(Result));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function IncludeHTTPPathDelimiter(const AStr: String): String;
|
||||||
|
|
||||||
|
Var
|
||||||
|
l : Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=AStr;
|
||||||
|
L:=Length(Result);
|
||||||
|
If (L>0) and (Result[L]<>'/') then
|
||||||
|
Result:=Result+'/';
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ExcludeHTTPPathDelimiter(const AStr: String): String;
|
||||||
|
|
||||||
|
Var
|
||||||
|
l : Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
L:=Length(AStr);
|
||||||
|
If (L>0) and (AStr[L]='/') then
|
||||||
|
Result:=Copy(AStr,1,L-1)
|
||||||
|
else
|
||||||
|
Result:=AStr;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
@ -18,7 +18,7 @@ unit webutil;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, httpdefs;
|
Classes, SysUtils, httpprotocol, httpdefs;
|
||||||
|
|
||||||
procedure DumpRequest (ARequest : TRequest; Dump : TStrings; Environment : Boolean = False);
|
procedure DumpRequest (ARequest : TRequest; Dump : TStrings; Environment : Boolean = False);
|
||||||
|
|
||||||
@ -37,32 +37,58 @@ procedure DumpRequest (ARequest : TRequest; Dump : TStrings; Environment : Boole
|
|||||||
Var
|
Var
|
||||||
I,J : integer;
|
I,J : integer;
|
||||||
N,V : String;
|
N,V : String;
|
||||||
|
H : THeader;
|
||||||
|
VA : THTTPVariableType;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
With ARequest, Dump do
|
With ARequest, Dump do
|
||||||
begin
|
begin
|
||||||
// All possible headers
|
// All possible headers
|
||||||
Add('<H1>All possible request headers:</H1>');
|
Add('<H1>HTTP 1.1 request headers:</H1>');
|
||||||
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
||||||
For I:=1 to NoHTTPFields do
|
For H in THeader do
|
||||||
begin
|
if (hdRequest in HTTPHeaderDirections[H]) then
|
||||||
AddNV(HTTPFieldNames[i],GetFieldByName(HTTPFieldNames[i]));
|
AddNV(HTTPHeaderNames[H],GetHeader(H));
|
||||||
end;
|
|
||||||
Add('</TABLE><P>');
|
Add('</TABLE><P>');
|
||||||
|
|
||||||
// Actually sent headers
|
// Actually sent headers
|
||||||
Add('<H1>Actually sent request headers:</H1>');
|
Add('<H1>Actually sent request headers:</H1>');
|
||||||
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
||||||
For I:=0 to FieldCount-1 do
|
For H in THeader do
|
||||||
AddNV(FieldNames[I],FieldValues[I]);
|
if (hdRequest in HTTPHeaderDirections[H]) and HeaderIsSet(H) then
|
||||||
|
AddNV(HTTPHeaderNames[H],GetHeader(H));
|
||||||
|
For Va in HeaderBasedVariables do
|
||||||
|
begin
|
||||||
|
V:=GetHTTPVariable(Va);
|
||||||
|
if V<>'' then
|
||||||
|
AddNV(THTTPHeader.GetVariableHeaderName(Va),V);
|
||||||
|
end;
|
||||||
|
For I:=0 to CustomHeaders.Count-1 do
|
||||||
|
begin
|
||||||
|
CustomHeaders.GetNameValue(I,N,V);
|
||||||
|
AddNV(N,V);
|
||||||
|
end;
|
||||||
Add('</TABLE><P>');
|
Add('</TABLE><P>');
|
||||||
|
|
||||||
// Actually sent headers, as text
|
// Actually sent headers, as text
|
||||||
Add('<H1>Actually sent request headers as text:</H1>');
|
Add('<H1>Actually sent request headers as text:</H1>');
|
||||||
For I:=0 to FieldCount-1 do
|
Add('<pre>');
|
||||||
Add(Fields[I]+'<BR>');
|
For H in THeader do
|
||||||
|
if (hdRequest in HTTPHeaderDirections[H]) and HeaderIsSet(H) then
|
||||||
|
Add(HTTPHeaderNames[H]+': '+GetHeader(H));
|
||||||
|
For Va in HeaderBasedVariables do
|
||||||
|
begin
|
||||||
|
V:=GetHTTPVariable(Va);
|
||||||
|
if V<>'' then
|
||||||
|
Add(THTTPHeader.GetVariableHeaderName(Va)+': '+V);
|
||||||
|
end;
|
||||||
|
For I:=0 to CustomHeaders.Count-1 do
|
||||||
|
begin
|
||||||
|
CustomHeaders.GetNameValue(I,N,V);
|
||||||
|
Add(N+': '+V);
|
||||||
|
end;
|
||||||
|
Add('</PRE>');
|
||||||
// Additional headers
|
// Additional headers
|
||||||
Add('<H1>Additional headers:</H1>');
|
Add('<H1>Additional protocol variables:</H1>');
|
||||||
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
Add('<TABLE BORDER="1"><TR><TD>Header</TD><TD>Value</TD></TR>');
|
||||||
AddNV('PathInfo',PathInfo);
|
AddNV('PathInfo',PathInfo);
|
||||||
AddNV('PathTranslated',PathTranslated);
|
AddNV('PathTranslated',PathTranslated);
|
||||||
|
Loading…
Reference in New Issue
Block a user