
Purpose: REpresentation State Tranfer (REST) Types and Classes on Client side
- this unit is a part of the Open Source Synopse mORMot framework 2, licensed under a MPL/GPL/LGPL three license - see LICENSE.md
| Unit Name | Description | |
|---|---|---|
| mormot.core.base | Framework Core Shared Types and RTL-like Functions | |
| mormot.core.buffers | Framework Core Low-Level Memory Buffer Process | |
| mormot.core.data | Framework Core Low-Level Data Processing Functions | |
| mormot.core.datetime | Framework Core Low-Level Date and Time Support | |
| mormot.core.interfaces | Framework Core Low-Level Interface/SOLID Processing | |
| mormot.core.json | Framework Core Low-Level JSON Processing | |
| mormot.core.log | Framework Core Logging | |
| mormot.core.os | Framework Core Low-Level Wrappers to the Operating-System API | |
| mormot.core.perf | Framework Core Performance and Monitoring Classes | |
| mormot.core.rtti | Framework Core Low-Level Cross-Compiler RTTI Definitions | |
| mormot.core.text | Framework Core Low-Level Text Processing | |
| mormot.core.threads | Framework Core Multi-Threading Support | |
| mormot.core.unicode | Framework Core Low-Level Unicode UTF-8 UTF-16 Ansi Conversion | |
| mormot.core.variants | Framework Core Low-Level Variants / TDocVariant process | |
| mormot.crypt.core | Framework Core Cryptographic Process (Hashing and Cypher) | |
| mormot.crypt.secure | Framework Core Authentication and Security Features | |
| mormot.db.core | Database Framework Core Types and Classes | |
| mormot.lib.gssapi | Low-level access to the GssApi on Linux/POSIX | |
| mormot.lib.sspi | Low-level access to the SSPI/SChannel API for Win32/Win64 | |
| mormot.orm.base | Object-Relational-Mapping (ORM) Low-Level Process | |
| mormot.orm.client | ORM Types and Classes for the Client side | |
| mormot.orm.core | Object-Relational-Mapping (ORM) Main Types and Classes | |
| mormot.orm.rest | Object-Relational-Mapping (ORM) Abstract REST Implementation | |
| mormot.rest.core | REpresentation State Tranfer (REST) Core Types and Classes | |
| mormot.soa.client | Interface-based SOA Process Types and Classes for Client-Side | |
| mormot.soa.core | Interface-based SOA Process Core Types and Classes |
| Objects | Description | |
|---|---|---|
| TBlockingCallback | Asynchrounous callback to emulate a synchronous/blocking process | |
| TInterfacedCallback | TInterfacedObject class which will notify a REST server when it is released | |
| TRestClientAuthentication | Abstract class used to implement client-side authentication | |
| TRestClientAuthenticationDefault | MORMot secure RESTful authentication scheme | |
| TRestClientAuthenticationHttpAbstract | Abstract class for implementing HTTP authentication | |
| TRestClientAuthenticationHttpBasic | Authentication using HTTP Basic scheme | |
| TRestClientAuthenticationNone | MORMot weak RESTful authentication scheme | |
| TRestClientAuthenticationSignedUri | Secure authentication scheme using URL-level digital signature | |
| TRestClientAuthenticationSspi | Authentication of the current logged user using Windows Security Support Provider Interface (SSPI) or GSSAPI library on Linux | |
| TRestClientAuthenticationUri | Weak authentication scheme using URL-level parameter | |
| TRestClientCallbackItem | Store information about registered interface callbacks | |
| TRestClientCallbacks | Store the references to active interface callbacks on a REST Client | |
| TRestClientLibraryRequest | REST client with direct access to a server logic through a .dll/.so library | |
| TRestClientRouting | Abstract Client side service routing | |
| TRestClientRoutingJsonRpc | Client calling context using simple REST for interface-based services | |
| TRestClientRoutingRest | Client calling context using simple REST for interface-based services | |
| TRestClientSession | Store the information about the current session | |
| TRestClientUri | Abstract REpresentational State Transfer (REST) client with URI |
TRestClientAuthentication = class(TObject)
Abstract class used to implement client-side authentication
- inherit from this class to implement expected authentication scheme
class function ClientSetUser(Sender: TRestClientUri; const aUserName, aPassword: RawUtf8; aPasswordKind: TRestClientSetUserPassword = passClear; const aHashSalt: RawUtf8 = ''; aHashRound: integer = 20000): boolean; virtual;
Class method to be used on client side to create a remote session
- call this method instead of TRestClientUri.SetUser() if you need a custom authentication class
- if saoUserByLogonOrID is defined in the server Options, aUserName may be a TAuthUser.ID and not a TAuthUser.LogonName
- if passClear is used, you may specify aHashSalt and aHashRound, to enable Pbkdf2HmacSha256() use instead of plain Sha256(), and increase security on storage side (reducing brute force attack via rainbow tables)
- will call the ModelRoot/Auth service, i.e. call TRestServer.Auth() published method to create a session for this user
- returns true on success
class procedure ClientSessionSign(Sender: TRestClientUri; var Call: TRestUriParams); virtual; abstract;
Class method to be called on client side to sign an URI
- used by TRestClientUri.Uri()
- shall match the method as expected by RetrieveSession() virtual method
TRestClientAuthenticationUri = class(TRestClientAuthentication)
Weak authentication scheme using URL-level parameter
class procedure ClientSessionSign(Sender: TRestClientUri; var Call: TRestUriParams); override;
Class method to be called on client side to add the SessionID to the URI
- append '&session_signature=SessionID' to the url
TRestClientAuthenticationSignedUri = class(TRestClientAuthenticationUri)
Secure authentication scheme using URL-level digital signature
- default suaCRC32 format of session_signature is
Hexa8(SessionID)+ Hexa8(Timestamp)+ Hexa8(crc32(SessionID + HexaSessionPrivateKey + Sha256('salt' + PassWord) + Hexa8(Timestamp) + url))
class function GetComputeSignature( algo: TRestAuthenticationSignedUriAlgo): TOnRestAuthenticationSignedUriComputeSignature;
Retrieve the method to compute the session_signature=.... value
class procedure ClientSessionSign(Sender: TRestClientUri; var Call: TRestUriParams); override;
Class method to be called on client side to sign an URI
- generate the digital signature as expected by overridden RetrieveSession()
- timestamp resolution is about 256 ms in the current implementation
TRestClientAuthenticationDefault = class(TRestClientAuthenticationSignedUri)
MORMot secure RESTful authentication scheme
- match TRestServerAuthenticationDefault class on server side
- this scheme will use a password stored via safe SHA-256 hashing in the TAuthUser ORM table
TRestClientAuthenticationNone = class(TRestClientAuthenticationUri)
MORMot weak RESTful authentication scheme
- this method will authenticate with a given username, but no signature
- match TRestServerAuthenticationNone class on server side
- on client side, this scheme is not called by TRestClientUri.SetUser() method - so you have to write:
TRestClientAuthenticationNone.ClientSetUser(Client,'User','');
TRestClientAuthenticationHttpAbstract = class(TRestClientAuthentication)
Abstract class for implementing HTTP authentication
- do not use this abstract class, but e.g. TRestClientAuthenticationHttpBasic
- this class will transmit the session_signature as HTTP cookie, not at URI level, so is expected to be used only from browsers or old clients
class function ClientSetUser(Sender: TRestClientUri; const aUserName, aPassword: RawUtf8; aPasswordKind: TRestClientSetUserPassword = passClear; const aHashSalt: RawUtf8 = ''; aHashRound: integer = 20000): boolean; override;
Class method to be used on client side to create a remote session
- call TRestClientAuthenticationHttpBasic.ClientSetUser() instead of TRestClientUri.SetUser(), and never the method of this abstract class
- needs the plain aPassword, so aPasswordKind should be passClear
- returns true on success
class procedure ClientSessionSign(Sender: TRestClientUri; var Call: TRestUriParams); override;
Class method to be called on client side to sign an URI in Auth Basic resolution is about 256 ms in the current implementation
- set "Cookie: mORMot_session_signature=..." HTTP header
class procedure ClientSetUserHttpOnly(Sender: TRestClientUri; const aUserName, aPasswordClear: RawUtf8); virtual;
Class method to be used on client side to force the HTTP header for the corresponding HTTP authentication, without creating any remote session
- call virtual protected method ComputeAuthenticateHeader()
- here the password should be given as clear content
- potential use case is to use a mORMot client through a HTTPS proxy, e.g. with TRestClientAuthenticationHttpBasic authentication
- then you can use TRestClientAuthentication*.ClientSetUser() to define any another "mORMot only" authentication
- this method is also called by the ClientSetUser() method of this class for a full client + server authentication via HTTP TRestClientAuthenticationHttp*.ClientSetUser()
TRestClientAuthenticationHttpBasic = class(TRestClientAuthenticationHttpAbstract)
Authentication using HTTP Basic scheme
- this protocol send both name and password as clear (just Base64 encoded) so should only be used over TLS / HTTPS, or for compatibility reasons
- match TRestServerAuthenticationHttpBasic class on server side
- will rely on TRestClientAuthenticationNone for authorization
- on client side, this scheme is not called by TRestClientUri.SetUser() method - so you have to write:
TRestClientAuthenticationHttpBasic.ClientSetUser(Client,'User','password');
- for a remote proxy-only authentication (without creating any mORMot session), you can write:
TRestClientAuthenticationHttpBasic.ClientSetUserHttpOnly(Client,'proxyUser','proxyPass');
TRestClientAuthenticationSspi = class(TRestClientAuthenticationSignedUri)
Authentication of the current logged user using Windows Security Support Provider Interface (SSPI) or GSSAPI library on Linux
- is able to authenticate the currently logged user on the client side, using either NTLM (Windows only) or Kerberos - it will allow to safely authenticate on a mORMot server without prompting the user to enter its password
- match TRestServerAuthenticationSspi class on server side
- if ClientSetUser() receives aUserName as '', aPassword should be either '' if you expect NTLM authentication to take place, or contain the SPN registration (e.g. 'mymormotservice/myserver.mydomain.tld') for Kerberos authentication
- if ClientSetUser() receives aUserName as 'DomainName\UserName', then authentication will take place on the specified domain, with aPassword as plain password value
TRestClientSession = record
Store the information about the current session
- as set after a successful TRestClientUri.SetUser() method
Authentication: TRestClientAuthenticationClass;
For internal use
HeartbeatSeconds: integer;
Frequency of Callback/_ping_ calls to maintain session and services
- will be used to call SessionRenewEvent at the specified period, so that the session and all sicClientDriven instances will be maintained on the server side as long as the client connection stands
- equals half SessionServerTimeout or 25 minutes (if lower) by default - 25 minutes matches the default service timeout of 30 minutes
- you may set 0 to disable this SOA-level heartbeat feature
HttpHeader: RawUtf8;
Access to the low-level HTTP header used for authentication
- you can force here your own header, e.g. a JWT as authentication bearer or as in TRestClientAuthenticationHttpAbstract.ClientSetUserHttpOnlyUser
- used e.g. by TRestClientAuthenticationHttpBasic
ID: cardinal;
The current session ID as set after a successful SetUser() method call
- equals 0 (CONST_AUTHENTICATION_SESSION_NOT_STARTED) if the session is not started yet - i.e. if SetUser() call failed
- equals 1 (CONST_AUTHENTICATION_NOT_USED) if authentication mode is not enabled - i.e. after a fresh Create() without SetUser() call
Server: RawUtf8;
The remote server executable name, as retrieved after a SetUser() success
ServerTimeout: integer;
The remote server session tiemout in minutes, as retrieved after a SetUser() success
- will be used to set SessionHeartbeatSeconds default
User: TAuthUser;
The current user as set by SetUser() method
- contains nil if no User is currently authenticated
- once authenticated, a TAuthUser instance is set, with its ID, LogonName, DisplayName, PasswordHashHexa and GroupRights (filled with a TAuthGroup ID casted as a pointer) properties - you can retrieve any optional binary data associated with this user via RetrieveBlobFields()
Version: RawUtf8;
The remote server version, as retrieved after a SetUser() success
TRestClientRouting = class(TObject)
Abstract Client side service routing
- match TRestServerUriContext reciprocal class
- never use this abstract class, but rather TRestClientRoutingRest or TRestClientRoutingJsonRpc classes
class function Supports: TRestClientSideInvoke; virtual;
Could be overriden to notify advances routing features
- default returns [] but TRestClientRoutingRest includes csiAsOctetStream
class procedure ClientSideInvoke(var uri: RawUtf8; ctxt: TRestClientSideInvoke; const method, params, clientDrivenID: RawUtf8; out sent, head: RawUtf8); virtual; abstract;
At Client Side, compute URI and BODY according to the routing scheme
- abstract implementation which is to be overridden
- as input, method should be the method name to be executed, params should contain the incoming parameters as JSON CSV (without []), and clientDriven ID should contain the optional Client ID value
- at output, should update the HTTP uri corresponding to the proper routing, and should return the corresponding HTTP body/headers within sent/head parameters
class procedure ClientSideInvoked(const uri: RawUtf8; ctxt: TRestClientSideInvoke; const method, params, clientDrivenID: RawUtf8; var resp, head: RawUtf8; var status: integer); virtual;
At Client Side, adjust STATUS and BODY according to the routing scheme
- this method does nothing by default
- could be used to adapt to custom BODY output, e.g. on error handling
TRestClientRoutingRest = class(TRestClientRouting)
Client calling context using simple REST for interface-based services
- match TRestServerRoutingRest reciprocal class
class function Supports: TRestClientSideInvoke; override;
Overriden to include csiAsOctetStream
class procedure ClientSideInvoke(var uri: RawUtf8; ctxt: TRestClientSideInvoke; const method, params, clientDrivenID: RawUtf8; out sent, head: RawUtf8); override;
At Client Side, compute URI and BODY according to RESTful routing scheme
- e.g. on input uri='root/Calculator', method='Add', params='1,2' and clientDrivenID='1234' -> on output uri='root/Calculator.Add/1234' and sent='[1,2]'
TRestClientRoutingJsonRpc = class(TRestClientRouting)
Client calling context using simple REST for interface-based services
- match TRestServerRoutingJsonRpc reciprocal class
class procedure ClientSideInvoke(var uri: RawUtf8; ctxt: TRestClientSideInvoke; const method, params, clientDrivenID: RawUtf8; out sent, head: RawUtf8); override;
At Client Side, compute URI and BODY according to JSON/RPC routing scheme
- e.g. on input uri='root/Calculator', method='Add', params='1,2' and clientDrivenID='1234' -> on output uri='root/Calculator' and sent={"method":"Add","params":[1,2],"id":1234}
TRestClientCallbackItem = record
Store information about registered interface callbacks
Factory: TInterfaceFactory;
/ information about the associated IInvokable
ID: TRestClientCallbackID;
The identifier of the callback, as sent to the server side
- computed from TRestClientUriCallbacks.fCurrentID counter
Instance: pointer;
Weak pointer typecast to the associated IInvokable variable
ReleasedFromServer: boolean;
Set to TRUE if the instance was released from the server
TRestClientCallbacks = class(TSynPersistent)
Store the references to active interface callbacks on a REST Client
Count: integer;
How many callbacks are registered
List: array of TRestClientCallbackItem;
List of registered interface callbacks
Owner: TRestClientUri;
The associated REST instance
constructor Create(aOwner: TRestClientUri); reintroduce;
Initialize the storage list
function DoRegister(aInstance: pointer; aFactory: TInterfaceFactory): TRestClientCallbackID; overload;
Register a callback event interface instance from a new computed ID
function FindAndRelease(aID: TRestClientCallbackID): boolean;
Find a matching entry
- will call FindIndex(aID) within Safe.Lock/Safe.Unlock
- returns TRUE if aID was found and aInstance/aFactory set, FALSE otherwise
function FindEntry(var aItem: TRestClientCallbackItem): boolean;
Find a matching callback
- will call FindIndex(aItem.ID) within Safe.Lock/Safe.Unlock
- returns TRUE if aItem.ID was found and aItem filled, FALSE otherwise
function FindIndex(aID: TRestClientCallbackID): PtrInt;
Find the index of the ID in the internal list
- warning: this method should be called within Safe.Lock/Safe.Unlock
function UnRegister(aInstance: pointer): boolean; overload;
Delete all callback events from the internal list, as specified by its instance
- note that the same IInvokable instance may be registered for several IDs
procedure DoRegister(aID: TRestClientCallbackID; aInstance: pointer; aFactory: TInterfaceFactory); overload;
Register a callback event interface instance from its supplied ID
TRestClientUri = class(TRest)
Abstract REpresentational State Transfer (REST) client with URI
- URI are standard Collection/Member implemented as ModelRoot/TableName/TableID
- handle RESTful commands GET POST PUT DELETE LOCK UNLOCK
- never call this abstract class, but inherit and override the InternalUri/InternalIsOpen/InternalOpen/InternalClose virtual abstract methods
- do NOT use this abstract class, but one of its fully implemented children
constructor Create(aModel: TOrmModel); override;
Initialize REST client instance
constructor RegisteredClassCreateFrom(aModel: TOrmModel; aDefinition: TSynConnectionDefinition; aServerHandleAuthentication: boolean); override;
Initialize REST client instance from a TSynConnectionDefinition
destructor Destroy; override;
Release memory and close client connection
- also unlock all still locked records by this client
function CallBack(method: TUriMethod; const aMethodName,aSentData: RawUtf8; out aResponse: RawUtf8; aTable: TOrmClass = nil; aID: TID = 0; aResponseHead: PRawUtf8 = nil): integer;
Wrapper to the protected URI method to call a method on the server, using a ModelRoot/[TableName/[ID/]]MethodName RESTful with any kind of request
- returns the HTTP error code (e.g. 200/HTTP_SUCCESS on success)
- for GET/PUT methods, you should better use CallBackGet/CallBackPut
function CallBackGet(const aMethodName: RawUtf8; const aNameValueParameters: array of const; out aResponse: RawUtf8; aTable: TOrmClass = nil; aID: TID = 0; aResponseHead: PRawUtf8 = nil): integer;
Wrapper to the protected URI method to call a method on the server, using a ModelRoot/[TableName/[ID/]]MethodName RESTful GET request
- returns the HTTP error code (e.g. 200/HTTP_SUCCESS on success)
- this version will use a GET with supplied parameters (which will be encoded with the URL), and append the expected signature (if needed)
function CallBackGetResult(const aMethodName: RawUtf8; const aNameValueParameters: array of const; aTable: TOrmClass = nil; aID: TID = 0): RawUtf8;
Wrapper to the protected URI method to call a method on the server, using a ModelRoot/[TableName/[ID/]]MethodName RESTful GET request
- returns the UTF-8 decoded JSON result (server must reply with one "result":"value" JSON object)
- this version will use a GET with supplied parameters (which will be encoded with the URL), and append the expected signature (if needed)
function CallBackPut(const aMethodName, aSentData: RawUtf8; out aResponse: RawUtf8; aTable: TOrmClass = nil; aID: TID = 0; aResponseHead: PRawUtf8 = nil): integer;
Wrapper to the protected URI method to call a method on the server, using a ModelRoot/[TableName/[ID/]]MethodName RESTful PUT request
- returns the HTTP error code (e.g. 200/HTTP_SUCCESS on success)
- this version will use a PUT with the supplied raw UTF-8 data
function FakeCallbackRegister(Sender: TServiceFactory; const Method: TInterfaceMethod; const ParamInfo: TInterfaceMethodArgument; ParamValue: pointer): TRestClientCallbackID; virtual;
Internal methods used by mormot.soa.client
function GetCurrentSessionUserID: TID; override;
Internal method to retrieve the current Session TAuthUser.ID
function IsOpen: boolean; virtual;
Check if connected to the server, or try to (re)create the connection
- convenient wrapper around InternalIsOpen and InternalOpen virtual methods
- return TRUE on success, FALSE on any connection error
- follows ConnectRetrySeconds property for optional retrial
- calls OnConnected/OnConnectionFailed events if set
function OrmInstance: TRestOrm;
Main access to the class implementing IRestOrm methods for this instance
- used internally to avoid ORM: IRestOrm reference counting and enable inlining of most simple methods, if possible
function Refresh(aID: TID; Value: TOrm; var Refreshed: boolean): boolean;
Backward compatibility redirections to the homonymous IRestOrmClient methods see IRestOrmClient documentation for the proper use information
function ServerRemoteLog(Level: TSynLogLevel; const FormatMsg: RawUtf8; const Args: array of const): boolean; overload;
Internal method able to emulate a call to TSynLog.Add.Log()
- will compute timestamp and event text, than call the overloaded ServerRemoteLog() method
function ServerRemoteLog(Sender: TEchoWriter; Level: TSynLogLevel; const Text: RawUtf8): boolean; overload; virtual;
Asynchronous call a 'RemoteLog' remote logging method on the server
- as implemented by mORMot's LogView tool in server mode
- to be used via ServerRemoteLogStart/ServerRemoteLogStop methods
- a dedicated background thread will run the transmission process without blocking the main program execution, gathering log rows in chunks in case of high activity
- map TOnTextWriterEcho signature, so that you will be able to set e.g.:
TSqlLog.Family.EchoCustom := aClient.ServerRemoteLog;
function ServerTimestampSynchronize: boolean;
You can call this method to call the remote URI root/Timestamp
- this can be an handy way of testing the connection, since this method is always available, even without authentication
- returns TRUE if the client time correction has been retrieved
- returns FALSE on any connection error - check LastErrorMessage and LastErrorException to find out the exact connection error
function ServiceContainer: TServiceContainer; override;
Access or initialize the internal IoC resolver, used for interface-based remote services, and more generaly any Services.Resolve() call
- create and initialize the internal TServiceContainerClient if no service interface has been registered yet
- may be used to inject some dependencies, which are not interface-based remote services, but internal IoC, without the ServiceRegister() or ServiceDefine() methods - e.g.
aRest.ServiceContainer.InjectResolver([TInfraRepoUserFactory.Create(aRest)],true);
function ServiceDefine(const aInterfaces: array of TGuid; aInstanceCreation: TServiceInstanceImplementation = sicSingle; const aContractExpected: RawUtf8 = ''): boolean; overload;
Register one or several Services on the client side via their interfaces
- this method expects the interface(s) to have been registered previously:
TInterfaceFactory.RegisterInterfaces([TypeInfo(IMyInterface),...]);
function ServiceDefine(const aInterface: TGuid; aInstanceCreation: TServiceInstanceImplementation = sicSingle; const aContractExpected: RawUtf8 = ''; aIgnoreAnyException: boolean = true): TServiceFactoryClient; overload;
Register a Service on the client side via its interface
- this method expects the interface to have been registered previously:
TInterfaceFactory.RegisterInterfaces([TypeInfo(IMyInterface),...]);
function ServiceDefineClientDriven(const aInterface: TGuid; out Obj; const aContractExpected: RawUtf8 = ''): boolean;
Register and retrieve the sicClientDriven Service instance
- this method expects the interface to have been registered previously:
TInterfaceFactory.RegisterInterfaces([TypeInfo(IMyInterface),...]);
function ServiceDefineSharedApi(const aInterface: TGuid; const aContractExpected: RawUtf8 = SERVICE_CONTRACT_NONE_EXPECTED; aIgnoreAnyException: boolean = false): TServiceFactoryClient;
Register a sicShared Service instance communicating via JSON objects
- will force SERVICE_CONTRACT_NONE_EXPECTED, ParamsAsJsonObject=true and ResultAsJsonObjectWithoutResult=true
- may be used e.g. for accessing a sessionless public REST/JSON API, i.e.
TRestServer.ServiceDefine(...).ResultAsJsonObjectWithoutResult := true
- this method expects the interface to have been registered previously:
TInterfaceFactory.RegisterInterfaces([TypeInfo(IMyInterface),...]);
- aIgnoreAnyException may be set to TRUE if the server is likely to not propose this service, and any exception is to be catched
function ServiceRegister(const aInterfaces: array of PRttiInfo; aInstanceCreation: TServiceInstanceImplementation = sicSingle; const aContractExpected: RawUtf8 = ''): boolean; overload; virtual;
Register one or several Services on the client side via their interfaces
- this methods expects a list of interfaces to be registered to the client (e.g. [TypeInfo(IMyInterface)])
- instance implementation pattern will be set by the appropriate parameter
- will return true on success, false if registration failed (e.g. if any of the supplied interfaces is not correct or is not available on the server)
- that is, server side will be called to check for the availability of each interface
- you can specify an optional custom contract for the first interface
function ServiceRegister(aInterface: PRttiInfo; aInstanceCreation: TServiceInstanceImplementation = sicSingle; const aContractExpected: RawUtf8 = ''; aIgnoreAnyException: boolean = true): TServiceFactory; overload;
Register a Service on the client side via its interface
- this methods expects one interface to be registered to the client, as
Client.ServiceRegister(TypeInfo(IMyInterface),sicShared);
- instance implementation pattern will be set by the appropriate parameter
- will return the corresponding fake class factory on success, nil if registration failed (e.g. if any of supplied interfaces is not correct or is not available on the server)
- that is, server side will be called to check for the availability of each interface
- you can specify an optional custom contract for the first interface
function ServiceRegisterClientDriven(aInterface: PRttiInfo; out Obj; const aContractExpected: RawUtf8 = ''): boolean; overload;
Register and retrieve the sicClientDriven Service instance
- will return TRUE on success, filling Obj output variable with the corresponding interface instance
- will return FALSE on error
function ServiceRetrieveAssociated(const aInterface: TGuid; out URI: TRestServerUriDynArray): boolean; overload;
Return all REST server URI associated to this client, for a given service
- here the service is specified as its TGuid, e.g. IMyInterface
- this method expects the interface to have been registered previously:
TInterfaceFactory.RegisterInterfaces([TypeInfo(IMyInterface),...]);
- the URI[] output array contains the matching server URIs, the latest registered in first position
- this methods is the reverse from ServicePublishOwnInterfaces: it allows to guess an associated REST server which may implement a given service
function ServiceRetrieveAssociated(const aServiceName: RawUtf8; out URI: TRestServerUriDynArray): boolean; overload;
Return all REST server URI associated to this client, for a given service name, the latest registered in first position
- will lookup for the Interface name without the initial 'I', e.g. 'Calculator' for ICalculator - warning: research is case-sensitive
- this methods is the reverse from ServicePublishOwnInterfaces: it allows to guess an associated REST server which may implement a given service
function SetUser(const aUserName, aPassword: RawUtf8; aHashedPassword: boolean = false): boolean;
Authenticate an User to the current connected Server
- will call the ModelRoot/Auth service, i.e. call TRestServer.Auth() published method to create a session for this user, with our secure TRestClientAuthenticationDefault authentication scheme
- returns true on success
- calling this method is optional, depending on your user right policy: your Server need to handle authentication
- if saoUserByLogonOrID is defined in the server Options, aUserName may be a TAuthUser.ID integer value and not a TAuthUser.LogonName
- on success, the SessionUser property map the logged user session on the server side
- if aHashedPassword is TRUE, the aPassword parameter is expected to contain the already-hashed value, just as stored in PasswordHashHexa (i.e. Sha256('salt'+Value) as in TAuthUser.SetPasswordPlain method)
- if SSPIAUTH conditional is defined, and aUserName='', a Windows authentication will be performed via TRestClientAuthenticationSspi - in this case, aPassword will contain the SPN domain for Kerberos (otherwise NTLM will be used), and table TAuthUser shall contain an entry for the logged Windows user, with the LoginName in form 'DomainName\UserName'
- you can directly create the class method ClientSetUser() of a given TRestClientAuthentication inherited class, if neither TRestClientAuthenticationDefault nor TRestClientAuthenticationSspi match your need
function Uri(const url, method: RawUtf8; Resp: PRawUtf8 = nil; Head: PRawUtf8 = nil; SendData: PRawUtf8 = nil; OutInternalState: PCardinal = nil): integer;
Main method calling the remote Server via a RESTful command
- redirect to the InternalUri() abstract method, which should be overridden for local, pipe, HTTP/1.1 or WebSockets actual communication
- this method will sign the url with the appropriate digital signature according to the current SessionUser property
- this method will retry the connection in case of authentication failure (i.e. if the session was closed by the remote server, for any reason - mostly a time out) if the OnAuthentificationFailed event handler is set
procedure CallbackNonBlockingSetHeader(out Header: RawUtf8); virtual;
To be called before CallBack() if the client could ignore the answer
- do nothing by default, but overriden e.g. in TRestHttpClientWebsockets
procedure DefinitionTo(Definition: TSynConnectionDefinition); override;
Save the TRestClientUri properties into a persistent storage object
- CreateFrom() will expect Definition.UserName/Password to store the credentials which will be used by SetUser()
procedure ServerRemoteLogStart(aLogClass: TSynLogClass; aClientOwnedByFamily: boolean);
Start to send all logs to the server 'RemoteLog' method-based service
- will associate the EchoCustom callback of the running log class to the ServerRemoteLog() method
- if aClientOwnedByFamily is TRUE, this TRestClientUri instance lifetime will be managed by TSynLogFamily - which is mostly wished
- if aClientOwnedByFamily is FALSE, you should manage this instance life time, and may call ServerRemoteLogStop to stop remote logging
- warning: current implementation will disable all logging for this TRestClientUri instance, to avoid any potential concern (e.g. for multi-threaded process, or in case of communication error): you should therefore use this TRestClientUri connection only for the remote log server, e.g. via TRestHttpClientGeneric.CreateForRemoteLogging() - do not call ServerRemoteLogStart() from a high-level business client!
procedure ServerRemoteLogStop;
Stop sending all logs to the server 'RemoteLog' method-based service
- do nothing if aClientOwnedByFamily was TRUE for ServerRemoteLogStart
class procedure ServiceNotificationMethodExecute(var Msg: TMessage);
Event to be triggered when a WM_* message is received from the internal asynchronous notification system, to run the callback in the main UI thread
- WM_* message identifier should have been set e.g. via the associated
ServiceNotificationMethodViaMessages(Form.Handle, WM_USER)
- message will be sent for any interface-based service method callback which expects no result (i.e. no out parameter nor function result), so is safely handled as asynchronous notification
- is defines as a class procedure, since the underlying TRestClientUri instance has no impact here: a single WM_* handler is enough for several TRestClientUri instances
procedure ServiceNotificationMethodViaMessages(hWnd: HWND; Msg: cardinal);
Set a HWND/WM_* pair to let interface-based services notification callbacks be processed safely in the main UI thread, via Windows messages
- by default callbacks are executed in the transmission thread, e.g. the WebSockets client thread: using UI Synchronize() method may trigger some unexpected race conditions, e.g. when asynchronous notifications are received during a blocking REST command - this message-based mechanism will allow safe and easy notification for any UI client application
- the associated ServiceNotificationMethodExecute() method shall be called in the client HWND TForm for the defined WM_* message
procedure SessionClose;
Clear session and call the /auth service on the server to notify shutdown
- is called by Destroy and SetUser/ClientSetUser methods, so you should not have usually to call this method directly
procedure SetOrmInstance(aORM: TRestOrmParent); override;
Called by TRestOrm.Create overriden constructor to set fOrm from IRestOrm
property Client: IRestOrmClient read fClient;
Main access to the IRestOrmClient methods of this instance
property ComputeSignature: TOnRestAuthenticationSignedUriComputeSignature read fComputeSignature write fComputeSignature;
Customize the session_signature signing algorithm with a specific function
- will be used by TRestServerAuthenticationSignedUri classes, e.g. TRestServerAuthenticationDefault instead of the algorithm specified by the server at session handshake
property ConnectRetrySeconds: integer read fConnectRetrySeconds write fConnectRetrySeconds;
How many seconds the client may try to connect after open socket failure
- is disabled to 0 by default, but you may set some seconds here e.g. to let the server start properly, and let the client handle exceptions to wait and retry until the specified timeout is reached
property LastErrorCode: integer read fLastErrorCode;
Low-level error code, as returned by server
- check this value about HTTP_* constants
- HTTP_SUCCESS or HTTP_CREATED mean no error
- otherwise, check LastErrorMessage property for additional information
- this property value will record status codes returned by Uri() method
property LastErrorException: ExceptClass read fLastErrorException;
Low-level exception class, if any
- will record any Exception class raised within Uri() method
- contains nil if Uri() execution did not raise any exception (which is the most expected behavior, since server-side errors are trapped into LastErrorCode/LastErrorMessage properties
property LastErrorMessage: RawUtf8 read fLastErrorMessage;
Low-level error message, as returned by server
- this property value will record content returned by Uri() method in case of an error, or '' if LastErrorCode is HTTP_SUCCESS or HTTP_CREATED
property MaximumAuthentificationRetry: integer read fMaximumAuthentificationRetry write fMaximumAuthentificationRetry;
Maximum additional retry occurrence
- defaut is 1, i.e. will retry once
- set OnAuthentificationFailed to nil in order to avoid any retry
property OnAfterCall: TOnClientCall read fOnAfterCall write fOnAfterCall;
This Event is called just after the raw InternalUri() method
- the callback could return false to close the connection
property OnAuthentificationFailed: TOnAuthentificationFailed read fOnAuthentificationFailed write fOnAuthentificationFailed;
This Event is called in case of remote authentication failure
- client software can ask the user to enter a password and user name
- if no event is specified, the Uri() method will return directly an HTTP_FORBIDDEN "403 Forbidden" error code
property OnBeforeCall: TOnClientCall read fOnBeforeCall write fOnBeforeCall;
This Event is called just before the raw InternalUri() method
- the callback could return true to continue, or false to abort
property OnConnected: TOnClientNotify read fOnConnected write fOnConnected;
Called by IsOpen when the raw connection is (re)established
property OnConnectionFailed: TOnClientFailed read fOnConnectionFailed write fOnConnectionFailed;
Called by IsOpen when it failed to connect
property OnError: TOnClientCall read fOnError write fOnError;
This Event is called if Uri() was not successful
- the callback could return true to retry the call
property OnFailed: TOnClientFailed read fOnFailed write fOnFailed;
This Event is called if Uri() was not successful
- the callback will have all needed information
- e.g. Call^.OutStatus=HTTP_NOTIMPLEMENTED indicates a broken connection
property OnIdle: TOnIdleSynBackgroundThread read fOnIdle write fOnIdle;
Set a callback event to be executed in loop during remote blocking process, e.g. to refresh the UI during a somewhat long request
- if not set, the request will be executed in the current thread, so may block the User Interface
- you can assign a callback to this property, calling for instance Application.ProcessMessages, to execute the remote request in a background thread, but let the UI still be reactive: the TLoginForm.OnIdleProcess and OnIdleProcessForm methods of mORMotUILogin.pas will match this property expectations
property OnIdleBackgroundThreadActive: boolean read GetOnIdleBackgroundThreadActive;
TRUE if the background thread is active, and OnIdle event is called during process
- to be used e.g. to ensure no re-entrance from User Interface messages
property OnSetUser: TOnClientNotify read fOnSetUser write fOnSetUser;
This Event is called when a user is authenticated
- is called always, on each TRestClientUri.SetUser call
- you can check the Sender.SessionUser property pointing to the current authenticated user, or nil if authentication failed
- could be used to refresh the User Interface layout according to current authenticated user rights, or to subscribe to some services via callbacks
property RetryOnceOnTimeout: boolean read fRetryOnceOnTimeout write fRetryOnceOnTimeout;
If the client shall retry once after "408 REQUEST TIMEOUT" server error
- this is about an HTTP error 408 returned by the server, e.g. when the ORM lock or transaction could not be acquired in a good enough time: this value does not apply to the client side timeout, e.g. at HTTP level
property ServicePublishOwnInterfaces: RawUtf8 read fServicePublishOwnInterfaces write fServicePublishOwnInterfaces;
Allow to notify a server the services this client may be actually capable
- when this client will connect to a remote server to access its services, it will register its own services, supplying its TRestServer instance, and its corresponding public URI, within its '_contract_' internal call
- it will allow automatic service discovery of Peer To Peer Servers, without the need of an actual centralized SOA catalog service: any client could retrieve an associated REST server for a given service, via the ServiceRetrieveAssociated method
property ServiceRoutingSupports: TRestClientSideInvoke read fServiceRoutingSupports;
Direct copy of ServicesRouting.Supports flags
property ServicesRouting: TRestClientRoutingClass read fServicesRouting write SetRoutingClass;
The routing class of the service remote request on client side
- by default, contains TRestClientRoutingRest, i.e. an URI-based layout which is secure (since will use our RESTful authentication scheme), and also very fast
- but TRestClientRoutingJsonRpc can e.g. be set (with TRestServerRoutingJsonRpc on server sides), if the client will rather use JSON/RPC alternative pattern
- NEVER set the abstract TRestClientRouting class on this property
property Session: TRestClientSession read fSession;
The current session information as set by a successful SetUser() call
property SessionHeartbeatSeconds: integer read fSession.HeartbeatSeconds write SetSessionHeartbeatSeconds;
Frequency of Callback/_ping_ calls to maintain session and services
- will be used to call SessionRenewEvent at the specified period, so that the session and all sicClientDriven instances will be maintained on the server side as long as the client connection stands
- equals half SessionServerTimeout or 25 minutes (if lower) by default - 25 minutes matches the default service timeout of 30 minutes
- you may set 0 to disable this SOA-level heartbeat feature
property SessionHttpHeader: RawUtf8 read fSession.HttpHeader write fSession.HttpHeader;
Access to the low-level HTTP header used for authentication
- you can force here your own header, e.g. a JWT as authentication bearer or as in TRestClientAuthenticationHttpAbstract.ClientSetUserHttpOnlyUser
property SessionID: cardinal read fSession.ID;
The current session ID as set after a successful SetUser() method call
- equals 0 (CONST_AUTHENTICATION_SESSION_NOT_STARTED) if the session is not started yet - i.e. if SetUser() call failed
- equals 1 (CONST_AUTHENTICATION_NOT_USED) if authentication mode is not enabled - i.e. after a fresh Create() without SetUser() call
property SessionServer: RawUtf8 read fSession.Server;
The remote server executable name, as retrieved after a SetUser() success
property SessionServerTimeout: integer read fSession.ServerTimeout;
The remote server session tiemout in minutes, as retrieved after a SetUser() success
- will be used to set SessionHeartbeatSeconds default
property SessionUser: TAuthUser read fSession.User;
The current user as set by SetUser() method
- contains nil if no User is currently authenticated
property SessionVersion: RawUtf8 read fSession.Version;
The remote server version, as retrieved after a SetUser() success
TRestClientLibraryRequest = class(TRestClientUri)
REST client with direct access to a server logic through a .dll/.so library
- use only one TLibraryRequest function for the whole communication
- the data is stored in Global system memory, and freed by GlobalFree()
constructor Create(aModel: TOrmModel; const LibraryName: TFileName); reintroduce; overload;
Connect to a server contained in a shared library
- this .dll/.so must contain at least a LibraryRequest entry
- raise an exception if the shared library is not found or invalid
constructor Create(aModel: TOrmModel; aRequest: TLibraryRequest); reintroduce; overload;
Connect to a server from a remote function
destructor Destroy; override;
Release memory and handles
TInterfacedCallback = class(TInterfacedObjectLocked)
TInterfacedObject class which will notify a REST server when it is released
- could be used when implementing event callbacks as interfaces, so that the other side instance will be notified when it is destroyed
constructor Create(aRest: TRest; const aGuid: TGuid); reintroduce;
Initialize the instance for a given REST client and callback interface
destructor Destroy; override;
Finalize the instance, and notify the TRestServer that the callback is now unreachable
- i.e. will call CallbackRestUnregister
procedure CallbackRestUnregister; virtual;
Notify the associated TRestServer that the callback is disconnnected
- i.e. will call TRestServer's TServiceContainer.CallBackUnRegister()
- this method will process the unsubscription only once
property Rest: TRest read fRest;
The associated TRestServer instance, which will be notified when the callback is released
property RestInterface: TGuid read fInterface write fInterface;
The interface type, implemented by this callback class
TBlockingCallback = class(TInterfacedCallback)
Asynchrounous callback to emulate a synchronous/blocking process
- once created, process will block via a WaitFor call, which will be released when CallbackFinished() is called by the process background thread
constructor Create(aTimeOutMs: integer; aRest: TRest; const aGuid: TGuid); reintroduce;
Initialize the callback instance
- specify a time out milliseconds period after which blocking execution should be handled as failure (if 0 is set, default 3000 will be used)
- you can optionally set a REST and callback interface for automatic notification when this TInterfacedCallback will be released
destructor Destroy; override;
Finalize the callback instance
function Reset: boolean; virtual;
Just a wrapper to reset the internal Event state to evNone
- may be used to re-use the same TBlockingCallback instance, after a successful WaitFor/CallbackFinished process
- returns TRUE on success (i.e. status was not beWaiting)
- if there is a WaitFor currently in progress, returns FALSE
function WaitFor: TBlockingEvent; virtual;
Called to wait for the callback to be processed, or trigger timeout
- will block until CallbackFinished() is called by the processing thread
- returns the final state of the process, i.e. beRaised or beTimeOut
procedure CallbackFinished(aRestForLog: TRestOrm; aServerUnregister: boolean = false); virtual;
Should be called by the callback when the process is finished
- the caller will then let its WaitFor method return
- if aServerUnregister is TRUE, will also call CallbackRestUnregister to notify the server that the callback is no longer needed
- will optionally log all published properties values to the log class of the supplied REST instance
property Event: TBlockingEvent read GetEvent;
The current state of process
- just a wrapper around Process.Event
- use Reset method to re-use this instance after a WaitFor process
property Process: TBlockingProcess read fProcess;
The associated blocking process instance
PRestClientCallbackItem = ^TRestClientCallbackItem;
Points to information about registered interface callbacks
TOnAuthentificationFailed = function(Retry: integer; var aUserName, aPassword: string; out aPasswordHashed: boolean): boolean of object;
Used by TRestClientUri.Uri() to let the client ask for an User name and password, in order to retry authentication to the server
- should return TRUE if aUserName and aPassword both contain some entered values to be sent for remote secure authentication
- should return FALSE if the user pressed cancel or the number of Retry reached a defined limit
- here input/output parameters are defined as plain string, to match the type expected by the client's User Interface, via UI properties, or e.g. from TLoginForm as defined in mORMotUILogin.pas unit
TOnClientCall = function(Sender: TRestClientUri; var Call: TRestUriParams): boolean of object;
Called by TRestClientUri.Uri() to notify some step
- e.g. as OnError Event when an error Call.OutStatus was returned
- return false to abort and fail the request
- as OnError with 403, could return true to retry after changing e.g. Sender.SessionHttpHeader with a renewed JWT bearer
- as OnBeforeCall or OnAfterCall, should return true to continue
TOnClientFailed = procedure(Sender: TRestClientUri; E: Exception; Call: PRestUriParams) of object;
Called by TRestClientUri.Uri() when an error occurred
- so that you may have a single entry point for all client-side issues
- information will be available in Sender's LastErrorCode and LastErrorMessage properties
- if the error comes from an Exception, it will be supplied as parameter
- the REST context (if any) will be supplied within the Call parameter, and in this case Call^.OutStatus=HTTP_NOTIMPLEMENTED indicates a broken connection
TOnClientNotify = procedure(Sender: TRestClientUri) of object;
Signature e.g. of the TRestClientUri.OnSetUser event handler
TOnRestAuthenticationSignedUriComputeSignature = function( privatesalt: cardinal; timestamp, url: PAnsiChar; urllen: integer): cardinal of object;
Function prototype for TRestClientAuthenticationSignedUri and TRestServerAuthenticationSignedUri computation of the session_signature parameter value
TRestAuthenticationSignedUriAlgo = ( suaCRC32, suaCRC32C, suaXXHASH, suaMD5, suaSHA1, suaSHA256, suaSHA512, suaSHA3 );
Algorithms known by TRestClientAuthenticationSignedUri and TRestServerAuthenticationSignedUri to digitaly compute the session_signature parameter value for a given URI
- by default, suaCRC32 will compute fast but not cryptographically secure
crc32(crc32(privatesalt, timestamp, 8), url, urllen)
- suaCRC32C and suaXXHASH are similar non-cryptographic alternatives
- but you can select other stronger alternatives, which result will be reduced to 32-bit hexadecimal - suaMD5 will be the fastest cryptographic hash available on all platforms (but if SHA-NI is available), for enhanced security, by calling e.g.
(aServer.AuthenticationRegister(TRestClientAuthenticationDefault) as TRestServerAuthenticationDefault).Algorithm := suaMD5;
- suaSHA1, suaSHA256, suaSHA512 and suaSHA3 will be slower, and may provide additional level of trust, depending on your requirements: note that since the hash is reduced to 32-bit resolution, those may not provide higher security than suaMD5 or suaSHA1
- note that SynCrossPlatformRest clients only implements suaCRC32 yet
TRestClientAuthenticationClass = class of TRestClientAuthentication;
Class-reference type (metaclass) used to define an authentication scheme
TRestClientCallbackID = type integer;
31-bit positive identifier of an interface callback, as sent to the server
TRestClientRoutingClass = class of TRestClientRouting;
Class used to define the Client side expected routing
- match TRestServerUriContextClass reciprocal meta-class
- most of the internal methods are declared as virtual, so it allows any kind of custom routing or execution scheme
- TRestClientRoutingRest and TRestClientRoutingJsonRpc classes are provided in this unit, to allow RESTful and JSON/RPC protocols on Client side
- you can retrieve the client class from the reciprocal server-side class using TRestServerUriContext.ClientRouting class method
TRestClientSetUserPassword = ( passClear, passHashed, passKerberosSpn );
Define how TRestClientAuthentication.ClientSetUser() should interpret the supplied password
- passClear means that the password is not encrypted, e.g. as entered by the user in the login screen
- passHashed means that the passwod is already hashed as in TAuthUser.PasswordHashHexa i.e. Sha256('salt'+Value)
- passKerberosSpn indicates that the password is the Kerberos SPN domain
TRestClientSideInvoke = set of ( csiAsOctetStream);
/ used to customize TRestClientRouting.ClientSideInvoke process
TSqlRestClientUri = TRestClientUri;
Backward compatibility types redirections
TSqlRestServerAuthenticationClientSetUserPassword = TRestClientSetUserPassword;
Backward compatibility types redirections
TSqlRestServerUriContextClientInvoke = TRestClientSideInvoke;
Backward compatibility types redirections