Componentes.Terceros.RemObj.../internal/5.0.23.613/1/RemObjects SDK for Delphi/Source/uROBaseSuperHttpServer.pas
david 2824855ea7 - Modificación del paquete RemObjects_Core_D10 para que sea un paquete de runtime/designtime (antes era designtime sólo)
- Recompilación en Delphi10 de todos los paquetes de RO para generar las DCU's en Lib\D10
- Recompilación en Delphi10 de todos los paquetes de DA para generar las DCU's en Lib\D10

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.RemObjects@9 b6239004-a887-0f4b-9937-50029ccdca16
2007-09-10 14:06:19 +00:00

1147 lines
35 KiB
ObjectPascal

unit uROBaseSuperHttpServer;
interface
{$I RemObjects.inc}
uses
SysUtils, Classes, uROClientIntf, uROServer, uROServerIntf, uROClasses,
uIPHttpHeaders, uROThreadPool, uROThread, uROClient, uROEventRepository,
uROHtmlServerInfo, uRODL, uROHTTPDispatch, uROTypes, uROSessions;
type
TROSuperHttpWaitingConnection = class(TObject)
private
fChannel: IUnknown;
fTimeout: TDateTime;
public
constructor Create(aChannel: IUnknown);
property Timeout: TDateTime read fTimeout;
property Channel: IUnknown read fChannel;
end;
TROBaseSuperHttpServer = class;
TROSuperHttpServerConnection = class;
IROSuperHttpServerConnection = interface
['{48AF124A-38F5-43A4-BD82-684A74EDF77F}']
function GetSelf: TROSuperHttpServerConnection;
function GetGuid: TGuid;
function GuidMatches(const aGuid: TGuid): Boolean;
procedure DisposeData;
end;
TROSuperHttpServerConnection = class(TInterfacedObject, IROSuperHttpServerConnection, IROActiveEventServer)
private
fServer: TROBaseSuperHttpServer;
fLastTalk: TDateTime;
fRemoteMaxPackageLength: Integer;
fConnectionId: TGuid;
fSessionId: TGuid;
fResponseQueue: TInterfaceList;
fWaitingConnections: TThreadList;
fEventId: Integer;
protected
function GetSelf: TROSuperHttpServerConnection;
public
constructor Create(aServer: TROBaseSuperHttpServer);
destructor Destroy; override;
procedure DisposeData;
function GetGuid: TGuid;
function GuidMatches(const aGuid: TGuid): Boolean;
property Server: TROBaseSuperHttpServer read fServer;
property LastTalk: TDateTime read fLastTalk write fLastTalk;
property ResponseQueue: TInterfaceList read fResponseQueue;
property WaitingConnections: TThreadList read fWaitingConnections;
property RemoteMaxPackageLength: Integer read fRemoteMaxPackageLength write fRemoteMaxPackageLength;
property ConnectionId: TGuid read fConnectionId write fConnectionId;
property SessionId: TGUID read fSessionId write fSessionId;
procedure EventsRegistered(aSender: TObject; aClient: TGUID);
procedure DispatchEvent(anEventDataItem: TROEventData;
aSessionReference: TGUID; aSender: TObject);
end;
TROSuperHttpServerResponse = class;
IROSuperHttpServerResponse = interface
['{9C33C4C2-F6AA-452C-9E67-F49F75F292F2}']
function GetSelf: TROSuperHttpServerResponse;
end;
TROSuperHttpServerResponse = class(TInterfacedObject, IROSuperHttpServerResponse)
private
fHeaders: TIPHttpResponseHeaders;
fData: TDynamicByteArray;
fConnection: IROSuperHttpServerConnection;
fAttempt: Integer;
function GetSelf: TROSuperHttpServerResponse;
public
constructor Create(aConn: IROSuperHttpServerConnection; aHeaders: TIPHttpResponseHeaders);
destructor Destroy; override;
property Headers: TIPHttpResponseHeaders read fHeaders;
property Data: TDynamicByteArray read fData write fData;
property Connection: IROSuperHttpServerConnection read fConnection write fConnection;
property Attempt: Integer read fAttempt write fAttempt;
end;
TROBaseSuperHttpServer = class(TROServer)
private
fTimer: TROThreadTimer;
fThreadPool: TROThreadPool;
fOwnsThreadPool: boolean;
fMaxPackageSize: Integer;
fConnections: TInterfaceList;
fServeRodl,
fServeInfoPage: Boolean;
fActive: Boolean;
fEventRepository: TROEventRepository;
fConnectionTimeout: Integer;
function GetConnection(const aGuid: TGuid): IROSuperHttpServerConnection;
procedure KillConnection(aConnection: IROSuperHttpServerConnection);
procedure SendError(aTarget: IUnknown; anError: string);
procedure IncomingData(aTarget: IUnknown; aRequestHeaders: TIPHttpRequestHeaders; aData: TDynamicByteArray; aDispatcher: TROMessageDispatcher);
procedure SendRodl(ATarget: IUnknown; aRequest: TIPHttpRequestHeaders; aDispatcher: TROMessageDispatcher; const aTargetService: string);
procedure AddWaitingRequest(aTarget: IUnknown; aConnection: IROSuperHttpServerConnection);
procedure ProcessPackage(aTarget: IROSuperHttpServerConnection; aHeaders: TIPHttpRequestHeaders; aMessageDispatcher: TROMessageDispatcher; aId: Integer; aData: TDynamicByteArray);
procedure SendAsyncResponse(aTarget: IROSuperHttpServerConnection; lHeaders: TIPHttpResponseHeaders; aData: TDynamicByteArray);
procedure SetThreadPool(const Value: TROThreadPool);
procedure cbCleanup(CurrentTickCount : Cardinal);
protected
procedure CloseConnection(aConnection: IUnknown); virtual; abstract;
procedure SendResponse(aTarget: IUnknown; aHeaders: TIPHttpResponseHeaders; aData: TDynamicByteArray; UserData: IInterface); virtual; abstract;
function HandleUnknownRequest(aTarget: IUnknown; arequest: TIPHttpRequestHeaders; aData: TDynamicByteArray): Boolean; virtual; abstract;
procedure SetDefaultHeaders(aTarget: IUnknown; aHeaders: TIPHttpResponseHeaders); virtual; abstract;
procedure HandleRequest(aTarget: IUnknown; aRequestHeaders: TIPHttpRequestHeaders; aRequest: TDynamicByteArray);
procedure ResponseFailed(aUserData: IInterface);
procedure ResponseSucessful(aUserData: IInterface);
procedure DoActivate; virtual;
procedure DoDeactivate; virtual;
function IntGetActive: Boolean; override;
procedure IntSetActive(const Value: Boolean); override;
function GetDispatchersClass : TROMessageDispatchersClass; override;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
published
property ServeRodl: Boolean read fServeRodl write fServeRodl default true;
property ServeInfoPage: Boolean read fServeInfoPage write fServeInfoPage default true;
property MaxPackageSize: Integer read fMaxPackageSize write fMaxPackageSize default 1 * 1024 * 1024;
property ConnectionTimeout: Integer read fConnectionTimeout write fConnectionTimeout default 5 * 60;
property ThreadPool: TROThreadPool read fThreadPool write SetThreadPool;
property EventRepository: TROEventRepository read fEventRepository write fEventRepository;
end;
const
ShHello = 0;
ShGoodbye = 1;
ShPackage = 2;
ShError = 3;
ShError_InvalidClientId = 0;
ShError_QueueFull = 1;
ShError_UnknownOption = 2;
ShOptions = 4;
ShAsyncWait = 5;
implementation
{ TROSuperHttpWaitingConnection }
constructor TROSuperHttpWaitingConnection.Create(aChannel: IUnknown);
begin
fChannel := aChannel;
end;
{ TROSuperHttpServerResponse }
constructor TROSuperHttpServerResponse.Create(aConn: IROSuperHttpServerConnection; aHeaders: TIPHttpResponseHeaders);
begin
inherited Create;
fHeaders := aHeaders;
if fHeaders = nil then fhEaders := TIPHttpResponseHeaders.Create;
fConnection := aConn;
end;
destructor TROSuperHttpServerResponse.Destroy;
begin
fHeaders.Free;
inherited Destroy;
end;
function TROSuperHttpServerResponse.GetSelf: TROSuperHttpServerResponse;
begin
result := Self;
end;
{ TROSuperHttpServerConnection }
procedure TROSuperHttpServerConnection.DisposeData;
var
i: Integer;
lConn: TROSuperHttpWaitingConnection;
lList: TList;
begin
FreeAndNil(fResponseQueue);
lList := fWaitingConnections.LockList;
for i := 0 to lList.Count -1 do
begin
lConn := TROSuperHttpWaitingConnection(lList[i]);
fServer.CloseConnection(lConn.Channel);
lConn.Free;
end;
fWaitingConnections.UnlockList;
FreeAndNil(fWaitingConnections);
end;
constructor TROSuperHttpServerConnection.Create(
aServer: TROBaseSuperHttpServer);
begin
fServer := aServer;
fResponseQueue := TInterfaceList.Create;
fWaitingConnections := TThreadList.Create;
end;
destructor TROSuperHttpServerConnection.Destroy;
begin
fResponseQueue.Free;
fWaitingConnections.Free;
inherited;
end;
function TROSuperHttpServerConnection.GetSelf: TROSuperHttpServerConnection;
begin
result := self;
end;
procedure TROSuperHttpServerConnection.DispatchEvent(
anEventDataItem: TROEventData; aSessionReference: TGUID; aSender: TObject);
var
lId: Integer;
lData: TDynamicByteArray;
begin
fResponseQueue.Lock;
fEventId := fEventId - 1;
if fEventId > 0 then fEventId := -1;
lId := fEventId;
fResponseQueue.Unlock;
SetLength(lData, anEventDataItem.Data.size + 5);
lData[0] := ShPackage;
lData[1] := lId;
lData[2] := lId shr 8;
lData[3] := lId shr 16;
lData[4] := lId shr 24;
anEventDataItem.Data.Seek(0, soFromBeginning);
anEventDataItem.Data.Read(lData[5], Length(lData) -5);
fServer.SendAsyncResponse(self, nil, lData);
end;
function TROSuperHttpServerConnection.GetGuid: TGuid;
begin
result := fConnectionId;
end;
function TROSuperHttpServerConnection.GuidMatches(
const aGuid: TGuid): Boolean;
begin
result := (fConnectionId.D1 = aGuid.D1) and
(fConnectionId.D2 = aGuid.D2) and
(fConnectionId.D3 = aGuid.D3) and
(fConnectionId.D4[0] = aGuid.D4[0]) and
(fConnectionId.D4[1] = aGuid.D4[1]) and
(fConnectionId.D4[2] = aGuid.D4[2]) and
(fConnectionId.D4[3] = aGuid.D4[3]) and
(fConnectionId.D4[4] = aGuid.D4[4]) and
(fConnectionId.D4[5] = aGuid.D4[5]) and
(fConnectionId.D4[6] = aGuid.D4[6]) and
(fConnectionId.D4[7] = aGuid.D4[7]);
end;
procedure TROSuperHttpServerConnection.EventsRegistered(aSender: TObject;
aClient: TGUID);
begin
// do nothing.
end;
{ TROBaseSuperHttpServer }
procedure TROBaseSuperHttpServer.AddWaitingRequest(aTarget: IUnknown;
aConnection: IROSuperHttpServerConnection);
var
lRealConnection: TROSuperHttpServerConnection;
lResp: IROSuperHttpServerResponse;
begin
lRealConnection := aConnection.GetSelf;
lRealConnection.ResponseQueue.Lock;
try
if lRealConnection.ResponseQueue.Count > 0 then begin
lResp := IROSuperHttpServerResponse(lRealConnection.ResponseQueue[0]);
lRealConnection.ResponseQueue.Delete(0);
Sendresponse(aTarget, lResp.GetSelf.Headers, lResp.GetSelf.Data, lResp);
exit;
end;
finally
lRealConnection.ResponseQueue.Unlock;
end;
lRealConnection.WaitingConnections.Add(TROSuperHttpWaitingConnection.Create(aTarget));
end;
procedure TROBaseSuperHttpServer.cbCleanup(CurrentTickCount: Cardinal);
var
lTimeoutTime, lConnTimeout: TDateTime;
i: Integer;
lList: TList;
lConn: IROSuperHttpServerConnection;
begin
lTimeoutTime := Now - (90 * (1.0 / (24 * 60 * 60)));
lConnTimeout := Now - (fConnectionTimeout * (1.0 / (24 * 60 * 60)));
fConnections.Lock;
try
for i := fconnections.Count -1 downto 0 do begin
lConn := IROSuperHttpServerconnection(fConnections[i]);
if lConn.GetSelf.fLastTalk < lConnTimeout then begin
KillConnection(lConn);
continue;
end;
lList := lConn.GetSelf.fWaitingConnections.LockList;
while lList.Count > 0 do
begin
if TROSuperHttpWaitingConnection(lList[0]).Timeout < lTimeoutTime then begin
SendResponse(TROSuperHttpWaitingConnection(lList[0]).fChannel, nil, nil, nil);
TROSuperHttpWaitingConnection(lList[0]).Free;
lList.Delete(0);
end else break;
end;
lConn.GetSelf.fWaitingConnections.UnlockList;
end;
finally
fConnections.Unlock;
end;
end;
constructor TROBaseSuperHttpServer.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
fMaxPackageSize := 1024 *1024 * 1;
fServeRodl := true;
fServeInfoPage := true;
fConnections := TInterfaceList.Create;
fConnectionTimeout := 5 * 60;
end;
destructor TROBaseSuperHttpServer.Destroy;
var
i: Integer;
begin
Active := False;
if fTimer <> nil then FreeAndNil(fTimer);
if fOwnsThreadPool then FreeAndNil(fThreadPool);
fConnections.Lock;
try
for i := fConnections.Count -1 downto 0 do begin
IROSuperHttpServerConnection(fConnections[i]).DisposeData;
end;
finally
fConnections.Unlock;
end;
fConnections.Free;
inherited Destroy;
end;
procedure TROBaseSuperHttpServer.DoActivate;
begin
if fTimer <> nil then
fTimer.Free;
fTimer := TROThreadTimer.Create(cbCleanup, 30000);
if fThreadPool = nil then begin
fThreadPool := TROThreadPool.Create(self);
fOwnsThreadPool := true;
end;
end;
procedure TROBaseSuperHttpServer.DoDeactivate;
begin
if fTimer <> nil then
fTimer.Free;
fTimer := nil;
end;
function TROBaseSuperHttpServer.GetConnection(
const aGuid: TGuid): IROSuperHttpServerConnection;
var
i: Integer;
begin
fConnections.Lock;
try
for i := fConnections.Count -1 downto 0 do begin
Result := IROSuperHttpServerConnection(fConnections[i]);
if Result.GuidMatches(aGuid) then exit;
end;
finally
fConnections.Unlock;
end;
result := nil;
end;
function TROBaseSuperHttpServer.IntGetActive: Boolean;
begin
result := fActive;
end;
procedure TROBaseSuperHttpServer.IntSetActive(const Value: Boolean);
begin
inherited;
if fActive <> Value then begin
fActive := Value;
if value then
DoActivate
else
DoDeactivate;
end;
end;
procedure TROBaseSuperHttpServer.KillConnection(
aConnection: IROSuperHttpServerConnection);
var
fListener: IROSessionsChangesListener;
begin
fConnections.Remove(aConnection);
if Supports(fEventRepository, IROSessionsChangesListener, fListener) then
flistener.SessionsChangedNotification(aConnection.GetSelf.SessionId, saRemoveActiveListener, aConnection.GetSelf);
aConnection.DisposeData;
end;
procedure TROBaseSuperHttpServer.SendError(aTarget: IUnknown;
anError: string);
var
lData: TDynamicByteArray;
lHeaders: TIPHttpResponseHeaders;
begin
anError := '<html><head><title>Internal Server Error</title></head><body><h1>Internal Server Error</h1>'+anError+'</body></html>';
lHeaders := TIPHttpResponseHeaders.Create;
try
lHeaders.Code := 500;
lHeaders.Reason := 'Internal Error';
lHeaders.Headers.Values['Content-Type'] := 'text/html';
SetLength(lData, Length(anError));
Move(anError[1], lData[0], Length(anError));
SendResponse(aTarget, lHeaders, lData, nil);
finally
lHeaders.Free;
end;
end;
procedure TROBaseSuperHttpServer.SendRodl(ATarget: IUnknown;
aRequest: TIPHttpRequestHeaders; aDispatcher: TROMessageDispatcher;
const aTargetService: string);
var
lResponse: TMemoryStream;
moduleinfo: IROModuleInfo;
aFormat: string;
lData: TDynamicByteArray;
lHeaders: TIPHttpResponseHeaders;
begin
lResponse := TMemoryStream.Create;
try
aFormat := 'text/xml';
if Supports(aDispatcher, IROModuleInfo, moduleinfo) then
moduleinfo.GetModuleInfo(lResponse, nil, aFormat)
else
GetRodl(lResponse, nil, aformat, GetRODLReader);
SetLength(lData, lResponse.Size);
lResponse.Seek(0, soFromBeginning);
lResponse.Read(lData[0], Length(lData));
except
SendError(aTarget, 'Error retrieving RODL file');
exit;
end;
lHeaders := TIPHttpResponseHeaders.Create;
try
SetDefaultHeaders(aTarget, lHeaders);
lHeaders.Headers.Values['Content-Type'] := aFormat;
SendResponse(ATarget, lHeaders, lData, nil);
finally
lHeaders.Free;
end;
end;
function SameGuid(const g1, g2: TGuid): Boolean;
begin
result := (g1.D1 = g2.D1) and
(g1.D2 = g2.D2) and
(g1.D3 = g2.D3) and
(g1.D4[0] = g2.D4[0]) and
(g1.D4[1] = g2.D4[1]) and
(g1.D4[2] = g2.D4[2]) and
(g1.D4[3] = g2.D4[3]) and
(g1.D4[4] = g2.D4[4]) and
(g1.D4[5] = g2.D4[5]) and
(g1.D4[6] = g2.D4[6]) and
(g1.D4[7] = g2.D4[7]);
end;
procedure TROBaseSuperHttpServer.IncomingData(aTarget: IUnknown;
aRequestHeaders: TIPHttpRequestHeaders; aData: TDynamicByteArray;
aDispatcher: TROMessageDispatcher);
var
lClientId: TGuid;
lResp: TDynamicByteArray;
lConnection: IROSuperHttpServerConnection;
i: Integer;
begin
if Length(aData) < 17 then begin
SendError(aTarget, 'Invalid Request');
exit;
end;
Move(aData[0], lClientId, Sizeof(lClientID));
case aData[16] of
ShHello:
begin
if Length(aData) <> (16 { cid:guid } + 1 { cmd } + 8 { hello command } +
4 { max package } + 16 { session guid }) then begin
SendError(aTarget, 'Invalid Request');
exit;
end;
if (aData[17] <> ord('R')) or (aData[18] <> ord('O')) or (aData[19] <> ord('S')) or
(aData[20] <> ord('H')) then begin
SendError(aTarget, 'Invalid Request');
exit;
end;
fConnections.Lock;
try
lConnection := GetConnection(lClientId);
if lConnection <> nil then begin
SetLength(lResp, 6);
lResp[0] := shError;
lResp[5] := shError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
lConnection := TROSuperHttpServerConnection.Create(self);
lConnection.GetSelf.ConnectionId := lClientId;
lConnection.GetSelf.LastTalk := Now;
lConnection.GetSelf.RemoteMaxPackageLength :=
aData[25] or
(Integer(aData[26]) shl 8) or
(Integer(aData[27]) shl 16) or
(Integer(aData[28]) shl 24);
lConnection.GetSelf.SessionId := TGuid((@aData[29])^);
fConnections.Add(lConnection);
if SameGuid(lConnection.GetSelf.SessionId, EmptyGUID) then
lConnection.GetSelf.SessionId := NewGuid();
if fEventRepository <> nil then begin
fEventRepository.AddSession(lConnection.GetSelf.SessionId, lConnection.GetSelf);
end;
finally
fconnections.Unlock;
end;
SetLength(lResp, 8 + 4 + 16);
lResp[0] :=ord('R');
lResp[1] := ord('O');
lResp[2] := ord('S');
lResp[3] :=ord('H');
lResp[4] := ord('1');
lResp[5] := ord('0');
lResp[6] := ord('0');
lResp[7] := ord('0');
lResp[8] := fMaxPackageSize;
lResp[9] := fMaxPackageSize shr 8;
lResp[10] := fMaxPackageSize shr 16;
lResp[11] := fMaxPackageSize shr 24;
Move(lConnection.GetSelf.SessionId, lResp[12], Sizeof(TGuid));
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
ShGoodbye:
begin
lConnection := GetConnection(lClientId);
if lConnection = nil then begin
SetLength(lResp, 6);
lResp[0] := ShError;
lResp[5] := ShError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
KillConnection(lConnection);
SendResponse(aTarget, nil, nil, nil);
exit;
end;
ShPackage:
begin
lConnection := GetConnection(lClientId);
if lConnection = nil then begin
SetLength(lResp, 6);
lResp[0] := ShError;
lResp[5] := ShError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
lConnection.GetSelf.LastTalk := Now;
if Length(aData) < 16 + 1 + 4 then begin
SendError(aTarget, 'Invalid Request');
exit;
end;
i := aData[17] or (Integer(aData[18]) shl 8) or (Integer(aData[19]) shl 16) or (Integer(aData[20]) shl 24);
SetLength(lResp, Length(aData)-21);
Move(aData[21], lResp[0], Length(lResp));
try
ProcessPackage(lConnection, aRequestHeaders, aDispatcher, i, lResp);
except
SetLength(lResp, 6);
lResp[0] := ShError;
lResp[5] := ShError_QueueFull;
lResp[1] := aData[0];
lResp[2] := aData[1];
lResp[3] := aData[2];
lResp[4] := aData[3];
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
SendResponse(aTarget, nil, nil, nil);
exit;
end;
ShError:
begin
SendResponse(aTarget, nil, nil, nil);
exit;
end;
ShOptions:
begin
lConnection := GetConnection(lClientId);
if lConnection = nil then begin
SetLength(lResp, 6);
lResp[0] := shError;
lResp[5] := ShError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
exit;
end
else begin
SetLength(lResp, 6);
lResp[0] := shError;
lResp[5] := ShError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
lConnection.GetSelf.LastTalk := Now;
exit;
end;
end;
ShAsyncWait:
begin
lConnection := GetConnection(lClientId);
if lConnection = nil then begin
SetLength(lResp, 6);
lResp[0] := shError;
lResp[5] := ShError_InvalidClientId;
SendResponse(aTarget, nil, lResp, nil);
exit;
end;
lConnection.GetSelf.LastTalk := Now;
AddWaitingRequest(aTarget, lConnection);
exit;
end;
else
SendError(aTarget, 'Invalid Request');
end;
end;
procedure TROBaseSuperHttpServer.HandleRequest(aTarget: IUnknown;
aRequestHeaders: TIPHttpRequestHeaders; aRequest: TDynamicByteArray);
var
lMd: TROMessageDispatcher;
lTempStr: TMemoryStream;
lQueryString,
lServiceName,
s,
lSubPath,
lDispatcher: string;
lResponseHeaders: TIPHttpResponseHeaders;
lResponse: TDynamicByteArray;
lRodl: TRODLLibrary;
begin
try
lDispatcher := aRequestHeaders.Path;
if Pos('?', lDispatcher) >0 then begin
lQueryString := copy(lDispatcher, pos('?', lDispatcher)+1, MaxInt);
Delete(lDispatcher, pos('?', lDispatcher), MaxInt);
end else
lQueryString := '';
if (lDispatcher <> '') and (lDispatcher[1] = '/') then delete(lDispatcher, 1,1);
if pos('/', lDispatcher) > 0 then
begin
lSubPath := copy(lDispatcher, pos('/', lDispatcher)+1, MaxInt);
lDispatcher := '/' + copy(lDispatcher, 1, pos('/', lDispatcher) - 1);
end else
begin
lDispatcher := '/' + lDispatcher;
lSubPath := '';
end;
if lQueryString <> '' then begin
if copy(lQueryString, 1, 8) = 'service=' then begin
lServiceName := copy(lQueryString, 9, MaxInt);
end else if pos('&service=', lQueryString) > 0 then begin
lServiceName := Copy(lQueryString, pos('&service=', lQueryString)+10, MaxInt);
end;
if pos('&', lServiceName) >0 then Delete(lServiceName, pos('&', lServiceName), MaxInt);
end;
lMd := TROHTTPMessageDispatchers(Dispatchers).GetDispatcherByPath(lDispatcher) as TROHTTPDispatcher;
if lmd = nil then begin
if HandleUnknownRequest(aTarget, aRequestHeaders, aRequest) then
exit
else
begin
if (lDispatcher = '/') and (fServeInfoPage) then begin
lResponseHeaders := TIPHttpResponseHeaders.Create;
try
SetDefaultHeaders(aTarget, lResponseHeaders);
lResponseHeaders.Headers.Values['Content-Type'] := 'text/html; charset=utf-8';
lrodl := GetRodlLibrary(GetRODLReader);
try
s := GetRodlServerInfo('', lRodl, Dispatchers);
finally
lRodl.Free;
end;
SetLength(lResponse, length(s));
Move(s[1], lResponse[0], Length(S));
SendResponse(aTarget, lResponseHeaders, lResponse, nil);
finally
lResponseHeaders.Free;
end;
exit;
end
else if (Lowercase(lDispatcher) = '/doc')and (fServeInfoPage) then begin
lResponseHeaders := TIPHttpResponseHeaders.Create;
try
SetDefaultHeaders(aTarget, lResponseHeaders);
if lSubPath = 'css' then
begin
s := GetRodlCss;
lResponseHeaders.Headers.Values['Content-Type'] := 'text/css';
end else
if lSubPath = 'xslt' then
begin
s := GetRodlStyleSheet;
lResponseHeaders.Headers.Values['Content-Type'] := 'text/xml';
end else if lSubPath = '' then
begin
lRodl := GetRodlLibrary(GetRODLReader);
try
lResponseHeaders.Headers.Values['Content-Type'] := 'text/xml';
s := GetRodlWithStyleSheet(lRodl, '/doc/xslt');
finally
lRodl.Free;
end;
end else
begin
SendError(aTarget, 'Invalid Path');
exit;
end;
SetLength(lResponse, length(s));
Move(s[1], lResponse[0], Length(S));
SendResponse(aTarget, lResponseHeaders, lResponse, nil);
finally
lResponseHeaders.Free;
end;
exit;
end
else if (LowerCase(lDispatcher) = '/favicon.ico') and (fServeInfoPage) then begin
lResponseHeaders := TIPHttpResponseHeaders.Create;
try
SetDefaultHeaders(aTarget, lResponseHeaders);
lResponseHeaders.Headers.Values['Content-Type'] := 'image/x-icon';
lTempStr := TMemoryStream.Create;
try
GetRodlFavIcon(lTempStr);
SetLengtH(lResponse, lTempStr.Size);
lTempStr.Position := 0;
lTempstr.Read(lResponse[0], Length(lResponse));
finally
lTempStr.Free;
end;
SendResponse(aTarget, lResponseHeaders, lResponse, nil);
finally
lResponseHeaders.Free;
end;
exit;
end
else if (Lowercase(lDispatcher) = '/rodl') and (fServeRodl) then begin
SendRodl(aTarget, aRequestHeaders, lMd, lServiceName);
exit;
end else begin
SendError(aTarget, 'No dispatcher configured for "'+lDispatcher+'".');
exit;
end;
end;
end;
if not lMd.Enabled then begin
SendError(aTarget, 'Dispatcher for "'+lDispatcher+'" is disabled.');
exit;
end;
if lMd.Message = nil then begin
SendError(aTarget, 'Message for dispatcher "'+lDispatcher+'" is has not been assigned.');
exit;
end;
if ((aRequestHeaders.Method = 'GET') or
(Length(aRequest) = 4)) and(fServeRodl) then begin
if (lSubPath = '') or (lSubPath = 'rodl') then begin
SendRodl(aTarget, aRequestHeaders, lmd, lServiceName);
exit;
end
else begin
SendError(aTarget, 'Message for dispatcher "'+lDispatcher+'" is has not been assigned.');
exit;
end;
end;
IncomingData(aTarget, aRequestHeaders, aRequest, lmd);
except
on e: Exception do
SendError(aTarget, e.Message);
end;
end;
procedure TROBaseSuperHttpServer.SendAsyncResponse(
aTarget: IROSuperHttpServerConnection; lHeaders: TIPHttpResponseHeaders;
aData: TDynamicByteArray);
var
lResponse: IROSuperHttpServerResponse;
lList: TList;
lWc: TROSuperHttpWaitingConnection;
begin
lResponse := TROSuperHttpServerResponse.Create(aTarget.GetSelf, lHeaders);
lResponse.GetSelf.fData := aData;
lResponse.GetSelf.fConnection := aTarget.GetSelf;
lList := aTarget.GetSelf.WaitingConnections.LockList;
try
if lList.Count > 0 then begin
lResponse.GetSelf.Attempt :=lResponse.GetSelf.Attempt + 1;
lWc := lList[0];
lList.Delete(0);
SendResponse(lWc.Channel, lResponse.GetSelf.Headers, lResponse.GetSelf.Data, lResponse);
lWc.Free;
exit;
end;
aTarget.GetSelf.ResponseQueue.Add(lResponse);
finally
aTarget.GetSelf.WaitingConnections.UnlockList;
end;
end;
procedure TROBaseSuperHttpServer.SetThreadPool(const Value: TROThreadPool);
begin
if fOwnsThreadPool then
FreeAndNil(fThreadPool);
fOwnsThreadPool := false;
fThreadPool := Value;
end;
type
TQueueItem = class(TInterfacedObject, IROThreadPoolCallback, IROTransport, IROHTTPTransport)
private
fTarget: IROSuperHttpServerConnection;
fHeaders: TIPHttpRequestHeaders;
fMessageDispatcher: TROMessageDispatcher;
fId: Integer;
fData: TDynamicByteArray;
fOwner: TROBaseSuperHttpServer;
public
constructor Create(aOwner: TROBaseSuperHttpServer; aTarget: IROSuperHttpServerConnection; aHeaders: TIPHttpRequestHeaders;
aMessageDispatcher: TROMessageDispatcher; aId: Integer;
aData: TDynamicByteArray);
destructor Destroy; override;
procedure Callback(Caller: TROThreadPool; aThread: TThread);
function GetTransportObject: TObject;
function GetContentType: String;
function GetHeaders(const aName: String): String;
function GetLocation: String;
function GetPathInfo: 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);
function GetClientAddress: String;
end;
procedure TROBaseSuperHttpServer.ProcessPackage(
aTarget: IROSuperHttpServerConnection; aHeaders: TIPHttpRequestHeaders;
aMessageDispatcher: TROMessageDispatcher; aId: Integer;
aData: TDynamicByteArray);
begin
fThreadPool.QueueItem(TQueueItem.Create(self, aTarget, aHeaders, aMessageDispatcher, aId, aData));
end;
procedure TROBaseSuperHttpServer.ResponseFailed(aUserData: IInterface);
var
lData: IROSuperHttpServerResponse;
lConn: IROSuperHttpServerConnection;
lList: TList;
lWc: TROSuperHttpWaitingConnection;
begin
if aUserData = nil then exit;
lData := IROSuperHttpServerResponse(aUserData);
if lData.GetSelf.Attempt = 16 then exit; // it won't arrive after 16 tries.
lConn := lData.GetSelf.Connection;
lList := lConn.GetSelf.WaitingConnections.LockList;
try
if lList.Count > 0 then begin
lData.GetSelf.Attempt := lData.GetSelf.Attempt + 1;
lWc := TROSuperHttpWaitingConnection(lList[0]);
lList.Delete(0);
SendResponse(lWc.Channel, lData.GetSelf.Headers, lData.GetSelf.Data, lData);
lWc.Free;
exit;
end;
lConn.GetSelf.ResponseQueue.Add(lData);
finally
lConn.GetSelf.WaitingConnections.UnlockList;
end;
end;
procedure TROBaseSuperHttpServer.ResponseSucessful(aUserData: IInterface);
begin
// nothing to do here.
end;
function TROBaseSuperHttpServer.GetDispatchersClass: TROMessageDispatchersClass;
begin
Result := TROHTTPMessageDispatchers;
end;
type
TByteArrayStream = class(TStream)
private
fPos: Integer;
fData: TDynamicByteArray;
protected
{$IFDEF DELPHI7UP}
function GetSize: Int64; override;
{$ENDIF}
public
constructor Create(aData: TDynamicByteArray);
function Write(const Buffer; Count: Integer): Integer; override;
function Read(var Buffer; Count: Integer): Integer; override;
function Seek(Offset: Integer; Origin: Word): Integer; override;
end;
{ TQueueItem }
procedure TQueueItem.Callback(Caller: TROThreadPool; aThread: TThread);
var
lStream, lResponse: TStream;
lResp: TDynamicByteArray;
begin
lStream := nil;
lResponse := TMemoryStream.Create;
try
if assigned(fOwner.OnReadFromStream) then begin
lStream := TMemoryStream.Create;
lStream.Write(fData[0], Length(fData));
lstream.Position := 0;
fOwner.OnReadFromStream(lStream);
end
else
lStream := TByteArrayStream.Create(fData);
fData := nil;
fOwner.DispatchMessage(self, lStream, lResponse);
if assigned(fOwner.OnWriteToStream) then
fOwner.OnWriteToStream(lResponse);
lResponse.Position := 0;
SetLength(lResp, lResponse.Size + 5);
lResp[0] := ShPackage;
lResp[1] := fId;
lResp[2] := fId shr 8;
lResp[3] := fId shr 16;
lResp[4] := fId shr 24;
lResponse.Read(lResp[5], lResponse.Size);
fOwner.SendAsyncResponse(fTarget, nil, lResp);
except
on E: Exception do begin
setLength(lResp, 4 + 1 + 1);
lResp[0] := ShError;
lResp[1] := fId;
lResp[2] := fId shr 8;
lResp[3] := fId shr 16;
lResp[4] := fId shr 24;
lResp[5] := ShError_QueueFull;
fOwner.SendAsyncResponse(fTarget, nil, lResp);
end;
end;
lStream.Free;
lResponse.Free;
end;
constructor TQueueItem.Create(aOwner: TROBaseSuperHttpServer;
aTarget: IROSuperHttpServerConnection; aHeaders: TIPHttpRequestHeaders;
aMessageDispatcher: TROMessageDispatcher; aId: Integer;
aData: TDynamicByteArray);
begin
inherited Create;
fOwner := aOwner;
fTarget := aTarget;
fHeaders := TIPHttpRequestHeaders.Create;
fHeaders.Headers.Assign(aHeaders.Headers);
fHeaders.Method := aHeaders.Method;
fHeaders.Version := aHeaders.Version;
fHeaders.Path := aHeaders.Path;
fMessageDispatcher := aMessageDispatcher;
fId := aId;
fData := aData;
end;
destructor TQueueItem.Destroy;
begin
fHeaders.Free;
inherited Destroy;
end;
function TQueueItem.GetClientAddress: String;
begin
result := '';
end;
function TQueueItem.GetContentType: String;
begin
result := fHeaders.ContentType;
end;
function TQueueItem.GetHeaders(const aName: String): String;
begin
result := fHeaders.Headers.Values[aName];
end;
function TQueueItem.GetLocation: String;
begin
result := '';
end;
function TQueueItem.GetPathInfo: String;
begin
result := fHeaders.Path;
end;
function TQueueItem.GetQueryString: String;
begin
result := fHeaders.Path;
if pos('?', Result) <> 0 then
result := copy(Result, pos('?', Result) + 1 ,MaxInt)
else
result := '';
end;
function TQueueItem.GetTargetURL: String;
begin
result := '';
end;
function TQueueItem.GetTransportObject: TObject;
begin
result := fOwner;
end;
function TQueueItem.GetUserAgent: String;
begin
result := fHeaders.Headers.Values['User-Agent'];
end;
procedure TQueueItem.SetContentType(const aValue: String);
begin
end;
procedure TQueueItem.SetHeaders(const aName, aValue: String);
begin
fHeaders.Headers.Values[aName] := aValue;
end;
procedure TQueueItem.SetPathInfo(const aValue: String);
begin
end;
procedure TQueueItem.SetTargetURL(const aValue: String);
begin
end;
procedure TQueueItem.SetUserAgent(const aValue: String);
begin
end;
{ TByteArrayStream }
constructor TByteArrayStream.Create(aData: TDynamicByteArray);
begin
inherited Create;
fData := aData;
fPos := 0;
end;
{$IFDEF DELPHI7UP}
function TByteArrayStream.GetSize: Int64;
begin
result := Length(fData);
end;
{$ENDIF}
function TByteArrayStream.Read(var Buffer; Count: Integer): Integer;
begin
if fPos + Count >= Length(fData) then Count := Length(fData) - fPos;
Move(fData[fPos], Buffer, Count);
fPos := fPos + Count;
result := Count;
end;
function TByteArrayStream.Seek(Offset: Integer; Origin: Word): Integer;
begin
case Origin of
soFromBeginning: fPos := Offset;
soFromCurrent: fPos := fPos + Offset;
soFromEnd: fPos := Length(fData) + Offset;
end;
if fPos < 0 then fPos := 0;
if fPos >= Length(fData) then fPos := Length(fData) -1;
Result := fPos;
end;
function TByteArrayStream.Write(const Buffer; Count: Integer): Integer;
begin
raise Exception.Create('Readonly stream cannot be written to');
end;
end.