mORMot and Open Source friends
Check-in [f454889da1]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:
  • added TSQLModel.URIMatch() method to allow sub-domains generic matching at database model level (so that you can set root='/root/sub1' URIs)
  • TSQLite3HttpServer now handles sub-domains generic matching (via TSQLModel.URIMatch call) at database model level (e.g. you can set root='/root/sub1' URIs)
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f454889da1f5956d81eff1a8a8502b7abe23a61c
User & Date: abouchez 2012-09-24 12:23:28
Context
2012-09-25
14:12
fixed potential GPF issue in TPdfWrite.AddUnicodeHex and TPdfWrite.AddHex check-in: 010393f034 user: abouchez tags: trunk
2012-09-24
12:23
  • added TSQLModel.URIMatch() method to allow sub-domains generic matching at database model level (so that you can set root='/root/sub1' URIs)
  • TSQLite3HttpServer now handles sub-domains generic matching (via TSQLModel.URIMatch call) at database model level (e.g. you can set root='/root/sub1' URIs)
check-in: f454889da1 user: abouchez tags: trunk
12:22
  • now FileSize() function won't raise any exception if the file does not exist
  • added TSynNameValue.InitFromIniSection() method
  • added TSynLog/ISynLog.LogLines() method for direct multi-line text logging
check-in: 70627d5ba7 user: abouchez tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/SQLite3Commons.pas.

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
682
683
684
685
686
687
688


689
690
691
692
693
694
695
....
3861
3862
3863
3864
3865
3866
3867

3868
3869
3870
3871
3872
3873
3874
....
3954
3955
3956
3957
3958
3959
3960




3961
3962
3963
3964
3965
3966
3967
.....
15322
15323
15324
15325
15326
15327
15328



15329
15330
15331
15332
15333
15334
15335
15336
.....
15453
15454
15455
15456
15457
15458
15459












15460
15461
15462
15463
15464
15465
15466
      properties of records: so our code is disabled (see PUBLISHRECORD
      conditional) :( - but you can use dynamic arrays of records
    - TPersistent can be now be specified for TSQLRecord published properties:
      a new sftObject field kind has been added - will be stored as TEXT in the
      database (following the ObjectToJSON serialization format) - TStrings or
      TRawUTF8List will be stored as a JSON array of string, and TCollection
      as a JSON array of objects, other TPersistent classes will have their
      published properties serialized as a JSON object  
    - introducing direct content filtering and validation using
      TSynFilterOrValidate dedicated classes
    - filtering is handled directly in the new TSQLRecord.Filter virtual method,
      or via some TSynFilter classes - TSynFilterUpperCase, TSynFilterUpperCaseU,
      TSynFilterLowerCase, TSynFilterLowerCaseU and TSynFilterTrim e.g.
    - validation is handled in the new TSQLRecord.Validate virtual method,
      or via some TSynValidate classes - TSynValidateRest, TSynValidateIPAddress,
................................................................................
    - huge code refactoring of the ORM model implementation: a new dedicated
      TSQLModelRecordProperties will contain per-TSQLModel parameters, whereas
      shared information retrieved by RTTI remain in TSQLRecordProperties - this
      will allow use of the same TSQLRecord in several TSQLModel at once, with
      dedicated SQL auto-generation and external DB settings
    - added aExternalTableName/Database optional parameters to method
      TSQLModel.VirtualTableRegister()


    - TSQLAuthUser and TSQLAuthGroup have now "index ..." attributes to their
      RawUTF8 properties, to allow direct handling in external databases
    - new protected TSQLRestServer.InternalAdaptSQL method, extracted from URI()
      process to also be called by TSQLRestServer.InternalListJSON() for proper
      TSQLRestServerStatic.AdaptSQLForEngineList(SQL) call
    - changed TSQLAccessRights and TSQLAuthGroup.SQLAccessRights CSV format
      to use 'first-last,' pattern to regroup set bits (reduce storage size)
................................................................................
  // tables may not exist in the main SQLite3 database, but in-memory or external
  // - don't modify the order of Tables inside this Model, if you publish
  // some TRecordReference property in any of your tables
  TSQLModel = class(TObject)
  private
    fTables: TSQLRecords;
    fRoot: RawUTF8;

    fTablesMax: integer;
    fActions: PEnumType;
    fEvents: PEnumType;
    fTableProps: TSQLModelRecordPropertiesDynArray;
    /// contains the caller of CreateOwnedStream()
    fOwner: TSQLRest;
    /// for every table, contains a locked record list
................................................................................
    // - reflects the internal private fIsUnique propery
    function GetIsUnique(aTable: TSQLRecordClass; aFieldIndex: integer): boolean;
    /// try to retrieve a table index from a SQL statement
    // - naive search of '... FROM TableName' pattern in the supplied SQL
    // - if EnsureUniqueTableInFrom is TRUE, it will check that only one Table
    // is in the FROM clause, otherwise it will return the first Table specified
    function GetTableIndexFromSQLSelect(const SQL: RawUTF8; EnsureUniqueTableInFrom: boolean): integer;





    /// assign an enumeration type to the possible actions to be performed
    // with this model
    // - call with the TypeInfo() pointer result of an enumeration type
    // - actions are handled by TSQLRecordForList in the SQLite3ToolBar unit
    procedure SetActions(aActions: PTypeInfo);
    /// assign an enumeration type to the possible events to be triggered
................................................................................
  SetLength(fTablesName,N);
  SetLength(fTablesNameIndex,N);
  SetLength(fTableProps,N);
  // initialize internal properties
  for i := 0 to fTablesMax do
    SetTableProps(Tables[i],i,(i=fTablesMax));
  // set the optional Root URI path of this Model



  fRoot := aRoot;
end;

function TSQLModel.GetMainFieldName(Table: TSQLRecordClass;
  ReturnFirstIfNoUnique: boolean=false): RawUTF8;
begin
  if Table<>nil then
    with Table.RecordProps do
................................................................................

function TSQLModel.getURI(aTable: TSQLRecordClass): RawUTF8;
begin
  if (self<>nil) and (Root<>'') then
    result := Root+'/'+aTable.RecordProps.SQLTableName else
    result := aTable.RecordProps.SQLTableName;
end;













function TSQLModel.NewRecord(const SQLTableName: RawUTF8): TSQLRecord;
var aClass: TSQLRecordClass;
begin
  aClass := Table[SQLTableName];
  if aClass=nil then
    result := nil else






|







 







>
>







 







>







 







>
>
>
>







 







>
>
>
|







 







>
>
>
>
>
>
>
>
>
>
>
>







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
....
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
....
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
.....
15329
15330
15331
15332
15333
15334
15335
15336
15337
15338
15339
15340
15341
15342
15343
15344
15345
15346
.....
15463
15464
15465
15466
15467
15468
15469
15470
15471
15472
15473
15474
15475
15476
15477
15478
15479
15480
15481
15482
15483
15484
15485
15486
15487
15488
      properties of records: so our code is disabled (see PUBLISHRECORD
      conditional) :( - but you can use dynamic arrays of records
    - TPersistent can be now be specified for TSQLRecord published properties:
      a new sftObject field kind has been added - will be stored as TEXT in the
      database (following the ObjectToJSON serialization format) - TStrings or
      TRawUTF8List will be stored as a JSON array of string, and TCollection
      as a JSON array of objects, other TPersistent classes will have their
      published properties serialized as a JSON object
    - introducing direct content filtering and validation using
      TSynFilterOrValidate dedicated classes
    - filtering is handled directly in the new TSQLRecord.Filter virtual method,
      or via some TSynFilter classes - TSynFilterUpperCase, TSynFilterUpperCaseU,
      TSynFilterLowerCase, TSynFilterLowerCaseU and TSynFilterTrim e.g.
    - validation is handled in the new TSQLRecord.Validate virtual method,
      or via some TSynValidate classes - TSynValidateRest, TSynValidateIPAddress,
................................................................................
    - huge code refactoring of the ORM model implementation: a new dedicated
      TSQLModelRecordProperties will contain per-TSQLModel parameters, whereas
      shared information retrieved by RTTI remain in TSQLRecordProperties - this
      will allow use of the same TSQLRecord in several TSQLModel at once, with
      dedicated SQL auto-generation and external DB settings
    - added aExternalTableName/Database optional parameters to method
      TSQLModel.VirtualTableRegister()
    - added TSQLModel.URIMatch() method to allow sub-domains generic matching
      at database model level (so that you can set root='/root/sub1' URIs)
    - TSQLAuthUser and TSQLAuthGroup have now "index ..." attributes to their
      RawUTF8 properties, to allow direct handling in external databases
    - new protected TSQLRestServer.InternalAdaptSQL method, extracted from URI()
      process to also be called by TSQLRestServer.InternalListJSON() for proper
      TSQLRestServerStatic.AdaptSQLForEngineList(SQL) call
    - changed TSQLAccessRights and TSQLAuthGroup.SQLAccessRights CSV format
      to use 'first-last,' pattern to regroup set bits (reduce storage size)
................................................................................
  // tables may not exist in the main SQLite3 database, but in-memory or external
  // - don't modify the order of Tables inside this Model, if you publish
  // some TRecordReference property in any of your tables
  TSQLModel = class(TObject)
  private
    fTables: TSQLRecords;
    fRoot: RawUTF8;
    fRootUpper: RawUTF8;
    fTablesMax: integer;
    fActions: PEnumType;
    fEvents: PEnumType;
    fTableProps: TSQLModelRecordPropertiesDynArray;
    /// contains the caller of CreateOwnedStream()
    fOwner: TSQLRest;
    /// for every table, contains a locked record list
................................................................................
    // - reflects the internal private fIsUnique propery
    function GetIsUnique(aTable: TSQLRecordClass; aFieldIndex: integer): boolean;
    /// try to retrieve a table index from a SQL statement
    // - naive search of '... FROM TableName' pattern in the supplied SQL
    // - if EnsureUniqueTableInFrom is TRUE, it will check that only one Table
    // is in the FROM clause, otherwise it will return the first Table specified
    function GetTableIndexFromSQLSelect(const SQL: RawUTF8; EnsureUniqueTableInFrom: boolean): integer;
    /// returns TRUE if the supplied URI matches the model's Root property
    // - allows sub-domains, e.g. if Root='root/sub1', then '/root/sub1/toto' and
    // '/root/sub1?n=1' will match, whereas '/root/sub1nope/toto' won't 
    function URIMatch(const URI: RawUTF8): boolean;

    /// assign an enumeration type to the possible actions to be performed
    // with this model
    // - call with the TypeInfo() pointer result of an enumeration type
    // - actions are handled by TSQLRecordForList in the SQLite3ToolBar unit
    procedure SetActions(aActions: PTypeInfo);
    /// assign an enumeration type to the possible events to be triggered
................................................................................
  SetLength(fTablesName,N);
  SetLength(fTablesNameIndex,N);
  SetLength(fTableProps,N);
  // initialize internal properties
  for i := 0 to fTablesMax do
    SetTableProps(Tables[i],i,(i=fTablesMax));
  // set the optional Root URI path of this Model
  if aRoot<>'' then
    if aRoot[length(aRoot)]='/' then
      fRoot := copy(aRoot,1,Length(aRoot)-1) else
      fRoot := aRoot;
end;

function TSQLModel.GetMainFieldName(Table: TSQLRecordClass;
  ReturnFirstIfNoUnique: boolean=false): RawUTF8;
begin
  if Table<>nil then
    with Table.RecordProps do
................................................................................

function TSQLModel.getURI(aTable: TSQLRecordClass): RawUTF8;
begin
  if (self<>nil) and (Root<>'') then
    result := Root+'/'+aTable.RecordProps.SQLTableName else
    result := aTable.RecordProps.SQLTableName;
end;

function TSQLModel.URIMatch(const URI: RawUTF8): boolean;
begin
  result := false;
  if (self=nil) or (fRoot='') or (URI='') then
    exit;
  if fRootUpper='' then
    UpperCaseCopy(fRoot,fRootUpper);
  if IdemPChar(pointer(URI),pointer(fRootUpper)) then
    if URI[length(fRootUpper)+1] in [#0,'/','?'] then
      result := true;
end;

function TSQLModel.NewRecord(const SQLTableName: RawUTF8): TSQLRecord;
var aClass: TSQLRecordClass;
begin
  aClass := Table[SQLTableName];
  if aClass=nil then
    result := nil else

Changes to SQLite3/SQLite3HttpServer.pas.

111
112
113
114
115
116
117



118
119
120
121
122
123
124
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488
489
490
491
      - made URI check case-insensitive (as for official RFC)
      - TSQLite3HttpServer will now call virtual TSQLRestServer.EndCurrentThread
        method in each of its terminating threads, to release any thread-specific
        resources (for instance, external connections in SQlite3DB)

    Version 1.18
      - added TSQLite3HttpServer.RemoveServer() method




}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  for i := 0 to high(aServers) do
    if (aServers[i]=nil) or (aServers[i].Model=nil) then
      ErrMsg := 'Invalid TSQLRestServer';
  if ErrMsg='' then
    for i := 0 to high(aServers) do
    with aServers[i].Model do
    for j := i+1 to high(aServers) do
      if aServers[j].Model.Root=Root then
        ErrMsg:= 'Duplicated Root URI';
  if ErrMsg<>'' then
     raise EModelException.CreateFmt('%s.Create: %s',[ClassName,ErrMsg]);
  SetLength(fDBServers,length(aServers));
  for i := 0 to high(aServers) do
  with fDBServers[i] do begin
    Server := aServers[i];
    RestAccessRights := HTTP_DEFAULT_ACCESS_RIGHTS;
................................................................................
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    fDBServers[Index].RestAccessRights := Value;
end;

function TSQLite3HttpServer.Request(
  const InURL, InMethod, InHeaders, InContent, InContentType: RawByteString;
    out OutContent, OutContentType, OutCustomHeader: RawByteString): cardinal;
var URL, Root, Head: RawUTF8;
    i,j: integer;
    P: PUTF8Char;
begin
  if (InURL='') or (InMethod='') or
     (OnlyJSONRequests and
      not IdemPChar(pointer(InContentType),'APPLICATION/JSON')) then
    // wrong Input parameters or not JSON request: 400 BAD REQUEST
    result := 400 else begin
    if InURL[1]='/' then  // try any left '/' from URL
      i := 2 else
      i := 1;
    URL := copy(InURL,i,maxInt);
    j := PosEx(RawUTF8('/'),URL,1); // extract Root (root/1234)
    if j=0 then
      j := PosEx(RawUTF8('?'),URL,1); // extract Root (root?session_signature=...)
    if j=0 then
      Root := URL else
      Root := copy(URL,1,j-1);

    result := 404; // page not found by default (in case of wrong URL)
    for i := 0 to high(fDBServers) do
    with fDBServers[i] do
      if IdemPropNameU(Server.Model.Root,Root) then
      with Server.URI(URL,InMethod,InContent,RawUTF8(OutContent),Head,RestAccessRights) do begin
        result := Lo;
        P := pointer(Head);
        if IdemPChar(P,'CONTENT-TYPE: ') then begin
          // change mime type if modified in HTTP header (e.g. GET blob fields)
          OutContentType := GetNextLine(P+14,P);
          Head := P;






>
>
>







 







|
|







 







|
|







|
<
<
|
<
<
<
<
<
<
>



|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474


475






476
477
478
479
480
481
482
483
484
485
486
487
      - made URI check case-insensitive (as for official RFC)
      - TSQLite3HttpServer will now call virtual TSQLRestServer.EndCurrentThread
        method in each of its terminating threads, to release any thread-specific
        resources (for instance, external connections in SQlite3DB)

    Version 1.18
      - added TSQLite3HttpServer.RemoveServer() method
      - TSQLite3HttpServer now handles sub-domains generic matching (via
        TSQLModel.URIMatch call) at database model level (e.g. you can set
        root='/root/sub1' URIs)

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  for i := 0 to high(aServers) do
    if (aServers[i]=nil) or (aServers[i].Model=nil) then
      ErrMsg := 'Invalid TSQLRestServer';
  if ErrMsg='' then
    for i := 0 to high(aServers) do
    with aServers[i].Model do
    for j := i+1 to high(aServers) do
      if aServers[j].Model.URIMatch(Root) then
        ErrMsg:= Format('Duplicated Root URI: %s and %s',[Root,aServers[j].Model.Root]);
  if ErrMsg<>'' then
     raise EModelException.CreateFmt('%s.Create: %s',[ClassName,ErrMsg]);
  SetLength(fDBServers,length(aServers));
  for i := 0 to high(aServers) do
  with fDBServers[i] do begin
    Server := aServers[i];
    RestAccessRights := HTTP_DEFAULT_ACCESS_RIGHTS;
................................................................................
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    fDBServers[Index].RestAccessRights := Value;
end;

function TSQLite3HttpServer.Request(
  const InURL, InMethod, InHeaders, InContent, InContentType: RawByteString;
    out OutContent, OutContentType, OutCustomHeader: RawByteString): cardinal;
var URL, Head: RawUTF8;
    i: integer;
    P: PUTF8Char;
begin
  if (InURL='') or (InMethod='') or
     (OnlyJSONRequests and
      not IdemPChar(pointer(InContentType),'APPLICATION/JSON')) then
    // wrong Input parameters or not JSON request: 400 BAD REQUEST
    result := 400 else begin
    if InURL[1]='/' then  // trim any left '/' from URL


      URL := copy(InURL,2,maxInt) else






      URL := InURL;
    result := 404; // page not found by default (in case of wrong URL)
    for i := 0 to high(fDBServers) do
    with fDBServers[i] do
      if Server.Model.URIMatch(URL) then
      with Server.URI(URL,InMethod,InContent,RawUTF8(OutContent),Head,RestAccessRights) do begin
        result := Lo;
        P := pointer(Head);
        if IdemPChar(P,'CONTENT-TYPE: ') then begin
          // change mime type if modified in HTTP header (e.g. GET blob fields)
          OutContentType := GetNextLine(P+14,P);
          Head := P;