unit uROIpHttpServer;
{----------------------------------------------------------------------------}
{ RemObjects SDK Library - Synapse Components }
{ }
{ compiler: Delphi 5 and up, Kylix 2 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Using this code requires a valid license of the RemObjects SDK }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I RemObjects.inc}
interface
uses
Classes, uROServer, uROClientIntf, uIPAsyncSocket, uIPAsyncHttpServer, uIPHttpHeaders,
uROThreadPool, uROServerIntf, uRORes, uROClient, uROHtmlServerInfo, uRODL, uROHTTPTools;
type
TROIpHTTPServer = class(TROServer)
private
fServeRodl, fServeInfoPage: Boolean;
fSendExceptionsAs500: boolean;
fServer: TIPAsyncHttpServer;
fThreadPool: TROThreadPool;
fOwnsThreadPool: Boolean;
fOnManualBindSocket: TNotifyEvent;
fOnGetCustomClientAccessPolicy: TROCustomClientAccessPolicyMethod;
fSendClientAccessPolicyXml: TROClientAccessPolicyType;
procedure SetThreadPool(const Value: TROThreadPool);
protected
procedure IntSetActive(const Value: boolean); override;
function IntGetActive : boolean; override;
function GetDispatchersClass : TROMessageDispatchersClass; override;
function GetPort: Integer;
procedure SetPort(const Value: Integer);
procedure IntRequest(Sender: TObject; aContext: IIPAsyncContext);
public
constructor Create(aComponent: TComponent); override;
destructor Destroy; override;
property Server: TIPAsyncHttpServer read fServer;
published
property Port:Integer read GetPort write SetPort;
property ServeInfoPage : Boolean read fServeInfoPage write fServeInfoPage default true;
property ServeRodl : Boolean read fServeRodl write fServeRodl default true;
property SendExceptionsAs500: boolean read fSendExceptionsAs500 write fSendExceptionsAs500 default true;
property OnGetRODLReader;
property ThreadPool: TROThreadPool read fThreadPool write SetThreadPool;
property OnManualBindSocket: TNotifyEvent read fOnManualBindSocket write fOnManualBindSocket;
property OnGetCustomClientAccessPolicy: TROCustomClientAccessPolicyMethod read fOnGetCustomClientAccessPolicy write fOnGetCustomClientAccessPolicy;
property SendClientAccessPolicyXml: TROClientAccessPolicyType read fSendClientAccessPolicyXml write fSendClientAccessPolicyXml default captAllowNone;
end;
implementation
uses
uROHTTPDispatch, TypInfo, SysUtils, uROIpTcpServer, uROClasses;
type
TROIpHttpWorker = class(TInterfacedObject, IROThreadPoolCallback, IROTransport, IROTCPTransport, IROHTTPTransport)
private
fOwner: TROIpHTTPServer;
fContext: IIPAsyncContext;
fQueryData: TStringList;
function GetClientAddress: String;
function GetTransportObject: TObject;
function GetContentType: String;
function GetHeaders(const aName: String): String;
function GetLocation: String;
function GetPathInfo: String;
function GetQueryParameter(const aName: String): String;
function GetQueryString: String;
function GetTargetURL: String;
function GetUserAgent: String;
procedure SetContentType(const aValue: String);
procedure SetHeaders(const aName: String; const aValue: String);
procedure SetPathInfo(const aValue: String);
procedure SetTargetURL(const aValue: String);
procedure SetUserAgent(const aValue: String);
public
constructor Create(aOwner: TROIpHTTPServer; aContext: IIPAsyncContext);
destructor Destroy; override;
procedure Callback(Caller: TROThreadPool; aThread: TThread);
end;
{ TROIpHTTPServer }
constructor TROIpHTTPServer.Create(aComponent: TComponent);
begin
inherited Create(aComponent);
fServer := TIPAsyncHttpServer.Create;
fServer.OnRequest := IntRequest;
fServer.Port := 8099;
fServeInfoPage := true;
fServeRodl := true;
end;
destructor TROIpHTTPServer.Destroy;
begin
Active := false;
fServer.Free;
if fOwnsThreadPool then
fThreadPool.Free;
inherited Destroy;
end;
function TROIpHTTPServer.GetDispatchersClass: TROMessageDispatchersClass;
begin
Result := TROHTTPMessageDispatchers;
end;
function TROIpHTTPServer.GetPort: Integer;
begin
result := fServer.Port;
end;
function TROIpHTTPServer.IntGetActive: boolean;
begin
result := fServer.Active;
end;
procedure TROIpHTTPServer.IntSetActive(const Value: boolean);
begin
inherited;
if Value = fserver.Active then exit;
if Value then begin
fServer.OnManualBind := fOnManualBindSocket;
if fThreadPool = nil then begin
fThreadPool := TROThreadPool.Create(nil);
fOwnsThreadPool := true;
end;
fServer.Active := true;
end else begin
fServer.Active := false;
end;
end;
procedure TROIpHTTPServer.SetPort(const Value: Integer);
begin
fServer.Port := Value;
end;
procedure TROIpHTTPServer.SetThreadPool(const Value: TROThreadPool);
begin
if fOwnsThreadPool then begin
FreeAndNil(fThreadPool);
end;
fOwnsThreadPool := false;
fThreadPool := Value;
end;
procedure TROIpHTTPServer.IntRequest(Sender: TObject;
aContext: IIPAsyncContext);
begin
try
fThreadPool.QueueItem(TROIpHttpWorker.Create(self, aContext));
except
aContext.GetSelf.Disconnect;
end;
end;
{ TROIpHttpWorker }
procedure TROIpHttpWorker.Callback(Caller: TROThreadPool;
aThread: TThread);
procedure WriteError(resp : TMemoryStream);
var
s: ansistring;
begin
s := '500 Invalid Path
'+{$IFDEF UNICODE}WideStringToAnsiString{$ENDIF}(err_CannotFindMessageDispatcher);
resp.Write(s[1], Length(s));
end;
var
Req: TByteArrayWrapper;
lRodl: TRODLLibrary;
root,
sub: string;
data: TDynamicByteArray;
disp: TROHTTPDispatcher;
ok: Boolean;
resp: TMemoryStream;
lIgnore: TROResponseOptions;
s: ansistring;
s1: string;
info: IRONamedModuleInfo;
lPolicyContent: AnsiString;
begin
req := TByteArrayWrapper.Create(TByteArray(fContext.RequestData));
resp := TMemoryStream.Create;
fcontext.Response.Headers.Values['Content-Type'] := fContext.Request.ContentType;
fcontext.Response.Code := 200;
fcontext.Response.Reason := 'OK';
ok := False;
try
root := fContext.Request.Path;
if Pos('?', Root) > 0 then Delete(Root, pos('/', root), MaxInt);
if (root <> '') and (root[1] = '/') then delete(root, 1,1);
if pos('/', root) > 0 then
begin
sub := copy(root, pos('/', root)+1, maxint);
root := '/' + copy(root, 1, pos('/', root) - 1);
end else
begin
root := '/' + root;
sub := '';
end;
disp := TROHTTPMessageDispatchers(fOwner.Dispatchers).GetDispatcherByPath(root) as TROHTTPDispatcher;
if disp <> nil then
begin
if (req.Size = 0) then begin
ok := fOwner.fServeRodl;
if ok then
ok := fOwner.IntDispatchMessage(disp, self, req, resp, lIgnore)
else begin
WriteError(resp);
SetContentType('text/html');
end;
end else if (sub = '') then
ok := fOwner.IntDispatchMessage(disp, self, req, resp, lIgnore)
else if (CompareText(sub, 'rodl') = 0) and fOwner.fServeRodl then
begin
if copy(disp.PathInfo, 1, 1) = '/' then SetPathInfo(disp.PathInfo) else SetPathInfo('/'+disp.PathInfo);
GetRodl(resp, self, s1, fOwner.GetRODLReader);
SetContentType(s1);
ok := true;
end else if Supports(disp.Message, IRONamedModuleInfo, info) and (CompareText(info.ModuleInfoName, sub) = 0) and fOwner.fServeRodl then
begin
if copy(disp.PathInfo, 1, 1) = '/' then SEtPathInfo(disp.PathInfo) else SetPathInfo('/'+disp.PathInfo);
info.GetModuleInfo(resp, self, s1);
SetContentType(s1);
ok := true;
end else
begin
ok := fOwner.IntDispatchMessage(disp, self, req, resp, lIgnore)
end;
end else if (CompareText(root, '/rodl') = 0) and (sub = '') and fOwner.fServeRodl then
begin
GetRodl(resp, self, s1, fOwner.GetRODLReader);
SetContentType(s1);
ok := true;
end else if fOwner.fServeInfoPage and (CompareText(root, '/doc') = 0) then
begin
if sub = 'css' then
begin
s := GetRodlCss;
resp.Write(s[1], Length(s));
SetContentType('text/css');
ok := true;
end else
if sub = 'xslt' then
begin
s := GetRodlStyleSheet;
resp.Write(s[1], Length(s));
SetContentType('text/xml');
ok := true;
end else if sub = '' then
begin
lRodl := GetRodlLibrary(fOwner.GetRODLReader);
try
SetContentType('text/xml');
s := GetRodlWithStyleSheet(lRodl, '/doc/xslt');
resp.Write(s[1], Length(s));
finally
lRodl.Free;
end;
ok := true;
end else
begin
WriteError(resp);
ok := false;
end;
end else if fOwner.fServeInfoPage and (CompareStr(root, '/favicon.ico') = 0) then
begin
GetRodlFavIcon(resp);
SetContentType('image/x-icon');
ok := true;
end else if fOwner.fServeInfoPage and (root = '/') then
begin
lrodl := GetRodlLibrary(fOwner.GetRODLReader);
try
s := GetRodlServerInfo('/', lRodl, fOwner.Dispatchers);
resp.Write(s[1], Length(s));
finally
lRodl.Free;
end;
ok := true;
end else
if CompareText(root, '/clientaccesspolicy.xml') = 0 then begin
case fOwner.fSendClientAccessPolicyXml of
captAllowAll: begin
lPolicyContent := GetClientAccessPolicy;
end;
captCustom: begin
if Assigned(fOwner.fOnGetCustomClientAccessPolicy) then
fOwner.fOnGetCustomClientAccessPolicy(fOwner, lPolicyContent);
end;
end;
if lPolicyContent <> '' then begin
SetContentType('text/xml');
resp.Write(lPolicyContent[1], Length(lPolicyContent));
end;
end else
begin
WriteError(resp);
ok := false;
end;
if not (ok or fOwner.fSendExceptionsAs500) then begin
fcontext.Response.Code := 500;
fcontext.Response.Reason := 'Internal Server Error';
end;
except
end;
req.Free;
try
SetLength(data, resp.Size);
resp.position := 0;
resp.Read(data[0], Length(data));
fContext.ResponseData := data;
fContext.SendResponse;
finally
resp.Free;
end;
end;
constructor TROIpHttpWorker.Create(aOwner: TROIpHTTPServer;
aContext: IIPAsyncContext);
begin
inherited Create;
fOwner := aOwner;
fContext := aContext;
end;
destructor TROIpHttpWorker.Destroy;
begin
fQueryData.Free;
inherited;
end;
function TROIpHttpWorker.GetClientAddress: String;
begin
result := fContext.Request.ClientAddress;
end;
function TROIpHttpWorker.GetContentType: String;
begin
result := fContext.Request.ContentType;
end;
function TROIpHttpWorker.GetHeaders(const aName: String): String;
begin
result := fContext.Request.Headers.Values[aname];
end;
function TROIpHttpWorker.GetLocation: String;
begin
Result := '';
end;
function TROIpHttpWorker.GetPathInfo: String;
begin
result := '';
end;
function TROIpHttpWorker.GetQueryParameter(const aName: String): String;
begin
if fQueryData = nil then begin
fQueryData := TStringList.Create;
fQueryData.Delimiter := '&';
fQueryData.DelimitedText := GetQueryString;
end;
result := fqueryData.Values[aName];
end;
function TROIpHttpWorker.GetQueryString: String;
begin
result := fContext.Request.Path;
if Pos('?', result) = 0 then result := '' else
result := Copy(Result, Pos('?', result)+1, MaxInt);
end;
function TROIpHttpWorker.GetTargetURL: String;
begin
result := '';
end;
function TROIpHttpWorker.GetTransportObject: TObject;
begin
result := FOwner;
end;
function TROIpHttpWorker.GetUserAgent: String;
begin
Result := fContext.Request.Headers.Values['User-Agent'];
end;
procedure TROIpHttpWorker.SetContentType(const aValue: String);
begin
fContext.Response.Headers.Values['Content-Type'] := aValue;
end;
procedure TROIpHttpWorker.SetHeaders(const aName, aValue: String);
begin
fContext.Response.Headers.Values[aName] := aValue;
end;
procedure TROIpHttpWorker.SetPathInfo(const aValue: String);
begin
end;
procedure TROIpHttpWorker.SetTargetURL(const aValue: String);
begin
end;
procedure TROIpHttpWorker.SetUserAgent(const aValue: String);
begin
fContext.Response.Headers.Values['User-Agent'] := aValue;
end;
initialization
RegisterServerClass(TROIpHTTPServer);
finalization
UnregisterServerClass(TROIpHTTPServer);
end.