You are not logged in.
In the code that I showed in the example, there are two events:
procedure TLogClientWebsockets.OnClientDisconnected(sender: TObject);
begin
// disconnect event handling is never called!!
end;
procedure TLogClientWebsockets.OnWebSocketsClosed(sender: TObject);
begin
// disconnect event handling is never called!!
end;None of these events is called, so I ask what I am doing wrong or how to do it correctly?
So there is no way to know from the client side (callback implementation side) that the connection has died?
My problem is protection against network errors and the possibility of resuming the connection. How to do it?
Based on example 31, I wrote a simple service for uploading logs using websockets.
However, I would like the client side (which implements the callback class) to receive an event in the event of a lost connection so that it can re-connect to the server and pass the callback interface.
I made this code for client:
procedure TLogClientWebsockets.OnClientDisconnected(sender: TObject);
begin
// disconnect event handling is never called!!
end;
procedure TLogClientWebsockets.OnWebSocketsClosed(sender: TObject);
begin
// disconnect event handling is never called!!
end;
procedure TLogClientWebsockets.Start();
begin
fModel := TSQLModel.Create([], cRemoteLogRootName);
fRestClient := TSQLHttpClientWebsockets.Create('127.0.0.1', cRemoteLogHTTPPort.ToString, fModel);
fRestClient.Model.Owner := fRestClient;
fRestClient.OnWebSocketsClosed := OnWebSocketsClosed;
fRestClient.WebSockets.OnWebSocketsClosed := OnWebSocketsClosed;
fRestClient.WebSockets.Settings.OnClientDisconnected := OnClientDisconnected;
fRestClient.WebSockets.Settings.HeartbeatDelay := 3000;
fRestClient.WebSockets.Settings.DisconnectAfterInvalidHeartbeatCount := 3;
fRestClient.WebSocketLoopDelay := 100;
fRestClient.WebSocketsUpgrade(cWebSocketEncryptionKey);
fRestClient.ServiceDefine([ILogService], sicShared);
if not fRestClient.Services.Resolve(ILogService, fService) then
raise EServiceException.Create('Service ILogService unavailable');
fLogManager := TLogManager.Create(TPanel(fLogPanel), 0, nil);
fCallback := TRemoteLogCallback.Create(fLogManager, fRestClient, IRemoteLogCallback, fLogPanel.Handle);
fService.StartLogService(fCallback);
end;Then I run the application and during transmission using TCPView (from Sysinternals) I close the socket that supports the websockets connection. On the server side, the onClientDisconnect event fires. But nothing is happening on the client's side!
How to detect connection loss from the client side?
I use Fiddler this time. It seems, however, that large files are also not compressed, see:

Maybe I'm wrong, but the server's answer is probably not gzipped. Please look at the headings:

What needs to be set that mORMot used gzip compression?
See on MDN documentation:
Maybe we do not understand a bit.
I'm doing an HTTP server with several REST servers that have dozens of methods.
Now a colleague who is going to write a JS or PHP client asks me for an API.
What should I give him? Pascal files with data types and interface definitions?
No, no, I need this for own convenience, only for internal use. For easier testing of clients written on other platforms.
Now I have to fold these addresses manually and I'm often wrong, I'm wasting many time.
I have an example server:
TMyService = class(TInterfacedObject, IMyService)
public
procedure GetSomeData(const ID: integer; out Data: TSomeData);
procedure GetOtherData(const ID: integer; out Data: TOtherData);
end;
aModel := TSQLModel.Create([]);
try
// create a fast in-memory ORM server
aRest := TSQLRestServerFullMemory.Create(aModel,'',false,false);
try
aRest.ServiceRegister(TMyService,[TypeInfo(IMyService)],sicShared);
aServer := TSQLHttpServer.Create('8090', [aRest], '+', useHttpApiRegisteringURI);
try
writeln('Background server is running'#10);
write('Press [Enter] to close the server.');
ReadLn;
finally
aServer.Free;
end;
finally
aDB.Free;
end;Is there a method/function to get from the server information about the available API in the form of a list of simple links for a specific service?
Something like that:
var
list : TStringList;
list := server.GetAvailableAPI('MyService');
//or maybe
list := aRest.GetAvailableAPI('https://server.adrress.com', 'MyService');in list:
https://server.adrress.com:8090/root/MyService/GetSomeData
https://server.adrress.com:8090/root/MyService/GetOtherDataI particularly care about combining the http address (with port if <> 80 or 443), root address, service name and method name.
Look closely. See that in the RenderJSON() function the Params parameter is next to the Args parameter:
const Args,Params: array of const;And Params parameter contains values as in FormatUTF8(). But for what is Args?
I try use method:
/// renders the {{mustache}} template from JSON defined context
// - the context is given via a JSON object, defined with parameters
// - you can specify a list of partials via TSynMustachePartials.CreateOwned,
// a list of Expression Helpers, or a custom {{"English text}} callback
// - is just a wrapper around Render(_JsonFastFmt())
// - you can write e.g. with the extended JSON syntax:
// ! html := mustache.RenderJSON('{name:?,value:?}',[],['Chris',10000]);
// - set EscapeInvert = true to force {{value}} NOT to escape HTML chars
// and {{{value}} escaping chars (may be useful e.g. for code generation)
function RenderJSON(const JSON: RawUTF8; const Args,Params: array of const;
Partials: TSynMustachePartials=nil; Helpers: TSynMustacheHelpers=nil;
OnTranslate: TOnStringTranslate=nil;
EscapeInvert: boolean=false): RawUTF8; overload;but what is Args parameter? In the above example Args is passed as empty array.
Ok, I read this article http://blog.synopse.info/post/2011/07/0 … te-binding and now I know that mORMot has own late binding, and fast :-)
Variant is a magic, but without compile time checked and code insight. And it is quite slow.
You are right, after change param type to RawUTF8 values are corrected. Thx Arnaud!
Enter/Leave critical section without try..finally.
In mORMot.pas, procedure ComputeResult:
if ForceServiceResultAsXMLObjectNameSpace='' then
ForceServiceResultAsXMLObjectNameSpace := Service.ResultAsXMLObjectNameSpace;
with Server.fStats do begin
EnterCriticalSection(fLock); <-- is there no lack of try finally?
inc(fServiceInterface);
Changed;
LeaveCriticalSection(fLock); <--
end;I have service with method:
function TServiceBase.LogOn(const aEmail, aPassHash, aAPIKey, aResourceIdentifier: RawByteString; aClientSalt: UInt32): RawUTF8;I call this from Delphi client and method is called properly, in log I see:
[MainEventLog] 2018-07-26 19:51:27.13 call ss.Rest.ServicesAccessAdministration.TAccessAdministrationRest(7E94AF60)
IAccessAdministrationService.LogOn["ZWdveWFAb25ldC5wbA==",
"Y2MyNTBmNTAzMzIyNmVhYjk4Y2ExODliMzBhZmMzYjAwODQ0OGVkMTg0YzFhYWQ0NmQ4NzIwMTQyZTJjMDk2ZA==","QVBJS0VZLUFCQ0RFLUZHSElK",
"WFlaLUFCQy0xMjM=",8493]but when I call from JS client (based on jquery) then request in log looks:
[MainEventLog] 2018-07-26 19:51:27.13 call ss.Rest.ServicesAccessAdministration.TAccessAdministrationRest(7E94AF60)
IAccessAdministrationService.LogOn{"aEmail":"test@test.pl","aPassHash":"2676afb19748e6fc5ed1320df9f8d4a7c9fb41b6ef997e2cb4998ff10dd1007c",
"aAPIKey":"KEY_FOR_TEST","aResourceIdentifier":"SomeResourceId","aClientSalt":693}and method LogOn is called with empty parameters (except aClientSalt, this param has value) :-(
I don't JS developer, where I should search problem?
In SynCommons GetTickCount64 is declared as function that return Int64 value:
var
/// the number of milliseconds that have elapsed since the system was started
// - compatibility function, to be implemented according to the running OS
// - will use the corresponding native API function under Vista+, or
// will emulate it for older Windows versions
GetTickCount64: function: Int64; stdcall;
procedure RetrieveSystemInfo;
var
[...]
begin
[...]
GetTickCount64 := GetProcAddress(Kernel,'GetTickCount64');
if not Assigned(GetTickCount64) then
GetTickCount64 := @GetSystemTimeMillisecondsForXP;but WinAPI function return UInt64:
https://msdn.microsoft.com/pl-pl/librar … s.85).aspx
Does the declaration from Int64 do not interfere with UInt64 from WinAPI?
1. EncryptPKCS7() generate random IV (initialization vector), the result will be different each time for the same input data
try this method:
/// perform the AES cypher in the corresponding mode
// - when used in block chaining mode, you should have set the IV property
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); virtual; abstract;but you must known and pass IV
2. You use encoding to Base64, but you expected result in hex?
AES and other block cipher algorithms uses a initialization vector as first block to encode input data.
But when I call method (or other with IVAtBeginning param):
TAESxxx.EncryptPKCS7(const Input: TBytes; IVAtBeginning: boolean): TBytes; with IVAtBeginnig = False, then as IV is used an empty array?
array of byte :-)
I need to encrypt stream (with chaining mode), how to do it best?
Like this?
procedure AESstreamCrypt(const aInStream, aOutStream: TStream; const aKey: TBytes; const aIV: TAESBlock);
var
lInput : TBytes;
lOutput : RawByteString;
begin
lAES := TAESCBC.Create(aKey[0], lKeySize);
try
lAES.IV := aIV;
aInStream.Position := 0;
// copy aInStream to TBytes local variable
SetLength(lInput, aInStream.Size);
aInStream.ReadBuffer(lInput[0], aInStream.Size);
//encypt TBytes
lOutput := lAES.EncryptPKCS7(lInput, False);
//copy encrypted data do output stream
aOutStream.Write(lOutput[1], Length(lOut));
finally
lAES.Free;
end;
end;So how to encrypt streams? By copying them to the array?
What chaining mode does TAESWriteStream use?
See:

Delphi 10.2
mORMot: 1.18.4512
Please read near topic: https://synopse.info/forum/viewtopic.php?id=4252
Maybe you encountered the same issue descripted in this topic...
I am voting for the extension of the TObjectDynArrayWrapper class by the OwnsObjects parameter.
This change don't break the existing interface (default OwnsObjects is True) and extends class functionality significantly.
Who else supports this idea? :-)
@Junior/RO
Source Code Pro Semibold
Now is ok (probably ;-)
Thx Arnaud

is not exactly like you wrote:
"if you use default TSQLRestRoutingREST routing, and defined a single RawByteString or TSQLRawBlob input parameter, it will be processed as a raw POST with binary body defined with mime-type 'application/octet-stream'"
Can you correct it?
But how to set it? For now I don't set the Content-Type header.
For send PDF file header is:
POST /server/SomeService.SendFilePart HTTP/1.1
Accept: */*
FILEID: 84317faa3d2516d189da1e356e12e4fe5f55eeea8970a09d26739ec690e0c4fb
Content-Type: application/pdf
User-Agent: Mozilla/5.0 (Windows; mORMot 1.18 TWinINet)
Host: localhost:8090
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Content-Length: 1048576
%PDF-1.4
%ŕáâă
2 0 obj
<</Filter /FlateDecode
/Length 11656951
>>
stream
xśĚÝÁŽćHš¬ç}_ĹÜŔNŇé$·
[...]for XML:
POST /server/SomeService.SendFilePart HTTP/1.1
Accept: */*
FILEID: 8472f3d762a2f42ebde6a9bf9fba0806d9c8e59221fc0dafd3fe05dd1a94050c
Content-Type: application/octet-stream
User-Agent: Mozilla/5.0 (Windows; mORMot 1.18 TWinINet)
Host: localhost:8090
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Content-Length: 1048576
<?xml version="1.0" encoding="UTF-8"?>
<Migration>
<Session Type="Online">
<Platform Type="Source">
[...]Content-Type is different for PDF and for XML, how to force properly value of Content-Type?
Not supported?
Then why you wrote above:
"As an alternative, if you use default TSQLRestRoutingREST routing, and defined a single RawByteString or TSQLRawBlob input parameter, it will be processed as a raw POST with binary body defined with mime-type 'application/octet-stream'. This may be more optimized for remote access over the Internet.
See https://synopse.info/fossil/info/4cfc722c69"
?
And what changes has this commit? https://synopse.info/fossil/info/4cfc722c69
btw on other (text) files this way acts properly (send without json and base64 encoding)
I found problem on this topic.
I get exception:
TSQLRestRoutingREST.Error:
{"errorCode":406, "errorText":"sicShared execution failed (probably due to bad input parameters) for TMyService.SendFilePart"}See for reason:

Thx Arnaud!
Ok, I remove this line, but I have a doubt and a question!
When I create TServiceFactoryServer object and specify the contract in the constructor:
sf := ServiceRegister(fMyService as TInterfacedObject, [TypeInfo(IMyService)], '2');then in ContractExpected property I have value: "2" (with quote chars)
sf.ContractExpected is equal "2"But (!) when I use setter:
sf.ContractExpected := '2';then property is set to a different value! ContractExpected property has a: 2 (without quote chars!)
sf.ContractExpected is equal 2This is IMHO a incorrect behavior, the value set by the constructor should be the same as set by property.
I found on server side:

But I have it in the code for a very long time! And maybe the last versions of the mORMot have changed something here...

And I found problematic place, in constructor:
constructor TServiceFactory.Create(aRest: TSQLRest; aInterface: PTypeInfo;
aInstanceCreation: TServiceInstanceImplementation; const aContractExpected: RawUTF8);See:

Client side:
fServiceFactory := fClient.ServiceRegister(TypeInfo(IMyInterface), sicShared, '2') as TServiceFactoryClient;server side:
sf := ServiceRegister(fMyService as TInterfacedObject, [TypeInfo(IMyService)], '2');
sf.ContractExpected := '2';and I still get exception:
{"Message":"TServiceFactoryClient.Create(): server's IMyService
contract differs from client's: expected [\"2\"], received [2] - you may need to upgrade your 1.0.0.221 client to match server expectations"}Why? Where I make mistake?
I always recompile server and client.
But why ContractExpected value has quoted chars around "1"? See Watch List and values of compared variables.
I get exception:
TServiceFactoryClient.Create(): server''s IMyService contract differs from
client''s: expected ["1"], received [1] -
you may need to upgrade your 1.0.0.219 client to match server expectations'But see screen from debug session (mORMot.pas)

This is my mistake or mORMot has problem? :-)
ps. latest mORMot, Delphi Tokyo
Is SynLZ binary compatible with other LZ algorithm? I want to compress files on client and sent to the server (and on server decompress then), but clients will be created not only in Delphi (eg in Java). Is the use of the SynLZ algorithm limited only to Delphi?
After commented this line TWinHTTP client connect properly. And what now?
Yes, you are right, I add more logs and problem occurs in line:
if not WinHttpAPI.SetOption(fSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
@WINHTTP_FLAG_SECURE_PROTOCOL_MODERN, SizeOf(WINHTTP_FLAG_SECURE_PROTOCOL_MODERN)) then
RaiseLastModuleError(winhttpdll,EWinHTTP);How resolve this issue?
I don't have Vista, problem occurs on our customer station (I have access only to his log file).
Yes, I use SSL connection. I don't use proxy.
First report from customer:
14:20:44.20: System: Windows Vista SP2 (6.0.6002)
14:20:50.20: mORMot [ver.1.18.4061] client TSQLHttpClientWinHTTP test....
14:20:51.20: Trying connect to REST server ...
14:20:51.20: 20171211 13205102 + mORMotHttpClient.TSQLHttpClientWinHTTP(03A54630).CallBackGet Companies/Timestamp
14:20:51.20: 20171211 13205112 + mORMotHttpClient.TSQLHttpClientWinHTTP(03A54630).007F7FBC
mORMotHttpClient.TSQLHttpClientGeneric.InternalURI (525)
14:20:51.20: 20171211 13205122 EXC EWinHTTP ("winhttp.dll error 87 (Parametr jest niepoprawny)") at 00694735
SynCrtSock.RaiseLastModuleError (3656) stack trace API 00694735
SynCrtSock.RaiseLastModuleError (3656) 0069ACD5
SynCrtSock.TWinHTTP.InternalConnect (10025) 00699631
SynCrtSock.THttpRequest.Create (9423) 007F9CA8
mORMotHttpClient.TSQLHttpClientRequest.InternalCheckOpen (916) 007F7FCC
mORMotHttpClient.TSQLHttpClientGeneric.InternalURI (527) 007AB0BA mORMot.CallInternalURI (37775) "Parametr jest niepoprawny" in polish mean "The parameter is incorrect"
Ok, I will collect this data on monday
Windows 10 and latest mORMot.
It looks like you do not understand what a mORMot is. And maybe you do not need it.
btw You have doubled const defined:

I discovered that on some systems (our customers) client on WinHTTP get error:
EWinHTTP ("winhttp.dll error 87 (The parameter is incorrect)")but WinINet client works properly, why?
I detect that on some systems client on WinHTTP get error:
"winhttp.dll error 87, The parameter is incorrect error"
but WinINet client works properly, why?
I write diagnostic app for our clients who have a various problems with connecting to server. The app connects through TSQLHttpClientWinHTT, TSQLHttpClientWinINet and sockets by Indy TIdTCPClient and reports messages to file.
I thought I could use curl based class too. If no then sorry :-)