Componentes.Terceros.RemObj.../internal/5.0.23.613/1/RemObjects SDK for Delphi/Source/uROBaseSuperHttpChannel.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

587 lines
18 KiB
ObjectPascal

unit uROBaseSuperHttpChannel;
{----------------------------------------------------------------------------}
{ RemObjects SDK Library - Core 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
{$IFDEF REMOBJECTS_TRIAL}uROTrial,{$ENDIF}
Classes, SysUtils, uROClient, uROClientIntf, uROAsync, uROClasses, SyncObjs,
{$IFDEF RemObjects_UseEncryption} uRoEncryption, {$ENDIF}uROThreadPool;
type
TDynByteArray = array of byte;
TROHttpWaitingRequest = class
private
fEvent: TROEvent;
fFreeEvent: Boolean;
fId: Integer;
fResultData: TDynByteArray;
fResultErrorCode: Integer;
public
property Id: Integer read fId;
property Event: TROEvent read fEvent;
property Resultdata: TDynByteArray read fResultData write fResultData;
property ResultErrorCode: Integer read fResultErrorCode write fResultErrorCode;
constructor Create(Id: Integer); overload;
constructor Create(Id: Integer; Ev: TROEvent); overload;
destructor Destroy; override;
end;
EROSuperHttpChannelException = class(EROException);
TROBaseSuperHttpChannel = class(TROTransportChannel, IROTransport,
IROActiveEventChannel, IROMultiThreadAwareChannel,
IROAsyncTransportChannel, IROActiveAsyncTransportChannel, IROTransportChannelEx)
private
fConnectWait: TROEvent;
fActive, fConnected: Boolean;
fSessionId, fConnectionId: TGuid;
fRemoteMaxPackageSize,
fMaxPackageSize: Integer;
fWaitingRequest: TThreadList;
fPackageCounter: Integer;
fOwnsThreadPool: Boolean;
fEventThreadPool: TROThreadPool;
fDispatchLock: TCriticalSection;
fWaitingThread: TThread;
fEventReceiver: IROEventReceiver;
fRequestTimeout: Integer;
fConnectTimeout: Integer;
fHttpRequestTimeout: Integer;
procedure SetActive(const Value: Boolean);
procedure ProcessEvent(aId: Integer; aData: TDynByteArray);
procedure SetEventThreadPool(const Value: TROThreadPool);
protected
procedure IntDispatch(aRequest: TStream; aResponse: TStream); override;
procedure IntSetServerLocator(aServerLocator: TROServerLocator); override;
function CheckStatus(const iMessageID: String): Boolean;
procedure RetrieveResponse(const iMessageID: String;
aResponse: TStream);
procedure BeforeDispatch(aMessage: IROMessage); override;
procedure RegisterEventReceiver(aReceiver: IROEventReceiver);
procedure UnregisterEventReceiver(aReceiver: IROEventReceiver);
function InvokeRequest(aRequest:TStream; iGetResponse:boolean=true):string; overload;
function InvokeRequest(aRequest:TStream; aGetResponse:boolean=true; aEvent: TROEvent=nil):string; overload;
procedure CancelRequest(aWaitingThread: Boolean); virtual; abstract;
procedure DispatchHttpRequest(aWaitingThread: Boolean; aRequest: TDynByteArray; out aResponse: TDynByteArray); virtual; abstract;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
property EventThreadPool :TROThreadPool read fEventThreadPool write SetEventThreadPool;
property ConnectionId: TGUID read fConnectionId write fConnectionId;
property SessionId: TGuid read fSessionId write fSessionId;
property Connected: Boolean read fConnected;
property Active: Boolean read fActive write SetActive;
property MaxPackageSize: Integer read fMaxPackageSize write fMaxPackageSize default 1024 * 1024;
property RequestTimeout: Integer read fRequestTimeout write fRequestTimeout default 60000;
property ConnectTimeout: Integer read fConnectTimeout write fConnectTimeout default 10000;
property HttpRequestTimeout: Integer read fHttpRequestTimeout write fHttpRequestTimeout default 10000;
end;
const
ShHello = 0;
ShGoodbye = 1;
ShPackage = 2;
ShError = 3;
ShError_InvalidClientId = 0;
ShError_QueueFull = 1;
ShError_UnknownOption = 2;
ShOptions = 4;
ShAsyncWait = 5;
implementation
uses Math;
type
TROSuperHttpThread = class(TThread)
private
fOwner: TROBaseSuperHttpChannel;
public
constructor Create(aOwner: TROBaseSuperHttpChannel);
procedure Execute; override;
end;
{ TROHttpWaitingRequest }
constructor TROHttpWaitingRequest.Create(Id: Integer);
begin
fId := Id;
fEvent := TROEvent.Create(nil, true, false, '');
fFreeEvent := true;
end;
constructor TROHttpWaitingRequest.Create(Id: Integer; Ev: TROEvent);
begin
fEvent := Ev;
if Fevent = nil then begin
fEvent := TROEvent.Create(nil, true, false, '');
fFreeEvent := true;
end;
fId := Id;
end;
destructor TROHttpWaitingRequest.Destroy;
begin
if fFreeEvent then fEvent.Free;
inherited;
end;
{ TROBaseSuperHttpChannel }
procedure TROBaseSuperHttpChannel.BeforeDispatch(aMessage: IROMessage);
begin
if not fConnected then begin
Active := true;
if (fConnectWait.WaitFor(fConnectTimeout) <> wrSignaled) or not fConnected then
raise EROSuperHttpChannelException.Create('Timeout connecting');
end;
aMessage.ClientID := fSessionId;
inherited;
end;
constructor TROBaseSuperHttpChannel.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
fMaxPackageSize := 1 * 1024 * 1024;
fRequestTimeout := 60000;
fConnectTimeout := 10000;
fHttpRequestTimeout := 10000;
fConnectWait := TROEvent.Create(nil, true, false, '');
fWaitingRequest := TThreadList.Create;
ThreadSafe := true;
fDispatchLock := TCriticalSection.Create;
end;
destructor TROBaseSuperHttpChannel.Destroy;
var
i: Integer;
lList: TList;
begin
if fOwnsThreadPool then fEventThreadPool.Free;
Active := False;
fConnectWait.Free;
lList := fWaitingRequest.LockList;
try
for i := 0 to lList.Count -1 do begin
TROHttpWaitingRequest(lList[i]).Free;
end;
finally
fWaitingRequest.UnlockList;
end;
fWaitingRequest.Free;
fDispatchLock.Free;
inherited;
end;
procedure TROBaseSuperHttpChannel.IntDispatch(aRequest,
aResponse: TStream);
var
lRequest, lResponse: TDynByteArray;
lList:TList;
lId: Integer;
lReq: TROHttpWaitingRequest;
begin
if aRequest.Size > fRemoteMaxPackageSize then raise EROSuperHttpChannelException.Create('Package too large');
SetLength(lRequest, 16 + 1 + 4 + aRequest.Size);
aRequest.Position := 0;
Move(fConnectionId, lRequest[0], 16);
lRequest[16] := ShPackage;
aRequest.Read(lRequest[21], aRequest.Size);
lList := fWaitingRequest.LockList;
try
inc(fPackageCounter);
if fPackageCounter < 0 then fPackageCounter := 1;
lId := fPackageCounter;
lReq := TROHttpWaitingRequest.Create(lId);
lList.Add(lReq);
finally
fWaitingRequest.UnlockList;
end;
try
lRequest[17] := lId;
lRequest[18] := lId shr 8;
lRequest[19] := lId shr 16;
lRequest[20] := lId shr 24;
fDispatchLock.Enter;
try
DispatchHttpRequest(false, lRequest, lResponse);
if Length(lResponse) >= 6 then begin
if lResponse[0] = shError then begin
case lResponse[5] of
ShError_InvalidClientId: raise EROSuperHttpChannelException.Create('Invalid client id');
ShError_QueueFull: raise EROSuperHttpChannelException.Create('Queue full');
else
raise EROSuperHttpChannelException.Create('Unknown error');
end;
end;
end;
finally
fDispatchLock.Release;
end;
if lReq.Event.WaitFor(fRequestTimeout) <> wrSignaled then
raise EROSuperHttpChannelException.Create('Timeout');
if Length(lReq.fResultData) <> 0 then begin
aResponse.Write(lReq.fResultData[0], length(lReq.fResultData))
end else begin
case lReq.fResultErrorCode of
ShError_InvalidClientId: raise EROSuperHttpChannelException.Create('Invalid client id');
ShError_QueueFull: raise EROSuperHttpChannelException.Create('Queue full');
else
raise EROSuperHttpChannelException.Create('Unknown error');
end;
end;
finally
lList := fWaitingRequest.LockList;
lList.Remove(lReq);
fWaitingRequest.UnlockList;
lReq.Free;
end;
end;
procedure TROBaseSuperHttpChannel.IntSetServerLocator(
aServerLocator: TROServerLocator);
begin
end;
procedure TROBaseSuperHttpChannel.SetActive(const Value: Boolean);
begin
if fActive = Value then exit;
fActive := Value;
if fActive then begin
fConnectWait.ResetEvent;
if fWaitingThread <> nil then fWaitingThread.Free;
fWaitingThread := TROSuperHttpThread.Create(self);
end else begin
fWaitingThread.Terminate;
CancelRequest(true);
fWaitingThread.Free;
fwaitingThread := nil;
end;
end;
procedure TROBaseSuperHttpChannel.SetEventThreadPool(
const Value: TROThreadPool);
begin
if fOwnsThreadPool and (fEventThreadPool <> nil) then fEventThreadPool.Free;
fEventThreadPool := Value;
fOwnsThreadPool := false;
end;
procedure TROBaseSuperHttpChannel.RegisterEventReceiver(
aReceiver: IROEventReceiver);
begin
fEventReceiver := aReceiver;
end;
procedure TROBaseSuperHttpChannel.UnregisterEventReceiver(
aReceiver: IROEventReceiver);
begin
if fEventReceiver = aReceiver then fEventReceiver := nil;
end;
function FindWaitingRequest(aList: TList; aId: Integer): TROHttpWaitingRequest;
var
i: Integer;
begin
for i := aList.count -1 downto 0 do begin
result := TROHttpWaitingRequest(aList[i]);
if result.fId = aId then exit;
end;
result := nil;
end;
function TROBaseSuperHttpChannel.CheckStatus(
const iMessageID: String): Boolean;
var
lList: TList;
wr: TROHttpWaitingRequest;
begin
lList := fWaitingRequest.LockList;
try
wr := FindWaitingRequest(lList, StrToInt(iMessageId));
if wr = nil then
result := true
else
result := (Length(wr.Resultdata) <> 0) or (wr.ResultErrorCode <> 0);
finally
fWaitingRequest.UnlockList;
end;
end;
procedure TROBaseSuperHttpChannel.RetrieveResponse(
const iMessageID: String; aResponse: TStream);
var
lList: TList;
wr: TROHttpWaitingRequest;
begin
lList := fWaitingRequest.LockList;
try
wr := FindWaitingRequest(lList, StrToInt(iMessageId));
finally
fWaitingRequest.UnlockList;
end;
if wr = nil then raise EROSuperHttpChannelException.Create('Invalid request id');
try
if wr.fEvent.WaitFor(fRequestTimeout) <> wrSignaled then raise EROSuperHttpChannelException.Create('Timeout');
if Length(wr.fResultData) > 0 then begin
aResponse.Write(wr.fresultData[0], Length(wr.fresultData));
aResponse.Position := 0;
end else begin
case wr.fResultErrorCode of
ShError_InvalidClientId: raise EROSuperHttpChannelException.Create('Invalid client id');
ShError_QueueFull: raise EROSuperHttpChannelException.Create('Queue full');
else
raise EROSuperHttpChannelException.Create('Unknown error');
end;
end;
finally
lList := fWaitingRequest.LockList;
try
lList.Remove(wr);
wr.Free;
finally
fWaitingRequest.UnlockList;
end;
end;
end;
function TROBaseSuperHttpChannel.InvokeRequest(aRequest: TStream;
iGetResponse: boolean): string;
begin
result := InvokeRequest(aRequest, iGetResponse, nil);
end;
function TROBaseSuperHttpChannel.InvokeRequest(aRequest: TStream;
aGetResponse: boolean; aEvent: TROEvent): string;
var
lRequest, lResponse: TDynByteArray;
lList:TList;
lId: Integer;
lReq: TROHttpWaitingRequest;
begin
if aRequest.Size > fRemoteMaxPackageSize then raise EROSuperHttpChannelException.Create('Package too large');
SetLength(lRequest, 16 + 1 + 4 + aRequest.Size);
aRequest.Position := 0;
Move(fConnectionId, lRequest[0], 16);
lRequest[16] := ShPackage;
aRequest.Read(lRequest[21], aRequest.Size);
lList := fWaitingRequest.LockList;
try
inc(fPackageCounter);
if fPackageCounter < 0 then fPackageCounter := 1;
lId := fPackageCounter;
if aGetResponse then begin
lReq := TROHttpWaitingRequest.Create(lId);
lList.Add(lReq);
end else
lReq := nil;
finally
fWaitingRequest.UnlockList;
end;
try
lRequest[17] := lId;
lRequest[18] := lId shr 8;
lRequest[19] := lId shr 16;
lRequest[20] := lId shr 24;
fDispatchLock.Enter;
try
DispatchHttpRequest(false, lRequest, lResponse);
if Length(lResponse) >= 6 then begin
if lResponse[0] = shError then begin
case lResponse[5] of
ShError_InvalidClientId: raise EROSuperHttpChannelException.Create('Invalid client id');
ShError_QueueFull: raise EROSuperHttpChannelException.Create('Queue full');
else
raise EROSuperHttpChannelException.Create('Unknown error');
end;
end;
end;
finally
fDispatchLock.Release;
end;
result := inttostr(lId);
except
lList := fWaitingRequest.LockList;
lList.Remove(lReq);
fWaitingRequest.UnlockList;
lReq.Free;
raise;
end;
end;
type
TROSuperEventCallback = class(TInterfacedObject, IROThreadPoolCallback)
private
fData: TDynByteArray;
fOwner: TROBaseSuperHttpChannel;
public
procedure Callback(Caller: TROThreadPool; aThread: TThread);
end;
procedure TROBaseSuperHttpChannel.ProcessEvent(aId: Integer;
aData: TDynByteArray);
var
lCb: TROSuperEventCallback;
begin
lCb := TROSuperEventCallback.Create;
lCb.fOwner := self;
lCb.fData := aData;
fEventThreadPool.QueueItem(lCb);
end;
{ TROSuperHttpThread }
constructor TROSuperHttpThread.Create(aOwner: TROBaseSuperHttpChannel);
begin
inherited Create(true);
fOwner := aOwner;
Resume;
end;
procedure TROSuperHttpThread.Execute;
var
lRequest,
lTmpData,
lResponse: TDynByteArray;
lList: TList;
lPackageId: Integer;
wr: TROHttpWaitingRequest;
begin
try
if IsEqualGuid(fOwner.fConnectionId, Emptyguid) then fOwner.fConnectionId := NewGuid;
SetLength(lRequest, 16 + 1 +8 + 4 + 16);
Move(fOwner.fConnectionId, lRequest[0], 16);
lRequest[16] := shHello;
lRequest[17] := ord('R');
lRequest[18] := ord('O');
lRequest[19] := ord('S');
lRequest[20] := ord('H');
lRequest[21] := ord('1');
lRequest[22] := ord('0');
lRequest[23] := ord('0');
lRequest[24] := ord('0');
lRequest[25] := fOwner.fMaxPackageSize;
lRequest[26] := fOwner.fMaxPackageSize shr 8;
lRequest[27] := fOwner.fMaxPackageSize shr 16;
lRequest[28] := fOwner.fMaxPackageSize shr 16;
Move(fOwner.fSessionId, lRequest[29], 16);
fOwner.fDispatchLock.Enter;
try
fOwner.DispatchHttpRequest(false, lRequest, lResponse);
finally
fOwner.fDispatchLock.Leave;
end;
if Length(lResponse) <> (16 + 8 + 4) then
raise EROSuperHttpChannelException.Create('Invalid response from server for connection initiation command');
if (lResponse[0] <> ord('R')) or (lResponse[1] <> ord('O')) or (lResponse[2] <> ord('S')) or (lResponse[3] <> ord('H')) then
raise EROSuperHttpChannelException.Create('Invalid response from server for connection initiation command');
fOwner.fRemoteMaxPackageSize := Integer(lResponse[8]) or (Integer(lResponse[9]) shl 8)
or (Integer(lResponse[10]) shl 16) or (Integer(lResponse[11]) shl 24);
move(lResponse[12], fOwner.fSessionId, 16);
fOwner.fConnected := true;
fOwner.fConnectWait.SetEvent;
SetLength(lRequest, 17);
move(fOwner.fConnectionId, lRequest[0], 16);
lRequest[16] := ShAsyncWait;
while not Terminated do begin
try
fOwner.DispatchHttpRequest(true, lRequest, lResponse);
except
continue;
end;
if Length(lResponse) <> 0 then begin
case lResponse[0] of
ShError: begin
if Length(lResponse) < 5 then continue;
lPackageId := Integer(lResponse[1]) or (Integer(lResponse[2]) shl 8)
or (Integer(lResponse[3]) shl 16) or (Integer(lResponse[4]) shl 24);
lList := fOwner.fWaitingRequest.LockList;
try
wr := FindWaitingRequest(lList, lPackageId);
if wr <> nil then begin
wr.fResultErrorCode := lResponse[4];
wr.fEvent.SetEvent;
end;
finally
fOwner.fWaitingRequest.UnlockList;
end;
end;
shPackage: begin
if Length(lResponse) < 5 then continue;
lPackageId := Integer(lResponse[1]) or (Integer(lResponse[2]) shl 8)
or (Integer(lResponse[3]) shl 16) or (Integer(lResponse[4]) shl 24);
setLength(lTmpData, Length(lResponse) -5);
Move(lResponse[5], lTmpData[0], Length(lTmpData));
if lPackageId < 0 then
fOwner.ProcessEvent(lPackageId, lTmpData)
else begin
lList := fOwner.fWaitingRequest.LockList;
try
wr := FindWaitingRequest(lList, lPackageId);
if wr <> nil then begin
wr.fResultData := lTmpData;
wr.fEvent.SetEvent;
end;
finally
fOwner.fWaitingRequest.UnlockList;
end;
end;
end;
end;
end;
end;
lRequest[16] := shGoodbye;
fOwner.fDispatchLock.Enter;
try
fOwner.DispatchHttpRequest(false, lRequest, lResponse);
finally
fOwner.fDispatchLock.Leave;
end;
except
// cannot let exceptions escape here
end;
end;
{ TROSuperEventCallback }
procedure TROSuperEventCallback.Callback(Caller: TROThreadPool;
aThread: TThread);
var
lData: TMemoryStream;
begin
if fOwner.fEventReceiver <> nil then begin
lData := TMemoryStream.Create;
try
lData.Write(fData[0], length(fData));
lData.Position := 0;
fOwner.fEventReceiver.Dispatch(lData, aThread);
finally
lData.Free;
end;
end;
end;
end.