#101 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-14 01:20:43

@itSDS

I've just tried all possible variants with Berlin 10.1:

program CRC32CTests;

{$APPTYPE CONSOLE}

uses
  SynCommons;

var
  Data: RawByteString;

begin
  Data := StringFromFile('..\..\index.html');
  WriteLn('crc32cfast  : ' + CardinalToHex(crc32cfast(0, PAnsiChar(Data), Length(Data))));
  WriteLn('crc32csse42 : ' + CardinalToHex(crc32csse42(0, PAnsiChar(Data), Length(Data))));
  ReadLn;
end.

32-bit:

crc32cfast  : 6FECA093
crc32csse42 : 6FECA093

64-bit:

crc32cfast  : 6FECA093
crc32csse42 : 6FECA093

Is it possible that you change content of index.html somehow?

Please, could you run the program above on your env.?
Still cann't reproduce your 79CACBF7 ETag value.

#102 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-13 21:59:16

@itSDS

Thanks for these details.
For ETag hash precalculation by the TAsset class is use crc32c function call (as it implemented in TSQLRestServerURIContext.Returns in mORMot.pas, line 38826).

Seems like you crc32c pre-calculation when you run assetslz.exe on Pre-build stage for file Tests\Assets\index.html is differ from expected by bdd-scenario: "6FECA093"

@ab

Is it possible that crc32c call (implemented as crc32cfast or crc32csse42) can return different results for x86/x64 platforms or on different CPU or differ for Debug/Release mode?

In the bdd-scenarios I used constant $6FECA093 to compare it against ETag's pre-calculated for index.html file when synlz-archive created on Pre-Build stage.
But on itSDS' platform the crc32c returns another constant $79CACBF7 for the same Tests\Assets\index.html. I saw that you using the same technique when run some tests and compare results against constant crc/hash. How is it possible that crc32c result is differ form expected constant?

#103 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-12 21:17:52

@itSDS

Concerning failure of EnableCacheByETag feature BDD-scenarios: as you see I cann't reproduce it on my env. even with Berlin 10.1.
Can you localize the issue or give me additional feedback what scenarios are failed on what SynTestCase.Check's.

#104 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-12 21:11:34

@itSDS,

Thanks! I've include this tool fix into Release 1.3.

itSDS wrote:

Which option do i have th set if i wan't the HTTPServer to get the Views of my MVC-Application from the assets ?

Unfortunately the current Mustache Template Engine implementation is heavily coupled with files layout and not diskIO-agnostic even on abstract levels.
I provided my suggestions of such refactoring through inheritance, but Arnaud decide to go in more decoupled way through delegation.
When he finished I can create boilerplate delegate to feed Mustache Engine with View and Partials from mem-cached boilerplate assets, but not from files.

As for now I do the next thing to get the same result:

  1. Segregate all non-static *.html and *.partial (or any other mustache-related templates) into Views directory (put it near Assets dir).

  2. Embed all these assets into exe with another three additional Pre-Build events

    ..\Tools\assetslz.exe Views Views.synlz
    ..\Tools\resedit.exe $(INPUTNAME).res rcdata VIEWS Views.synlz
    DEL Views.synlz
  3. In the server startup code I unpacked all Views somewhere on production folder (when you restart your server all unpacked files will not be overwritten if not changed)

    var
      Views: TAssets;
    ...
      Views.Init;
      Views.DecompressFromResource('Views');
      Views.SaveAssets(GetViewsDir);
    
      FApplication := TMyApplication.Create;
      FApplication.Start(FServer, GetViewsDir);
  4. Then create TMVCApplication with Mustache Views and Partials loaded from this folder:

      TMyApplication = class(TMVCApplication, IMyApplication)
      protected
        function CreateViews(const AFolder: string): TMVCViewsAbtract;
      public
        procedure Start(Server: TSQLRestServer; const Folder: string = ''); reintroduce;
    ...
    procedure TMyApplication.Start(Server: TSQLRestServer; const Folder: string);
    begin
      inherited Start(Server, TypeInfo(IMyApplication));
      FMainRunner := TMVCRunOnRestServer.Create(Self, fRestServer, '',
        CreateViews(Folder), [{publishStatic, }bypassAuthentication]);
    ...
    function TMyApplication.CreateViews(const AFolder: string): TMVCViewsAbtract;
    var
      Params: TMVCViewsMustacheParameters;
    begin
      FillChar(Params, SizeOf(Params), 0);
      with Params do
      begin
        Folder := AFolder;
        FileTimestampMonitorAfterSeconds := 0;
        ExtensionForNotExistingTemplate := '';
        Helpers := TSynMustache.HelpersGetStandardList;
      end;
      Result := TMVCViewsMustache.Create(FFactory.InterfaceTypeInfo, Params,
        (fRestModel as TSQLRestServer).LogClass);
    end;
itSDS wrote:

You should display the mormot version in the test output so you don't have to ask wink

@ab

Can you add mORMot version console logout during test suite execution? Maybe right after "Generated with: * compiler" line.

#106 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-12 20:27:52

Release 1.2

Minor Accept-Encoding fix and code refactoring:

  • fix "Accept-Encoding" parsing when gzip in the end of encodings list

  • make Pre-Build events notice more visible in tests and demo

  • minor code refactoring

#107 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-12 16:07:28

@itSDS,

Test it on Win 8.1 Pro + Delphi 10.1 Berlin Version 24.0.22858.6822 + Synopse mORMot Framework 1.18.2734 + Boilerplate HTTP Server 1.1.0 = All BDD Scenarios passed.

#108 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-12 15:00:31

@isSDS,

Thanks for feedback: I've changed the Pre-Build Notice to be more visible.

What version of mORMot framework did you use (you can take it from SynopseCommit.inc)?
As I understand, you test it with Rad Studio Berlin 10.1 - this mean that you didn't use http://synopse.info/files/mORMot.7z stable version (it's not compatible with 10.1 due to SynCommons (line 22002) GetDelphiCompilerVersion bug, missed {$else} condition for unknown compilers).

I do my tests on Win 8.1 Pro + Rad Studio XE8 22.0.19908.869

  • mORMot 1.18.2688 + Boilerplate 1.0.0 = All BDD Scenarios Passed

  • mORMot 1.18.2688 + Boilerplate 1.0.0 = All BDD Scenarios Passed

  • mORMotNightlyBuild.zip 1.18.2734 + Boilerplate 1.1.0 = All BDD Scenarios Passed

  • mORMotNightlyBuild.zip 1.18.2734 + Boilerplate 1.1.0 = All BDD Scenarios Passed

Can you localize what exact scenarios of TBoilerplateHTTPServerShould.EnableCacheByETag failed?
To do this just comment procedure EnableCacheByETag; and add it to the top of class definition with following protected:

  TBoilerplateHTTPServerShould = class(TSynTestCase)
    procedure EnableCacheByETag;
  protected
    procedure CallInherited;
    procedure ServeExactCaseURL;
    procedure LoadAndReturnAssets;
    procedure SpecifyCrossOrigin;
    procedure SpecifyCrossOriginForImages;
    procedure SpecifyCrossOriginForFonts;
    procedure SpecifyCrossOriginTiming;
    procedure DelegateBadRequestTo404;
    procedure DelegateForbiddenTo404;
    procedure DelegateNotFoundTo404;
    procedure SetXUACompatible;
    procedure SetP3P;
    procedure ForceMIMEType;
    procedure ForceTextUTF8Charset;
    procedure ForceUTF8Charset;
    procedure ForceHTTPS;
    procedure SupportWWWRewrite;
    procedure SetXFrameOptions;
    procedure SupportContentSecurityPolicy;
    procedure DelegateBlocked;
    procedure SupportStrictSSL;
    procedure PreventMIMESniffing;
    procedure EnableXSSFilter;
    procedure DeleteXPoweredBy;
    procedure FixMangledAcceptEncoding;
    procedure SupportGZipByMIMEType;
    procedure ForceGZipHeader;
    procedure SetCacheNoTransform;
    procedure SetCachePublic;
//    procedure EnableCacheByETag;
    procedure EnableCacheByLastModified;
    procedure SetExpires;
    procedure SetCacheMaxAge;
    procedure EnableCacheBusting;
    procedure SupportStaticRoot;
    procedure DelegateRootToIndex;
    procedure DeleteServerInternalState;
    procedure DelegateIndexToInheritedDefault;
    procedure Delegate404ToInherited_404;
  end;

This will give you an ability to experiment only with this feature.
All debugger interruptions show failed test cases in stack trace windows.

I will check BDD scenarios additionally with Berlin 10.1.

@ab,
If some checks are failed in test case - how show their Check(condition, text) this text during console logout?
Because "!  - Enable cache by E tag: 4 / 15 FAILED  28.34ms" is not detailed enough.

#109 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-10 11:02:31

@ab,

0. Ok then, let's merge this project into main mORMor framework trunk. Assets folder contains all web specific assets required by boilerplate best web app practices (it's a compilation of many years best practices made by web masters community for each element in index.html, 404.html or files/folders)

1. Please let me know when you make (partials, helpers, views) Abstract Mustache Views really abstract from any storage representation (RT_RCDATA, Files, DB, JSON). I mean refactor to more low-concrete level all diskIO specific artifacts like .ViewStaticFolder and others. Will inheritance here will be more appropriate then event delegation?

TMVCViewsAbtract or TCustomMVCViews (Delphi like naming convention)
    .fFactory
    .fLogClass
    .Render(methodIndex, Context, View); virtual; abstract;
    .Create(aInterface: PTypeInfo; aLogClass: TSynLogClass);
    .Factory: TInterfaceFactory

TMVCViewsMustache = class(TMVCViewsAbtract)
    .fViewPartials: TSynMustachePartials;
    .fViewHelpers: TSynMustacheHelpers;
    .fViews: array of record // follows fFactory.Methods[]
      .Mustache: TSynMustache;
      .Template: RawUTF8;
      .MethodName: not TFileName but RawUTF8 or SockString;
    .RegisterExpressionHelpers

TMVCViewsMustacheFiles = class(TMVCViewsMustache)
    .fViews: array of record // follows fFactory.Methods[]
      .Locker: IAutoLocker; // not sure maybe it needs to be in TMVCViewsMustache or even higher
      .SearchPattern: TFileName;
      .FileName: TFileName;
      .ShortFileName: TFileName;
      .FileExt: TFileName;
      .FileTimeStamp: TDateTime;
      .FileTimeStampCheckTick: Int64;
    .ViewTemplateFolder
    .ViewStaticFolder

TMVCViewsMustacheMemory = class(TMVCViewsMustache)

...

TMVCViewsMustacheDB = class(TMVCViewsMustache)

TMVCViewsMustacheJSON = class(TMVCViewsMustache)

TMVCViewsMustacheDoc = class(TMVCViewsMustache)

Segregate the next methods to separate routines (in Web App, SPA's we needn't to have low-level tables on such abstract layer)

    CreateExpressionHelpersCommon or CreateCommonExpressionHelpers
    CreateExpressionHelpersForCrypto
    CreateExpressionHelpersForTables

To use like this:

    MyViews.RegisterExpressionHelpers(CreateCommonExpressionHelpers);
    MyViews.RegisterExpressionHelpers(CreateExpressionHelpersForCrypto);
    MyViews.RegisterExpressionHelpers(CreateExpressionHelpersForTables(...));

What do your think?

2. How SynCommons.ResourceSynLZToRawByteString works on Lazarus and Kylix? If I add/create smth in .rc, .res files on pre-compilations stage, this content will be included/accessible on Linux?

#110 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-09 22:04:29

Hi, ab,

Sure, why not, but you have to help me resolve the next three concerns:

1. You will use Boilerplate, not BoilerPlate naming variant - this is one solid word fully aligned with huge boilerplate community smile

2. Please confirm that you also helps me to update (if needed) both tools required for resource embedding assetslz and resedit - they both must serve all suggested compilers (my concerns mostly following Linux support, I have no understanding how *.res resources created/updated/embedded/loaded there). Without them big piece of valuable mem-cached code will be lost. Also without this tools it will be unable to build and deploy applications on cloud services in Instant Deployment and single-file-distribution manner. Just image fully operable HTTP Server hosted on cloud without needs in any mounted disks (SQLite 3 / Logs / Shutdown Session States are easily segregated to other instances). So synlz-packed mem-cached path is important for me.

3. There is another big issue, which still required unnecessary DiskIO operations on server: you designed TMVCViewsMustache and even higher level TMVCViewsAbtract classes in mORMotMVC.pas with DiskIO / files existence in-mind (properties like .ViewTemplateFolder, .ViewStaticFolder, .ViewTemplateFileTimestampMonitor) - such decision even on an abstract level not smoothly aligned with mem-cached boilerplate paradigm. Right now to fully use of TMVCApplication functionality, I have to decompress all Mustache-related *.html and *.partial assets somewhere on production disk (usually it is [UserProfile]/AppData/Roaming/[MyApp] directory) and then load unpacked files again in memory with standard TMVCViewsMustache constructor. Maybe together we can redesign these two mustache classes faster or if you busy I can create TMVCViewsMustache descendant (smth. like TMVCAssetsMustache or TMVCViewsBoilerplateMustache) which can work with mem-cached mustache templates/partials directly. But descendant still contains not-needed properties related to files, folders, all this diskIO not mem-cached stuff.

Oh, please be informed that all test-coverage build in modern user-centered behavior-driven design manner (features, scenarios, cases, steps, *should, given*, when*, then*, you know - all this stuff...), not plain TDD. Will it be an issue? I want to keep this tests for future safe refactoring and degradation prevention.

The HTML Boilerplate is actively evolving (jQuery 3.0.0 integration is coming) so how I can keep it actual and aligned with this best HTTP practices and libraries If you merge all this code into mORMot trunk?

Thanks

#111 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-09 19:50:07

Release 1.1.0
  • minor "Cache-Control" parameters order changes

  • added bpoDelegateIndexToInheritedDefault to delegate "/" to Default() method

  • added bpoDelegate404ToInherited_404 to delegate "/404" requests to _404() method

The two new features allows you to delegate "/" and "/404.html" rendering to IMVCApplication .Default() and ._404() methods accordingly.

#112 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-09 16:26:53

edwinsn,

Yes, you can safely replace your TSQLHttpServer with inherited TBoilerplateHTTPServer.

I'm not sure about Linux because I used RT_RCDATA resource embedding into exe,

Please let me know when you check it under Linux or help to to make it more cross-platform

#113 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-09 13:48:04

Hi, edwinsn,

Boilerplate is just a template.
Good starting point for your web app.

You can use it with any favorite JS (Ember, Redux, Angular 2) or CSS frameworks (Modernizr is already embedded).

What mORMotBP do - is fix mangled GZip encoding, prevent clickjacking, GZip more different content types (with more compression level) then standard mORMot HTTP API, embed all your assets into exe, handle strict ssl, serve cross-origins for images or fonts, delegate assets transferring from application to low-level API, prebuild ETags or content-type definitions to save some CPU time, save your disk IO, give you different html cache strategies "from the box", etc...

For example if you find that usage of ETag can bring you Deployment risks and possible lawsuit from customers in USA or EU, you can switch to another effective cache strategy by changing bpoEnableCacheByETag to bpoEnableCacheByLastModified, etc.

So you can develop your web app locally in debug mode with Mustache templates in Assets folder and then embed everything into single production-ready exe.

Look into BoilerplateHTTPServer.pas TBoilerplateOptions for full list of available fixups and properties.

#114 Re: mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-09 11:13:17

I tested it and build it with XE8.

I will be appreciate, if you test it with other compilers, I'm not sure when Pre-Build events pipeline was introduced in Delphi.

#115 Re: mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-06-09 11:10:33

Oh, you right.
I missed that Map2Mab.dpr can embed symbol mapping it into exe.

#116 Re: mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-06-08 23:43:20

Hi ab,

Now all resources can be synlz-packed, embedded and returned in memcached manner with TBoilerplateHTTPServer class instance.

Even more: the assets can be pre-compressed and cached as files on production server, this allows delegate file transfers to low-level HTTP api (like http.sys) even gzipp'ed variants!

And now you can embed and unpack on production your .map / .mab project files (single file run/distribution strategy).

Unfortunately there is no way to ask TSynLog* get info not from disk but from or in-memory *.mab file during stack-trace logout.
Maybe ab will add this feature in the next releases.

#117 mORMot 1 » Memcached Boilerplate HTTP Server for Synopse mORMot Framework » 2016-06-08 23:32:25

Eugene Ilyin
Replies: 72

Hi all,

I'm happy to introduce you Boilerplate HTTP Server for Synopse mORMot Framework

marmot.jpg

GitHub: https://github.com/eugeneilyin/mORMotBP

Releases: mORMotBP/releases

Features
  • Fully aligned with HTML5 Boilerplate HTTP configs with more than 50+ options and properties.

  • Designed for Delphi 6 up to Delphi 10.4.1 Sydney, Kylix 3 (over CrossKylix), and FPC, targeting Windows or Linux, 32-bit or 64-bit architectures.

  • Embed all static assets into application file as highly compressed synlz archive (see assetslz and resedit tools). This allows to build single file distribution fully aligned with Instant Deployment approach.

  • Save your cloud hosting Disk IO operations by return all static assets from mem-cached repository.

  • Don't spend CPU cycles to assets compression, all your assets will be pre-compressed by assetslz tool.

  • Support GZip Level 9 maximum compression.

  • Support Zopfli compression (save up to 5-15% of traffic and delivery time compared to max GZip compression).

  • Support Brotli compression as per RFC 7932 (save another 15%-25% of traffic and delivery time compared to Zopfli compression).

  • Support Dynamic Brotli compression (save about 25% CPU usage compared to GZip on 64-bit systems and 10% less delivery time and traffic utilization).

  • Ability to delegate compressed files transferring to low-level HTTP.sys high-performance library (see .StaticRoot property) and free your server threads for more interesting work.

  • Support ETag/Last-Modified or more user-friendly Last-Modified/If-Modified-Since cache strategies.

  • Server-side Expires or Cache-Control: max-age cache strategies.

  • Different cache busting strategies (see bpoEnableCacheBusting and bpoEnableCacheBustingBeforeExt options).

  • Fix well-known mangled Accept-Encoding values in HTTP headers.

  • Block access to files that can expose sensitive information (see bpoDelegateBlocked option).

  • Apply many HTTP headers corrections following HTML5 Boilerplate settings.

  • Support Content Security Policy Level 2 / Level 3 (see CSP.pas unit for details).

  • Support external computable Assets and URL requests with content/file transfers and redirects.

  • Support 1490 MIME Types file extensions combined from IANA, Apache, and Mozilla.

  • You can safely replace anywhere your TSQLHttpServer with
    TBoilerplateHTTPServer = class(TSQLHttpServer)

All code is fully covered with tests supported by the mORMot Test Suite.

Fill free to use or give any feedback!

#118 mORMot 1 » Scope delivery from Default() to Mustache didn't come with respect » 2016-05-16 18:50:26

Eugene Ilyin
Replies: 1

Let's look into the TSynMustache.DateFmt mustache helper:

SynMustache.pas
...
class procedure TSynMustache.DateFmt(const Value: variant; out result: variant);
...
  with _Safe(Value)^ do
    if (Kind=dvArray) and (Count=2) and (TVarData(Values[0]).VType=varDate) then
      result := FormatDateTime(Values[1],TVarData(Values[0]).VDate) else
      SetVariantNull(result);

It's pretty simple: the DateFmt helper expects varDate as a first parameter and then call FormatDateTime.

Let's have a simple Mustache template with copyright notice and put it into Views\Default.html:

...
<div>(c) {{DateFmt Scope.Now,"yyyy"}}</div>
...

So, we are ready to use the full power of suggested MVVM approach.

Let's introduce simple Default interface-based method:

procedure TMyApplication.Default(var Scope: variant);
begin
  if VarIsEmptyOrNull(Scope) then
  begin
    TDocVariant.NewFast(Scope);
    Scope.Now := NowUTC;
  end;
end;

As expected Scope.Now contains current UTC server time stamp with appropriate varDate type.

You can expect that this Scope will be used as a data context for the mustache template rendering. Nope! smile

Scope will be transferred to Mustache Renderer through serialization/deserialization with no respect to initial variant types (like varDate).

Here how it works under the hood:

mORMotMVC.pas

procedure TMVCRendererAbstract.ExecuteCommand(aMethodIndex: integer);
...
              if not exec.ExecuteJson([fApplication.fFactoryEntry],pointer(fInput),WR,true) then
...
            WR.SetText(methodOutput);
...
            renderContext := _JsonFast(methodOutput);
            Renders(renderContext,action.ReturnedStatus,false);

As you see we serialize our Default(Scope) call into RawUTF8 methodOutput variable and then deserialize it into renderContext with _JsonFast.

As the result all accurate data types placed into Scope were lost and replaced with strings (but not varDate for example).

What we have as the result? DateFmt expects varDate but received varString and returns nothing.

How to resolve it?

Of course you can suggest smth like:

Scope.Copyright := '(c) 2016';
Scope.Year := 2016;

This is not good:

1. You mix data (Model) and template representation (View) concepts which provides representation leaks.
2. You didn't use nice DateFmt helper because there are no ways to pass varDate from interface-based MVVM pattern to mustache template scope.

This serialization/deserialization is so deep in the Mustache kernel processing that I didn't see easy ways how to resolve it.

Any suggestions?

#119 Re: mORMot 1 » How assign TDocVariant property with TDynArray values? » 2016-04-19 12:15:19

I want to return all logons, not the first one: OneFieldValues, not OneFieldValue.
Where did you take all this magic methods .InitArrayFrom, .AddJSONArraysAsJSONObject(), JSONObjectAsJSONArrays(). They didn't exist in 1.18.

I don't insist on all this vars. I'm just trying to find simple, clear, transparent way to feed TDocVariant properties with TDynArrays.
One line solution will be perfect, but seems not possible.
Any TDynArray provided by requests to REST need some additional vars/DA's/magic to return this arrays as JSON names with JSON objects:
{"name1",[v1, v2, ...], "name2":[v1, v2, v3, ...]}.

Seems like this is most compact and fast/optimal way without creating stub records:

procedure TTestServer.Logons(Ctxt: TSQLRestServerURIContext);
var
  LogonsDA: TDynArray;
  Logons: TRawUTF8DynArray;
  Writer: TTextWriter;
begin
  if OneFieldValues(TSQLAuthUser, 'LogonName', '', Logons) then
  begin
    TAutoFree.One(Writer, TTextWriter.CreateOwnedStream);
    Writer.AddString('{"logons":');
    LogonsDA.Init(TypeInfo(TRawUTF8DynArray), Logons);
    Writer.AddDynArrayJSON(LogonsDA);
    Writer.AddString('}');
    Ctxt.Returns(Writer.Text);
  end;
end;

When you requesting /root/logons this method provides expected answer: {"logons":["Admin","Supervisor","User"]}

Hm... 3 vars + auto locker and 14 lines of code not so compact, transparent and clear as:

if OneFieldValues(TSQLAuthUser, 'LogonName', '', Logons) then
  Ctxt.Returns(['logons', Logons]);

#120 Re: mORMot 1 » How assign TDocVariant property with TDynArray values? » 2016-04-19 09:55:48

Hi ab,

Thanks for fast replay,
Unfortunatelly _ObjFast(['names', _ArrFast(['ab', 'Eugene']) not appropriate because OneFieldValues() works with TRawUTF8DynArray, but not a static content.
Seem like usage of .InitArrayFrom is that what I need, but unfortunately it doesn't exist in 1.18 smile

I'm just playing with Auth* layer of framework and found that returning a simple list of logons in form of {'logons' : ['L1', 'L2', ...]} is not trivial.

Here is a tiny example:

  TTestServer = class(TSQLRestServerFullMemory)
    procedure Logons(Ctxt: TSQLRestServerURIContext);
  end;
procedure TTestServer.Logons(Ctxt: TSQLRestServerURIContext);
var
  Logons: TRawUTF8DynArray;
  Values: variant;
begin
  OneFieldValues(TSQLAuthUser, 'LogonName', '', Logons);
//  TDocVariantData(Values).InitArrayFrom(Logons, JSON_OPTIONS_FAST);
  Ctxt.ReturnsJson(_ObjFast(['logons', Values]));
end;
  FModel := TSQLModel.Create([]);
  FServer := TTestServer.Create(FModel, True);
  FServer.ServiceMethodByPassAuthentication('logons');
  FServer.CreateMissingTables;

I commented error line due to:
[dcc32 Error] TestServer.pas(26): E2003 Undeclared identifier: 'InitArrayFrom'

So, when I'm requesting /root/logons in browser, I'm expecting JSON logons in the form mentioned above.
Do you have any replacements for not existed InitArrayFrom?

Thanks

#121 Re: mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-04-19 02:43:43

Hi ab,

We can store all static resources not only html/css, but all related html/css/js/txt/svg files and mustache (Views) templates in the packed RCDATA-format in exe.
For this internal archive we can use any appropriate compression: .gz, .zip, .bz2, .7z, or even the most extreme in the world .paq8px.

What we need - is to decompress all this static data into .gz and plain files somewhere on production disk (may be My Documents folder / User temp folder / User AppData Roaming folder) - because we want http.sys to do all dirty work for file handling/sending. That's why we need both variants of each file (compressed .gz and not compressed plain) depends on client browser HTTP request 'Accept-Encoding: ' parameter.

We need to have two ways for file update checks (by modification DateTime or by content hash (SHA2-256 or SHA2-512 or even SHA3))
We need to have four options: check file updates on every request / periodically / subscribe to FileSystem about archive folder change modifications / don't check
We need to decide now to decompress all file in the first run: in main thread blocking manner or in background thread manner
We need to decide where store cache.db TSQLite3 file with FileName,Hash,DateTime table database and updated it according to folder changes.
Recomputed cache.db is like single fossil database without content, but only file descriptors - this database can be part of this archive too.

All this gives us zero maintenance (just run single .exe) and instant deployment (just replace .exe and restart) abilities.
Also we don't need Views mustache coping during production deployment - all this will be unpacked from RCDATA internal archive.

#122 mORMot 1 » How assign TDocVariant property with TDynArray values? » 2016-04-19 01:58:35

Eugene Ilyin
Replies: 5

Hi,

I didn't find in sources or documentation appropriate assignment routine or class method
for easy array of Type or T*DynArray assignment to TDocVariant properties with dvArray type.

Let's imagine, that  we received from TSQLRest instance RawUTF8 list of Names and another RawUTF8 list of Groups.

var
  Names: TRawUTF8DynArray;
  Groups: TRawUTF8DynArray;

Now we want to return this data to clients in JSON format like this one:

{"names":["ab","Eugene"],"groups":["G1","G2","G3","G4"]}

The most effective way will be _ObjFast() usage like this:

  Result := _ObjFast(['names', Names, 'groups', Groups]);

But this simple solution returns:

'{"names":null,"groups":null}'

May be it's because mORMor unable to do implicit conversion of T*DynArray's to TDocVariant properties with dvArray kind (TVariantDynArray).

So to build required JSON I have to move from this simple one line code to this solution:

var
  NamesDA: TDynArray; // dyn array wrapper must be declared before wrapped value
  Names: TRawUTF8DynArray;
  NamesCount: Integer;

  GroupsDA: TDynArray; // dyn array wrapper must be declared before wrapped value
  Groups: TRawUTF8DynArray;
  GroupCount: Integer;

  Content: Variant;
begin
  NamesDA.Init(TypeInfo(TRawUTF8DynArray), Names, @NamesCount);
  Names := TRawUTF8DynArrayFrom(['ab', 'Eugene']);
  NamesCount := 2;

  GroupsDA.Init(TypeInfo(TRawUTF8DynArray), Groups, @GroupCount);
  Groups := TRawUTF8DynArrayFrom(['G1', 'G2', 'G3', 'G4']);
  GroupCount := 4;

  TDocVariant.New(Content);
  Content.names := _JsonFast(NamesDA.SaveToJSON);
  Content.groups := _JsonFast(GroupsDA.SaveToJSON);

  Result := VariantSaveJSON(Content);
end;

As you see this is a much bigger then one line of code and required heavy _JsonFast / .SaveToJSON serialization/deserialization just for simple array assignment;

Question #1: Is a dyn array wrapper must be declared before a wrapped value?

Question #2: How to assign V.names with Names(or NamesDA) without intermediate JSON slow resource consuming serialization/deserialization?

Another way it to apply CoC principle and wrap result over record, but it looks heavy/synthetic for simple array to JSON assignment:

type
  TStub = packed record
    names: TRawUTF8DynArray;
    groups: TRawUTF8DynArray;
  end;
var
  Stub: TStub;
begin
  Stub.names := TRawUTF8DynArrayFrom(['ab', 'Eugene']);
  Stub.groups := TRawUTF8DynArrayFrom(['G1', 'G2', 'G3', 'G4']);
  Result := RecordSaveJSON(Stub, TypeInfo(TStub));
end;

Better, but not compact and ideal as expected and require fake stub record.

Question #3: In this example is record must be declared as packed?

Question #4: How get ideal _ObjFast(['names', Names, 'groups', Groups]); one line of code?

Thanks,

P.S.
Small off-topic: why it was decided not to create TBoolDynArray in SynCommons.pas(line 1030) in 1.18 ?
Is nobody never returns an array of Boolean to the clients?

#123 Re: mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-04-18 08:14:33

Hi ab,

Thanks for suggested patch, what is the secret of so fast code production? smile
I think that I spend an hour for accurate cache tests creation (only static file preparing in tests and 304-regression checks take a lot of time for me).

As you mentioned the patch is relevant only to custom THttpServer.
For http.sys solution the .gz file preparation and handling on user temp directory required (to use the best from two worlds: the windows kernel mode file transfer and GZiped compression with 304-ETags cache).

I think about some high level delegate in function Request(Context: THttpServerRequest): Cardinal; override; in TSQLHttpServer.

Something like TStaticResourcceSQLHttpServer or TCachedSQLHttpServer with ability:

  • Run separate thread in background and decompress RCDATA archive included into .exe as RCDATA resource to folder:
    CSIDL_APPDATA/EXE Camel Case Name With Spaces/Cache or
    USER_TEMP_FOLDER/EXE Camel Case Name With Spaces/Cache

  • Creates for each html/css/js files filename.ext.gz variant or get it from RCDATA directly

  • Keep in memory only hashed list of URI/File/FileGZ/ETag/Modified records but not the actual files

  • In Request(Context: THttpServerRequest) method before inherited behavior check URI against hashed list and return the file with http.sys kernel mode or send 304 when If-None-Match presence found.

#124 Re: mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-04-18 07:45:55

Hi esmondb,

esmondb wrote:

Probably the 'static content compression' feature needs to be turned on in IIS.

If you mean useHttpApiRegisteringURI HTTP server (THttpApiServer) based on http.sys windows kernel library - it doesn't gzip files too.
This is because of performance optimization introduced by mORmot overlord. He preferred delegate file transfer to windows kernel as the best possible performance solution.
But http.sys doesn't compress files too.

As for your IIS suggestion to use StaticCompressionModule (based on Inetsrv\Compstat.dll) this is IIS specific feature available in IIS only, but not in mORMot framework.
mORMot doesn't rely or require IIS presence on windows to run.

It's interesting that 30 - MVC Server example based on mustache templates gzip all content:

Request:
  Host: localhost:8092
  User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Accept-Language: en-US,en;q=0.5
  Accept-Encoding: gzip, deflate
  Etag: "23C0189A"

Responce:
  Accept-Encoding: gzip
  Content-Encoding: gzip
  Content-Length: 4258
  Content-Type: text/html; charset=UTF-8
...

So the mustache HTTP processor have to compute all page html/js/css content from templates and give it back as a cached data stream but not as a static file.
This decision excludes us from kernel windows http.sys sending but gives us gziped computed content based on mustache.

Interesting that blog.css request to /blog/.static/blog.css is gzipped too from 2.35 Kb file content to 0.98 Kb of actual network traffic.
The answer is that blog.css bypass windows kernel file sending and Context.OutContentType in THttpApiServer.Execute not equal HTTP_RESP_STATICFILE but "text/css".

Interesting, how is .static mustache data cached (like blog.css file):

  • Computed each time in every request to check against crc32c ETag value

  • Has some timeout to save CPU for unnecessary computation and do it periodically

  • Checks .static/* file date/time modification to update memory cache and recompute ETag

  • Subscribed to file system and hooked when file modified to update internal cache and recompute ETag

  • Has some lazy and permanent crc32c computation which is done only ones per first request and then always give the same static data from memory

#125 mORMot 1 » Why HTTP_RESP_STATICFILE (Ctxt.ReturnFile) not gzipped? » 2016-04-18 01:53:03

Eugene Ilyin
Replies: 10

Hi, I found a strange behavior of Ctxt.ReturnFile method in mORMot 1.18.1530 when worked under useHttpSocket (THttpServer) server.

Let's have a simple method-based service with 304-support:

TTestServer = class(TSQLRestServerFullMemory)
published
  procedure Q123(Ctxt: TSQLRestServerURIContext);
end;

procedure TTestServer.Q123(Ctxt: TSQLRestServerURIContext);
begin
  Ctxt.ReturnFile('D:\123.html', True);
end;

When you request /q123 the static content is received by browser but not GZipped (SynZip.CompressGZip is registered but not called).

The issue is in SynCrtSock.pas:

procedure THttpServer.Process
...
3681:    // handle case of direct sending of static file (as with http.sys)
3682:    if (Context.OutContent<>'') and (Context.OutContentType=HTTP_RESP_STATICFILE) then
...
3690:          Context.OutContentType := ''; // 'Content-type: ...' in OutCustomHeader

So we identify that we want to send static file and use Context.OutContentType for this need, after that we clear this field.

Then we call CompressDataAndWriteHeaders:

3732:    ClientSock.CompressDataAndWriteHeaders(Context.OutContentType,Context.fOutContent);

Then we call CompressDataAndGetHeaders:

procedure THttpSocket.CompressDataAndWriteHeaders
...
4116:    OutContentEncoding := CompressDataAndGetHeaders(fCompressHeader,fCompress,
4117:      OutContentType,OutContent);

And inside CompressDataAndGetHeaders:

function CompressDataAndGetHeaders
...
2376:  if (integer(Accepted)<>0) and (OutContentType<>'') and (Handled<>nil) then begin

As you see because .OutContentType was cleared and actual content type stored in .OutCustomHeader we didn't apply compression to response data.
That decision to use .OutContentType for 2 different ways as static file identification and as content type provider violates SRP (Single Responsibility Principle) and prevent compression of static files.

I see three possible solutions:

  1. Restore .OutContentType in 3690 from .OutCustomHeader;

  2. Provide separate approach for static files identification to keep SRP;

  3. Maybe I do smth wrong and mORMot has an alternative method to return compressed static files. smile

Thanks

P. S.
Btw, the 304 response works perfectly in "ETag / If-None-Match" combination, but Chrome didn't add If-None-Match: for second request, anyway this is an another issue (maybe because of cache control HTTP header absent in default HTTP response).

#126 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-29 22:12:56

Here is the Microsoft note for previous API version:

By default, HttpSendHttpResponse uses "Microsoft-HTTPAPI/1.0" as the "Server:" header. If an application specifies a server header in a response, that value is placed as the first part of the server header, followed by a space and then "Microsoft-HTTPAPI/1.0".

#127 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-29 22:05:01

Hi mpv,
Let me know if you success with it.
Also I've tried to find this char sequence in http.sys to set with OllyDbg control break-point, but no luck yet.

#128 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-28 15:58:15

From the IIS perspective, according to this: there are no ways to remove Microsoft-HTTPAPI/2.0 postfix.

DisableServerHeader only applies to the Server header added by
HTTP.SYS IFF an HTTP API application does not set the Server header.
IIS intentionally sets and removes the Server header, so that registry
key has no relevance on IIS6.

Most people ask about removing the Server header because they ran some
security scanner that complains about it, or they read somewhere that
removing Server header "improves" security, so they want to do it.
However, since removing the Server header does neither, IIS team does
not consider the Server header a problem and its remove a solution, so
there is no built-in feature anywhere. You will have to search for the
add-on modules to remove the header but that may have other side
effects.

#129 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-28 14:15:33

Just to illustrate what I'm talking about, when you didn't remove http.sys "Server: ... Microsoft-HTTPAPI/2.0»" postfix:

This can occurred from time to time:

   Microsoft Security Bulletin MS15-034 - Critical
   Vulnerability in HTTP.sys Could Allow Remote Code Execution (3042553)

And that what we have aftermath:

2e0059faf6c24802a3a469bbb8ac13cd.png

Or even that:

0ab70b906d954275a33c1c5195e705d1.png

Just one of the version of this exploit looks like very simple http-request for me:

GET /%7Bwelcome.png HTTP/1.1
User-Agent: Wget/1.13.4 (linux-gnu)
Accept: */*
Host: [server-ip]
Connection: Keep-Alive
Range: bytes=18-18446744073709551615

Again, maybe I too paranoid here, but showing to everybody in the world that my solution is using specific OS, Framework, and http.sys looks a little bit nervous.

#130 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-28 13:12:01

Hi, ab,

I use single exe-file for prod server + single db-file for database - interface and implementation is merged in one simple, light, portable solution (just run exe-file on amazon node or any other dedicated service and Wuala! is less than a second production deployment has been complete).

If this solution will be available to clients without load-balancer: any hacker will know OS, framework, and used core windows library just by reading the "Server: " line in any browser.

Do you think that there are no security concerns here and Windows (or http.sys or mORMot) zero-day vulnerabilities are not applicable?

I have an answer here. What is your experience and feedback from other developers following not changeable "Server: " post-fix in production systems?
It will be nice if I'm just being to paranoid (I have no so many real hackers attack to be ensure that most of security breaches are closed).

Thanks

P.S. as I understand this synopse.info forum interface is "server:lighttpd/1.4.26" but forum functional implementation is hidden somewhere deeper.

#131 Re: mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-27 19:13:30

Hi ab, thanks for reply.

It's not a HTTP packet size related issue ("corresponding to a MTU of 1500 bytes" SynCrtSock.pas: 615)

I'm fully understand that http.sys is a kernel-level subsystem used by mORMot or IIS.
But many people faced with "Server: " changes/hides issue when use IIS.
That why I mentioned links with advice how to fix it on http.sys level.

I tried to [comment/change/extend/make empty] the code that you mentioned, but the append of 'Microsoft-HTTPAPI/2.0' is happen on the http.sys level an suggested registry FIX doesn't help to remove it.

As for my primary interest - it's security:

    I'm happy to see mORMot on the "Server: " HTTP Response Header, but I'm totally not happy to tell operational system and used API additionally to end-users.

Of course Load Balancer (Route 53 / nginx / Apache) can hide it from end-user, but for dev/qa/uat env. I'm prefer not to use it when show pre-prod demos to clients.

The framework seems very mature and it's strange for me that nobody tries to remove MS post-fix in "Server: " http-response.

As for long-term solution (and possible migration to Linux kernel in future) I decide to use useHttpSocket and forget about http.sys option (even with possible performance penalties of such decision).

#132 mORMot 1 » How to remove 'Microsoft-HTTPAPI/2.0'? » 2016-03-27 17:14:46

Eugene Ilyin
Replies: 11

Hi,

I'm use Win 8.1 Pro as my dev env for mORMot 1.18.1530 HTTP Server application.

As you know, when your TSQLHttpServer is choose to run over THttpApiServer, the http.sys always add 'Server: ... Microsoft-HTTPAPI/2.0' into HTTP response header.

According to:
   http://marc-lognoul.me/2012/12/04/share … y-reasons/
   https://blogs.msdn.microsoft.com/varunm … e-headers/
   http://blog.paulbouwer.com/2013/01/09/a … ows-azure/
   http://stackoverflow.com/questions/1178 … eader-iis7

To remove this piece in response you need to add:

    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters

        DisableServerHeader : REG_DWORD = 1

Then restart http.sys service (interesting that this service is not visible under services.msc)

    net stop http
    net start http

The issue is that all this manipulations doesn't help me to exclude this 'Microsoft-HTTPAPI/2.0' in HTTP Responses sad

Even put it into .OutCustomHeaders doesn't help.

function TSampleHTTPServer.Request(Context: THttpServerRequest): Cardinal;
begin
  Result := inherited Request(Context);
  Context.OutCustomHeaders := Context.OutCustomHeaders + 'Server: Sample'#$D#$A;
end;

Anything else can help me to fix it or useHttpSocket is the only option?

Thanks

Board footer

Powered by FluxBB