You are not logged in.
That is great, looking forward to next one.
I have found out that it is a bug in 2.2 stable in TSqlDBOleDBMSSQL2018ConnectionProperties which you have fixed in master commit
Revision: 8e0ecde3ba26324e9d270493c58c16393355afb4
Author: Arnaud Bouchez <ab@synopse.info>
Date: 25/1/2024 01:45:48
Message:
fixed TSqlDBOleDBMSSQL2018ConnectionProperties
as reported by https://github.com/synopse/mORMot2/issues/238
----
Modified: src/db/mormot.db.sql.oledb.pas
Modified: src/mormot.commit.inc
I will use master until you get next stable one. I just need to find a way to stop rebuild my app when there is an update in master.
This happens to another machine too. The same application and same machine work okay if the application is compiled with master branch of mORMot 2. Does 2.2 stable support driver behind TSqlDBOleDBMSSQL2018ConnectionProperties?
I use one of mORMot 2's great features which is "CreateMissingTables" to not writing db scripts for updating my MS SQL database, initially I used master branch for developing and then wanted to use 2.2 stable for production, but I found 2.2 stable did not work and got the exception per title when calling rest server's CreateMissingTables, I used the sample project "ex\ThirdPartyDemos\martin-doyle\05-HttpDaemonORM\src\Project05HttpDaemon.dproj" to verify it and it failed with 2.2 stable too, below is the modified code to use MS SQL:
procedure TSampleDaemon.Start;
var
Props: TSqlDBOleDBConnectionProperties;
begin
Props := TSqlDBOleDBMSSQL2018ConnectionProperties.Create('localhost','mormot','','');
SQLite3Log.Enter(self);
Model := CreateSampleModel;
VirtualTableExternalRegister(Model,[TOrmSample],Props);
// SampleServer := TSampleServer.Create(Model, ChangeFileExt(Executable.ProgramFileName,'.db'));
SampleServer := TSampleServer.Create(Model, ':memory:');
SampleServer.DB.Synchronous := smOff;
SampleServer.DB.LockingMode := lmExclusive;
SampleServer.Server.CreateMissingTables; //<-- throw exception: EOleSysError "Class not registered"
HttpServer := TRestHttpServer.Create(HttpPort,[SampleServer],'+',HTTP_DEFAULT_MODE,4 );
HttpServer.AccessControlAllowOrigin := '*';
SQLite3Log.Add.Log(sllInfo, 'HttpServer started at Port: ' + HttpPort);
end;
There was also a following exception:
Fatal exception ESqlite3Exception raised with message
Error SQLITE_ERROR (1) [Step] using 3.44.2 - Class not registered
Do I have to use master or there is a quick fix ?
Looks like the problem is solved by using ip address instead of machine name, i.e.,
Props := TSqlDBOleDBMSSQL2018ConnectionProperties.Create('192.168.0.101','Training','sa','123');
@ttomas
I did use SQL profiler on SQL server and I could see the execute time is quite normal.
@ab
Added port number to the connection string, but it didn't work for me.
tried with TADOConnection with 'Provider=SQLOLEDB;Data Source=...', it had the similar speed number, i.e., some time it went up to above 3s, but haven't seen as this slow in SQL studio which also running from my laptop and connect to same db on another PC. Will try ODBC next time.
I used example projects from "ex\ThirdPartyDemos\martin-doyle\05-HttpDaemonORM" as the base and connect it to MS SQL by using TSqlDBOleDBMSSQL2018ConnectionProperties like below
Props := TSqlDBOleDBMSSQL2018ConnectionProperties.Create('MyAnotherPC','Training','sa','123');
SQLite3Log.Enter(self);
Model := CreateSampleModel;
VirtualTableExternalRegister(Model,[TOrmSample],Props);
SampleServer := TSampleServer.Create(Model, ':memory:');
The server and client were running on my laptop and the SQL server was running on another PC, these two machines were connected to the same Wifi network which I believe is 300M network.
Below server log shows it took 1s+ for reading the last ID and 3s+ for insert a record, is this normal (I have tried many times and it looks to me that it always like that after server is start up) or there is something I can tune up it to speed up?
20240512 11100250 " DB mormot.db.sql.oledb.TSqlDBOleDBStatement(027c6f70) Prepare t=1.08s q=select max(ID) from dbo.Sample
20240512 11100250 " SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c6f70) Execute t=1.09s q=select max(ID) from dbo.Sample
20240512 11100251 " DB mormot.db.sql.oledb.TSqlDBOleDBStatement(027c7090) Prepare t=38us q=insert into dbo.Sample (ID,Name,Question,Time) values (?,?,?,?)
20240512 11100251 " SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c7090) Execute t=11.49ms wr=1 q=insert into dbo.Sample (ID,Name,Question,Time) values (3,'','',135846605441)
20240512 11100252 " srvr Write POST root/Sample=201 out=0 B in 1.12s
20240512 11100252 " - 01.128.548
20240512 11122835 # + server.TSampleServer(02ac9b20).URI POST root/Sample in=54 B
20240512 11122835 # + mormot.db.sql.oledb.TSqlDBOleDBConnection(0283bb70).Create
20240512 11122835 # - 00.002.040
20240512 11122835 # + mormot.db.sql.oledb.TSqlDBOleDBConnection(0283bb70).Connect
20240512 11123143 # - 03.122.602
20240512 11123143 # DB mormot.db.sql.oledb.TSqlDBOleDBStatement(027c71b0) Prepare t=3.12s q=insert into dbo.Sample (ID,Name,Question,Time) values (?,?,?,?)
20240512 11123144 # SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c71b0) Execute t=3.13s wr=1 q=insert into dbo.Sample (ID,Name,Question,Time) values (4,'','',135846605596)
20240512 11123144 # srvr Write POST root/Sample=201 out=0 B in 3.15s
20240512 11123144 # - 03.153.490
but some time following reading could be much faster:
20240512 11202701 % DB mormot.db.sql.oledb.TSqlDBOleDBStatement(027c73f0) Prepare t=1.14s q=select top(1) ID,Name,Question,Time from dbo.Sample where Name=?
20240512 11202702 % SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c73f0) Execute t=1.14s q=select top(1) ID,Name,Question,Time from dbo.Sample where Name=''
20240512 11202702 % srvr Read GET root=200 out=62 B in 1.17s
20240512 11202703 % ret mormot.rest.server.TRestServerRoutingRest(02a478f0) [{"ID":5,"Name":"m2","Question":"tst 1","Time":135846606086}]
20240512 11202703 % - 01.179.860
20240512 11203207 " + server.TSampleServer(02ac9b20).URI GET root in=68 B
20240512 11203207 " DB mormot.db.sql.oledb.TSqlDBOleDBStatement(027c7510) Prepare t=119us q=select top(1) ID,Name,Question,Time from dbo.Sample where Name=?
20240512 11203208 " SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c7510) Execute t=8.94ms q=select top(1) ID,Name,Question,Time from dbo.Sample where Name=''
20240512 11203208 " srvr Read GET root=200 out=62 B in 21.97ms
20240512 11203209 " ret mormot.rest.server.TRestServerRoutingRest(02a478f0) [{"ID":5,"Name":"m2","Question":"tst 1","Time":135846606086}]
20240512 11203210 " - 00.056.932
20240512 11204361 # + server.TSampleServer(02ac9b20).URI POST root/Sample in=52 B
20240512 11204361 # SQL mormot.db.sql.oledb.TSqlDBOleDBStatement(027c71b0) Execute t=8.03ms wr=1 q=insert into dbo.Sample (ID,Name,Question,Time) values (6,'','',135846606123)
20240512 11204361 # srvr Write POST root/Sample=201 out=0 B in 20.70ms
20240512 11204361 # - 00.021.199
It seems a problem in TIdTCPServer component, in its OnExecute event handler, if I use ReadLn to wait for the pack starter, in some cases (in my cases, hited by multiple packs at the same time and cut the connection in the middle etc.), it will keep firing OnExecute event even there is no connectioin at all (i.e., close the sender applications), change it from ReadLn to WaitFor seems can avoid the infinite loop:
conn.IOHandler.ReadLn(mllp_header, -2 {IdTimeoutInfinite}, conn.IOHandler.MaxLineLength); ==> conn.IOHandler.WaitFor(mllp_header);
Thanks for being helping me.
I now can reproduce the issue on my dev PC, but there is something I don't understand. The new problem is that so far I can only reproduce it if I run the application via Windows Run, after it becomes 100% CPU0, if I attach Delphi debugger to it, CPU0 dropped immediately to normal and stay like that after I continue running it, and after attached to the debugger, I could not reproduce it any more, neither if I run the application start from debugger, so I am now have to add debugging string/log to trace it instead of debugging it within the debugger.
The customer's VM was created as 4 vCPUs in Azure, running CPU Get NumberOfCores, NumberOfLogicalProcessers /Format:List returns
NumberOfCores=2
NumberOfLogicalProcessors=4
Now I can reproduce on my own test VM which is half of customer's core, i.e., NumberOfCores=1, NumberOfLogicalProcessors=2.
With Process Explorer from Sysinternals tool set, I can see there are two threads using 100% of CPU0, even I suspend one of them the remained one still uses 100% CPU0.
Maybe next step is to see if I can reproduce it on a real PC.
Greate to know that recent CPUs have improved on this issue and that sounds correct as my application had been running okay for years in an on-premise VM until it moved to Azure VM and it started to have this 100$ CPU0 and non-responsive issue.
Is Azure VM not friendly to (Delphi) multi-thread application? Do you know if we need to do any optimization for Delphi application to run okay on Azure VM?
Hi ab,
I have an application compiled by Dephi 2007, it is a multi-thread application, but when it is busy, it seems only uses one core, i.e., CPU0 will be used 100% but the others are sitll very low usage, and thus the box (a Windows 2019 server VM) becomes no response, do you think this sympton (only one core is used 100%) is also related to the same LOCK issue in Delphi?
You can add some exceptions to be ignored to LogFamily.ExceptionIgnore list
Isn't that globally ignore that type of exception? But I just want to ignore in that function.
Hi AB,
How do I not to log the exception I explicitly want them to be silent somewhere but do not want this type of exception globally silent:
try
TryLoadAsFormat1(data);
// no exception, is format 1
// do some thing here
except
// do nothing, silent the exception, but the ISynLog will log this exception which I don't need
end;
Thanks,
I replace
try
LVal := TDocVariantData(LDoc.Values[i]); //conversion is failed when the value is an integer
except
LIdx := TSQLResourceIndex.Create;
LIdx.JPath := AParentPath + '.' + LDoc.Names[i];
LIdx.JValue := LDoc.Values[i];
LPathValues.Add(LIdx);
Continue;
end;
with
LVal := _Safe(LDoc.Values[i])^;
and it seems working fine.
FYI, I then updated database (Sqlite) with the list of LIdx (TSQLResourceIndex), it took about 4~5 seconds to insert 35 records, I did not satisfy the performance but then I found the Batch updating in the performance sample, I switched to the batch updating and bang, it only took <200ms! Amazing, great work ab.
Hi ab,
Thanks for your reply.
I filled up JDoc by using _Json function and then cast it to a TDocVariantData:
var
v : Variant;
l : TList<TSQLResourceIndex>;
begin
v := _Json('{"abc":123,"b":"c", "c": {"d":"e"}, "e": [1,2,3]}');
l := GetPathAndValue(TDocVariantData(v));
Hi,
After I get the variant from _Json, how do I get every name:value pair down to the very most deep ones in the hierarchy of a Json?
For example:
var
v : Variant;
begin
v := _Json('{"abc":123,"b":"c", "c": {"d":"e"}, "e": [1,2,3]}');
then I want to list the name(Json path):value pair of the most deep ones (leaves in the hierarchy):
$.abc:123
$.b:c
$.c.d:e
$.e[0]:1
$.e[1]:2
$.e[2]:3
here is my code:
function GetPathAndValue( JDOC: TDocVariantData; AParentPath: string ='$'): TList<TSQLResourceIndex>;
var
LPathValues: TList<TSQLResourceIndex>;
LDoc : TDocVariantData;
i, j: Integer;
LVal : TDocVariantData;
LIdx, LNewIdx : TSQLResourceIndex;
LList : TList<TSQLResourceIndex>;
begin
LDoc := JDOC;
LPathValues := TList<TSQLResourceIndex>.Create;
for i := 0 to LDoc.Count -1 do
begin
try
LVal := TDocVariantData(LDoc.Values[i]); //conversion is failed when the value is an integer
except
LIdx := TSQLResourceIndex.Create;
LIdx.JPath := AParentPath + '.' + LDoc.Names[i];
LIdx.JValue := LDoc.Values[i];
LPathValues.Add(LIdx);
Continue;
end;
if LVal.Kind = dvObject then
begin
LList := GetPathAndValue(LVal, AParentPath+'.'+LDoc.Names[i]) ;
for LIdx in LList do
begin
LNewIdx := TSQLResourceIndex.Create;
LNewIdx.JPath := LIdx.JPath;
LNewIdx.JValue := LIdx.JValue;
LPathValues.Add(LNewIdx);
end;
LList.Free;
end
else if (Lval.Kind = dvArray) then
begin
for j := 0 to LVal.Count -1 do
begin
LList := GetPathAndValue(TDocVariantData(LVal.Values[j]),AParentPath+'.'+LDoc.Names[i]);
for LIdx in LList do
begin
LNewIdx := TSQLResourceIndex.Create;
LNewIdx.JPath := LIdx.JPath;
LNewIdx.JValue := LIdx.JValue;
LPathValues.Add(LNewIdx);
end;
LList.Free;
end;
end
else begin
LIdx := TSQLResourceIndex.Create;
LIdx.JPath := AParentPath + '.' + LDoc.Names[i];
LIdx.JValue := LDoc.Values[i];
LPathValues.Add(LIdx);
end;
end;
Result := LPathValues;
end;
GetPathAndValue(TDocVariantData(v));
Although above code does the job, but I was wondering if there is a better code for checking what is in the value, instead of doing
try
LVal := TDocVariantData(LDoc.Values[i]); //conversion is failed when the value is an integer
except
...
end;
The SQlite3 engine is expected to run in exclusive mode.
So if two processes (a mORMot server and SynDBExplorer) do access the very same database at the same time, the cache between the two is inconsistent.
This is as expected.To consult the database in real time, expose the SQlite3 main mORMot instance using e.g. the mORMotDDD remote administration interface.
Don't know the "administration interface" yet, but I have made a little change to the explorer. Instead of generating "SELECT * ..." query when a table on the left list is double clicked, I make the double click to generate the complete list of the column in the SELECT query, like what SQL Studio does.
procedure TDBExplorerFrame.ListTableDblClick(Sender: TObject);
var
i, j: integer;
Fields: TSQLDBColumnDefineDynArray;
LField: TSQLDBProcColumnDefine;
LSQL : string;
begin
i := ListTable.ItemIndex;
if i>=0 then
begin
Props.GetFields(S2U(ListTable.Items[i]),Fields);
LSQL := 'SELECT ' + Fields[0].ColumnName ;
for j:=1 to Length(Fields) -1 do
begin
LSQL := LSQL + ', ' + Fields[j].ColumnName
end;
LSQL := LSQL + ' FROM ' + StringToUTF8(ListTable.Items[i]);
AddSQL(UTF8ToString(LSQL),
true,ssShift in fListTableShiftState);
end;
end;
Tested with data change (i.e., inserted records from out of SynDB Explorer), the "select * ..." query did returned new records, so it seems only the columns are cached some where. Where do I disable the caching for this situation?
I created a class:
TSQLIntegerDataTypes = class (TSQLRecord)
private
fByte : Byte ;
published
property AByte: Byte read fByte write fByte;
end;
Ran the application and a Sqlite db file created, opened SynDB Explorer and connect it to the db, executed query "select * from IntegerDataTypes", I got column ID and AByte in the result.
While kept SynDB Explorer running, then I upgraded the class to add one more property as following code:
TSQLIntegerDataTypes = class (TSQLRecord)
private
fByte : Byte ;
fShortInt : ShortInt;
published
property AByte: Byte read fByte write fByte;
property AShortInt: ShortInt read fShortInt write fShortInt;
end;
Compiled and ran the application again, the db was now updated, went to SynDB Explorer and executed the same query again, I got the same result without column AShortInt, but if I executed this equivalent query "select ID,AByte, AShortInt from IntegerDataTypes", I got the column AShortInt!
Is this a bug or a feature?
Tried today's Github's copy, still failed.
Checked Delphi's TNetHTTPClient and TNetHTTPRequest, they are using winhttp.dll as well.
I also try to call functions of WinHTTP directly:
procedure TDetailViewForm.Button1Click(Sender: TObject);
var
hSession : HINTERNET;
hConnect : HINTERNET;
hRequest : HINTERNET;
hResult : Boolean;
dwSize : DWord;
begin
// memo1.Text := HttpGet(Edit1.Text);
hSession := WinHttpOpen('Mozilla/5.0 (Windows; mORMot 1.18 TWinHTTP)',WINHTTP_ACCESS_TYPE_NO_PROXY,'','',0);
if (hSession=nil) then raise Exception.Create('Failed to create session');
hConnect := WinHttpConnect(hSession,'shop.global-health.com',443,0);
if (hConnect=nil) then raise Exception.Create('Failed to connect');
hRequest := WinHttpOpenRequest(hConnect,'GET','/',nil,nil,nil,8388864);
if (hRequest=nil) then raise Exception.Create('Failed to open request');
if not WinHttpSendRequest(hRequest,nil,0,0,0,0,0) then
begin
raise Exception.Create(IntToStr(GetLastError()))
end;
hResult := WinHttpReceiveResponse(hRequest,nil);
if not hResult then raise Exception.Create('Failed to receive data, error:' + inttostr(GetLastError()));
end;
the value of parameters to the function calls are copied from debuging HttpGet, these code has not failure with "WINHTTP_ACCESS_TYPE_NO_PROXY", but will get error 12029 when calling WinHttpSendRequest if change it to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY.
I then change code in SynCrtSock (line 8266 of 24/5/2017) from
if fProxyName='' then
OpenType := WINHTTP_ACCESS_TYPE_DEFAULT_PROXY else
OpenType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
to
if fProxyName='' then
OpenType := WINHTTP_ACCESS_TYPE_NO_PROXY else
OpenType := WINHTTP_ACCESS_TYPE_NAMED_PROXY;
and it works for my machine.
But why? Is it a bug or I should change my system/network settings?
No proxy, but could be firewall as it runs OK outside of firewall, but what settings could be?
I then wrote a simple test application with RAD 10.2:
One form with two buttons, one button call mORMot's HttpGet, another one use TNetHttpRequest which comes with RAD 10.2,
code behind mORMot's button:
procedure TDetailViewForm.Button1Click(Sender: TObject);
begin
memo1.Text := HttpGet(Edit1.Text);
end;
code behind TNetHTTPRequest button:
procedure TDetailViewForm.Button2Click(Sender: TObject);
begin
NetHttpRequest1.URL := Edit1.Text;
NetHttpRequest1.Execute();
end;
code for response from TNetHTTPRequest:
procedure TDetailViewForm.NetHTTPRequest1RequestCompleted(const Sender: TObject;
const AResponse: IHTTPResponse);
begin
Memo1.Text := AResponse.ContentAsString();
end;
Ran on a machine with URL "https://shop.global-health.com", mORMot button did not get response, but TNetHTTPRequest did. If URL changed to "http://shop.global-health.com", both worked.
If we bought an application developed in mORMot and it has this problem, what can our IT guy do to find out what issue could be in the company's firewall/network settings?
Strangely, all HttpGet failed in SynSelfTest unit when I ran TestSQL3 compiled with Berlin 10.2 Starter and Delphi 2007 Enterprise. The URL's corresponding json file needs to be delete to get the HttpGet line executed.
The error is "Project TestSQL3.exe raised exception class EWinHTTP with message 'winhttp.dll error 12029 (A connection with the server could not be established)'.
The URLs are all fine if open them with browser. Also tested URLs with PostMan and AdvancedREST Client, two of them need to specify header "User-Agent", otherwise all fine.
HttpGet seems working with "http" version of the URLs instead of "https" version.
Any pointer?
Actual issue is inside mainForm.toLog function. Fixed by [70cc3773fb]
This fix does stop the AV, it would be nice if it can actual tell the script author that the variable passed in is undefined.
Also, I have noticed that there is another set of TSMEngineManager and TSMEngine etc in units SynSM, and they use Spider Monkey v24 instead of v45 in SyNode,
what are the main differences? What are the different using scenarios?
Hi mpv,
Re: SyNode Sample 02, I have found that if the code was referring to a non-existed property of the main form (wrong spelling or capitalized etc.), it raised an generic AV which is not helpful at all, for example, this line of code "mainForm.toLog(mainForm.Caption);" will raise an AV in which does not tell you the line no, what type of error etc., but "mainForm.ToLog(mainForm.caption);" does. What can we do to improve catching/avoid this type of error?
@Bo In this case end users should program his scripts as a module - see how modules work
User script myCalculation.js:const fs = require('fs') ... function onInit(){ ....} function onDone(){..} function doCalculation(){...} module.exports = doCalculation // another way is to export a several methods // module.exports.onInit = onInit; module.exports.onDone = onDone; module.exports.doCalculation = doCalculation;
Your code
FEngine.Evaluate('require("myCalculation")()', 'eval.js', 1, res)
Modules (user code in your case) are wrapped in closure by require function - you can see this in debugger
This will be the equivalent of specifying an entry function in using MSScript.ocx as the script engine wrapper.
There is no way to "clear" the module once it is loaded (like there is no way to "un-uses" unit in Delphi ). And normally you never need such.
Sample 02 execute only selected lines, so to read file twice (for example) in first time select end execute
...
In any case module required only once
The solution you provided is OK in this sample (i.e., interactive), what if the engine is pooled and its script is loaded from script files which are written by the end users (for example, an integration engine runs customized scripts for data processing)? How the end users should program the scripts to avoid "redefine" issue we are talking here?
I also want know how to clear the moudles loaded, I just used it Free every times and Create a New TSMEngine...
How did you do it?
Initially, I used following code and it threw AVs:
// evaluate a text from mSource memo
FEngine := FSMManager.ThreadSafeEngine(nil);
try
if mSource.SelText <> '' then
FEngine.Evaluate(mSource.SelText, 'mSourceSelected.js', 1, res)
else
FEngine.Evaluate(mSource.lines.Text, 'mSource.js', 1, res);
finally
FreeAndNil(FEngine);
end;
Then I change the way it freed and it seemed OK now:
// evaluate a text from mSource memo
FEngine := FSMManager.ThreadSafeEngine(nil);
try
if mSource.SelText <> '' then
FEngine.Evaluate(mSource.SelText, 'mSourceSelected.js', 1, res)
else
FEngine.Evaluate(mSource.lines.Text, 'mSource.js', 1, res);
finally
//FreeAndNil(FEngine);
FSMManager.ReleaseCurrentThreadEngine();
end;
Hi mpv,
It seems that the script cannot be run more than once in "SyNode\Samples\02 - Bindings ", the error is "redeclaration of const fs.",
I guess the question is how to clear the script before next calling to FEngine.Evaluate?
Strange, the blog.synopse.info is not reachable from my desktop computer with all browsers, but it is reachable from my mobile phone with Chrome.
What should I look into to fix the desktop computer?
I was experiencing the same problem a few days ago, it was the Chrome and Edge that could not open the blog, IE was fine. I now can confirm that it works fine again on Chrome.
Hi Ab,
There is a bug on line 275 in SynDBExplorerMain.pas. It should only free the connection if it is created for setting up a new connection, otherwise it throws an AV if an update connection is cancelled.
So the line should be something like
if res=100 then
FreeAndNil(C);
@EMartin,
I guess what juwo wanted to know was what happen if a client connect to a server on which was running on a different "Data Model" in which had slightly different TSQLRecords (different fields, different field type etc.). For example, the server and the clients were all on the version 1.0, and then the server was upgraded to 2.0, but not all of the clients were upgraded to 2.0, thus some of them would be using a different version of "Data Model", is there a way to avoid this situation or it has been guarded by the mORMot already?
Another test case:
Running echo server on Windows 10, running browser locally, both Chrome and Edge are failed to connect, but IE can connect and gets echos.
Does Synopse Websocket only support IE?
Here is another issue:
I ran the echo server on a Windows Server 2003 R2 Standard Edition, and ran the test html in both Chrome and Edge, got different result:
Chrome: could not connect at all.
Edge: the server reported it was connected, but not the browser, and then the connection dropped automatically after a while.
What can I check why the echo server cannot be connected? And why the behavior are different between Chrome and Edge?
Usually, this is a port issue.
The port you expect to bind is already used by some service or program.
but I believe this is not the case for me as I created the NodeJS echo server on the same port, if I forgot to stop NodeJS one, the sample echo server could not started until I closed the NodeJS one.
Followed this link (http://cjihrig.com/blog/websockets-in-n … windows-7/) and created a websocket echo server by using Nodejs, it worked with the test html, so it looks like the Synopse sample echo server has something wrong, anybody else has encountered this before?
Tried another machine, did work.
So the problem could be the machine's environment, but what could affect a websocket server? As the browser is working with other websocket echo server.
Hi,
I am trying to run the sample "Project31SimpleEchoServer", but it seems not working.
Loaded the html, got following in the text area:
WebSocket - status 0
onerror - code:undefined, reason:undefined, wasClean:undefined, status:3
onclose - code:1006, reason:, wasClean:false, status:3
Checked it in the Developer Tools, got following error:
Project31SimpleEchoServer.html:18 WebSocket connection to 'ws://desktop-emq701g/' failed: HTTP Authentication failed; no valid credentials available
init @ Project31SimpleEchoServer.html:18
onload @ Project31SimpleEchoServer.html:72
What do I need to config to be able to run this sample? I did not see other people here reported this.
I have been struggling for hours trying to get HTTPD.SYS to work with SSL under Windows 10
...
I'm guessing it doesn't like the GUID? People seem to just make up a GUID and it works for them. Any suggestions?
Erick
The GUID does not play an important role at all, I made up one too. Most likely would be certificate's issue. I tried self-sign certificate by following the step's in the document but was not successful, then I reused the set of certificates from a production server and it worked immediately since then.
Hi ab,
I have read the framework document again regarding "send the parameters as a JSON object body" and response format, I believe that what you have suggested is doable. But I have not found how to return errors as http status code, because my specification requests it to return http 400 error when parameter validation failed. How do I achieve that?
Thanks,
You can send the parameters as a JSON object body, even with interface based services.
You may define e.g. IAdmin interface and the corresponding method:procedure User(const adminUserId, adminPassword, orgUserid, orgPassword, orgName, orgTelephone, orgCorrespondenceEmail, orgAddress: RawUTF8);
Then you can override the routing class to match your expectations at URI level.
...
Thanks ab, I will try "JSON object body".
Is it possible to implement following RESTful API specification with mORMot? (please note this is not a database application, and the RESTful API is already published)
To create a user:
POST root/api/admin/user
with request Body:
{
"adminUserid" : "joe",
"adminPassword" : "password",
"orgUserid" : "GHRestOrg" ,
"orgPassword" : "password123" ,
"orgName" : "Test Rest Organisation" ,
"orgTelephone" : "04-345-1234",
"orgCorrespondenceEmail" : "info@test.com" ,
"orgAddress" : "Plato Towers, No 300/10" ,
}
I guess what I wanted to know is if there is a way to use custom URI and JSON format of request and response.
As far as I am aware, mORMot's interface based/method based services have different URI and JSON schema compare to above specification.
Please check http://synopse.info/fossil/info/9fd010dc6b
It is a quick fix in SynDBExplorer, but I think the root cause is in IsSelect function.
MS SQL has no problem with line feeds, the problem is IsSelect failed to check the Select statement which has line feeds immediately after 'select', thus SynDBExplorer executes the select statement as no result SQL and seems to me that it is failed to run the query.
I was playing with SynDB Explorer and trying with Query Builder, but strangely, the query generated by Query Builder did not return anything, even a simplest query like:
select
lastuid.*
from
lastuid
but the same query ran correctly in SQL Studio with lines of data returned (SynDB Explorer was connecting to a MS SQL database).
After a few tries, I found if I merge the lines to one line then the query runs OK with data returned:
select lastuid.* from lastuid
With some debugging, I now narrow it down to following check will failed:
Check(isSelect('select'+#13#10+ ' * from toto'));
Looks like it does not treat #13#10 as separator.
Hi Ab,
Now I can reproduce your result. The difference is the "s" must not be a global variable.
With Delphi 7 and Delphi 10.1, the last s is 'toto', as expected.
result=test s=toto
This is strange, mine running result did not get "result=test" either!!!
Same result with XE5 and 2007.
Hi Ab,
I am reading your blog "How function results are allocated" (http://blog.synopse.info/post/2012/04/1 … -allocated) and running the code you provided in the blog to understand the problem, but what I have found is that the result is different, it was still 'test' instead of 'toto'. I am running code in Berlin 10.1 as following:
function GetString: string;
// is compiled as procedure GetString(var result: string);
begin
if result='' then
result := 'test' else
writeln('result=',result);
end;
function GetRaise: string;
// is compiled as procedure GetRaise(var result: string);
begin
result := 'toto';
raise Exception.Create('Problem');
end;
var s: string;
begin
// here s=''
s := GetString; // called as GetString(s);
// here s='test'
s := GetString; // called as GetString(s);
// will write 'result=test' on the console
try
s := GetRaise; // called as GetRaise(s);
finally
// here s='toto'
writeln('s=',s);
Readln(s);
end;
end.
Have something changed in Delphi since you wrote the blog or have I done something wrong?