lazarus-ccr/components/flashfiler/sourcelaz/ffllcomm.pas
2016-12-07 13:31:59 +00:00

1947 lines
70 KiB
ObjectPascal

{*********************************************************}
{* FlashFiler: Base unit for transports & cmd handlers *}
{*********************************************************}
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower FlashFiler
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1996-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
{$I ffdefine.inc}
unit ffllcomm;
interface
uses
classes,
forms,
windows,
ffllbase,
ffllcomp,
fflleng,
fflllog,
ffllreq,
ffllthrd,
ffnetmsg;
type
{ TffDataMessage contains the message information passed from a transport
to a server command handler, plugin command handler, or engine manager. }
PffDataMessage = ^TffDataMessage;
TffDataMessage = record
dmMsg : Longint; { the unique ID identifying the msg type }
dmClientID : TffClientID; { the client sending the message }
dmRequestID : Longint; { the unique ID of the request }
dmTime : TffWord32; { the time the message was received }
dmRetryUntil : TffWord32;
dmErrorCode : TffResult;
dmData : pointer;
dmDataLen : TffMemSize;
end;
{ The following options may be used to control logging in the transports.
Values:
fftpLogErrors - Write errors to the event log.
fftpLogRequests - Write requests to the event log. If in Send mode
then logs all sent requests. If in Listen mode then logs all received
requests.
fftpLogReplies - If in Send mode then logs all received replies. If in
Listen mode then logs all sent replies. }
TffTransportLogOption = (fftpLogErrors,
fftpLogRequests, fftpLogReplies);
TffTransportLogOptions = set of TffTransportLogOption;
{ A transport will send a request to the server. When the reply is
received, the transport must notify the object submitting the request.
To be notified, the object submitting the request must define a procedure
of type TffReplyCallback. Parameters passed to this procedure are as
follows:
@param msgID The message identifier returned by the server.
@param errorCode The error code returned by the server.
@param replyData The data returned by the server.
@param replyDataLen The length of the data returned by the server.
@param replyType The format of the data: byteArray (e.g., packed record)
or stream.
@param replyCookie The replyCookie parameter originally supplied to the
TffBaseTransport.Request method. The meaning of this parameter is
specific to the object submitting the request. For the
TffRemoteServerEngine, this is a pointer to TffProxyClient.
}
TffReplyCallback = procedure(msgID : Longint;
errorCode : TffResult;
replyData : pointer;
replyDataLen : Longint;
replyCookie : Longint);
TffBasePluginCommandHandler = class; { forward declaration }
TffBaseEngineManager = class; { forward declaration }
TffBaseTransport = class; { forward declaration }
{ This is the base class for the command handler. A command handler
receives requests from a transport and routes them to a destination.
The base class supports routing of commands to plugins that have
themselves with the command handler. }
TffBaseCommandHandler = class(TffStateComponent)
protected {private}
FManager : TffBaseEngineManager;
{-The engine manager that may receive shutdown and startup requests
through this command handler. Note that the command handler doesn't
really know about shutdown and startup requests. The engine manager
is like a special plugin. If a plugin does not handle the message,
it is routed to the engine manager. The engine manager may or may
not handle the message. }
FPlugins : TffThreadList;
{-The list of plugins that reference the command handler. }
FSkipInitial : Boolean; {!!.01}
{-Internal state that reflects whether the Engine Manager Wizard has
created this component as a proxy (true) or not}
FTransports : TffThreadList;
{-The list of transports that reference the command handler. }
protected
procedure bchFreeMsg(msg : PffDataMessage); virtual;
{ When a transport passes off a request to the command handler, it
becomes the command handler's responsibility to free the message
data associated with the request. This method frees the TffDataMessage
structure as well as the message content contained by TffDataMessage.
Command handlers should call this method, or find some other way of
freeing the memory, once a request has been processed. }
function bchGetTransport(aInx : Integer) : TffBaseTransport; virtual;
{ Retrieves a transport from the command handler's list.}
function bchGetTransportCount : Longint; virtual;
{ Retrieves the number of transports owned by this command
handler.}
procedure bchSetEngineManager(aManager : TffBaseEngineManager); virtual;
{-Used to set the manager to which messages may be routed. }
procedure scSetState(const aState : TffState); override;
{ This method is called when the command handler's state changes.
This implementation sets the state of the associated transports. }
property SkipInitial : Boolean {BEGIN !!.01}
read FSkipInitial
write FSkipInitial;
{ This property is used by the engine manager wizard. It's purpose is
to keep the bchSetEngineManger routine from generating an access
violation when the expert creates a new engine manager } {END !!.01}
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure FFAddDependent(ADependent : TffComponent); override; {!!.11}
procedure FFRemoveDependent(ADependent : TffComponent); override; {!!.11}
procedure Process(Msg : PffDataMessage); virtual;
{ This method is called by the transport in order to process a message.
The default implementation forwards the message to the registered
plugin(s). If a plugin does not handle the message and an engine
manager has been specified, the message is forwarded to the
engine manager. If the message is not handled, a reply is sent to
the client stating the message is unrecognized. }
property TransportCount : Longint read bchGetTransportCount;
{ The number of transports passing requests to this command handler.}
property Transports[aInx : Longint] : TffBaseTransport
read bchGetTransport;
{ Use this property to access the transports connected to the command
handler. }
published
property EngineManager : TffBaseEngineManager
read FManager write bchSetEngineManager;
end;
{This is the base class for a plugin engine. All plugin engines inherit from
this class. A client application may interface with a plugin engine
via direct calls to the plugin engine or via calls to a remote plugin
engine.
To create a custom plugin engine, you must do the following:
1. Create an abstract plugin engine that defines the interface of your
engine.
2. From the abstract plugin engine, create a real plugin engine that
implements the engine interface.
3. From the abstract plugin engine, create a remote plugin engine. Assign
it a property Transport of type TffBaseTransport. The remote plugin
engine is placed on the client application and transfers the commands to
a listener on the server. The commands are routed from the listener to
a plugin command handler to your real plugin engine.
4. From the abstract TffBasePluginCommandHandler class, create a command
handler for the plugin. }
TffBasePluginEngine = class(TffStateComponent)
private
FPluginCmdHandlers : TffThreadList;
{-The list of plugin command handlers registered with this engine. }
protected
procedure scSetState(const aState : TffState); override;
{-Sets the state of the engine. This will also set the state of any
associated plugin command handlers. }
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure FFAddDependent(ADependent : TffComponent); override; {!!.11}
procedure FFRemoveDependent(ADependent : TffComponent); override; {!!.11}
end;
{This is the base class for a plugin command handler. A plugin command
handler receives requests through a standard command handler. It passes
the requests on to a plugin engine.
As a plugin designer, you will need to create a class that inherits from
TffBasePluginCommandHandler. The class must recognize the messages to be
handled by your real plugin engine.
Note: Descendants of TffBaseCommandHandler must free the message data in
their overridden Process methods. However, this does not apply to
plugin command handlers. That is because they are typically passed a
request from TffBaseCommandHandler.Process and
TffBaseCommandHandler.Process handles the freeing of the message data
on behalf of the plugin command handlers. }
TffBasePluginCommandHandler = class(TffStateComponent)
protected
FCmdHandler : TffBaseCommandHandler;
FPluginEngine : TffBasePluginEngine;
{-The plugin engine receiving commands through this plugin. }
procedure pchSetCmdHandler(aCmdHandler : TffBaseCommandHandler); virtual;
{-The command handler forwarding commands to this plugin command
handler. }
procedure pchSetPluginEngine(anEngine : TffBasePluginEngine); virtual;
{-The plugin engine receiving commands through this plugin. This method
calls TffBasePluginEngine.AddCmdHandler. Because a plugin command
handler is associated with a specific plugin engine class, the plugin
designer must specify his own PluginEngine property. The custom
PluginEngine property should eventually call this SetPluginEngine
method. }
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure FFNotificationEx(const AOp : Byte; AFrom : TffComponent; {!!.11}
const AData : TffWord32); override; {!!.11}
procedure Process(Msg : PffDataMessage; var handled : boolean); virtual; abstract;
{ This method is called by a command handler when it has a message that
may be processed by a plugin. If the plugin handles the message,
set handled to True. }
published
property CommandHandler : TffBaseCommandHandler read FCmdHandler
write pchSetCmdHandler;
{ The command handler passing requests to this plugin command handler. }
end;
{The engine manager is a type of data module that contains one or more engines
(e.g., TffBasePluginEngine or TffBaseServerEngine) and controls their
startup and shutdown. The manager can be controlled by the GUI of its
parent application or remotely via startup and shutdown commands received
through a command handler. }
TffBaseEngineManager = class(TDataModule)
private
FCmdHandlers : TffThreadList;
{-The command handlers registered with the engine manager. }
protected
procedure bemAddCmdHandler(aCmdHandler : TffBaseCommandHandler); virtual;
{-When a command handler references an engine manager, it registers
itself with the engine manager via this method. }
function bemGetCmdHandler(aInx : Longint) : TffBaseCommandHandler; virtual;
{-Returns a specified command handler registered with the engine
manager. }
function bemGetCmdHandlerCount : Longint;
{-Returns the number of command handlers routing requests to the engine
manager. }
procedure bemRemoveCmdHandler(aCmdHandler : TffBaseCommandHandler); virtual;
{-When a command handler no longer references an engine manager, it
unregisters itself with the engine manager via this method. }
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure Process(msg : PffDataMessage; var handled : boolean); virtual; abstract;
{ The command handler calls this method when it has a message that is
not handled by another engine. }
procedure Restart; virtual; abstract;
{ Use this method to stop and restart all engines and their associated
components. }
procedure Shutdown; virtual; abstract;
{ Use this method to stop all engines and their associated components.
Because the associated components (i.e., the manager's command handler)
are shutdown, the manager may not be instructed to restart. The manager
must be instructed to restart from the server GUI or the computer
must be restarted. }
procedure Startup; virtual; abstract;
{ Use this method to start all engines and their associated components. }
procedure Stop; virtual; abstract;
{ Use this method to stop all engines but leave their associated
components in an active state. This allows a Startup command to be
received from a remote client. }
public
property CmdHandler[aInx : Longint] : TffBaseCommandHandler
read bemGetCmdHandler;
property CmdHandlerCount : Longint read bemGetCmdHandlerCount;
end;
TffAddClientEvent = procedure(Listener : TffBaseTransport;
const userID : TffName;
const timeout : Longint;
const clientVersion : Longint;
var passwordHash : TffWord32;
var aClientID : TffClientID;
var errorCode : TffResult;
var isSecure : boolean;
var serverVersion : Longint) of object;
{ This is the type of event raised when a listening transport requires a
new clientID in order to establish a new client connection.
Inputs:
UserID - Provided by the client application and assumed to be the
login ID of an existing user.
Timeout - The timeout value associated with client-level operations.
ClientVersion - The client's version number. The server should use
this to determine if the client is compatible.
Outputs:
Passwordhash - The user's encrypted password, supplied by the event
handler. In situations where a secure connection is to be established,
this hash can be used to encrypt the outgoing communications.
aClientID - The unique identifier assigned to the client. The client
must supply this ID with each subsequent request sent to th server.
If the value zero is returned for this parameter then it is assumed
a failure occurred.
errorCode - If an error occurred then the error code is returned in
this parameter.
isSecure - If True then the server requires this connection to be
encrypted. If False then no encryption is required.
serverVersion - The server's version number. Gives the client the
opportunity to determine if any compatibility issues are present. }
TffConnectionLostEvent = procedure(Sender : TffBaseTransport;
aClientID : TffClientID) of object;
{ This is the type of event raised when a client connection is
unexpectedly terminated by the other end of the connection.
aClientID is the unique client identifier returned by
EstablishConnection. }
TffRemoveClientEvent = procedure(Listener : TffBaseTransport;
const aClientID : TffClientID;
var errorCode : TffResult) of object;
{ This is the type of event raised when a listening transport needs to
disconnect a client. AClientID is the unique client identifier returned
by TffAddClientEvent when the connection was initially established.
errorCode will be zero if the client was successfully removed or a non-zero
value if an error occurred. }
TffTransportMode = (fftmSend, fftmListen);
{ The valid modes for a transport. Values:
fftmSend - The transport sends messages.
fftmListen - The transport listens for messages. }
{ This is the base transport class. It includes support for sending and
receiving requests. A transport that receives requests is referred to as
a listener. A transport that sends requests is to as a sender.
To use a transport, you must do the following:
1. Instantiate the transport.
2. Set the ServerName property.
3. Set the State to ffesInitializing, ffesStarting, and then ffesStarted.
This normally occurs when a server engine starts up and sets the states
of the command handlers connected to the server. Each command handler
then passes on the state to the transports connected to the command
handler.
4. Obtain a clientID by calling the EstablishConnection method.
5. Submit requests to the transport using either the Post or Request
methods. You cannot call Post or Request unless you have a valid
clientID.
6. When you have finished using the transport, call
TerminateConnection for each established connection.
7. After terminating the connections, set the State to ffesShuttingDown
and then ffesInactive. }
TffBaseTransport = class(TffStateComponent)
protected {private}
{ We need a scheme in the class to store potential properties, and
then apply them. To do this we add BeginUpdate, and EndUpdate methods
to the class. When BeginUpdate is called the _* fields will be set to
match their associated fields. While updating, property set methods
store their values in _* Fields. When EndUpdate is called the _*
values are copied into their associated fields. BeginUpdate, and
EndUpdate are reference counted. IOW if BeginUpdate is called twice,
then EndUpdate must also be called twice.}
FCmdHandler : TffBaseCommandHandler;
_FCmdHandler : TffBaseCommandHandler;
{-The command handler to which requests are routed. }
FEnabled : boolean;
_FEnabled : boolean;
{-If True then the transport can send/receive messages. Note that
it will send/receive only if enabled and state = ffesStarted. }
_FLogEnabled : Boolean;
{-If True then event logging is enabled. Defaults to False. }
FLogOptions : TffTransportLogOptions;
_FLogOptions : TffTransportLogOptions;
{-The type of logging to be performed. }
FMode : TffTransportMode;
_FMode : TffTransportMode;
{-The current mode of the transport. }
FMsgCount : Longint;
{-The number of messages processed by this transport. }
FOnAddClient : TffAddClientEvent;
{-Event handler to call when need to establish a new client. }
FOnConnectionLost : TffConnectionLostEvent;
{-Handler for OnConnectionLost. }
FOnRemoveClient : TffRemoveClientEvent;
{-Event handler to call when need to remove an existing client. }
_FOnStateChange : TNotifyEvent;
{-Event handler to call when the transport's state has changed. }
FRespondToBroadcasts : boolean;
_FRespondToBroadcasts : Boolean;
{-If True and FListen := True then this transport will respond to
broadcasts for active listeners. }
FServerName : TffNetAddress;
_FServerName : TffNetAddress;
{-The name of the server to which this transport connects. }
FServerNameRequired : boolean;
{-This variable influences the btCheckServerName method.
If set to True then a servername is required. There may be some
transports where a servername is not required (e.g., Single User
Protocol in TffLegacyTransport) in which case those transports should
set this variable to False. }
_FState : TffState;
{-The state of the transport. }
FUpdateCount : Integer; { Update ReferenceCount field }
protected
{ Property access methods }
function btGetCmdHandler : TffBaseCommandHandler; virtual;
procedure btSetCmdHandler(aCmdHandler : TffBaseCommandHandler); virtual;
{-The command handler forwarding commands to this plugin command
handler. }
function btGetEnabled : boolean; virtual;
procedure btSetEnabled(const aEnabled : boolean); virtual;
{-Whether or not the transport is enabled. }
function btGetLogOptions : TffTransportLogOptions; virtual;
procedure btSetLogOptions(const anOptions : TffTransportLogOptions); virtual;
{-The type of information to be logged. }
function btGetMode : TffTransportMode; virtual;
procedure btSetMode(const aMode : TffTransportMode); virtual;
{-Whether or not the transport is to listen for requests. For a Client
set Mode to fftmSend. For a Server, set Mode to fftmListen. }
procedure btSetOnStateChange(const aHandler : TNotifyEvent); virtual;
{-Event raised when transport's state changes. }
function btGetRespondToBroadcasts : Boolean; virtual;
procedure btSetRespondToBroadcasts(const aRespond : Boolean); virtual;
{-Whether or not a transport in server mode (i.e., Listen = True) is
to respond to broadcast messages. }
function btGetServerName : string; virtual; {!!.10}
procedure btSetServername(const aServername : string); virtual; {!!.10}
{-For a transport in Listen mode (i.e., Server), the server's name. For
a transport in Send mode (i.e., Client), the name of the server to
which the client is to send information. The implementation for this
class does not perform any validation. Transport subclasses should
perform their own validation. }
{ Other protected methods }
procedure btCheckListener;
{ When setting certain properties or calling certain methods, this
method is called to ensure the transport is in listening mode. If the
transport is not listening then this method raises exception
ffsce_MustBeListening. }
procedure btCheckSender;
{ When setting certain properties or calling certain methods, this
method is called to ensure the transport is in sending mode. If the
transport is not a sender then this method raises exception
ffsce_MustBeSender. }
procedure btCheckServerName;
{ Verifies the servername has been specified. }
function btGetConnectionID(const anIndex : Longint) : TffClientID; virtual; abstract;
{ Used to obtain the IDs of the protocol's connections. Handy for when
a server wants to send messages to one or more client connections. }
procedure btInternalReply(msgID : Longint;
errorCode : TffResult;
replyData : pointer;
replyDataLen : Longint); virtual;
{ This method is called from TffBaseTransport.Reply. It must send the
reply to the client. The base implementation verifies the transport
is started and is listening. }
procedure btStoreSelfInThreadvar; virtual;
{-This method stores Self in ffitvTransport. This is isolated into
its own function because an inherited class may need to Reply to
a message (e.g., add client) before calling the inherited Process
method where the setting of ffitvTransport is normally done. }
procedure btBeginUpdatePrim; virtual;
procedure btEndUpdatePrim; virtual;
procedure lcSetLogEnabled(const aEnabled : boolean); override;
property UpdateCount : Integer
read FUpdateCount;
{-This represents the current updating state. If updating is taking
place this value will be > 0 }
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure BeginUpdate;
{ redirect property set routines to _* fields }
procedure CancelUpdate;
{ cancel the property changes. }
procedure EndUpdate;
{ Apply the new properties. }
procedure AutoConnectionLost(Sender : TffBaseTransport;
aClientID : TffClientID);
function ConnectionCount : Longint; virtual; abstract;
{ Returns the number of established connections. For a sender (i.e.,
client), this will be the number of connections to the remote server.
For a listener (i.e., server), this will be the number of
connections establshed by remote clients. }
class function CurrentTransport : TffBaseTransport;
{ Returns the transport used by the current thread. In other words,
the transport pointed to by ffitvTransportID. }
function EstablishConnection(const aUserName : TffName;
aPasswordHash : integer;
timeOut : Longint;
var aClientID : TffClientID ) : TffResult; virtual; abstract;
{ Use this method to establish a connection with the server. If the
return code is DBIERR_NONE then aClientID will contain the clientID
supplied by the server. This clientID must be used in all subsequent
requests to the server. }
function GetName : string; virtual; abstract;
{ Retrieves the transport's name. Must be specified for each subclass.
Note that this is not a class function because we want the legacy
transport to be able to return a name based upon the selected protocol.
}
procedure GetServerNames(aList : TStrings; const timeout : Longint); virtual; abstract;
{ Returns the list of servers available via this transport. Timeout
is the number of milliseconds in which all responses must be
received. }
function IsConnected : boolean; virtual; abstract;
{ This method returns True if the transport is connected to a server. }
procedure FFNotificationEx(const AOp : Byte; AFrom : TffComponent; {!!.11}
const AData : TffWord32); override; {!!.11}
procedure Post(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
requestData : pointer;
requestDataLen : Longint;
timeout : Longint;
replyMode : TffReplyModeType); virtual; abstract;
{ Call this method in order to submit a request to the transport.
The request will be routed to the remote transport. This method
does not expect a reply and will return as soon as the request is
handed off. This method may be called when in Send or Listen mode.
Parameters are as follows:
@param transportID - For use by future protocols.
@param clientID - The ID of the client submitting the request. This
must be the clientID originally supplied by the server or it may be
zero for unsecured calls (e.g., initially asking for a connection
to the server).
@param msgID - The type of message being sent.
@param requestData - Pointer to a data buffer containing the message
data.
requestDataLen - The length of requestData.
timeout - The number of milliseconds in which the operation must
complete.
replyMode - Indicates whether or not the request should wait for the
request to be sent to the server.
}
procedure Process(Msg : PffDataMessage); virtual;
{ When in listening mode, this method is called when a message is
to be processed by the transport. }
class procedure Reply(msgID : Longint;
errorCode : TffResult;
replyData : pointer;
replyDataLen : Longint); virtual;
{ When acting as a listener, this method is called to send a reply back
to a client. The base implementation stores a pointer to Self in
the threadvar fftviTransportID. This allows the command handler to
call TffBaseTransport.Reply(...) without having to know which
transport told it to process the command.
Implementation:
fftviTransport.InternalReply(...)
}
procedure Request(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
timeout : Longint;
requestData : pointer;
requestDataLen : Longint;
replyCallback : TffReplyCallback;
replyCookie : Longint); virtual; abstract;
{ When the transport is in Send mode, call this method in order to
submit a request to the transport.
Parameters are as follows:
@param transportID - For use by future transports.
@param clientID - The ID of the client submitting the request. This
must be the clientID originally supplied by the server or it may be
zero for unsecured calls (e.g., initially asking for a connection
to the server).
@param msgID - The type of message being sent.
@param timeout - The number of milliseconds in which a reply must be
received from the server.
@param requestData - Pointer to a data buffer containing the message
data.
@param requestDataLen - The length of requestData.
@param replyCallback - The procedure to be called when the reply
has been received from the server.
@param replyCookie - Whatever the calling object wants it to be. This
parameter is supplied to the replyCallback.
}
procedure ResetMsgCount; virtual;
{ Resets the MsgCount property to zero. }
function Sleep(const timeOut : Longint) : boolean; virtual;
{ Use this function to have the client disconnect from the server but
leave the server-side resources intact so that the client may
reconnect at a later time. Returns True if the Sleep was successful or
False if the Sleep failed or is not supported.
Note that any activity on the client side will cause the connection to
be re-opened. }
function Supported : boolean; virtual;
{ Returns True if the transport is supported on this workstation
otherwise returns False. }
procedure TerminateConnection(const aClientID : TffClientID;
const timeout : Longint); virtual; abstract;
{ Use this method to terminate a connection with the server. aClientID
is the clientID originally returned in the call to
EstablishConnection. }
procedure Work; virtual; abstract;
{ Based upon the transport's mode, this method tells the transport to
perform some work:
1. When in sending mode, start sending requests and processing replies.
2. When in listening mode, start listening for requests and passing
requests off to the command handler.
}
property ConnectionIDs[const anIndex : Longint] : TffClientID
read btGetConnectionID;
{ Use this to access the client IDs of a listening transport. }
published
property CommandHandler : TffBaseCommandHandler
read btGetCmdHandler
write btSetCmdHandler;
{ The command handler to which requests are routed. }
property Enabled : boolean
read btGetEnabled
write btSetEnabled
default False;
{ Use this property to control whether or not the transport can send
or receive messages as per its Mode property. If this property is
set to True, the State property must still be set to ffesStarted
before the transport will actually send or receive messages. }
property EventLogOptions : TffTransportLogOptions
read btGetLogOptions
write btSetLogOptions
default []; {!!.01}
{ The type of logging to be performed. Applicable only when
EventLogEnabled = True and EventLog is assigned. }
property Mode : TffTransportMode
read btGetMode
write btSetMode
default fftmSend;
{ Use this property to determine whether the transport should be used for
sending requests or listening for requests. }
property MsgCount : Longint
read FMsgCount;
{ The number of messages processed by this transport. }
property OnAddClient : TffAddClientEvent
read FOnAddClient
write FOnAddClient;
{ The handler for the event raised when a listening transport must
establish a new connection. }
property OnConnectionLost : TffConnectionLostEvent
read FOnConnectionLost
write FOnConnectionLost;
{ This event is raised when the other end of the connection unexpectedly
hangs up on the transport. }
property OnRemoveClient : TffRemoveClientEvent
read FOnRemoveClient
write FOnRemoveClient;
{ The handler for the event raised when a listening transport must
disconnect an existing client. }
property OnStateChange : TNotifyEvent
read scOnStateChange
write btSetOnStateChange;
{ Raised when the transport's state changes. }
property RespondToBroadcasts : boolean
read btGetRespondToBroadcasts
write btSetRespondToBroadcasts
default False;
{ Use this property to indicate wheher or not a listener should respond
to a broadcast for active listeners. }
property ServerName : string {!!.10}
read btGetServerName
write btSetServerName;
{ The name and address of the server to be accessed by this transport. }
end;
{ This class provides support for protocols requiring a thread pool. }
TffThreadedTransport = class(TffBaseTransport)
protected {private}
FThreadPool : TffThreadPool;
{-The thread pool providing threads to this transport. }
FUnsentRequestQueue : TffThreadQueue;
{-When in Send mode and a client submits a request, the transport creates
a TffRequest object and places it in this queue.}
FWaitingForReplyList : TffThreadList;
{-When a request has been submitted to the server, the TffRequest
object is appended to this list. }
protected
procedure SetThreadPool(aPool : TffThreadPool); virtual;
{-Sets the thread pool to be used by this transport. }
procedure tpInternalRequest(aRequest : TffRequest;
timeout : Longint;
aCookie : HWND); virtual;
{-Internal method for sending a request. aRequest is the request to
send. timeout is the number of milliseconds the transport should wait
for a reply to the request. aCookie can be used as the transport sees
fit. }
procedure tpLogReq(aRequest : TffRequest;
const prefix : string); virtual;
{ Write a request to the event log. }
procedure tpLogReq2(const aPrefix : string;
const aRequestID : Longint;
const aClientID : TffClientID;
const aMsgID : Longint;
const aData : pointer;
const aDataLen : Longint;
const aTimeout : Longint);
{ Write a reply to the event log. Used by a transport in Listen mode. }
procedure tpLogReqMisc(const aMsg : string); virtual;
{ Write a request-related string to the event log. }
procedure tpLogReply(aRequest : TffRequest); virtual;
{ Write a reply to the event log. }
procedure tpLogReply2(const aRequestID : Longint;
const aClientID : TffClientID;
const aMsgID : Longint;
const aDataLen : Longint;
const anError : TffResult);
{ Write a reply to the event log. Used by a transport in Listen mode. }
public
constructor Create(aOwner : TComponent); override;
destructor Destroy; override;
procedure FFNotificationEx(const AOp : Byte; AFrom : TffComponent; {!!.11}
const AData : TffWord32); override; {!!.11}
{-Called when the thread pool we're referencing has been operated upon.
We need to catch the case where the thread pool has been removed
from the form. }
procedure Post(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
requestData : pointer;
requestDataLen : Longint;
timeout : Longint;
replyMode : TffReplyModeType); override;
{ This method is called when a request is to be sent but a reply is
not needed. This implementation does the following:
1. Creates a TffRequest instance.
2. Assigns the request data to the TffRequest instance.
3. Adds the TffRequest instance to the Unsent Request Queue.
4. Exits from this method since a reply is not needed. }
procedure Request(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
timeout : Longint;
requestData : pointer;
requestDataLen : Longint;
replyCallback : TffReplyCallback;
replyCookie : Longint); override;
{ This method is called when a proxy client submits a request to the
transport. This implementation does the following:
1. Creates a TffRequest instance.
2. Assigns the request data to the TffRequest instance.
3. Adds the TffRequest instance to the Unsent Request Queue.
4. Calls TffRequest.WaitForReply. At this point, the calling
thread is blocked until a reply is received or a timeout
occurs.
5. When TffRequest.WaitForReply returns, the reply is on the TffRequest
object. This method calls replyCallback, passing the message ID,
error code, reply data, length, and cookie.
6. The TffRequest instance is freed. Could also be recycled to
improve performance. In either case, the TffRequest instance
frees the memory occupied by the reply.
}
published
property ThreadPool : TffThreadPool read FThreadPool write SetThreadPool;
{ The thread pool providing worker threads for this protocol. }
end;
const
ffc_Data = 'Data';
ffc_ReqAborted = '*** Req %d aborted, Clnt %d, Err %d, Tmout %d';
ffc_ReqLogString = '%s: %d, Clnt %d, Msg %d, Len %d, Tmout %d';
ffc_ReplyLogString = 'Reply: %d, Clnt %d, Msg %d, Len %d, Err %d';
ffc_SendErr = 'Snd Err %d: %s, Req %d, Clnt %d, Msg %d, Len %d, Tmout %d';
ffcl_RequestLatencyAdjustment : Longint = 500;
{-The number of additional milliseconds to wait for a reply. }
implementation
{Begin !!.03}
uses
ffSrBase, {!!.13}
SysUtils;
{End !!.03}
{$I ffconst.inc}
{$I ffllscst.inc}
{ The following thread variable is an optimization for the TffBaseTransport.
A rule is that the thread that processes a request must be the
thread to send a reply back to the client. Since the reply is initiated
outside the transport, we don't want to pass a lot of information
about the connection.
Our solution is to store a pointer to the transport issuing the request
in a threadvar. This allows a command handler to call TffBaseTransport.Reply
without having to know the originating Transport. }
threadvar
ffitvTransportID : Longint; { Pointer to the transport that originally
passed the request to the command handler. }
{===TffBaseCommandHandler============================================}
constructor TffBaseCommandHandler.Create(aOwner : TComponent);
begin
inherited Create(aOwner);
FManager := nil;
FPlugins := TffThreadList.Create;
FTransports := TffThreadList.Create;
end;
{--------}
destructor TffBaseCommandHandler.Destroy;
begin
{ Make sure we have a clean shutdown. }
if scState <> ffesInactive then
scSetState(ffesInactive);
FFNotifyDependents(ffn_Destroy); {!!.11}
FPlugins.Free; {!!.11}
FTransports.Free; {!!.11}
if assigned(FManager) and (not FSkipInitial) then {!!.01}
FManager.bemRemoveCmdHandler(Self);
inherited Destroy;
end;
{--------}
procedure TffBaseCommandHandler.bchFreeMsg(msg : PffDataMessage);
begin
if Msg^.dmDataLen > 0 then
FFFreeMem(Msg^.dmData, Msg^.dmDataLen);
FFFreeMem(Msg, SizeOf(TffDataMessage));
end;
{--------}
function TffBaseCommandHandler.bchGetTransportCount: Integer;
begin
Result := FTransports.Count;
end;
{--------}
function TffBaseCommandHandler.bchGetTransport(aInx: Integer): TffBaseTransport;
begin
Result := TffBaseTransport(TffIntListItem(FTransports[aInx]).KeyAsInt);
end;
{--------}
procedure TffBaseCommandHandler.bchSetEngineManager(aManager : TffBaseEngineManager);
{-Used to set the manager to which messages may be routed. }
begin
if FSkipInitial then begin {BEGIN !!.01}
FManager := aManager;
Exit;
end; {END !!.01}
if assigned(FManager) then FManager.bemRemoveCmdHandler(Self);
if assigned(aManager) then aManager.bemAddCmdHandler(Self);
end;
{Begin !!.11}
{--------}
procedure TffBaseCommandHandler.FFAddDependent(ADependent : TffComponent);
var
aListItem : TffIntListItem;
begin
inherited;
if ADependent is TffBaseTransport then begin
aListItem := TffIntListItem.Create(Longint(ADependent));
with FTransports.BeginWrite do
try
Insert(aListItem);
finally
EndWrite;
end;
end
else if ADependent is TffBasePluginCommandHandler then begin
aListItem := TffIntListItem.Create(Longint(ADependent));
with FPlugins.BeginWrite do
try
Insert(aListItem);
finally
EndWrite;
end;
end;
end;
{--------}
procedure TffBaseCommandHandler.FFRemoveDependent(ADependent : TffComponent);
begin
inherited;
if ADependent is TffBaseTransport then
with FTransports.BeginWrite do
try
Delete(Longint(ADependent));
finally
EndWrite;
end
else if ADependent is TffBasePluginCommandHandler then
with FPlugins.BeginWrite do
try
Delete(Longint(ADependent));
finally
EndWrite;
end;
end;
{End !!.11}
{--------}
procedure TffBaseCommandHandler.Process(Msg : PffDataMessage);
var
aPlugin : TffBasePluginCommandHandler;
Handled : boolean;
anIndex : Longint;
begin
Handled := False;
{ See if a plugin recognizes the message. }
if assigned(FPlugins) then
with FPlugins.BeginRead do
try
for anIndex := 0 to pred(Count) do begin
aPlugin := TffBasePluginCommandHandler(TffIntListItem(Items[anIndex]).KeyAsInt);
aPlugin.Process(Msg, Handled);
if Handled then break;
end;
finally
EndRead;
end;
{ If no plugin recognizes the message and we have an engine manager
then see if the engine manager will handle the message. }
if not Handled and assigned(FManager) then
FManager.Process(Msg, Handled);
{ If the message has not been handled by this point, tell the client this
is an unrecognized message. Note that we are calling a TffBaseTransport
class function which gets the reply to the correct transport. }
{Begin !!.13}
if not Handled then begin
lcLog(Format(ffStrResServer[ffErrUnknownMsg], [Msg.dmMsg]));
TffBaseTransport.Reply(Msg.dmMsg, ffErrUnknownMsg, nil, 0);
end;
{End !!.13}
end;
{--------}
procedure TffBaseCommandHandler.scSetState(const aState : TffState);
var
aTransport : TffBaseTransport;
anIndex : Longint;
NextState : TffState;
OldState : TffState;
begin
if (aState = scState) or {!!.01}
(aState in [ffesStopping, ffesStopped]) then exit; {!!.01}
OldState := scState;
aTransport := nil;
try
if assigned(FTransports) then
with FTransports.BeginRead do
try
while scState <> aState do begin
{ Based upon our current state & the target state, get the next state. }
NextState := ffStateDiagram[scState, aState];
{ Move all transports to the specified state. }
try
for anIndex := pred(Count) downto 0 do begin
aTransport := TffBaseTransport(TffIntListItem(Items[anIndex]).KeyAsInt);
if aTransport.Enabled then
aTransport.scSetState(NextState);
end;
except
on E:Exception do begin
{ If a transport raises an exception, disable the transport.
The server must be restarted before we try this transport
again. }
lcLog(format('Transport state failure: %s',
[aTransport.GetName, E.message]));
try
aTransport.State := ffesFailed;
aTransport.Enabled := False;
except
{ Eat any exception raised by changing the state. }
end;
end;
end;
scState := NextState;
{ Call the appropriate internal method for this state. }
case NextState of
ffesInactive :
scShutdown;
ffesInitializing :
scInitialize;
ffesStarting :
scStartup;
ffesShuttingDown :
scPrepareForShutdown;
end; { case }
if assigned(scOnStateChange) then
scOnStateChange(Self);
end; { while }
finally
EndRead;
end;
except
scState := OldState;
raise;
end;
end;
{====================================================================}
{===TffBasePluginCommandHandler======================================}
constructor TffBasePluginCommandHandler.Create(aOwner : TComponent);
begin
inherited Create(aOwner);
FCmdHandler := nil;
FPluginEngine := nil;
end;
{--------}
destructor TffBasePluginCommandHandler.Destroy;
begin
if assigned(FCmdHandler) then
FCmdHandler.FFRemoveDependent(Self); {!!.11}
if assigned(FPluginEngine) then
FPluginEngine.FFRemoveDependent(Self); {!!.11}
inherited Destroy;
end;
{Begin !!.11}
{--------}
procedure TffBasePluginCommandHandler.FFNotificationEx
(const AOp : Byte; AFrom : TffComponent;
const AData : TffWord32);
begin
inherited;
if AOp in [ffn_Destroy, ffn_Remove] then begin
if AFrom = FCmdHandler then begin
FCmdHandler.FFRemoveDependent(Self);
FCmdHandler := nil;
end
else if AFrom = FPluginEngine then begin
FPluginEngine.FFRemoveDependent(Self);
FPluginEngine := nil;
end;
end;
end;
{--------}
procedure TffBasePluginCommandHandler.pchSetCmdHandler(aCmdHandler : TffBaseCommandHandler);
{-The command handler forwarding commands to this plugin command
handler. }
begin
if aCmdHandler <> FCmdHandler then begin
if assigned(FCmdHandler) then
FCmdHandler.FFRemoveDependent(Self);
if assigned(aCmdHandler) then
aCmdHandler.FFAddDependent(Self);
FCmdHandler := aCmdHandler;
end;
{Note: It is entirely possible for the plugin command handler to be active
and have its associated command handler set to nil. In such a case, the
plugin command handler never receives PrepareForShutdown and Shutdown
commands. }
end;
{--------}
procedure TffBasePluginCommandHandler.pchSetPluginEngine(anEngine : TffBasePluginEngine);
begin
if anEngine <> FPluginEngine then begin
if assigned(FPluginEngine) then
FPluginEngine.FFRemoveDependent(Self);
if assigned(anEngine) then
anEngine.FFAddDependent(Self);
FPluginEngine := anEngine;
end;
end;
{End !!.11}
{====================================================================}
{===TffBasePluginEngine==============================================}
constructor TffBasePluginEngine.Create(aOwner : TComponent);
begin
inherited Create(aOwner);
FPluginCmdHandlers := TffThreadList.Create;
end;
{--------}
destructor TffBasePluginEngine.Destroy;
{Begin !!.11}
begin
scSetState(ffesInactive);
FFNotifyDependents(ffn_Destroy);
FPluginCmdHandlers.Free;
inherited Destroy;
end;
{--------}
procedure TffBasePluginEngine.FFAddDependent(ADependent : TffComponent);
var
aListItem : TffIntListItem;
begin
inherited;
if ADependent is TffBasePluginCommandHandler then begin
aListItem := TffIntListItem.Create(Longint(ADependent));
with FPluginCmdHandlers.BeginWrite do
try
Insert(aListItem);
finally
EndWrite;
end;
end;
end;
{--------}
procedure TffBasePluginEngine.FFRemoveDependent(ADependent : TffComponent);
begin
inherited;
if ADependent is TffBasePluginCommandHandler then
with FPluginCmdHandlers.BeginWrite do
try
Delete(Longint(ADependent));
finally
EndWrite;
end;
end;
{End !!.11}
{--------}
procedure TffBasePluginEngine.scSetState(const aState : TffState);
{-Sets the state of the engine. This will also set the state of any
associated plugin command handlers. }
var
aCmdHandler : TffBasePluginCommandHandler;
anIndex : Longint;
NextState : TffState;
OldState : TffState;
begin
{ If we are at the specified state then exit without doing anything. }
if aState = scState then exit;
OldState := scState;
try
if assigned(FPluginCmdHandlers) then
with FPluginCmdHandlers.BeginRead do
try
while scState <> aState do begin
{ Based upon our current state & the target state, get the next state. }
NextState := ffStateDiagram[scState, aState];
{ Move all command handlers to that state. }
for anIndex := 0 to pred(FPluginCmdHandlers.Count) do begin
aCmdHandler := TffBasePluginCommandHandler(TffIntListItem(Items[anIndex]).KeyAsInt);
if not (aState in [ffesStopping, ffesStopped,
ffesUnsupported, ffesFailed]) then
aCmdHandler.scSetState(aState);
end;
{ Call the appropriate method for the new state. }
case NextState of
ffesInactive, ffesStopped :
scShutdown;
ffesInitializing :
scInitialize;
ffesStarting :
scStartup;
ffesStopping, ffesShuttingDown :
scPrepareForShutdown;
end; { case }
{ Update our state. }
scState := NextState;
if assigned(scOnStateChange) then
scOnStateChange(Self);
end;
finally
EndRead;
end;
except
{ Some kind of failure occurred. We need to rollback the engine to its
original state. We will leave the command handlers as is. }
scState := OldState;
raise;
end;
end;
{====================================================================}
{===TffBaseEngineManager=============================================}
constructor TffBaseEngineManager.Create(aOwner : TComponent);
begin
FCmdHandlers := TffThreadList.Create;
inherited Create(aOwner);
end;
{--------}
destructor TffBaseEngineManager.Destroy;
var
aCmdHandler : TffBaseCommandHandler;
anIndex : Longint;
begin
{ Note: The real engine manager must do a graceful shutdown of the server
engine. }
if assigned(FCmdHandlers) then
with FCmdHandlers.BeginWrite do
try
{ Make sure none of the plugin command handlers reference this engine. }
for anIndex := pred(Count) downto 0 do begin
aCmdHandler := TffBaseCommandHandler(TffIntListItem(Items[anIndex]).KeyAsInt);
aCmdHandler.bchSetEngineManager(nil);
end;
finally
EndWrite;
FCmdHandlers.Free;
end;
inherited Destroy;
end;
{--------}
procedure TffBaseEngineManager.bemAddCmdHandler(aCmdHandler : TffBaseCommandHandler);
var
aListItem : TffIntListItem;
begin
aListItem := TffIntListItem.Create(Longint(aCmdHandler));
with FCmdHandlers.BeginWrite do
try
Insert(aListItem);
aCmdHandler.FManager := Self;
finally
EndWrite;
end;
end;
{--------}
function TffBaseEngineManager.bemGetCmdHandler(aInx : Longint) : TffBaseCommandHandler;
begin
with FCmdHandlers.BeginRead do
try
Result := TffBaseCommandHandler(TffIntListItem(Items[aInx]).KeyAsInt);
finally
EndRead;
end;
end;
{--------}
function TffBaseEngineManager.bemGetCmdHandlerCount : Longint;
begin
Result := FCmdHandlers.Count;
end;
{--------}
procedure TffBaseEngineManager.bemRemoveCmdHandler(aCmdHandler : TffBaseCommandHandler);
begin
aCmdHandler.FManager := nil;
with FCmdHandlers.BeginWrite do
try
Delete(Longint(aCmdHandler));
finally
EndWrite;
end;
end;
{====================================================================}
{===TffBaseTransport=================================================}
procedure TffBaseTransport.AutoConnectionLost(Sender : TffBaseTransport;
aClientID : TffClientID);
begin
Sender.FFNotifyDependentsEx(ffn_ConnectionLost, aClientID);
end;
{--------}
constructor TffBaseTransport.Create(aOwner : TComponent);
begin
inherited Create(aOwner);
FCmdHandler := nil;
FEnabled := False;
FMode := fftmSend;
FRespondToBroadcasts := False;
FServerName := '';
FServerNameRequired := True;
scState := ffesInactive;
OnConnectionLost := AutoConnectionLost;
end;
{--------}
destructor TffBaseTransport.Destroy;
begin
FFNotifyDependents(ffn_Destroy);
if assigned(FCmdHandler) then
FCmdHandler.FFRemoveDependent(Self); {!!.11}
inherited Destroy;
end;
{--------}
procedure TffBaseTransport.BeginUpdate;
begin
if FUpdateCount = 0 then begin
{ Give the descendent classes a chance to set their stored properties }
btBeginUpdatePrim;
{ Set the _* fields to match their counterparts }
_FCmdHandler := FCmdHandler;
_FEnabled := FEnabled;
_FLogEnabled := FLogEnabled;
_FLogOptions := FLogOptions;
_FMode := FMode;
_FOnStateChange := scOnStateChange;
_FRespondToBroadcasts := FRespondToBroadcasts;
_FServerName := FServerName;
_FState := scState;
end;
Inc(FUpdateCount);
end;
{--------}
procedure TffBaseTransport.btBeginUpdatePrim;
begin
{ do nothing }
end;
{--------}
procedure TffBaseTransport.CancelUpdate;
begin
FUpdateCount := 0;
end;
{--------}
procedure TffBaseTransport.EndUpdate;
begin
if FUpdateCount <> 0 then begin
Dec(FUpdateCount);
if FUpdateCount = 0 then begin
{ Let the descendent classes do their work }
btEndUpdatePrim;
{ Update the fields with the new values in their _* counterparts }
{ We do not set the private field directly, since some processing may
need to be done by a properties write method. }
CommandHandler := _FCmdHandler;
{ Make sure State is set prior to Enabled property and other
state-dependent properties. }
State := _FState;
Enabled := _FEnabled;
EventLogEnabled := _FLogEnabled;
EventLogOptions := _FLogOptions;
Mode := _FMode;
OnStateChange := _FOnStateChange;
RespondToBroadcasts := _FRespondToBroadcasts;
ServerName := _FServerName;
end;
end;
end;
{--------}
procedure TffBaseTransport.btEndUpdatePrim;
begin
{ do nothing }
end;
{--------}
function TffBaseTransport.btGetCmdHandler : TffBaseCommandHandler;
begin
Result := FCmdHandler;
end;
{--------}
function TffBaseTransport.btGetEnabled : boolean;
begin
Result := FEnabled;
end;
{--------}
function TffBaseTransport.btGetLogOptions : TffTransportLogOptions;
begin
Result := FLogOptions;
end;
{--------}
function TffBaseTransport.btGetMode : TffTransportMode;
begin
Result := FMode;
end;
{--------}
function TffBaseTransport.btGetRespondToBroadcasts : Boolean;
begin
Result := FRespondToBroadcasts;
end;
{--------}
function TffBaseTransport.btGetServerName : string; {!!.10}
begin
Result := FServerName;
end;
{--------}
procedure TffBaseTransport.btSetCmdHandler(aCmdHandler : TffBaseCommandHandler);
begin
if (FUpdateCount > 0) then
_FCmdHandler := aCmdHandler
else begin
{Check to make sure the new property is different.}
if FCmdHandler = aCmdHandler then Exit;
if assigned(FCmdHandler) then
FCmdHandler.FFRemoveDependent(Self); {!!.11}
if assigned(aCmdHandler) then
aCmdHandler.FFAddDependent(Self); {!!.11}
FCmdHandler := aCmdHandler; {!!.11}
end;
end;
{--------}
procedure TffBaseTransport.btSetEnabled(const aEnabled : Boolean);
begin
if (FUpdateCount > 0) then
_FEnabled := aEnabled
else begin
{Check to make sure the new property is different.}
if FEnabled = aEnabled then Exit;
{ If the transport is being disabled but the State indicates some
amount of activity then make sure the transport is inactive. }
if (not aEnabled) and (scState <> ffesInactive) then begin
FFNotifyDependents(ffn_Deactivate);
scSetState(ffesInactive);
end;
FEnabled := aEnabled;
end;
end;
{--------}
procedure TffBaseTransport.btSetLogOptions(const anOptions : TffTransportLogOptions);
begin
if (UpdateCount > 0) then
_FLogOptions := anOptions
else
FLogOptions := anOptions;
end;
{--------}
procedure TffBaseTransport.btSetMode(const aMode : TffTransportMode);
begin
if (FUpdateCount > 0) then
_FMode := aMode
else begin
{Check to make sure the new property is different.}
if FMode = aMode then Exit;
scCheckInactive;
FMode := aMode;
end;
end;
{--------}
procedure TffBaseTransport.btSetOnStateChange(const aHandler : TNotifyEvent);
begin
if (FUpdateCount > 0) then
_FOnStateChange := aHandler
else
scOnStateChange := aHandler;
end;
{--------}
procedure TffBaseTransport.btSetRespondToBroadcasts(const aRespond : Boolean);
begin
if (FUpdateCount > 0) then
_FRespondToBroadcasts := aRespond
else
FRespondToBroadcasts := aRespond;
end;
{--------}
procedure TffBaseTransport.btSetServername(const aServername : string); {!!.10}
begin
if (FUpdateCount > 0) then
_FServerName := aServerName
else begin
{Check to make sure the new property is different.}
if FServerName = aServername then Exit;
scCheckInactive;
FServerName := aServerName;
end;
end;
{--------}
procedure TffBaseTransport.btCheckListener;
begin
if FMode = fftmSend then
RaiseSCErrorCode(ffsce_MustBeListener);
end;
{--------}
procedure TffBaseTransport.btCheckSender;
begin
if FMode = fftmListen then
RaiseSCErrorCode(ffsce_MustBeSender);
end;
{--------}
procedure TffBaseTransport.btCheckServerName;
begin
if FServerNameRequired and (FServerName = '') then
RaiseSCErrorCode(ffsce_MustHaveServerName);
end;
{--------}
procedure TffBaseTransport.btInternalReply(msgID : Longint;
errorCode : TffResult;
replyData : pointer;
replyDataLen : Longint);
begin
scCheckStarted;
end;
{--------}
procedure TffBaseTransport.lcSetLogEnabled(const aEnabled : Boolean);
begin
if (UpdateCount > 0) then
_FLogEnabled := aEnabled
else
FLogEnabled := aEnabled;
end;
{--------}
procedure TffBaseTransport.Process(Msg : PffDataMessage);
begin
btStoreSelfInThreadvar;
{ If we have a command handler, tell the command handler to process the
message. }
if assigned(FCmdHandler) then begin
{ Increment the message count. Note: This happens whether or not the
message was handled by a command handler, plugin command handler, or
server engine. }
InterlockedIncrement(FMsgCount);
FCmdHandler.Process(Msg);
end;
end;
{--------}
class function TffBaseTransport.CurrentTransport : TffBaseTransport;
begin
Result := TffBaseTransport(ffitvTransportID);
end;
{--------}
{Rewritten !!.11}
procedure TffBaseTransport.FFNotificationEx(const AOp : Byte; AFrom : TffComponent;
const AData : TffWord32);
begin
inherited;
if AOp in [ffn_Destroy, ffn_Remove] then
if (AFrom = FCmdHandler) then begin
FCmdHandler.FFRemoveDependent(Self);
FCmdHandler := nil
end
else if (AFrom = FEventLog) then begin
FEventLog.FFRemoveDependent(Self);
FEventLog := nil;
end;
end;
{--------}
class procedure TffBaseTransport.Reply(msgID : Longint;
errorCode : TffResult;
replyData : pointer;
replyDataLen : Longint);
begin
CurrentTransport.btInternalReply(msgID, errorCode,
replyData, replyDataLen);
end;
{--------}
procedure TffBaseTransport.ResetMsgCount;
begin
FMsgCount := 0;
end;
{--------}
function TffBaseTransport.Sleep(const timeOut : Longint) : boolean;
begin
Result := False;
end;
{--------}
function TffBaseTransport.Supported : boolean;
begin
Result := True;
end;
{--------}
procedure TffBaseTransport.btStoreSelfInThreadvar;
begin
{ Store a pointer to this instance so the command handler may quickly
find us and submit a reply. }
ffitvTransportID := Longint(Self);
end;
{====================================================================}
{===TffThreadedTransport=============================================}
constructor TffThreadedTransport.Create(aOwner : TComponent);
begin
inherited Create(aOwner);
FThreadPool := nil;
FUnsentRequestQueue := TffThreadQueue.Create;
FWaitingForReplyList := TffThreadList.Create;
end;
{--------}
destructor TffThreadedTransport.Destroy;
var
anIndex : Longint;
aRequest : TffRequest;
begin
FFNotifyDependents(ffn_Destroy);
if assigned(FThreadPool) then
FThreadPool.FFRemoveDependent(Self); {!!.11}
if assigned(FUnsentRequestQueue) then
with FUnsentRequestQueue.BeginWrite do
try
for anIndex := pred(Count) downto 0 do begin
aRequest := TffRequest(TffIntListItem(Items[anIndex]).KeyAsInt);
aRequest.Free;
end;
finally
EndWrite;
Free;
end;
if assigned(FWaitingForReplyList) then
with FWaitingForReplyList.BeginWrite do
try
for anIndex := pred(Count) downto 0 do begin
aRequest := TffRequest(TffIntListItem(Items[anIndex]).KeyAsInt);
aRequest.Free;
end;
finally
EndWrite;
Free;
end;
inherited Destroy;
end;
{--------}
{Rewritten !!.11}
procedure TffThreadedTransport.FFNotificationEx(const AOp : Byte; AFrom : TffComponent;
const AData : TffWord32);
begin
inherited;
if (AFrom = FThreadPool) and
(AOp in[ffn_Destroy, ffn_Remove]) then begin
FThreadPool.FFRemoveDependent(Self);
FThreadPool := nil;
end;
end;
{--------}
procedure TffThreadedTransport.SetThreadPool(aPool : TffThreadPool);
begin
if aPool <> FThreadPool then begin
if assigned(FThreadPool) then
FThreadPool.FFRemoveDependent(Self); {!!.11}
if Assigned(aPool) then begin
FThreadPool := aPool;
FThreadPool.FFAddDependent(Self); {!!.11}
end;
end;
end;
{--------}
procedure TffThreadedTransport.Post(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
requestData : pointer;
requestDataLen : Longint;
timeout : Longint;
replyMode : TffReplyModeType);
var
aRequest : TffRequest;
anItem : TffIntListItem;
begin
scCheckStarted;
aRequest := TffRequest.Create(clientID, msgID, requestData,
requestDataLen, timeout, replyMode);
anItem := TffIntListItem.Create(Longint(aRequest));
with FUnsentRequestQueue.BeginWrite do
try
Enqueue(anItem);
finally
EndWrite;
end;
if replyMode = ffrmNoReplyWaitUntilSent then begin
aRequest.WaitForReply(timeout);
if not aRequest.Aborted then
aRequest.Free
else
with aRequest do
tpLogReqMisc(format(ffc_ReqAborted,[Longint(aRequest), ClientID,
ErrorCode, Timeout]));
end;
end;
{--------}
procedure TffThreadedTransport.Request(transportID : Longint;
clientID : TffClientID;
msgID : Longint;
timeout : Longint;
requestData : pointer;
requestDataLen : Longint;
replyCallback : TffReplyCallback;
replyCookie : Longint);
var
aRequest : TffRequest;
begin
scCheckStarted;
aRequest := TffRequest.Create(clientID, msgID, requestData, requestDataLen,
timeout, ffrmReplyExpected);
tpInternalRequest(aRequest, timeout, 0);
if assigned(replyCallback) then
replyCallback(aRequest.ReplyMsgID, aRequest.ErrorCode,
aRequest.ReplyData, aRequest.ReplyDataLen,
replyCookie);
if not aRequest.Aborted then
aRequest.Free
else
with aRequest do
tpLogReqMisc(format(ffc_ReqAborted,[Longint(aRequest), ClientID,
ErrorCode, Timeout]));
end;
{--------}
procedure TffThreadedTransport.tpInternalRequest(aRequest : TffRequest;
timeout : Longint;
aCookie : HWND);
var
anItem : TffIntListItem;
begin
anItem := TffIntListItem.Create(Longint(aRequest));
with FUnsentRequestQueue.BeginWrite do
try
Enqueue(anItem);
finally
EndWrite;
end;
{ Wait for the reply. If a timeout occurs, assume the request object
will be freed by the transport thread at some point. Timeout exceptions
are raised to the calling object. }
if timeout = 0 then
aRequest.WaitForReply(timeout)
else
aRequest.WaitForReply(timeout + ffcl_RequestLatencyAdjustment);
end;
{--------}
procedure TffThreadedTransport.tpLogReq(aRequest : TffRequest;
const prefix : string);
begin
if FLogEnabled and (fftpLogRequests in FLogOptions) and
assigned(FEventLog) and assigned(aRequest) then
with aRequest do begin
FEventLog.WriteStringFmt(ffc_ReqLogString,
[prefix, Longint(aRequest), ClientID, MsgID,
RequestDataLen, Timeout]);
FEventLog.WriteBlock('Data', aRequest.RequestData,
aRequest.RequestDataLen);
end;
end;
{--------}
procedure TffThreadedTransport.tpLogReq2(const aPrefix : string;
const aRequestID : Longint;
const aClientID : TffClientID;
const aMsgID : Longint;
const aData : pointer;
const aDataLen : Longint;
const aTimeout : Longint);
begin
FEventLog.WriteStringFmt(ffc_ReqLogString,
[aPrefix, aRequestID, aClientID, aMsgID,
aDataLen, aTimeout]);
FEventLog.WriteBlock(ffc_Data, aData, aDataLen);
end;
{--------}
procedure TffThreadedTransport.tpLogReqMisc(const aMsg : string);
begin
if FLogEnabled and (fftpLogRequests in FLogOptions) and
assigned(FEventLog) then
FEventLog.WriteString(aMsg);
end;
{--------}
procedure TffThreadedTransport.tpLogReply(aRequest : TffRequest);
begin
if FLogEnabled and (fftpLogReplies in FLogOptions) and
assigned(FEventLog) and assigned(aRequest) then
with aRequest do begin
FEventLog.WriteStringFmt(ffc_ReplyLogString,
[Longint(aRequest), ClientID, ReplyMsgID,
ReplyDataLen, ErrorCode]);
FEventLog.WriteBlock(ffc_Data, ReplyData, ReplyDataLen);
end;
end;
{--------}
procedure TffThreadedTransport.tpLogReply2(const aRequestID : Longint;
const aClientID : TffClientID;
const aMsgID : Longint;
const aDataLen : Longint;
const anError : TffResult);
begin
{ Assumption: Calling routine will only call if it is legitimate to log
the data. We do it this way so that we avoid passing tons
of data on the stack. }
FEventLog.WriteStringFmt(ffc_ReplyLogString,
[aRequestID, aClientID, aMsgID, aDataLen, anError]);
end;
{====================================================================}
end.