#1401 Re: mORMot 1 » Thread-safety of mORMot » 2013-09-13 07:37:00

mpv

Another small optimization, and we got 19710 Request Per Second!
Result for command: > ab -c128 -n10000 -k http://localhost:888/m3/muchacha  (run 10000 request 128 concurrent thread, keep-alive)

This is ApacheBench, Version 2.3 <$Revision: 1178079 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)


Server Software:        Microsoft-HTTPAPI/2.0
Server Hostname:        localhost
Server Port:            888

Document Path:          /m3/muchacha
Document Length:        191 bytes

Concurrency Level:      128
Time taken for tests:   0.507 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    10000
Total transferred:      3940000 bytes
HTML transferred:       1910000 bytes
Requests per second:    19710.61 [#/sec] (mean)
Time per request:       6.494 [ms] (mean)
Time per request:       0.051 [ms] (mean, across all concurrent requests)
Transfer rate:          7583.96 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    6   3.0      6     115
Waiting:        0    6   3.0      6     115
Total:          0    6   3.0      6     115

Test source ( this JS code evaluated into every mOROMot working thread )
mustache.js taken here: https://github.com/janl/mustache.js

var muchacha = (function(){
  var 
    data = [{message: "hi!!!"}, {message: "lo!!"}];

var
  tpl='<!DOCTYPE html>\
<html>\
<head><title>Fortunes</title></head>\
<body>\
<table>\
<tr><th>id</th><th>message</th></tr>\
{{#.}}\
<tr><td>{{id}}</td><td>{{message}}</td></tr>\
{{/.}}\
</table>\
</body>\
</html>';

var compiled = Mustache.compile(tpl);
  
  return function(req, resp){
	var output = compiled(data);

  	resp.writeEnd(output);
	resp.writeHead('Content-type: text/html');
  	resp.statusCode = 200;
  }
})();

#1402 Re: mORMot 1 » Thread-safety of mORMot » 2013-09-12 19:00:41

mpv

In the "Test requirements" section exist remark about "If you have limited time, we recommend you start with the easiest tests (1 through 3)" - so we can participate without template. For HTML template I can add Mustache support. SpiderMonkey  implementation not yet ready for including  into mORMot, but this use-case I can implement......
I just test  - not-optimized version give 1300 RPS for they template (w/o database support but using poor hardware (Core i5U). So I can implement section 4.

I think we can try to use sqlite3....
I also do not use git, so can't help in this part.

UPD.
Small optimization and I got 1750 RPS smile

#1403 Re: mORMot 1 » Thread-safety of mORMot » 2013-09-12 08:53:23

mpv

I found very interesting site http://www.techempower.com/benchmarks/
I think it's a good idea to participate (for framework popularization).
It seems we must be in top of list for Windows platform smile http://www.techempower.com/benchmarks/# … est=update

#1405 Re: mORMot 1 » Best way to send files » 2013-09-11 16:18:38

mpv

Yes, I rewrite my code in this manner - everything work good and now I'm compatible with main mORMot branch smile
Just one remark - when I send static files in this manner I get incorrect statistics:

TSQLRestServer.URI()
.....
  inc(fStats.fOutcomingBytes,length(Call.OutHead)+length(Call.OutBody)+16);

we actually increase out bytes by fileName length
As for me is good to add fStats.fileTransfered counter and count number of files we send (calculating real file length is slow?)

#1406 Re: mORMot 1 » Best way to send files » 2013-09-11 13:50:14

mpv

I want to discuss this http://synopse.info/fossil/info/ce1141092e  ticket.

I agree with you about  using FindIniNameValue()  and work with InHead. Everything is OK here.

But what should I do to send static file with custom content type? Seams I must add CONTENT-TYPE twice into OutHeader?

   Ctxt.Call.OutHeader := HEADER_CONTENT_TYPE + HTTP_RESP_STATICFILE + #13#10 + HEADER_CONTENT_TYPE + getMyCustomContentType();

I'm right?

#1407 Re: mORMot 1 » Windows authentication on the server » 2013-08-08 11:08:29

mpv

Oh. Thanks! I miss what I can use HTTP service name. But in all case this operation required Domain Admin right and as you sad  domain sometimes not configured properly. Chaa, do you apply this patch? (SSPI is your's module)

#1408 Re: mORMot 1 » Windows authentication on the server » 2013-08-07 15:02:50

mpv

Another proposal:
I fight with Negotiate auth from browser. I found all browser try to use Kerberos scheme even if in local network. So if my server not a windows service I can't authorize clients using windows credential. Solution is to force NTLM auth ( pass WWW-Authenticate: NTLM in header).

As for me we need to redefine this section:

const
  /// identification name used for SSPI authentication
  SECPKGNAMEINTERNAL = 'Negotiate';
  /// HTTP header to be set for SSPI authentication
  SECPKGNAMEHTTPWWWAUTHENTICATE = 'WWW-Authenticate: '+SECPKGNAMEINTERNAL;
  /// HTTP header pattern received for SSPI authentication
  SECPKGNAMEHTTPAUTHORIZATION = 'AUTHORIZATION: NEGOTIATE ';

in this way

/// force NTLM authorization instead of Negotiate
///  used it we start our server application not as a Windows service but as a application
procedure ForceNTLM(IsNTLM: boolean);

var
  /// identification name used for SSPI authentication 'Negotiate' or 'NTLM';
  SECPKGNAMEINTERNAL: RawUTF8;
  /// HTTP header to be set for SSPI authentication  'WWW-Authenticate: NTLM' ||  'WWW-Authenticate: Negotiate';
  SECPKGNAMEHTTPWWWAUTHENTICATE: RawUTF8;
  /// HTTP header pattern received for SSPI authentication 'AUTHORIZATION: NTLM ' || 'AUTHORIZATION: NEGOTIATE '
  SECPKGNAMEHTTPAUTHORIZATION: RawUTF8; 

......
implementation

procedure ForceNTLM(IsNTLM: boolean);
begin
  if IsNTLM then begin
    SECPKGNAMEINTERNAL := 'NTLM';
    SECPKGNAMEHTTPWWWAUTHENTICATE := 'WWW-Authenticate: NTLM';
    SECPKGNAMEHTTPAUTHORIZATION = 'AUTHORIZATION: NTLM ';
  end else begin
    SECPKGNAMEINTERNAL := 'Negotiate';
    SECPKGNAMEHTTPWWWAUTHENTICATE := 'WWW-Authenticate: Negotiate';
    SECPKGNAMEHTTPAUTHORIZATION = 'AUTHORIZATION: NEGOTIATE ';
  end;
end;

initialization
  ForceNTLM(False);

in case I know my server must not use kerberos I call forceNTLM(True) and everything is OK smile

#1409 Re: mORMot 1 » Windows authentication on the server » 2013-08-07 07:27:49

mpv

Small proposal:
we need to initialize TSQLRestServer.fSessionCounter in some not 0 value. In current realization if we use non-signed auth scheme ( TSQLRestServerAuthenticationNone for example) we have such collision:
user A got session ID = 1
user B got session ID = 2
I restart server
user B call auth - got session ID = 1
user A call RetrieveSession with his old session ID = 1 and got credential of user B sad

my proposial is:

constructor TSQLRestServer.Create;
......
with SystemTime do // set start session counter = miliseconds from midnight
    fSessionCounter := (wHour*60*60 + wMinute * 60 + wSecond)*1000 +  wMilliSeconds;

not good also but partially help.

#1410 Re: mORMot 1 » trees structure » 2013-08-06 16:19:15

mpv

Currently for tree I prefer  Materialized Path + parentID pattern. In fact for every tree table I add 3 additional column: parentID, treePath, isLeaf. Tree path is list of parent ID's separanet by '/' varchar(4000)
so I have something like this:
ID  parentID   treePath  isLeaf  name
1     NULL           /1/          0         the root
2       1               /1/2/       0         child 1
3       2               /1/2/3     1         child 1 1

Such structure VERY good for store large amount of data  and very quick select of childs:
  - select * from tree where treePath like '/1/%' - return all nested child of element with ID = 1 using indexed field treePath
  - select * from tree where treePath where parentID = 1 - return all direct child of element using indexed field parentID

Selecting of parent  list is simple but not very quick
  -  select * from tree where treePath like '%/3' - no index used sad. In this case I try to use some database- depended feature like  "connect by" in case of Oracle

2 contra - limited level of tree depth (max is varchar (4000) = newar 15 levels ) and hard to update parentID in case element have child.

#1411 Re: mORMot 1 » Code integration / ORM » 2013-07-20 14:08:59

mpv

I'm little confused with ID field in ORM. Actually on database level mORMot create INT64 field type, but in source used integer. I remember this problem is already discussed in this forum, but not found topic. I once again try to move my soft from Syn* level to mORMot* level and this is big blocker for me (I use one database-wide ID generator  for all table with first 4 digit reserved for clientID (so I got something like GUID) for easy replication between different database. So I easy overflow int32 ID in real application. Is it possible (may be in future) to change  ID from int32 to Int64 ?

#1412 Re: mORMot 1 » REGEXP operator for SQLite3 » 2013-07-19 09:46:28

mpv

UPS. I found this in documentation:

The compiled form of a regular expression is not altered during  match-
       ing, so the same compiled pattern can safely be used by several threads
       at once.

       If the just-in-time optimization feature is being used, it needs  sepa-
       rate  memory stack areas for each thread. See the pcrejit documentation
       for more details.

So if we call pcre_study without PCRE_STUDY_JIT_COMPILE flag (as in current version) -  YES - it thread-safe

#1413 Re: mORMot 1 » REGEXP operator for SQLite3 » 2013-07-19 09:36:06

mpv

Little question: is PCRE thread safe?
I mean if I compile regexp (Compiled) in main thread and when use it from concurrent threads in this way:
function TStaticRule.Mutch(const Location: RawUTF8): boolean;
begin
  Result := (Compiled <> nil) and (pcre_exec(Compiled, extra, pointer(Location), Length(Location), 0, PCRE_NO_UTF8_CHECK, nil, 0)>=0);
end;
Or I must compile it for every thread separately?

#1416 mORMot 1 » Transaction commit/rollback due TSQLDBConnection.Disconnect » 2013-07-05 13:01:56

mpv
Replies: 4

I found very unexpected behavior: if I stop server all uncommited transaction is COMMITed (tested for Oracle). I expect rollback in this case. This is because of

procedure TSQLDBOracleConnection.Disconnect; 
...
        Check(SessionEnd(fContext,fError,fSession,OCI_DEFAULT),fError,false,sllError);  <---- The transaction specified by the service context is implicitly committed   [url]http://docs.oracle.com/cd/A97630_01/appdev.920/a96584/oci15r14.htm[/url]

It seems for MS SQL I have to get  SQLSTATE 25000 (Invalid transaction state) in similar situation.

My propose is to check transaction state and rollback  uncommited transaction in procedure TSQLDBConnection.Disconnect; to solve this problem.
If we decide not do it on connection level, I can rollback manually in OnHttpThreadTerminate, but I think default behavior for disconnecting must be Rollback.
In other library sometimes present parameter Connection.RollbackOnDisconnect: boolean;

Which option we choose?

#1417 Re: mORMot 1 » Bug in Oracle interface? Please confirm or confute » 2013-06-28 21:00:03

mpv

2peak.tibor try to generate insert statement without params and run it via sqlplus\toad
Something like.
insert into insdemo values(`b001`,`b002`,..........);

Is it work for 95 column table?

#1418 Re: mORMot 1 » JSONToObject and null string property » 2013-06-28 07:49:15

mpv

AB, you are absolutely right about null in JSON. Current implementation work in right way (http://json.org/  -  string MUST be in ""). Please - not change current realization.

#1419 Re: mORMot 1 » mORMot: demonic version » 2013-06-14 06:42:41

mpv

As for me mORMot currently is not ready for server-side use in non-Windows platform because of dependents of http.sys or, in case plain socket - IOCP. So even if we compile some part under Linux it is only possible to use mORMot as CGI.
I think many time about alternative for IOCP and found good lib https://github.com/joyent/libuv. It currently used in node.js for cross-platform async IO and supported by some M$ contributors. So may be this is a way....

#1420 Re: mORMot 1 » why the DB is locked » 2013-06-04 15:28:27

mpv

But only one application (EXE with build if sqlite3.c) at time can access to SQLite file. This is a SQLite design.  There is not some kind of external "instance"(like Oracle listener)  what handle concurrency, blocking and so on from different application - all this function are compiled into your application. So if you try to use different tools to access one SQLite3 file at the same time this is the same as put 2 separate  instance of (for example) MS SQL to look on the one mdf file.

#1421 Re: mORMot 1 » 995 error in server - can mormot split packages? » 2013-06-04 14:53:55

mpv

If you use one of sicClientDriven, sicPerSession, sicPerUser or sicPerGroup modes - look on TServiceFactoryServer property TimeoutSec

#1422 Re: mORMot 1 » Authentication: Adding new user » 2013-06-02 20:38:12

mpv

By regisrer url AB mean   call to   THttpApiServer.AddUrlAuthorize  under windows administrator user. See here http://synopse.info/forum/viewtopic.php?id=989 and in SAD by keyword AddUrlAuthorize

#1423 mORMot 1 » ISO8601 timezone and mORMot » 2013-06-02 13:47:36

mpv
Replies: 1

By now mORMot use ISO8601 date/time format without timezone information ( '2013-06-02T16:10:00' ). ISO8601 says, we must look on such string as on LOCAL time. So if I'm in GMT+3 timezone '2013-06-02T16:10:00' =  '2013-06-02T13:10:00Z'. But then I use mORMot server and browser client different browser use different algoritm to parse ISO date/time. Examples:

// FireFox think this is a local time - as defined in ISO8601
-> var d = new Date('2013-06-02T14:14:14'); JSON.stringify(d);
<- "\"2013-06-02T11:14:14.000Z\""
//but IE 9 and Chrome think this is UTC0 time
<- ""2013-06-02T14:14:14.000Z"" 

So my suggestion is to add "Z" in  *TimeToIso8601* serialization/deserialization functions, so I always know time is in UTC format.
All browser work correctly with ISO8601 with timezone format '2013-06-02T14:14:14Z'.

And it gives us the opportunity to not only work correctly with browsers-type client, but to handle clients from different time zones.
Regression (previous mORMot version/mORMot user who do not need timezone information) may ignore timezone information and think about dates as about local dates as it is now. User who need timezones may store information in DB in UTC timezone, perform client-level conversion to local time (as browser does)  and handle clients from different timezone (as this forum software does, for example).
What do you think?

#1424 Re: mORMot 1 » Changing the http.sys queue length from default 1000 » 2013-05-14 17:46:35

mpv

What tool do you use to create such load? What OS is your server? May be you reached max number of socket? See here for example http://www.catalyst.com/kb/100072

#1425 Re: mORMot 1 » Changing the http.sys queue length from default 1000 » 2013-05-13 20:17:07

mpv

but I assume the thread pool is for THttpServer but not THttpAPIServer

Please, pay attention to THttpApiServer.Clone method. This one creates the thread pool. Each cloned  thread registers itself to http.sys and then http.sys manages it in the similar way as HttpServer does. As far as I understand you overflow not length of queue, but time request wait in queue. So AB is right - increase thread pool size.

#1426 Re: mORMot 1 » Compress Files » 2013-05-01 18:00:09

mpv

There are many examples - you can easy found it using search http://synopse.info/forum/search.php by word "compress". For example this topic http://synopse.info/forum/viewtopic.php?id=1159 or this http://synopse.info/forum/viewtopic.php?id=48

#1428 Re: mORMot 1 » Interface based services » 2013-04-27 12:35:23

mpv

This is exactly what I had in mind, the two procedures. One on the TSQLRestServer level ( do it using event OnServerNotFoundURI(Ctxt: TSQLRestServerCallBackParams) as you propose is good). The second is on  the service level. But for service level TMyInterfaceImplementation._default() I propose not one parameter Ctxt, but the same way we define parameter for other interface-based methods. ( ok - we dont need magic URI parameter - I can retrive it from ServiceContext variable).

#1429 Re: mORMot 1 » Does anyone know any alternative to SOAP client for Delphi? » 2013-04-26 16:32:04

mpv

Form my POV implementing SOAP client is possible if using existing mORMot classes SynCrtSock.TWinHTTP for communication layer and customJSONSerializer's to serialize/deserialize SOAP messages to delphi classes.
it certainly will require some effort, but..
Or use SynCrtSock.TWinHTTP for communication and this project http://www.philo.de/xml/ to parse XML smile

#1430 Re: mORMot 1 » Interface based services » 2013-04-26 16:19:55

mpv

I thought about something like that.

If I understand well, we need only a GET method to retrieve some static content?

In general - no.
For static file service - yes, only GET. But I plane to use similar services realization for my ORM  - so I need all type of HTTP request (GET|PUT|POST|....) to be passed to interface-based methods.

About passing Ctxt: TSQLRestServerCallBackParams to _default method - it maybe just for optimization, for interface-based services I already have access to TSQLRestServerCallBackParams via ServiceContext.Session.
The advantage of parameter list in interface methods, i.e. not

IStaticRequest = interface(IInvokable)
    ['{84A6479B-4DE8-4B7A-93BB-4CE8935FE03C}']
    procedure _default(var Ctxt: TSQLRestServerCallBackParams);
end;

but separate parameter list

IMyService = interface(IInvokable)
    ['{796479B-7248-367A-794B-4CE8935F739C}']
     function ComputeAdd(a,b: integer): integer;
    function _default(const URI: RawUTF8; const asMIME: RawUTF8; .....): TServiceCustomAnswer; // only URI parameter retrieved from URL tail (if exist in parameter list), all other - depending of routing schema
end;

TStaticFileService = class(TInterfaceObject, IMyService)
  public
    // will be called if no method is recognized, because only of _default is a method name
     function _default(const URI: RawUTF8; const asMIME: RawUTF8; .....): TServiceCustomAnswer; // only URI parameter retrived from URL tail, all other - depending of routing schema
     // normal service method
     function ComputeAdd(a,b: integer): integer;
  end;

is:
1) I do not need to parse the parameters manually, and
2) I have a _contract_ - it very important if team consist of many developer.


But to have such a procedure would be very good at TSQLRestServer level

TMyServer = class(TSQLRestServer)
published
  procedure _default(var Ctxt: TSQLRestServerCallBackParams);

This give me ability to handle request of type [GET|POST|...] http://myserver/root. For example in my current implementation I return server contract in case of  GET http://myserver/root?_contract_  or do redirect for browser in case of GET http://myserver/root to http://myserver/root/static/index.html

procedure TMyServer._default(var Ctxt: TSQLRestServerCallBackParams);
begin
...
  if Ctxt.Method = mGET then begin //static file only for GET request
    if Ctxt.URI='' then begin
      Result := hscMovedPermanently;
      Ctxt.OutCustomHeaders := LOCATION_REDIRECT_HEADER + Ctxt.Call.URL + '/static/index.html';
  .......
  if (Ctxt.Method = mGET) and (Ctxt.Parameters <> nil) and IdemPChar(Ctxt.Parameters, '_CONTRACT_') then begin
    //return global server contract
  end
 .....
  
end;

#1431 Re: mORMot 1 » Interface based services » 2013-04-26 08:43:32

mpv

I've added the new sicPerThread mode.

Magnifiquement, as always smile

we may be able to add some new routing/parameter marshalling, as you expect

I do not want a new routing scheme - I just want to expand a little two existing  by adding "default" behavior and one "magic" parameter retrieved from URI and combine it with existing rmREST and rmJSON_RPC   smile

This give me ability to rebuild my server using interface-based services and be compatible with framework not on Syn* level as today, but on mORMot* level. The next step(SpiderMonkey integration to mORMot) will be much easier for me after that..

#1432 Re: mORMot 1 » blob handling » 2013-04-24 19:44:48

mpv

Please, look at  this topic http://synopse.info/forum/viewtopic.php?id=347
If you use search http://synopse.info/forum/search.php with keyword TSQLRawBlob you found many other examples.

#1433 Re: mORMot 1 » Interface based services » 2013-04-24 15:52:26

mpv

Some additional ideas....
I have a "static file" transfer METHOD - just transfer files from server to client (something like "Samples\09 - HttpApi web server") . Now it is implemented as a published method of TSQLRestServer descendant

TMyServer = class(TSQLRestServer)
...
published
  procedure static(var Ctxt: TSQLRestServerCallBackParams);
  procedure YUI(var Ctxt: TSQLRestServerCallBackParams);
  .......
end;

I want to rewrite it using interface-based services. This give me ability to connect "static file transfer" functionality to any server (TSQLRestServerFullMemory  for example). In fact, I do want to give up all the published methods and rewrite all via interface-based service.
In current implementation I call TMyServer.static method by put
<script charset="utf-8" src="http://localhost:8080/root/static/app.js"></script>
in HTML file.
This is not interface-based service URL format. Yes, I can change this to:
<script charset="utf-8" src="http://localhost:8080/root/static.get?file=app.js"></script>  but this is not KISS from HTML POV.

Another example - I have YUI method - realization of YUI interface using my ORM as background.
I want to rewrite it as a method of interface-based service IYUI to.

To solve my tasks I need something like this:

  IStatic = interface(IInvokable)
    ['{84A6479B-4DE8-4B7A-93BB-4CE8935FE03C}']
    function default(const URI: RawUTF8; const asMIME: RawUTF8): TServiceCustomAnswer;
  end;
  Server.ServiceRegister(TStaticFileService,[TypeInfo(IStatic)], sicShared);

"Magic", I want to offer is:
1. "default" method - this is the method that should be called if no ".method" passed in URL but for ServicesRouting=rmREST. This give me possibility to write GET /root/ServiceName.... instead of GET /root/ServiceName.MethodName....
2. "URI" parameter - if parameter with name "URI": RawUTF8 exist in method parameter list value of this parameter retrieved from URL tail. This give me possibility to write  GET /root/static/pictures/myimage.gif?asMIME=application\/pdf and server call my function
TStaticFileService.default("/pictures/myimage.gif", "application\/pdf");

What do you think - is it make sense?

#1434 Re: mORMot 1 » Interface based services » 2013-04-24 05:18:09

mpv

AB, i do what I need in thread-safe way (the same you do with threadSafeConnection). So for a moment i can use sicSingle and there is no ned to implement sicPerThread. Sorry to trouble you. I clous feature request? Or we lave it for future need?

#1435 Re: mORMot 1 » Interface based services » 2013-04-23 15:05:04

mpv

optExecInMainThread absolutely impossible - in this case I got single-thread application. But I need multi-thread... And I don't want to free created instance each time - just create it during first call to service and free when server die.

#1436 Re: mORMot 1 » Interface based services » 2013-04-23 14:13:02

mpv

in my case each thread must contain a copy of the interpreter with a lot of compiled scripts. Create an instance of the interpreter and compiling scripts is time consuming operation and should be used to perform once for each thread.

#1437 Re: mORMot 1 » Interface based services » 2013-04-23 11:36:22

mpv

I try to rewrite my application to use interface-based services. In my case I need per-thread service instance. Is it possible to add TServiceInstanceImplementation.sicPerThread instance implementation pattern? From from the POV of the client it will look like sicShared  as I understand.

#1438 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-18 18:10:13

mpv

There is no need to put connectionID into header - we just need it on the server. NTLM scheme authorize connection - it mean if client close socket and open another it MUST repeat NTLM handshake. This is why we must use only keep-alive HTTP connection and can use http.sys level Req.connectionID parameter to identify 2nd part of hanshake (or socket handle in case of plain socket server) - and all other client requests. After success handshake we MUST use http.sys req.connectionID(socket handle) for each client request to authorize client request - this is client sessionID. Current implementation of NTLM do not do it (as I understand) - currently it is a mix of  NTLM authentification and mORMot authorization. I`ll make code sample tomorrow - it seems my english very poor to explain what I meen.

#1439 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-17 16:28:01

mpv

smile Very good! I make some remarks in tickets:
-  in Authentication classes - http://synopse.info/fossil/info/8c8a2a880c  seems "this presentation" link is broken...
- in Authorization classes - http://synopse.info/fossil/info/7c079c49d6 - As for me it will be good to combine onUserAuth + authorization class hierarchy. For example in my case in some services I do not use TSQLAuthUser/Group ( some of my services not use any DB at all - only LDAP for auth - for example cache service in case of ACTIVE-ACTIVE load balancing on main server)

#1440 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-15 17:32:27

mpv

And about logout for basic/DIGEST type of auth - the easyest way is to implement logout method  and drop session as we did  in current implementation(in case of basic or digest where is only one session per user name possible of course). The same algoritm we can use for other auth type, for negotiate found session by connectionID, for mormot by sessionid.

#1441 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-15 16:16:30

mpv

I'm not sure how you want to combine 1. and 2. in the same time
3.4.5. is OK.
For Digest - yes - digest store md5(Username + ':' + realm + ':' + Password) as a password hash. So to use Digest combined with other methods the only way is to add 2 password column in user table - for mORMot and for Digest.

I suspect the RegisterAuthenticationScheme() should be for the whole server

I also

Do several authentication schemes make sense?

I'm not sure that I understood correctly. If you mean:

 
TSQLRestServer.RegisterAuthenticationScheme('mORMot', TMoromAuth);  // here I mean mORMot algorithm using HEADER
TSQLRestServer.RegisterAuthenticationScheme('Negotiate', TNegotiateAuth);  
TSQLRestServer.RegisterAuthenticationScheme('Basic', TBasicAuth);  

then yes - it make sense. In this case Delphi client choose mORMot, browser for domain user choose Negotiate (as more strong auth method)  and for non-domain user - Basic

if you mean use mORMot-style (session_signature in URI) and header style auth in the same time - no, it not make sense as for me.

And from my POV here we need:
6. add connectionID attribute to TSQLRestServerURIParams ( for the http.sys server =  Req^.ConnectionId, for socket-based = Int64(Socket), for namedPaper = Int64(GetNamedPipeClientProcessId/GetNamedPipeClientSessionId), for messages = Int64(window handle)

7. add event onUserAuth in witch I can check user credential and fill user GroupRights (I actually get a user groups from LDAP and associate it with group binary mask). See also http://synopse.info/forum/viewtopic.php?id=1218

#1442 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-15 13:58:56

mpv

I'm still not convinced that DIGEST and BASIC may be used on browser side nice & easily: you need to enter a password in an awfull login window, and there is no mean to logout

It's easy to implement DIGEST and BASIC auth in browser without ugly login window in AJAX application - we just catch 401 responce in global level and show our login window.  Examlpe of pure javaScript digest auth is here http://marcin-michalski.pl/2012/11/01/j … ript-ajax/ (see JavaScript client section).

About security - YES I agree with you - BASIC is not secure at all. Digest is secure in case we use HTTPS + qop="auth-int" + timestamp in server nonce.
But sometimes we need basic auth. The first example - using ApatchBench for benchmark. In current implementation in mORMot I set HandleUserAuthentication = false before benchmark, but this is not real-life bench. If we implement multiple auth, I can set BASIC and test real business logic.

Which authentication scheme are worth implementing?

I think mORMot / Negotiate / Basic.

Will we allow authentication with lower security than our own mechanism?

It's on developer choice. If I register BASIC AUTH schema for  my server - I have low security, if register only mORMot - Hi security.

Will it break the KISS design?

IMHO - not. In case I develop non-Delphi ( JavaScript, VisualBasic, ObjectiveC, Java client for Android) app it's much SIMPLE  to use basic scheme when I create prototype and then switch to more secure schema it my prototype will need someone.

A RegisterAUTHScheme () method is perhaps easier to work. With dedicated classes to implement mode, instead of method callbacks.

You are 100% right, my mistake.

#1443 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-14 16:36:49

mpv

I propose  add support for multiple AUTH schema in mORMot.
I understand what it require some breaking changes, but as a result we got auth, what compatible with  standards and give for mORMot some additional benefits, (we can implement OAuth for example, very easy to use mORMot from browser, we can implement cache according to HTTP standards).
So, the idea is:
1) implement something like TSQLRestServer.RegisterAUTHScheme(const schemeName RawUTF8; onAuthentificate: function(Ctxt), onGetAuthentificationParams: function(Ctxt), onAuthorize: function(Ctxt)).

2) in TSQLRestServer.URI instead of

  URI.Error('',HTML_FORBIDDEN); // 403 in case of authentication failure

return

  
URI.Error('', 401);
  for each AuthScheme in registeredAuthScheme
     URI.Call.OutHead.Add('WWW-Authenticate:' + AuthScheme.SchemeName + AuthScheme.onGetAuthentificationParams(Ctxt));
  end;

HTTP protocol allow to add multiple WWW-Authenticate headers. The task of the client to choose the strongest of the supported schemes.

In case we register schemas [mORMot, Negotiate, NTLM, Digest, Basic] we got something like:

-> GET /root/something
<- 401 
  WWW-Authenticate: mORMot nonce="nonce_value"  
  WWW-Authenticate: Negotiate ...
  WWW-Authenticate: NTLM ...
  WWW-Authenticate: Digest realm="mORMot digest", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
  WWW-Authenticate: Basic realm= "mORMot basic"'

in case of Delphi client or other client what support mORMot auth scheme next step is:

-> GET /root/something
  Authorization: mORMot UserName=..., PassWord=..., ClientNonce=... 
<- 401 
  WWW-Authenticate: mORMot session number and a private key

a) second step
-> GET /root/something
  Authorization: mORMot session_signature="zzzzzzzzzzzzzzzzzzzzzzzzzzzz"
<- 200 Ok
  content here

all other client request simple add  header

   Authorization: mORMot session_signature="...."

Browser or client are not familiar with  mORMot auth scheme just choose another. For example if I do request to intranet zone IE ignore WWW-Authenticate: mORMot and choose Negitiate, if request go to "not trusted" site - Digest and so on.

3) In TSQLRestServer.URI if HandeAuthentification=true check "Authorization: schemaName" header, find Auth scheme with schemaName from registered and call AuthScheme.onAuthirize(..)
for mORMot schema, for example, onAuthirize do the same as  in TSQLRestServer.URI in current implemetation.

4) for Neginiare and NTLM schema we can use chaa imlementation given here: http://synopse.info/forum/viewtopic.php?pid=5809#p5809
  We just must add ConnectionId to the Call structure instead of Remote-IP from chaa's code.   ConnectionId is retrived as Eric wtite in this post:
http://synopse.info/forum/viewtopic.php?pid=5816#p5816
"To the Process method I just added a connectionId parameter, which takes the Req^.ConnectionId for the http.sys server, and a Int64(Socket) for the thread/socket-based one"

Negotiate is a connection authentication scheme - not http authentication scheme, so client must be keep-alive, ConnectionId must be used instead of sessionCardinal. onAuthirize(..) handler for Negotiate just look session with ID = ConnectionId exist in sessionList.

5) Also I propose to add custom handler for  user password checking for each schema (it allow not use TAuthUser/TAuthGroup but, for example, LDAP) and   the same handler to calculate Group's for user ( In my case, for examlpe, I need to check LDAP groups for user and assotiate it with TAuthGroup

sorry for my english sad

#1444 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-13 08:34:26

mpv

Yes, I understand that headers are HTTP specific... But in current auth scheme does not make sense to send 304 response.

This is because in case of BROWSER,  client cache is implemented on BROWSER level (I'm not control it) for each URI. So scenario:

-> GET root\entitiy\1?session_signature=11
<- 200 OK
    ETag:1etag
    resp body

10ms after
-> GET root\entitiy\1?session_signature=11 (signature not changed because of 250 ms period)
    HEADER  IF-NONE-MATCH: 1etag  (here is ETag I got for this URI on prev responce. This header is added by BROWSER)
<- 304 Not modified

1 min after:
-> GET root\entitiy\1?session_signature=12  (from logical POV i try to GET the same entity, but from browser POV is not because URI cahnged)
    HEADER (BROWSER not include here  IF-NONE-MATCH: header, because no entry in browser cache for URI root\entitiy\1?session_signature=12
<- 200
    ETag:1
    resp body

So, browser client use cache only 250 ms (the time session_signature not changed) after this period GET request going with another URI
And the worst - I overflow browser cache, because I never send GET root\entitiy\1?session_signature=11 in future

Also responce may cached by proxy between server and client. And I don't control proxy at all.

Yes - it is possible to implement cache at higher level I already done ( I get server responce, store in in browser indexedDB with key = crc32(request body) store ETag=CRC32(respondeBody) in indexedDB, next time I send POST with body {bla-bla, ETag: xxx}, server analyse post body and return 200 with body {notModified: true} in case nothing changed. But such implementation requier HUGE amount of javaScript client code, do not use caching responces on proxy between client and server, need drums in non indexedDB browser and so on.

In fact we already widely use HEADER in mORMot (JSON_CONTENT_TYPE_HEADER in every responce) so I don't see contra's to use it in AUTH request. But in result we have STANDART auth scheme, supported by ANY HTTP compartible client  ( we can use digest-md5 and NTLM and continue to add session_signature for autentification but in header section).

One example is WebDAV - webDAV client use NTLM or digest auth and don't know anything about session_signature. I can't  bypass auth on server because depending of user I must return different content.
Second - I need to connect to mORMot from Excel (get some data and build report using pivotTable). If use standatd auth scheme I do nothing, just cheate XMLHTTPRequest COM object and send request - server authorise me using Kerberos (NTLM). In current scheme I must write lot of code.
Third - I have iPad client for my server written on ObjectiveC. I spent couple of time to explain for Objective C developer mORMot auth schema. Developer spand lot of time to implement it.
And this happens every time I need to connect new type of client to my server sad

#1445 Re: mORMot 1 » Response caching and 304 Not Modified » 2013-04-12 21:09:06

mpv

To implement http level cache (not only etag but "not modified since" and so on) we must first change auth scheme used in mormot.
Now we put different session_signature in every url, so browser decide
root\blabla?session_signarute=1 and
root\blabla?session_signarute=2
is different url and cashe is therefore impossible.
So my suggestion is to change auth scheme. As minimum - move session signature to request header section, as optimal - use standart HTTP algoritm for auth(we can add out algorotm to 'WWW-Authenticate: Synopse, NTLM, digest` - in this case delphi client can use synopse algoritm, browser NTLM or digest).
I know all PRO of current imlpementation, but the contra is -
1) hard to work from browser,
2) HTTP level cache is impossible
3) impossible to work with 3party software - for example I have idea to implement WebDAV access to my server, but webDAV client know nothing about  current auth algoritm.
What do you think?

#1446 Re: SyNode » Adding JavaScript support for mORMot framework » 2013-01-30 14:45:21

mpv

Unfortunately I could not complete the implementation of the SpiderMonkey support before my vacation. In the next two weeks I will not have access to a computer, but I will continue to work after 10 February. I hope during the holidays I will have fresh ideas smile

#1448 Re: mORMot 1 » Smart Mobile Studio mORMot class server » 2013-01-30 13:27:25

mpv

CORS is good news. Actually in modern system it used instead JSONP. But AB, maybe better to move CORS support from TSQLHttpServer  to THttpServerGeneric?

#1449 Re: mORMot 1 » Too Many Fields » 2013-01-26 15:55:54

mpv

Please, look at comments in SynCommons

const
  /// maximum number of fields in a database Table
  // - is included in SynCommons so that all DB-related work will be able to
  // share the same low-level types and functions (e.g. TSQLFieldBits,
  // TJSONWriter, TSynTableStatement, TSynTable)
  // - default is 64, but can be set to any value (64, 128, 192 and 256 optimized)
  // - this constant is used internaly to optimize memory usage in the
  // generated asm code, and statically allocate some arrays for better speed
  MAX_SQLFIELDS = 64;

so just set this const to 128 and recompile.

2AB - maybe change to 128 in main trunc - I'm unfortunately have objects with large number of fields to.

#1450 Re: SyNode » Adding JavaScript support for mORMot framework » 2013-01-25 09:22:55

mpv

Yes, editor is cool. We can even use it in desktop applications using Delphi Chromium embedded (http://code.google.com/p/delphichromiumembedded/)

Board footer

Powered by FluxBB