source upload

This commit is contained in:
Razor12911
2022-01-17 22:16:47 +02:00
parent 12936d065b
commit 098e8c48de
1778 changed files with 1206749 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.9" jmeter="3.0 r1743807">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan - MormotREST.exe" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1464256651000</longProp>
<longProp name="ThreadGroup.end_time">1464256651000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Headers" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Cache-Control</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Pragma</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json; charset=UTF-8</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.HelloWorld" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.HelloWorld</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.Sum" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">[78.6,13.3]</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.Sum</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.GetCustomRecord" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value"></stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.GetCustomRecord</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.SendCustomRecord" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">[ &#xd;
{ &#xd;
&quot;ResultCode&quot;:200,&#xd;
&quot;ResultStr&quot;:&quot;Awesome&quot;,&#xd;
&quot;ResultArray&quot;:[ &#xd;
&quot;str_0&quot;,&#xd;
&quot;str_1&quot;,&#xd;
&quot;str_2&quot;&#xd;
],&#xd;
&quot;ResultTimeStamp&quot;:&quot;2016-06-01T19:42:14&quot;&#xd;
}&#xd;
]</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.SendCustomRecord</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.SendMultipleCustomRecords" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">[ &#xd;
{ &#xd;
&quot;ResultCode&quot;:200,&#xd;
&quot;ResultStr&quot;:&quot;Awesome&quot;,&#xd;
&quot;ResultArray&quot;:[ &#xd;
&quot;str_0&quot;,&#xd;
&quot;str_1&quot;,&#xd;
&quot;str_2&quot;&#xd;
],&#xd;
&quot;ResultTimeStamp&quot;:&quot;2016-06-01T19:42:14&quot;&#xd;
},&#xd;
{&#xd;
&quot;SimpleString&quot;: &quot;Simple string, Простая строка&quot;,&#xd;
&quot;SimpleInteger&quot;:100500,&#xd;
&quot;AnotherRecord&quot;: {&#xd;
&quot;ResultCode&quot;:200,&#xd;
&quot;ResultStr&quot;:&quot;Awesome&quot;,&#xd;
&quot;ResultArray&quot;:[ &#xd;
&quot;str_0&quot;,&#xd;
&quot;str_1&quot;,&#xd;
&quot;str_2&quot;&#xd;
],&#xd;
&quot;ResultTimeStamp&quot;:&quot;2016-06-01T19:42:14&quot; &#xd;
}&#xd;
}&#xd;
]</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.SendMultipleCustomRecords</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="/service/RestMethods.GetMethodCustomResult" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">127.0.0.1</stringProp>
<stringProp name="HTTPSampler.port">777</stringProp>
<stringProp name="HTTPSampler.connect_timeout">500</stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/service/RestMethods.GetMethodCustomResult</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@@ -0,0 +1,64 @@
# mORMot.REST
Testing mORMot REST capabilities, a project from George.
FPC version by Alf, retrieved from https://github.com/LongDirtyAnimAlf/mORMot.REST
# Presentation
It's my first meeting with mORMot, so, something may be unoptimized or implemented wrong.
Feel free to post your suggestions. Don't forget that this is not final build, there is a lot to do.
I use REST services via interfaces, IMO it's more friendly for developers.
You may find that project group contain both server and client projects.
In addition, you can use .jmx files with apache jmeter to simulate multiclient heavy load via HTTP connection.
![screenshot](https://raw.githubusercontent.com/GitStorageOne/mORMot.REST/master/screenshot.png)
# 2do
Category / Status | Feature
--- | ---
**mORMot REST architecture** |
`done` | Services via interfaces
**Protocol usage** |
`done` | HTTP (socket)
`done` | HTTP (http.sys)
`done` | HTTPS (http.sys + SSL)
`done` | HTTP (http.sys + AES-CFB 256)
`done` | HTTP (web socket)
`done` | WebSocket (bidirectional, JSON)
`done` | WebSocket (bidirectional, Binary)
`done` | WebSocket (bidirectional, Binary + AES)
`done` | Named pipe (on local PC or LAN network)
**Method interfaces** |
`done` | IRestMethods (no session support).
`.` | IRestMethodsEx (session support, callback support).
**Authentication schemes** |
`done` | No authentication
`done` | Default
`done` | None
`done` | HTTP Basic
`done` | SSPI
**Authorization** |
`done` | Allow all to execute any method
`done` | Deny all to execute any method
`done` | Follow method / group settings
**jmx test plan** (without authentication) |
`done` | Method call via URL
`done` | Send parameters via body
`done` | Method result as JSON object
`done` | Send JSON objects via body
`done` | Send multiple JSON objects via body
`done` | Custom method result
`done` | Detect empty parameters from server side
**Other** |
`done` | Improve certificate installation to support windows 7 (for https).
`.` | HTTP/s proxy bypass
`.` | Improve speed while log output enabled
`?` | Apply roles and groups without restart (if possible, not sure, need check)
`(optional)` | Refactoring
Official Synopse mORMot repository [available here][mormot-repo].
Official Synopse mORMot documentation [available here][mormot-docs].
[mormot-repo]: <https://github.com/synopse/mORMot>
[mormot-docs]: <http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html>

View File

@@ -0,0 +1,261 @@
object Form1: TForm1
Left = 0
Top = 0
Caption = 'CL.mORMot REST test'
ClientHeight = 469
ClientWidth = 1174
Color = clBtnFace
Constraints.MinHeight = 205
Constraints.MinWidth = 815
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignSize = (
1174
469)
PixelsPerInch = 96
TextHeight = 13
object LabelAuthenticationMode: TLabel
Left = 37
Top = 38
Width = 74
Height = 13
Caption = 'Authentication:'
end
object LabelProtocol: TLabel
Left = 68
Top = 11
Width = 43
Height = 13
Caption = 'Protocol:'
end
object Label1: TLabel
Left = 799
Top = 11
Width = 80
Height = 13
Anchors = [akTop, akRight]
Caption = 'Server and port:'
end
object Label2: TLabel
Left = 780
Top = 38
Width = 99
Height = 13
Anchors = [akTop, akRight]
Caption = 'Login and password:'
end
object EditServerAdress: TEdit
Left = 885
Top = 8
Width = 99
Height = 21
Anchors = [akTop, akRight]
TabOrder = 0
Text = '127.0.0.1'
TextHint = 'Server adress (IP or HostName)'
end
object EditServerPort: TEdit
Left = 990
Top = 8
Width = 65
Height = 21
Anchors = [akTop, akRight]
TabOrder = 1
Text = '777'
TextHint = 'Port'
end
object ButtonStartStop: TButton
Left = 1061
Top = 16
Width = 105
Height = 33
Anchors = [akTop, akRight]
Caption = 'Start client'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 2
OnClick = ButtonStartStopClick
end
object GroupBoxIRestMethods: TGroupBox
Left = 8
Top = 75
Width = 720
Height = 54
Caption = 'IRestMethods (InstanceImplementation = sicSingle)'
TabOrder = 3
object ButtonMethHelloWorld: TButton
Left = 8
Top = 19
Width = 81
Height = 23
Caption = 'HelloWorld'
TabOrder = 0
OnClick = ButtonMethHelloWorldClick
end
object ButtonMethSum: TButton
Left = 95
Top = 19
Width = 50
Height = 23
Caption = 'Sum'
TabOrder = 1
OnClick = ButtonMethSumClick
end
object ButtonGetCustomRecord: TButton
Left = 151
Top = 19
Width = 137
Height = 23
Caption = 'GetCustomRecord'
TabOrder = 2
OnClick = ButtonGetCustomRecordClick
end
object ButtonMethSendCustomRecord: TButton
Left = 294
Top = 19
Width = 115
Height = 23
Caption = 'SendCustomRecord'
TabOrder = 3
OnClick = ButtonMethSendCustomRecordClick
end
object ButtonMethSendMultipleCustomRecords: TButton
Left = 415
Top = 19
Width = 154
Height = 23
Caption = 'SendMultipleCustomRecords'
TabOrder = 4
OnClick = ButtonMethSendMultipleCustomRecordsClick
end
object ButtonMethGetMethodCustomResult: TButton
Left = 572
Top = 19
Width = 141
Height = 23
Caption = 'GetMethodCustomResult'
TabOrder = 5
OnClick = ButtonMethGetMethodCustomResultClick
end
end
object ComboBoxAuthentication: TComboBox
Left = 117
Top = 35
Width = 388
Height = 21
Style = csDropDownList
ItemIndex = 1
TabOrder = 4
Text = 'Default'
OnChange = ComboBoxAuthenticationChange
Items.Strings = (
'No authentication'
'Default'
'None'
'HttpBasic'
'SSPI')
end
object MemoLog: TMemo
Left = 8
Top = 135
Width = 1158
Height = 298
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Consolas'
Font.Style = []
ParentFont = False
ScrollBars = ssVertical
TabOrder = 5
end
object ButtonCLS: TButton
Left = 10
Top = 439
Width = 38
Height = 22
Anchors = [akLeft, akBottom]
Caption = 'CLS'
TabOrder = 6
OnClick = ButtonCLSClick
end
object CheckBoxAutoScroll: TCheckBox
Left = 57
Top = 441
Width = 69
Height = 17
Anchors = [akLeft, akBottom]
Caption = 'auto scroll'
Checked = True
State = cbChecked
TabOrder = 7
end
object CheckBoxDisableLog: TCheckBox
Left = 133
Top = 441
Width = 203
Height = 17
Anchors = [akLeft, akBottom]
Caption = 'disable log (for max performance test)'
TabOrder = 8
OnClick = CheckBoxDisableLogClick
end
object ComboBoxProtocol: TComboBox
Left = 117
Top = 8
Width = 388
Height = 21
Style = csDropDownList
ItemIndex = 7
TabOrder = 9
Text = ' '#8250' WebSocket ( bidirectional, binary + AES-CFB 256)'
OnChange = ComboBoxProtocolChange
Items.Strings = (
'HTTP ( socket )'
' '#8250' HTTP ( fast http.sys )'
' '#8250#8250' HTTPS ( fast http.sys + SSL )'
' '#8250#8250' HTTP ( fast http.sys + AES-CFB 256 )'
'HTTP ( web socket )'
' '#8250' WebSocket ( bidirectional, JSON )'
' '#8250' WebSocket ( bidirectional, binary )'
' '#8250' WebSocket ( bidirectional, binary + AES-CFB 256)'
'Named pipe')
end
object EditUserLogin: TEdit
Left = 885
Top = 35
Width = 99
Height = 21
Anchors = [akTop, akRight]
TabOrder = 10
Text = 'George'
TextHint = 'Login'
end
object EditUserPassword: TEdit
Left = 990
Top = 35
Width = 65
Height = 21
Anchors = [akTop, akRight]
TabOrder = 11
Text = '123'
TextHint = 'Password'
end
object TimerRefreshLogMemo: TTimer
OnTimer = TimerRefreshLogMemoTimer
Left = 56
Top = 144
end
end

View File

@@ -0,0 +1,263 @@
object Form1: TForm1
Left = 250
Height = 265
Top = 247
Width = 815
Caption = 'CL.mORMot REST test'
ClientHeight = 265
ClientWidth = 815
Color = clBtnFace
Constraints.MinHeight = 205
Constraints.MinWidth = 815
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
OnCreate = FormCreate
OnDestroy = FormDestroy
Position = poScreenCenter
LCLVersion = '1.7'
object LabelAuthenticationMode: TLabel
Left = 37
Height = 13
Top = 38
Width = 74
Caption = 'Authentication:'
ParentColor = False
end
object LabelProtocol: TLabel
Left = 68
Height = 13
Top = 11
Width = 43
Caption = 'Protocol:'
ParentColor = False
end
object Label1: TLabel
Left = 440
Height = 13
Top = 11
Width = 80
Anchors = [akTop, akRight]
Caption = 'Server and port:'
ParentColor = False
end
object Label2: TLabel
Left = 421
Height = 13
Top = 38
Width = 99
Anchors = [akTop, akRight]
Caption = 'Login and password:'
ParentColor = False
end
object EditServerAdress: TEdit
Left = 526
Height = 21
Top = 8
Width = 99
Anchors = [akTop, akRight]
TabOrder = 0
Text = '127.0.0.1'
TextHint = 'Server adress (IP or HostName)'
end
object EditServerPort: TEdit
Left = 631
Height = 21
Top = 8
Width = 65
Anchors = [akTop, akRight]
TabOrder = 1
Text = '777'
TextHint = 'Port'
end
object ButtonStartStop: TButton
Left = 702
Height = 33
Top = 16
Width = 105
Anchors = [akTop, akRight]
Caption = 'Start client'
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
OnClick = ButtonStartStopClick
ParentFont = False
TabOrder = 2
end
object GroupBoxIRestMethods: TGroupBox
Left = 8
Height = 69
Top = 75
Width = 720
Caption = 'IRestMethods (InstanceImplementation = sicSingle)'
ClientHeight = 51
ClientWidth = 716
TabOrder = 3
object ButtonMethHelloWorld: TButton
Left = 8
Height = 23
Top = 19
Width = 81
Caption = 'HelloWorld'
OnClick = ButtonMethHelloWorldClick
TabOrder = 0
end
object ButtonMethSum: TButton
Left = 95
Height = 23
Top = 19
Width = 50
Caption = 'Sum'
OnClick = ButtonMethSumClick
TabOrder = 1
end
object ButtonGetCustomRecord: TButton
Left = 151
Height = 23
Top = 19
Width = 137
Caption = 'GetCustomRecord'
OnClick = ButtonGetCustomRecordClick
TabOrder = 2
end
object ButtonMethSendCustomRecord: TButton
Left = 294
Height = 23
Top = 19
Width = 115
Caption = 'SendCustomRecord'
OnClick = ButtonMethSendCustomRecordClick
TabOrder = 3
end
object ButtonMethSendMultipleCustomRecords: TButton
Left = 415
Height = 23
Top = 19
Width = 154
Caption = 'SendMultipleCustomRecords'
OnClick = ButtonMethSendMultipleCustomRecordsClick
TabOrder = 4
end
object ButtonMethGetMethodCustomResult: TButton
Left = 572
Height = 23
Top = 19
Width = 141
Caption = 'GetMethodCustomResult'
OnClick = ButtonMethGetMethodCustomResultClick
TabOrder = 5
end
end
object ComboBoxAuthentication: TComboBox
Left = 117
Height = 21
Top = 35
Width = 292
ItemHeight = 13
ItemIndex = 1
Items.Strings = (
'No authentication'
'Default'
'None'
'HttpBasic'
'SSPI'
)
OnChange = ComboBoxAuthenticationChange
Style = csDropDownList
TabOrder = 4
Text = 'Default'
end
object MemoLog: TMemo
Left = 8
Height = 77
Top = 152
Width = 799
Anchors = [akTop, akLeft, akRight, akBottom]
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Consolas'
ParentFont = False
ScrollBars = ssVertical
TabOrder = 5
end
object ButtonCLS: TButton
Left = 10
Height = 22
Top = 235
Width = 38
Anchors = [akLeft, akBottom]
Caption = 'CLS'
OnClick = ButtonCLSClick
TabOrder = 6
end
object CheckBoxAutoScroll: TCheckBox
Left = 57
Height = 19
Top = 235
Width = 69
Anchors = [akLeft, akBottom]
Caption = 'auto scroll'
Checked = True
State = cbChecked
TabOrder = 7
end
object CheckBoxDisableLog: TCheckBox
Left = 133
Height = 19
Top = 235
Width = 204
Anchors = [akLeft, akBottom]
Caption = 'disable log (for max performance test)'
OnClick = CheckBoxDisableLogClick
TabOrder = 8
end
object ComboBoxProtocol: TComboBox
Left = 117
Height = 21
Top = 8
Width = 292
ItemHeight = 13
ItemIndex = 7
Items.Strings = (
'HTTP ( socket )'
' HTTP ( fast http.sys )'
' HTTPS ( fast http.sys + SSL )'
' HTTP ( fast http.sys + AES-CFB 256 )'
'HTTP ( web socket )'
' WebSocket ( bidirectional, JSON )'
' WebSocket ( bidirectional, binary )'
' WebSocket ( bidirectional, binary + AES-CFB 256)'
'Named pipe'
)
OnChange = ComboBoxProtocolChange
Style = csDropDownList
TabOrder = 9
Text = ' WebSocket ( bidirectional, binary + AES-CFB 256)'
end
object EditUserLogin: TEdit
Left = 526
Height = 21
Top = 35
Width = 99
Anchors = [akTop, akRight]
TabOrder = 10
Text = 'George'
TextHint = 'Login'
end
object EditUserPassword: TEdit
Left = 631
Height = 21
Top = 35
Width = 65
Anchors = [akTop, akRight]
TabOrder = 11
Text = '123'
TextHint = 'Password'
end
object TimerRefreshLogMemo: TTimer
OnTimer = TimerRefreshLogMemoTimer
left = 56
top = 144
end
end

View File

@@ -0,0 +1,356 @@
unit RestClientFormUnit;
// mORMot RESTful API test case 1.02
interface
uses
// RTL
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF}
{$ifdef FPC}
LCLType,
{$endif}
Messages,
SysUtils,
Classes,
Generics.Collections,
Forms,
Dialogs,
Controls,
StdCtrls,
ExtCtrls,
// mORMot
mORMot,
SynLog,
SynCommons,
mORMotHttpClient, // tmp
// Custom
RestClientUnit,
RestMethodsInterfaceUnit;
type
TCustomRecord = record helper for rCustomRecord
procedure FillFromClient();
end;
lClientAction = (Auto, Start, Stop, Restart);
TForm1 = class(TForm)
EditServerAdress: TEdit;
EditServerPort: TEdit;
ButtonStartStop: TButton;
TimerRefreshLogMemo: TTimer;
GroupBoxIRestMethods: TGroupBox;
ButtonMethHelloWorld: TButton;
ButtonMethSum: TButton;
ButtonGetCustomRecord: TButton;
LabelAuthenticationMode: TLabel;
ComboBoxAuthentication: TComboBox;
ButtonMethSendCustomRecord: TButton;
MemoLog: TMemo;
ButtonCLS: TButton;
CheckBoxAutoScroll: TCheckBox;
CheckBoxDisableLog: TCheckBox;
ButtonMethSendMultipleCustomRecords: TButton;
LabelProtocol: TLabel;
ComboBoxProtocol: TComboBox;
EditUserLogin: TEdit;
EditUserPassword: TEdit;
Label1: TLabel;
Label2: TLabel;
ButtonMethGetMethodCustomResult: TButton;
procedure FormCreate(Sender: TObject);
procedure ButtonStartStopClick(Sender: TObject);
procedure ButtonCLSClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure TimerRefreshLogMemoTimer(Sender: TObject);
procedure ButtonMethHelloWorldClick(Sender: TObject);
procedure ButtonMethSumClick(Sender: TObject);
procedure ButtonGetCustomRecordClick(Sender: TObject);
procedure ComboBoxAuthenticationChange(Sender: TObject);
procedure ButtonMethSendCustomRecordClick(Sender: TObject);
procedure CheckBoxDisableLogClick(Sender: TObject);
procedure ButtonMethSendMultipleCustomRecordsClick(Sender: TObject);
procedure ComboBoxProtocolChange(Sender: TObject);
procedure ButtonMethGetMethodCustomResultClick(Sender: TObject);
private
{ Private declarations }
function LogEvent(Sender: TTextWriter; Level: TSynLogInfo; const Text: RawUTF8): boolean;
procedure StartStopClient(ClientAction: lClientAction = Auto);
public
{ Public declarations }
end;
TLocalLog = class
Level: TSynLogInfo;
Text: RawUTF8;
end;
var
Form1: TForm1;
{$ifdef FPC}
LogThreadSafeList: TList<TLocalLog>;
{$else}
LogThreadSafeList: TThreadList<TLocalLog>;
{$endif}
implementation
{$ifdef FPC}
{$R *.lfm}
{$else}
{$R *.dfm}
{$endif}
{ TCustomResult }
procedure TCustomRecord.FillFromClient();
var
i: Integer;
begin
ResultCode := 200;
ResultStr := 'Awesome';
ResultTimeStamp := Now();
SetLength(ResultArray, 3);
for i := 0 to 2 do
ResultArray[i] := 'str_' + i.ToString();
end;
{ Form1 }
// On Form1 create
procedure TForm1.FormCreate(Sender: TObject);
begin
// Create thread safe List with log data class
{$ifdef FPC}
LogThreadSafeList := TList<TLocalLog>.Create();
{$else}
LogThreadSafeList := TThreadList<TLocalLog>.Create();
{$endif}
// Enable logging
with TSQLLog.Family do
begin
Level := LOG_VERBOSE;
EchoCustom := LogEvent;
NoFile := True;
end;
end;
// On Form1 destory
procedure TForm1.FormDestroy(Sender: TObject);
var
i: Integer;
List: TList<TLocalLog>;
begin
// Clear and destroy LogThreadSafeList
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList();
{$endif}
for i := 0 to List.Count - 1 do
List.Items[i].Free;
List.Clear;
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
FreeAndNil(LogThreadSafeList);
end;
{ CLIENT EVENTS, START / STOP }
// Depends on the client status, start or stop client (create or destroy objects)
procedure TForm1.StartStopClient(ClientAction: lClientAction = Auto);
var
pClientCreated: boolean;
ClientSettings: rClientSettings;
begin
pClientCreated := RestClient.Initialized;
// Unload current client if required
RestClient.DeInitialize();
// Create client if required
if ((ClientAction = lClientAction.Auto) and not pClientCreated) or ((ClientAction = lClientAction.Restart) and pClientCreated) or (ClientAction = lClientAction.Start) then
begin
ClientSettings.Protocol := lProtocol(ComboBoxProtocol.ItemIndex);
ClientSettings.AuthMode := lAuthenticationMode(ComboBoxAuthentication.ItemIndex);;
ClientSettings.HostOrIP := EditServerAdress.Text;
ClientSettings.Port := EditServerPort.Text;
ClientSettings.UserLogin := StringToUTF8(EditUserLogin.Text);
ClientSettings.UserPassword := StringToUTF8(EditUserPassword.Text);
RestClient.Initialize(ClientSettings);
end;
end;
// Processing mORMot log event
function TForm1.LogEvent(Sender: TTextWriter; Level: TSynLogInfo; const Text: RawUTF8): boolean;
var
List: TList<TLocalLog>;
LogEventData: TLocalLog;
begin
Result := False;
if Assigned(LogThreadSafeList) then
begin
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList;
{$endif}
try
LogEventData := TLocalLog.Create();
LogEventData.Level := Level;
LogEventData.Text := Text;
List.Add(LogEventData);
Result := True;
finally
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
end;
end;
end;
{ UI }
// Grabbing new events from thread safe list
procedure TForm1.TimerRefreshLogMemoTimer(Sender: TObject);
var
List: TList<TLocalLog>;
i: Integer;
begin
if Assigned(LogThreadSafeList) then
begin
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList;
{$endif}
try
if Assigned(Form1) and not Application.Terminated and (List.Count > 0) then
begin
for i := 0 to List.Count - 1 do
begin
Form1.MemoLog.Lines.BeginUpdate();
Form1.MemoLog.Lines.Add(string(List.Items[i].Text));
Form1.MemoLog.Lines.EndUpdate();
List.Items[i].Free;
end;
List.Clear();
{$IFDEF MSWINDOWS}
if CheckBoxAutoScroll.Checked then
SendMessage(Form1.MemoLog.Handle, WM_VSCROLL, SB_BOTTOM, 0);
{$ENDIF}
end;
finally
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
end;
end;
if RestClient.Initialized then
ButtonStartStop.Caption := 'Stop client'
else
ButtonStartStop.Caption := 'Start client';
end;
// Changing client protocol
procedure TForm1.ComboBoxProtocolChange(Sender: TObject);
begin
{$IFDEF MSWINDOWS}
EditServerPort.Enabled := lProtocol(ComboBoxProtocol.ItemIndex) <> lProtocol.NamedPipe;
{$endif}
StartStopClient(Restart);
end;
// Changing client authentication mode
procedure TForm1.ComboBoxAuthenticationChange(Sender: TObject);
begin
{$IFDEF MSWINDOWS}
if lAuthenticationMode(ComboBoxAuthentication.ItemIndex) = lAuthenticationMode.SSPI then
begin
EditUserLogin.Text := '';
EditUserPassword.Text := '';
end;
{$endif}
StartStopClient(Restart);
end;
// Clears log memo
procedure TForm1.ButtonCLSClick(Sender: TObject);
begin
MemoLog.Clear;
end;
// Button start stop client
procedure TForm1.ButtonStartStopClick(Sender: TObject);
begin
StartStopClient();
end;
// Checkbox Enable/Disable logging to memo (slow down performance when enabled)
procedure TForm1.CheckBoxDisableLogClick(Sender: TObject);
begin
if not CheckBoxDisableLog.Checked then
TSQLLog.Family.Level := LOG_VERBOSE
else
TSQLLog.Family.Level := [];
end;
{ IRestMethods execution }
procedure TForm1.ButtonMethHelloWorldClick(Sender: TObject);
begin
if Assigned(RestClient.RestMethods) then
RestClient.RestMethods.HelloWorld();
end;
procedure TForm1.ButtonMethSendCustomRecordClick(Sender: TObject);
var
CustomResult: rCustomRecord;
begin
if Assigned(RestClient.RestMethods) then
begin
CustomResult.FillFromClient();
RestClient.RestMethods.SendCustomRecord(CustomResult);
end;
end;
procedure TForm1.ButtonMethSumClick(Sender: TObject);
begin
if Assigned(RestClient.RestMethods) then
RestClient.RestMethods.Sum(Random(100) + 0.6, Random(100) + 0.3);
end;
procedure TForm1.ButtonGetCustomRecordClick(Sender: TObject);
var
CustomResult: rCustomRecord;
begin
if Assigned(RestClient.RestMethods) then
CustomResult := RestClient.RestMethods.GetCustomRecord();
end;
procedure TForm1.ButtonMethSendMultipleCustomRecordsClick(Sender: TObject);
var
cr: rCustomRecord;
ccr: rCustomComplicatedRecord;
begin
if Assigned(RestClient.RestMethods) then
begin
cr.FillFromClient();
ccr.SimpleString := 'Simple string, Простая строка, 単純な文字列';
ccr.SimpleInteger := 100500;
ccr.AnotherRecord := cr;
RestClient.RestMethods.SendMultipleCustomRecords(cr, ccr);
end;
end;
procedure TForm1.ButtonMethGetMethodCustomResultClick(Sender: TObject);
var
ServiceCustomAnswer: TServiceCustomAnswer;
begin
if Assigned(RestClient.RestMethods) then
ServiceCustomAnswer := RestClient.RestMethods.GetMethodCustomResult();
end;
end.

View File

@@ -0,0 +1,250 @@
unit RestClientUnit;
interface
uses
// RTL
SysUtils,
Classes,
StrUtils,
Dialogs,
// mORMot
mORMot,
mORMotHttpClient,
SynCommons,
// Custom
RestMethodsInterfaceUnit;
type
{$ifndef MSWINDOWS}
TSQLHttpClientWinHTTP=TSQLHttpClientWinSock;
{$endif}
lProtocol = (HTTP_Socket, HTTP_HTTPsys{$ifdef MSWINDOWS}, HTTPsys_SSL{$endif}, HTTPsys_AES, HTTP_WebSocket, WebSocketBidir_JSON, WebSocketBidir_Binary, WebSocketBidir_BinaryAES{$ifdef MSWINDOWS}, NamedPipe{$endif});
lAuthenticationMode = (NoAuthentication, Default, None, HttpBasic{$ifdef MSWINDOWS}, SSPI{$endif});
rClientSettings = record
Protocol: lProtocol;
AuthMode: lAuthenticationMode;
HostOrIP: string;
Port: string;
UserLogin: RawUTF8;
UserPassword: RawUTF8;
end;
rConnectionSettings = record
SendTimeout: Cardinal;
ReceiveTimeout: Cardinal;
ConnectTimeout: Cardinal;
procedure LanNetworkPreset();
end;
tRestClient = class
private
fModel: TSQLModel;
fClientSettings: rClientSettings;
fConnectionSettings: rConnectionSettings;
fInitialized: boolean;
public
fClient: TSQLRestClientURI;
RestMethods: IRestMethods;
property Initialized: boolean read fInitialized;
constructor Create();
destructor Destroy(); override;
function Initialize(ClSettings: rClientSettings; ConSettings: rConnectionSettings): boolean; overload;
function Initialize(ClSettings: rClientSettings): boolean; overload;
procedure DeInitialize();
end;
var
RestClient: tRestClient;
implementation
{ rConnectionSettings }
procedure rConnectionSettings.LanNetworkPreset();
begin
SendTimeout := 5000;
ReceiveTimeout := 5000;
ConnectTimeout := 10000;
end;
{ tRestClient }
constructor tRestClient.Create();
begin
fConnectionSettings.LanNetworkPreset();
end;
destructor tRestClient.Destroy();
begin
DeInitialize();
inherited;
end;
function tRestClient.Initialize(ClSettings: rClientSettings; ConSettings: rConnectionSettings): boolean;
begin
fConnectionSettings := ConSettings;
Result := Initialize(ClSettings);
end;
function tRestClient.Initialize(ClSettings: rClientSettings): boolean;
begin
Result := False;
// Destroy current object
DeInitialize();
// Client initialization (for better understanding, each section contain separate code, later should be refactored)
fClientSettings := ClSettings;
fModel := TSQLModel.Create([], ROOT_NAME);
case fClientSettings.Protocol of
HTTP_Socket:
begin
fClient := TSQLHttpClientWinSock.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWinSock(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
end;
HTTP_HTTPsys:
begin
fClient := TSQLHttpClientWinHTTP.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWinHTTP(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
TSQLHttpClientWinHTTP(fClient).Compression := [hcSynShaAes];
end;
{$ifdef MSWINDOWS}
HTTPsys_SSL:
begin
fClient := TSQLHttpClientWinHTTP.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, True, '', '', fConnectionSettings.SendTimeout,
fConnectionSettings.ReceiveTimeout, fConnectionSettings.ConnectTimeout);
TSQLHttpClientWinHTTP(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
TSQLHttpClientWinHTTP(fClient).Compression := [hcSynShaAes];
end;
{$endif}
HTTPsys_AES:
begin
fClient := TSQLHttpClientWinHTTP.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWinHTTP(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
TSQLHttpClientWinHTTP(fClient).Compression := [hcSynShaAes];
end;
HTTP_WebSocket:
begin
fClient := TSQLHttpClientWebsockets.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWebsockets(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
end;
WebSocketBidir_JSON:
begin
fClient := TSQLHttpClientWebsockets.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWebsockets(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
(fClient as TSQLHttpClientWebsockets).WebSocketsUpgrade('', True);
end;
WebSocketBidir_Binary:
begin
fClient := TSQLHttpClientWebsockets.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWebsockets(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
(fClient as TSQLHttpClientWebsockets).WebSocketsUpgrade('', False);
end;
WebSocketBidir_BinaryAES:
begin
fClient := TSQLHttpClientWebsockets.Create(AnsiString(fClientSettings.HostOrIP),
AnsiString(fClientSettings.Port), fModel, false, '', '',
fConnectionSettings.SendTimeout, fConnectionSettings.ReceiveTimeout,
fConnectionSettings.ConnectTimeout);
TSQLHttpClientWebsockets(fClient).KeepAliveMS := CONNECTION_TIMEOUT;
(fClient as TSQLHttpClientWebsockets).WebSocketsUpgrade('2141D32ADAD54D9A9DB56000CC9A4A70', False);
end;
{$ifdef MSWINDOWS}
NamedPipe:
begin
fClient := TSQLRestClientURINamedPipe.Create(fModel, '\\' + fClientSettings.HostOrIP + '\pipe\mORMot_' + NAMED_PIPE_NAME);
end;
{$endif}
else
begin
DeInitialize();
raise Exception.Create('Selected protocol not available in this build.');
end;
end;
case fClientSettings.AuthMode of
// NoAuthentication
NoAuthentication:
begin
// nothing to do here
end;
// TSQLRestServerAuthenticationDefault
Default:
begin
fClient.SetUser(fClientSettings.UserLogin, fClientSettings.UserPassword);
end;
// TSQLRestServerAuthenticationNone
None:
begin
TSQLRestServerAuthenticationNone.ClientSetUser(fClient, fClientSettings.UserLogin, fClientSettings.UserPassword);
end;
// TSQLRestServerAuthenticationHttpBasic
HttpBasic:
begin
TSQLRestServerAuthenticationHttpBasic.ClientSetUser(fClient, fClientSettings.UserLogin, fClientSettings.UserPassword);
end;
// TSQLRestServerAuthenticationSSPI
{$ifdef MSWINDOWS}
SSPI:
begin
TSQLRestServerAuthenticationSSPI.ClientSetUser(fClient, fClientSettings.UserLogin, fClientSettings.UserPassword);
end;
{$endif}
else
begin
DeInitialize();
raise Exception.Create('Selected Authentication mode not available in this build.');
end;
end;
// Preparing
if not fClient.ServerTimeStampSynchronize() then
begin
ShowMessage(UTF8ToString(fClient.LastErrorMessage));
exit;
end;
// Service initialization
fClient.ServiceDefine([IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
// Result := Client.Services['RestMethods'].Get(RestServerMethods);
Result := fClient.Services.Resolve(IRestMethods, RestMethods); // same result, but no chance to make mistake with service name
fInitialized := Result;
end;
procedure tRestClient.DeInitialize();
begin
RestMethods := nil;
if Assigned(fClient) then
FreeAndNil(fClient);
if Assigned(fModel) then
FreeAndNil(fModel);
fInitialized := False;
end;
initialization
RestClient := tRestClient.Create();
finalization
if Assigned(RestClient) then
FreeAndNil(RestClient);
end.

View File

@@ -0,0 +1,45 @@
unit RestMethodsInterfaceUnit;
interface
uses
mORMot;
type
rCustomRecord = record
ResultCode: integer;
ResultStr: string;
ResultArray: array of string;
ResultTimeStamp: TDateTime;
end;
rCustomComplicatedRecord = record
SimpleString: string;
SimpleInteger: integer;
AnotherRecord: rCustomRecord;
end;
IRestMethods = interface(IInvokable)
['{4EB49814-A4A9-40D2-B85A-137DDF43C72C}']
function HelloWorld(): string;
function Sum(val1, val2: Double): Double;
function GetCustomRecord(): rCustomRecord;
function SendCustomRecord(const CustomResult: rCustomRecord): Boolean;
function SendMultipleCustomRecords(const CustomResult: rCustomRecord; const CustomComplicatedRecord: rCustomComplicatedRecord): Boolean;
function GetMethodCustomResult(): TServiceCustomAnswer; // without default {result:[]}
// function ProperErrorHanding(): boolean;
end;
const
ROOT_NAME = 'service';
SERVICE_INSTANCE_IMPLEMENTATION = TServiceInstanceImplementation.sicSingle;
NAMED_PIPE_NAME = 'Rest_Test.0001';
CONNECTION_TIMEOUT = 3000; // Default mORMot value
implementation
initialization
TInterfaceFactory.RegisterInterfaces([TypeInfo(IRestMethods)]); // to use directly IRestMethods instead of TypeInfo(IRestMethods)
end.

View File

@@ -0,0 +1,378 @@
object Form1: TForm1
Left = 0
Top = 0
Caption = 'SRV.mORMot REST test'
ClientHeight = 661
ClientWidth = 1084
Color = clBtnFace
Constraints.MinHeight = 325
Constraints.MinWidth = 1100
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignSize = (
1084
661)
PixelsPerInch = 96
TextHeight = 13
object LabelPortCap: TLabel
Left = 867
Top = 28
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = 'Port:'
ExplicitLeft = 866
end
object LabelAuthenticationMode: TLabel
Left = 37
Top = 38
Width = 74
Height = 13
Caption = 'Authentication:'
end
object LabelProtocol: TLabel
Left = 68
Top = 11
Width = 43
Height = 13
Caption = 'Protocol:'
end
object LabelHTTPSnote: TLabel
Left = 511
Top = 11
Width = 235
Height = 13
Caption = 'Don'#39't forget enable https support (check exe dir)'
Font.Charset = DEFAULT_CHARSET
Font.Color = 136
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
Visible = False
StyleElements = [seClient, seBorder]
end
object EditPort: TEdit
Left = 897
Top = 25
Width = 60
Height = 21
Anchors = [akTop, akRight]
TabOrder = 0
Text = '777'
end
object ButtonStartStop: TButton
Left = 969
Top = 19
Width = 105
Height = 33
Anchors = [akTop, akRight]
Caption = 'Start server'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 1
OnClick = ButtonStartStopClick
end
object MemoLog: TMemo
Left = 8
Top = 255
Width = 1066
Height = 370
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Consolas'
Font.Style = []
ParentFont = False
ScrollBars = ssVertical
TabOrder = 2
end
object ButtonCLS: TButton
Left = 10
Top = 631
Width = 38
Height = 22
Anchors = [akLeft, akBottom]
Caption = 'CLS'
TabOrder = 3
OnClick = ButtonCLSClick
end
object CheckBoxAutoScroll: TCheckBox
Left = 57
Top = 633
Width = 69
Height = 17
Anchors = [akLeft, akBottom]
Caption = 'auto scroll'
Checked = True
State = cbChecked
TabOrder = 4
end
object ComboBoxAuthentication: TComboBox
Left = 117
Top = 35
Width = 388
Height = 21
Style = csDropDownList
ItemIndex = 1
TabOrder = 5
Text = 'Default'
OnChange = ComboBoxAuthenticationChange
Items.Strings = (
'No authentication'
'Default'
'None'
'HttpBasic'
'SSPI')
end
object ButtonShowAuthorizationInfo: TButton
Left = 511
Top = 36
Width = 42
Height = 19
Caption = 'Info'
TabOrder = 6
OnClick = ButtonShowAuthorizationInfoClick
end
object CheckBoxDisableLog: TCheckBox
Left = 133
Top = 633
Width = 203
Height = 17
Anchors = [akLeft, akBottom]
Caption = 'disable log (for max performance test)'
TabOrder = 7
OnClick = CheckBoxDisableLogClick
end
object ComboBoxProtocol: TComboBox
Left = 117
Top = 8
Width = 388
Height = 21
Style = csDropDownList
DropDownCount = 10
ItemIndex = 7
TabOrder = 8
Text = ' '#8250' WebSocket ( bidirectional, binary + AES-CFB 256)'
OnChange = ComboBoxProtocolChange
Items.Strings = (
'HTTP ( socket )'
' '#8250' HTTP ( fast http.sys // require admin rights else s' +
'ocket )'
' '#8250#8250' HTTPS ( fast http.sys + SSL // require admin rights )'
' '#8250' HTTP ( fast http.sys + AES-CFB 256 // require admin' +
' rights )'
'HTTP ( web socket )'
' '#8250' WebSocket ( bidirectional, JSON )'
' '#8250' WebSocket ( bidirectional, binary )'
' '#8250' WebSocket ( bidirectional, binary + AES-CFB 256)'
'Named pipe')
end
object GroupBoxMethodGroupConfiguration: TGroupBox
Left = 164
Top = 62
Width = 520
Height = 187
Caption = 'Method / Group configuration'
TabOrder = 9
object ListViewMethodGroups: TListView
Left = 10
Top = 16
Width = 503
Height = 133
Columns = <
item
Caption = ' Method'
Width = 150
end
item
Caption = ' Allow group by name'
Width = 160
end
item
Caption = ' Deny group by name'
Width = 160
end>
Groups = <
item
Header = 'IRestMethods'
GroupID = 0
State = [lgsNormal]
HeaderAlign = taLeftJustify
FooterAlign = taLeftJustify
Subtitle = 'InstanceImplementation = sicSingle'
TitleImage = -1
end>
HideSelection = False
Items.ItemData = {
05860200000600000000000000FFFFFFFFFFFFFFFF0200000000000000000000
000A480065006C006C006F0057006F0072006C00640014550073006500720073
002C00410064006D0069006E006900730074007200610074006F007200730068
0674240B53006F006D0065006F006E00650045006C007300650068FF73240000
0000FFFFFFFFFFFFFFFF02000000000000000000000003530075006D00145500
73006500720073002C00410064006D0069006E00690073007400720061007400
6F0072007300686F7424008054742400000000FFFFFFFFFFFFFFFF0200000000
000000000000000F47006500740043007500730074006F006D00520065006300
6F00720064000E410064006D0069006E006900730074007200610074006F0072
0073005013742400985C742400000000FFFFFFFFFFFFFFFF0200000000000000
0000000010530065006E00640043007500730074006F006D005200650063006F
00720064000E410064006D0069006E006900730074007200610074006F007200
7300588F7424004041742400000000FFFFFFFFFFFFFFFF020000000000000000
00000019530065006E0064004D0075006C007400690070006C00650043007500
730074006F006D005200650063006F007200640073000E410064006D0069006E
006900730074007200610074006F00720073003812742400E887742400000000
FFFFFFFFFFFFFFFF020000000000000000000000154700650074004D00650074
0068006F00640043007500730074006F006D0052006500730075006C00740014
550073006500720073002C00410064006D0069006E0069007300740072006100
74006F0072007300B08774240010387424FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFF}
GroupView = True
ReadOnly = True
RowSelect = True
TabOrder = 0
ViewStyle = vsReport
OnClick = ListViewMethodGroupsClick
OnSelectItem = ListViewMethodGroupsSelectItem
end
object ButtonSaveRoleConfiguration: TButton
Left = 463
Top = 155
Width = 50
Height = 25
Caption = 'Save'
TabOrder = 1
OnClick = ButtonSaveRoleConfigurationClick
end
object EditAllowGroupNames: TEdit
Left = 136
Top = 157
Width = 160
Height = 21
TabOrder = 2
TextHint = 'Allow group names (sep by ",")'
end
object EditDenyAllowGroupNames: TEdit
Left = 298
Top = 157
Width = 159
Height = 21
TabOrder = 3
TextHint = 'Deny group names (sep by ",")'
end
end
object RadioGroupAuthorizationPolicy: TRadioGroup
Left = 8
Top = 62
Width = 150
Height = 73
Caption = 'Authorization policy'
ItemIndex = 2
Items.Strings = (
'Allow all'
'Deny all'
'Follow groups settings')
TabOrder = 10
OnClick = RadioGroupAuthorizationPolicyClick
end
object GroupBoxUsers: TGroupBox
Left = 690
Top = 62
Width = 383
Height = 187
Caption = 'Users'
TabOrder = 11
object ListViewUsers: TListView
Left = 9
Top = 16
Width = 365
Height = 133
Columns = <
item
Caption = ' User'
Width = 110
end
item
Caption = ' Password'
Width = 110
end
item
Caption = ' Group'
Width = 110
end>
HideSelection = False
Items.ItemData = {
05960000000200000000000000FFFFFFFFFFFFFFFF02000000FFFFFFFF000000
0006470065006F0072006700650003310032003300B8EB761B0E410064006D00
69006E006900730074007200610074006F007200730058EE761B00000000FFFF
FFFFFFFFFFFF02000000FFFFFFFF000000000441006C00650078000333003200
310090EE761B0555007300650072007300C8EE761BFFFFFFFFFFFFFFFF}
ReadOnly = True
RowSelect = True
TabOrder = 0
ViewStyle = vsReport
OnClick = ListViewUsersClick
OnSelectItem = ListViewUsersSelectItem
end
object EditUserGroup: TEdit
Left = 223
Top = 157
Width = 83
Height = 21
TabOrder = 1
TextHint = 'Group'
end
object ButtonSaveUsers: TButton
Left = 312
Top = 155
Width = 62
Height = 25
Caption = 'Save/Add'
TabOrder = 2
OnClick = ButtonSaveUsersClick
end
object EditUserName: TEdit
Left = 53
Top = 157
Width = 83
Height = 21
TabOrder = 3
TextHint = 'User'
end
object ButtonDeleteUser: TButton
Left = 9
Top = 155
Width = 38
Height = 25
Caption = 'DeL'
TabOrder = 4
OnClick = ButtonDeleteUserClick
end
object EditUserPassword: TEdit
Left = 138
Top = 157
Width = 83
Height = 21
TabOrder = 5
TextHint = 'Password'
end
end
object TimerRefreshLogMemo: TTimer
OnTimer = TimerRefreshLogMemoTimer
Left = 56
Top = 264
end
end

View File

@@ -0,0 +1,368 @@
object Form1: TForm1
Left = 238
Height = 377
Top = 103
Width = 1100
Caption = 'SRV.mORMot REST test'
ClientHeight = 377
ClientWidth = 1100
Color = clBtnFace
Constraints.MinHeight = 325
Constraints.MinWidth = 1100
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
OnCreate = FormCreate
OnDestroy = FormDestroy
Position = poScreenCenter
LCLVersion = '1.7'
object LabelPortCap: TLabel
Left = 883
Height = 13
Top = 28
Width = 24
Anchors = [akTop, akRight]
Caption = 'Port:'
ParentColor = False
end
object LabelAuthenticationMode: TLabel
Left = 37
Height = 13
Top = 38
Width = 74
Caption = 'Authentication:'
ParentColor = False
end
object LabelProtocol: TLabel
Left = 68
Height = 13
Top = 11
Width = 43
Caption = 'Protocol:'
ParentColor = False
end
object LabelHTTPSnote: TLabel
Left = 511
Height = 13
Top = 11
Width = 235
Caption = 'Don''t forget enable https support (check exe dir)'
Font.Color = 136
Font.Height = -11
Font.Name = 'Tahoma'
ParentColor = False
ParentFont = False
Visible = False
end
object EditPort: TEdit
Left = 913
Height = 21
Top = 25
Width = 60
Anchors = [akTop, akRight]
TabOrder = 0
Text = '777'
end
object ButtonStartStop: TButton
Left = 985
Height = 33
Top = 19
Width = 105
Anchors = [akTop, akRight]
Caption = 'Start server'
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
OnClick = ButtonStartStopClick
ParentFont = False
TabOrder = 1
end
object MemoLog: TMemo
Left = 8
Height = 69
Top = 272
Width = 1082
Anchors = [akTop, akLeft, akRight, akBottom]
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Consolas'
ParentFont = False
ScrollBars = ssVertical
TabOrder = 2
end
object ButtonCLS: TButton
Left = 10
Height = 22
Top = 347
Width = 38
Anchors = [akLeft, akBottom]
Caption = 'CLS'
OnClick = ButtonCLSClick
TabOrder = 3
end
object CheckBoxAutoScroll: TCheckBox
Left = 57
Height = 19
Top = 347
Width = 69
Anchors = [akLeft, akBottom]
Caption = 'auto scroll'
Checked = True
State = cbChecked
TabOrder = 4
end
object ComboBoxAuthentication: TComboBox
Left = 117
Height = 21
Top = 35
Width = 388
ItemHeight = 13
ItemIndex = 1
Items.Strings = (
'No authentication'
'Default'
'None'
'HttpBasic'
'SSPI'
)
OnChange = ComboBoxAuthenticationChange
Style = csDropDownList
TabOrder = 5
Text = 'Default'
end
object ButtonShowAuthorizationInfo: TButton
Left = 511
Height = 19
Top = 36
Width = 42
Caption = 'Info'
OnClick = ButtonShowAuthorizationInfoClick
TabOrder = 6
end
object CheckBoxDisableLog: TCheckBox
Left = 133
Height = 19
Top = 347
Width = 204
Anchors = [akLeft, akBottom]
Caption = 'disable log (for max performance test)'
OnClick = CheckBoxDisableLogClick
TabOrder = 7
end
object ComboBoxProtocol: TComboBox
Left = 117
Height = 21
Top = 8
Width = 388
DropDownCount = 10
ItemHeight = 13
ItemIndex = 7
Items.Strings = (
'HTTP ( socket )'
' HTTP ( fast http.sys // require admin rights else socket )'
' HTTPS ( fast http.sys + SSL // require admin rights )'
' HTTP ( fast http.sys + AES-CFB 256 // require admin rights )'
'HTTP ( web socket )'
' WebSocket ( bidirectional, JSON )'
' WebSocket ( bidirectional, binary )'
' WebSocket ( bidirectional, binary + AES-CFB 256)'
'Named pipe'
)
OnChange = ComboBoxProtocolChange
Style = csDropDownList
TabOrder = 8
Text = ' WebSocket ( bidirectional, binary + AES-CFB 256)'
end
object GroupBoxMethodGroupConfiguration: TGroupBox
Left = 164
Height = 203
Top = 62
Width = 520
Caption = 'Method / Group configuration'
ClientHeight = 185
ClientWidth = 516
TabOrder = 9
object ListViewMethodGroups: TListView
Left = 10
Height = 133
Top = 16
Width = 503
Columns = <
item
Caption = ' Method'
Width = 150
end
item
Caption = ' Allow group by name'
Width = 160
end
item
Caption = ' Deny group by name'
Width = 160
end>
HideSelection = False
Items.LazData = {
AB01000006000000FFFFFFFFFFFFFFFFFFFFFFFF020000000A00000048656C6C
6F576F726C641400000055736572732C41646D696E6973747261746F72730B00
0000536F6D656F6E65456C7365FFFFFFFFFFFFFFFFFFFFFFFF02000000030000
0053756D1400000055736572732C41646D696E6973747261746F727300000000
FFFFFFFFFFFFFFFFFFFFFFFF020000000F000000476574437573746F6D526563
6F72640E00000041646D696E6973747261746F727300000000FFFFFFFFFFFFFF
FFFFFFFFFF020000001000000053656E64437573746F6D5265636F72640E0000
0041646D696E6973747261746F727300000000FFFFFFFFFFFFFFFFFFFFFFFF02
0000001900000053656E644D756C7469706C65437573746F6D5265636F726473
0E00000041646D696E6973747261746F727300000000FFFFFFFFFFFFFFFFFFFF
FFFF02000000150000004765744D6574686F64437573746F6D526573756C7414
00000055736572732C41646D696E6973747261746F727300000000FFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFF
}
ReadOnly = True
RowSelect = True
TabOrder = 0
ViewStyle = vsReport
OnClick = ListViewMethodGroupsClick
OnSelectItem = ListViewMethodGroupsSelectItem
end
object ButtonSaveRoleConfiguration: TButton
Left = 463
Height = 25
Top = 155
Width = 50
Caption = 'Save'
OnClick = ButtonSaveRoleConfigurationClick
TabOrder = 1
end
object EditAllowGroupNames: TEdit
Left = 136
Height = 21
Top = 157
Width = 160
TabOrder = 2
TextHint = 'Allow group names (sep by ",")'
end
object EditDenyAllowGroupNames: TEdit
Left = 298
Height = 21
Top = 157
Width = 159
TabOrder = 3
TextHint = 'Deny group names (sep by ",")'
end
end
object RadioGroupAuthorizationPolicy: TRadioGroup
Left = 8
Height = 73
Top = 62
Width = 150
AutoFill = True
Caption = 'Authorization policy'
ChildSizing.LeftRightSpacing = 6
ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
ChildSizing.EnlargeVertical = crsHomogenousChildResize
ChildSizing.ShrinkHorizontal = crsScaleChilds
ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
ClientHeight = 55
ClientWidth = 146
ItemIndex = 2
Items.Strings = (
'Allow all'
'Deny all'
'Follow groups settings'
)
OnClick = RadioGroupAuthorizationPolicyClick
TabOrder = 10
end
object GroupBoxUsers: TGroupBox
Left = 690
Height = 203
Top = 62
Width = 383
Caption = 'Users'
ClientHeight = 185
ClientWidth = 379
TabOrder = 11
object ListViewUsers: TListView
Left = 9
Height = 133
Top = 16
Width = 365
Columns = <
item
Caption = ' User'
Width = 110
end
item
Caption = ' Password'
Width = 110
end
item
Caption = ' Group'
Width = 110
end>
HideSelection = False
Items.LazData = {
7300000002000000FFFFFFFFFFFFFFFFFFFFFFFF020000000600000047656F72
6765030000003132330E00000041646D696E6973747261746F7273FFFFFFFFFF
FFFFFFFFFFFFFF0200000004000000416C657803000000333231050000005573
657273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
}
ReadOnly = True
RowSelect = True
TabOrder = 0
ViewStyle = vsReport
OnClick = ListViewUsersClick
OnSelectItem = ListViewUsersSelectItem
end
object EditUserGroup: TEdit
Left = 223
Height = 21
Top = 157
Width = 83
TabOrder = 1
TextHint = 'Group'
end
object ButtonSaveUsers: TButton
Left = 312
Height = 25
Top = 155
Width = 62
Caption = 'Save/Add'
OnClick = ButtonSaveUsersClick
TabOrder = 2
end
object EditUserName: TEdit
Left = 53
Height = 21
Top = 157
Width = 83
TabOrder = 3
TextHint = 'User'
end
object ButtonDeleteUser: TButton
Left = 9
Height = 25
Top = 155
Width = 38
Caption = 'DeL'
OnClick = ButtonDeleteUserClick
TabOrder = 4
end
object EditUserPassword: TEdit
Left = 138
Height = 21
Top = 157
Width = 83
TabOrder = 5
TextHint = 'Password'
end
end
object TimerRefreshLogMemo: TTimer
OnTimer = TimerRefreshLogMemoTimer
left = 56
top = 264
end
end

View File

@@ -0,0 +1,102 @@
{ This is an automatically generated lazarus resource file }
LazarusResources.Add('TForm1','FORMDATA',[
'TPF0'#6'TForm1'#5'Form1'#4'Left'#2'1'#6'Height'#3'y'#1#3'Top'#2'}'#5'Width'#3
+'L'#4#7'Caption'#6#20'SRV.mORMot REST test'#12'ClientHeight'#3'y'#1#11'Clien'
+'tWidth'#3'L'#4#5'Color'#7#9'clBtnFace'#21'Constraints.MinHeight'#3'E'#1#20
+'Constraints.MinWidth'#3'L'#4#10'Font.Color'#7#12'clWindowText'#11'Font.Heig'
+'ht'#2#245#9'Font.Name'#6#6'Tahoma'#8'OnCreate'#7#10'FormCreate'#9'OnDestroy'
+#7#11'FormDestroy'#8'Position'#7#14'poScreenCenter'#10'LCLVersion'#6#3'1.7'#0
+#6'TLabel'#12'LabelPortCap'#4'Left'#3's'#3#6'Height'#2#13#3'Top'#2#28#5'Widt'
+'h'#2#24#7'Anchors'#11#5'akTop'#7'akRight'#0#7'Caption'#6#5'Port:'#11'Parent'
+'Color'#8#0#0#6'TLabel'#23'LabelAuthenticationMode'#4'Left'#2'%'#6'Height'#2
+#13#3'Top'#2'&'#5'Width'#2'J'#7'Caption'#6#15'Authentication:'#11'ParentColo'
+'r'#8#0#0#6'TLabel'#13'LabelProtocol'#4'Left'#2'D'#6'Height'#2#13#3'Top'#2#11
+#5'Width'#2'+'#7'Caption'#6#9'Protocol:'#11'ParentColor'#8#0#0#6'TLabel'#14
+'LabelHTTPSnote'#4'Left'#3#255#1#6'Height'#2#13#3'Top'#2#11#5'Width'#3#235#0
+#7'Caption'#6'1Don''t forget enable https support (check exe dir)'#10'Font.C'
+'olor'#3#136#0#11'Font.Height'#2#245#9'Font.Name'#6#6'Tahoma'#11'ParentColor'
+#8#10'ParentFont'#8#7'Visible'#8#0#0#5'TEdit'#8'EditPort'#4'Left'#3#145#3#6
+'Height'#2#21#3'Top'#2#25#5'Width'#2'<'#7'Anchors'#11#5'akTop'#7'akRight'#0#8
+'TabOrder'#2#0#4'Text'#6#3'777'#0#0#7'TButton'#15'ButtonStartStop'#4'Left'#3
+#217#3#6'Height'#2'!'#3'Top'#2#19#5'Width'#2'i'#7'Anchors'#11#5'akTop'#7'akR'
+'ight'#0#7'Caption'#6#12'Start server'#10'Font.Color'#7#12'clWindowText'#11
+'Font.Height'#2#245#9'Font.Name'#6#6'Tahoma'#10'Font.Style'#11#6'fsBold'#0#7
+'OnClick'#7#20'ButtonStartStopClick'#10'ParentFont'#8#8'TabOrder'#2#1#0#0#5
+'TMemo'#7'MemoLog'#4'Left'#2#8#6'Height'#2'E'#3'Top'#3#16#1#5'Width'#3':'#4#7
+'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#8'akBottom'#0#10'Font.Color'#7#12
+'clWindowText'#11'Font.Height'#2#244#9'Font.Name'#6#8'Consolas'#10'ParentFon'
+'t'#8#10'ScrollBars'#7#10'ssVertical'#8'TabOrder'#2#2#0#0#7'TButton'#9'Butto'
+'nCLS'#4'Left'#2#10#6'Height'#2#22#3'Top'#3'['#1#5'Width'#2'&'#7'Anchors'#11
+#6'akLeft'#8'akBottom'#0#7'Caption'#6#3'CLS'#7'OnClick'#7#14'ButtonCLSClick'
+#8'TabOrder'#2#3#0#0#9'TCheckBox'#18'CheckBoxAutoScroll'#4'Left'#2'9'#6'Heig'
+'ht'#2#19#3'Top'#3'['#1#5'Width'#2'E'#7'Anchors'#11#6'akLeft'#8'akBottom'#0#7
+'Caption'#6#11'auto scroll'#7'Checked'#9#5'State'#7#9'cbChecked'#8'TabOrder'
+#2#4#0#0#9'TComboBox'#22'ComboBoxAuthentication'#4'Left'#2'u'#6'Height'#2#21
+#3'Top'#2'#'#5'Width'#3#132#1#10'ItemHeight'#2#13#9'ItemIndex'#2#1#13'Items.'
+'Strings'#1#6#17'No authentication'#6#7'Default'#6#4'None'#6#9'HttpBasic'#6#4
+'SSPI'#0#8'OnChange'#7#28'ComboBoxAuthenticationChange'#5'Style'#7#14'csDrop'
+'DownList'#8'TabOrder'#2#5#4'Text'#6#7'Default'#0#0#7'TButton'#27'ButtonShow'
+'AuthorizationInfo'#4'Left'#3#255#1#6'Height'#2#19#3'Top'#2'$'#5'Width'#2'*'
+#7'Caption'#6#4'Info'#7'OnClick'#7' ButtonShowAuthorizationInfoClick'#8'TabO'
+'rder'#2#6#0#0#9'TCheckBox'#18'CheckBoxDisableLog'#4'Left'#3#133#0#6'Height'
+#2#19#3'Top'#3'['#1#5'Width'#3#204#0#7'Anchors'#11#6'akLeft'#8'akBottom'#0#7
+'Caption'#6'&disable log (for max performance test)'#7'OnClick'#7#23'CheckBo'
+'xDisableLogClick'#8'TabOrder'#2#7#0#0#9'TComboBox'#16'ComboBoxProtocol'#4'L'
+'eft'#2'u'#6'Height'#2#21#3'Top'#2#8#5'Width'#3#132#1#13'DropDownCount'#2#10
+#10'ItemHeight'#2#13#9'ItemIndex'#2#7#13'Items.Strings'#1#6#28'HTTP '
+' ( socket )'#6'I '#226#128#186' HTTP ( fast http.sys // requi'
+'re admin rights else socket )'#6'D '#226#128#186#226#128#186' HTTPS '
+'( fast http.sys + SSL // require admin rights )'#6'K '#226#128#186' HTTP '
+' ( fast http.sys + AES-CFB 256 // require admin rights )'#6' HTTP '
+' ( web socket )'#6'& '#226#128#186' WebSocket ( bidirectional, JS'
+'ON )'#6'( '#226#128#186' WebSocket ( bidirectional, binary )'#6'5 '#226#128
+#186' WebSocket ( bidirectional, binary + AES-CFB 256)'#6#10'Named pipe'#0#8
+'OnChange'#7#22'ComboBoxProtocolChange'#5'Style'#7#14'csDropDownList'#8'TabO'
+'rder'#2#8#4'Text'#6'5 '#226#128#186' WebSocket ( bidirectional, binary + AE'
+'S-CFB 256)'#0#0#9'TGroupBox GroupBoxMethodGroupConfiguration'#4'Left'#3#164
+#0#6'Height'#3#203#0#3'Top'#2'>'#5'Width'#3#8#2#7'Caption'#6#28'Method / Gro'
+'up configuration'#12'ClientHeight'#3#185#0#11'ClientWidth'#3#4#2#8'TabOrder'
+#2#9#0#9'TListView'#20'ListViewMethodGroups'#4'Left'#2#10#6'Height'#3#133#0#3
+'Top'#2#16#5'Width'#3#247#1#7'Columns'#14#1#7'Caption'#6#7' Method'#5'Width'
+#3#150#0#0#1#7'Caption'#6#20' Allow group by name'#5'Width'#3#160#0#0#1#7'Ca'
+'ption'#6#19' Deny group by name'#5'Width'#3#160#0#0#0#13'HideSelection'#8#8
+'ReadOnly'#9#9'RowSelect'#9#8'TabOrder'#2#0#9'ViewStyle'#7#8'vsReport'#7'OnC'
+'lick'#7#25'ListViewMethodGroupsClick'#12'OnSelectItem'#7#30'ListViewMethodG'
+'roupsSelectItem'#0#0#7'TButton'#27'ButtonSaveRoleConfiguration'#4'Left'#3
+#207#1#6'Height'#2#25#3'Top'#3#155#0#5'Width'#2'2'#7'Caption'#6#4'Save'#7'On'
,'Click'#7' ButtonSaveRoleConfigurationClick'#8'TabOrder'#2#1#0#0#5'TEdit'#19
+'EditAllowGroupNames'#4'Left'#3#136#0#6'Height'#2#21#3'Top'#3#157#0#5'Width'
+#3#160#0#8'TabOrder'#2#2#8'TextHint'#6#31'Allow group names (sep by ",")'#0
+#0#5'TEdit'#23'EditDenyAllowGroupNames'#4'Left'#3'*'#1#6'Height'#2#21#3'Top'
+#3#157#0#5'Width'#3#159#0#8'TabOrder'#2#3#8'TextHint'#6#29'Deny group names '
+'(sep by ",")'#0#0#0#11'TRadioGroup'#29'RadioGroupAuthorizationPolicy'#4'Lef'
+'t'#2#8#6'Height'#2'I'#3'Top'#2'>'#5'Width'#3#150#0#8'AutoFill'#9#7'Caption'
+#6#20'Authorization policy'#28'ChildSizing.LeftRightSpacing'#2#6#29'ChildSiz'
+'ing.EnlargeHorizontal'#7#24'crsHomogenousChildResize'#27'ChildSizing.Enlarg'
+'eVertical'#7#24'crsHomogenousChildResize'#28'ChildSizing.ShrinkHorizontal'#7
+#14'crsScaleChilds'#26'ChildSizing.ShrinkVertical'#7#14'crsScaleChilds'#18'C'
+'hildSizing.Layout'#7#29'cclLeftToRightThenTopToBottom'#27'ChildSizing.Contr'
+'olsPerLine'#2#1#12'ClientHeight'#2'7'#11'ClientWidth'#3#146#0#9'ItemIndex'#2
+#2#13'Items.Strings'#1#6#9'Allow all'#6#8'Deny all'#6#22'Follow groups setti'
+'ngs'#0#7'OnClick'#7'"RadioGroupAuthorizationPolicyClick'#8'TabOrder'#2#10#0
+#0#9'TGroupBox'#13'GroupBoxUsers'#4'Left'#3#178#2#6'Height'#3#203#0#3'Top'#2
+'>'#5'Width'#3#127#1#7'Caption'#6#5'Users'#12'ClientHeight'#3#185#0#11'Clien'
+'tWidth'#3'{'#1#8'TabOrder'#2#11#0#9'TListView'#13'ListViewUsers'#4'Left'#2#9
+#6'Height'#3#133#0#3'Top'#2#16#5'Width'#3'm'#1#7'Columns'#14#1#7'Caption'#6#5
+' User'#5'Width'#2'n'#0#1#7'Caption'#6#9' Password'#5'Width'#2'n'#0#1#7'Capt'
+'ion'#6#6' Group'#5'Width'#2'n'#0#0#13'HideSelection'#8#8'ReadOnly'#9#9'RowS'
+'elect'#9#8'TabOrder'#2#0#9'ViewStyle'#7#8'vsReport'#7'OnClick'#7#18'ListVie'
+'wUsersClick'#12'OnSelectItem'#7#23'ListViewUsersSelectItem'#0#0#5'TEdit'#13
+'EditUserGroup'#4'Left'#3#223#0#6'Height'#2#21#3'Top'#3#157#0#5'Width'#2'S'#8
+'TabOrder'#2#1#8'TextHint'#6#5'Group'#0#0#7'TButton'#15'ButtonSaveUsers'#4'L'
+'eft'#3'8'#1#6'Height'#2#25#3'Top'#3#155#0#5'Width'#2'>'#7'Caption'#6#8'Save'
+'/Add'#7'OnClick'#7#20'ButtonSaveUsersClick'#8'TabOrder'#2#2#0#0#5'TEdit'#12
+'EditUserName'#4'Left'#2'5'#6'Height'#2#21#3'Top'#3#157#0#5'Width'#2'S'#8'Ta'
+'bOrder'#2#3#8'TextHint'#6#4'User'#0#0#7'TButton'#16'ButtonDeleteUser'#4'Lef'
+'t'#2#9#6'Height'#2#25#3'Top'#3#155#0#5'Width'#2'&'#7'Caption'#6#3'DeL'#7'On'
+'Click'#7#21'ButtonDeleteUserClick'#8'TabOrder'#2#4#0#0#5'TEdit'#16'EditUser'
+'Password'#4'Left'#3#138#0#6'Height'#2#21#3'Top'#3#157#0#5'Width'#2'S'#8'Tab'
+'Order'#2#5#8'TextHint'#6#8'Password'#0#0#0#6'TTimer'#19'TimerRefreshLogMemo'
+#7'OnTimer'#7#24'TimerRefreshLogMemoTimer'#4'left'#2'8'#3'top'#3#8#1#0#0#0
]);

View File

@@ -0,0 +1,552 @@
unit RestServerFormUnit;
// mORMot RESTful API test case 1.02
interface
uses
// RTL
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF}
{$ifdef FPC}
LCLType,
{$endif}
Messages,
SysUtils,
Classes,
Generics.Collections,
Forms,
Dialogs,
Controls,
StdCtrls,
ExtCtrls,
// mORMot
mORMot,
mORMotHttpServer,
SynLog,
SynCommons,
// Custom
RestServerUnit, ComCtrls;
type
lServerAction = (Auto, Start, Stop, Restart);
TForm1 = class(TForm)
EditPort: TEdit;
LabelPortCap: TLabel;
ButtonStartStop: TButton;
MemoLog: TMemo;
ButtonCLS: TButton;
TimerRefreshLogMemo: TTimer;
CheckBoxAutoScroll: TCheckBox;
LabelAuthenticationMode: TLabel;
ComboBoxAuthentication: TComboBox;
ButtonShowAuthorizationInfo: TButton;
CheckBoxDisableLog: TCheckBox;
LabelProtocol: TLabel;
ComboBoxProtocol: TComboBox;
ListViewMethodGroups: TListView;
GroupBoxMethodGroupConfiguration: TGroupBox;
RadioGroupAuthorizationPolicy: TRadioGroup;
ButtonSaveRoleConfiguration: TButton;
EditAllowGroupNames: TEdit;
EditDenyAllowGroupNames: TEdit;
GroupBoxUsers: TGroupBox;
ListViewUsers: TListView;
EditUserGroup: TEdit;
ButtonSaveUsers: TButton;
EditUserName: TEdit;
ButtonDeleteUser: TButton;
EditUserPassword: TEdit;
LabelHTTPSnote: TLabel;
procedure ButtonStartStopClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ButtonCLSClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure TimerRefreshLogMemoTimer(Sender: TObject);
procedure ComboBoxAuthenticationChange(Sender: TObject);
procedure ButtonShowAuthorizationInfoClick(Sender: TObject);
procedure CheckBoxDisableLogClick(Sender: TObject);
procedure ComboBoxProtocolChange(Sender: TObject);
procedure ListViewMethodGroupsSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
procedure ListViewUsersSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
procedure ButtonSaveRoleConfigurationClick(Sender: TObject);
procedure ButtonSaveUsersClick(Sender: TObject);
procedure RadioGroupAuthorizationPolicyClick(Sender: TObject);
procedure ButtonDeleteUserClick(Sender: TObject);
procedure ListViewUsersClick(Sender: TObject);
procedure ListViewMethodGroupsClick(Sender: TObject);
private
function LogEvent(Sender: TTextWriter; Level: TSynLogInfo; const Text: RawUTF8): Boolean;
function GetAuthModeDescription(AM: lAuthenticationMode): string;
function FillMethodAuthorizationRulesFromUI(RestServerSettings: TRestServerSettings): Boolean;
procedure StartStopServer(ServerAction: lServerAction = Auto);
public
{ Public declarations }
end;
TLocalLog = class
Level: TSynLogInfo;
Text: RawUTF8;
end;
var
Form1: TForm1;
{$ifdef FPC}
LogThreadSafeList: TList<TLocalLog>;
{$else}
LogThreadSafeList: TThreadList<TLocalLog>;
{$endif}
implementation
{$ifdef FPC}
{$R *.lfm}
{$else}
{$R *.dfm}
{$endif}
{ TForm1 }
// On Form1 create
procedure TForm1.FormCreate(Sender: TObject);
begin
// Create thread safe List with log data class
{$ifdef FPC}
LogThreadSafeList := TList<TLocalLog>.Create();
{$else}
LogThreadSafeList := TThreadList<TLocalLog>.Create();
{$endif}
// Enable logging
with TSQLLog.Family do
begin
Level := LOG_VERBOSE;
EchoCustom := LogEvent;
NoFile := True;
end;
{$IFNDEF MSWINDOWS}
// no named pipes on non-windows
ComboBoxProtocol.Items.Delete(ComboBoxProtocol.Items.Count-1);
// on Linux, ports below 1000 are priviledged (root only)
EditPort.Text:='7777';
{$ENDIF}
end;
// On Form1 destory
procedure TForm1.FormDestroy(Sender: TObject);
var
i: integer;
List: TList<TLocalLog>;
begin
// Clear and destroy LogThreadSafeList
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList;
{$endif}
for i := 0 to List.Count - 1 do
List.Items[i].Free;
List.Clear;
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
FreeAndNil(LogThreadSafeList);
end;
{ SERVER EVENTS, START / STOP }
// Fill RestServerSettings object with groups, users and method authorization settings
function TForm1.FillMethodAuthorizationRulesFromUI(RestServerSettings: TRestServerSettings): Boolean;
var
i, j, l: integer;
AddedGroups, GroupsToAdd: TStringList;
AuthGroup: rAuthGroup;
AuthUser: rAuthUser;
MethodAuthorizationSettings: rMethodAuthorizationSettings;
SQLAccessRights: TSQLAccessRights;
GroupName, MethodName, UserName: string;
begin
Result := True;
// Prepare temp objects
AddedGroups := TStringList.Create();
GroupsToAdd := TStringList.Create();
GroupsToAdd.LineBreak := ',';
GroupsToAdd.Duplicates := dupIgnore;
// For REST service, only one rule required = reService, will be applyed to all groups
SQLAccessRights.AllowRemoteExecute := [reService];
// Fill group names from columns "Allow" and "Deny"
for i := 0 to ListViewMethodGroups.Items.Count - 1 do
GroupsToAdd.Text := GroupsToAdd.Text + ListViewMethodGroups.Items[i].SubItems.Strings[0] + ',' + ListViewMethodGroups.Items[i].SubItems.Strings[1];
// Add groups
for i := 0 to GroupsToAdd.Count - 1 do
begin
GroupName := GroupsToAdd.Strings[i];
if AddedGroups.IndexOf(GroupName) = -1 then
begin
// Ok, new group here, we must add it before continue
AuthGroup.Name := StringToUTF8(GroupName);
AuthGroup.SessionTimeout := 10;
AuthGroup.SQLAccessRights := SQLAccessRights;
if RestServerSettings.AddGroup(AuthGroup) then
// Add group to local temp list, to avoid double processing
AddedGroups.Add(GroupName)
else
begin
Result := False;
ShowMessage('Group "' + GroupName + '" was not added, for some reason.');
end;
end;
end;
GroupsToAdd.Clear;
if Result then
begin
// Add method authorization settings
for i := 0 to ListViewMethodGroups.Items.Count - 1 do
begin
MethodName := ListViewMethodGroups.Items[i].Caption;
MethodAuthorizationSettings.MethodName := StringToUTF8(MethodName);
SetLength(MethodAuthorizationSettings.AllowedGroups, 0);
SetLength(MethodAuthorizationSettings.DeniedGroups, 0);
// Allowed groups
GroupsToAdd.Text := ListViewMethodGroups.Items[i].SubItems.Strings[0];
for j := 0 to GroupsToAdd.Count - 1 do
begin
GroupName := GroupsToAdd.Strings[j];
if AddedGroups.IndexOf(GroupName) <> -1 then
begin
l := Length(MethodAuthorizationSettings.AllowedGroups);
SetLength(MethodAuthorizationSettings.AllowedGroups, l + 1);
MethodAuthorizationSettings.AllowedGroups[l] := StringToUTF8(GroupName);
end
end;
GroupsToAdd.Clear;
// Denied groups
GroupsToAdd.Text := ListViewMethodGroups.Items[i].SubItems.Strings[1];
for j := 0 to GroupsToAdd.Count - 1 do
begin
GroupName := GroupsToAdd.Strings[j];
if AddedGroups.IndexOf(GroupName) <> -1 then
begin
l := Length(MethodAuthorizationSettings.DeniedGroups);
SetLength(MethodAuthorizationSettings.DeniedGroups, l + 1);
MethodAuthorizationSettings.DeniedGroups[l] := StringToUTF8(GroupName);
end
end;
if not RestServerSettings.AddMethodAuthorizationSettings(MethodAuthorizationSettings) then
begin
Result := False;
ShowMessage('Method authorization settings for method"' + UTF8ToString(MethodAuthorizationSettings.MethodName) + '" was not added, for some reason.');
end;
end;
GroupsToAdd.Clear;
if Result then
begin
// Add users
for i := 0 to ListViewUsers.Items.Count - 1 do
begin
UserName := ListViewUsers.Items[i].Caption;
AuthUser.LogonName := StringToUTF8(UserName);
AuthUser.DisplayName := AuthUser.LogonName;
AuthUser.PasswordPlain := StringToUTF8(ListViewUsers.Items[i].SubItems.Strings[0]);
AuthUser.Group := StringToUTF8(ListViewUsers.Items[i].SubItems.Strings[1]);
if not RestServerSettings.AddUser(AuthUser) then
begin
Result := False;
ShowMessage('User "' + UserName + '" was not added, for some reason.');
end;
end;
end;
end;
// Cleanup
AddedGroups.Free;
GroupsToAdd.Free;
end;
// Depends on the server status, start or stop server (create or destroy objects)
procedure TForm1.StartStopServer(ServerAction: lServerAction = Auto);
var
pServerCreated: Boolean;
RestServerSettings: TRestServerSettings;
begin
pServerCreated := RestServer.Initialized;
// Unload current server if required
if pServerCreated then
RestServer.DeInitialize();
// Create server if required
if ((ServerAction = lServerAction.Auto) and not pServerCreated) or ((ServerAction = lServerAction.Restart) and pServerCreated) or (ServerAction = lServerAction.Start) then
begin
// Create server object with selected Protocol and Auth mode
RestServerSettings := TRestServerSettings.Create();
RestServerSettings.Protocol := lProtocol(ComboBoxProtocol.ItemIndex);
RestServerSettings.Port := EditPort.Text;
RestServerSettings.AuthenticationMode := lAuthenticationMode(ComboBoxAuthentication.ItemIndex);
RestServerSettings.AuthorizationPolicy := lAuthorizationPolicy(RadioGroupAuthorizationPolicy.ItemIndex);
// Fill method authorization rules from UI
FillMethodAuthorizationRulesFromUI(RestServerSettings);
// Start server
RestServer.Initialize(RestServerSettings);
end;
end;
// Processing mORMot log event
function TForm1.LogEvent(Sender: TTextWriter; Level: TSynLogInfo; const Text: RawUTF8): Boolean;
var
List: TList<TLocalLog>;
LogEventData: TLocalLog;
begin
Result := False;
if Assigned(LogThreadSafeList) then
begin
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList;
{$endif}
try
LogEventData := TLocalLog.Create();
LogEventData.Level := Level;
LogEventData.Text := Text;
List.Add(LogEventData);
Result := True;
finally
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
end;
end;
end;
{ UI }
// Grabbing new events from thread safe list
procedure TForm1.TimerRefreshLogMemoTimer(Sender: TObject);
var
List: TList<TLocalLog>;
i: integer;
begin
if Assigned(LogThreadSafeList) then
begin
{$ifdef FPC}
List := LogThreadSafeList;
{$else}
List := LogThreadSafeList.LockList;
{$endif}
try
if Assigned(Form1) and not Application.Terminated and (List.Count > 0) then
begin
for i := 0 to List.Count - 1 do
begin
Form1.MemoLog.Lines.BeginUpdate();
Form1.MemoLog.Lines.Add(string(List.Items[i].Text));
Form1.MemoLog.Lines.EndUpdate();
List.Items[i].Free;
end;
List.Clear();
{$IFDEF MSWINDOWS}
if CheckBoxAutoScroll.Checked then
SendMessage(Form1.MemoLog.Handle, WM_VSCROLL, SB_BOTTOM, 0);
{$ENDIF}
end;
finally
{$ifndef FPC}
LogThreadSafeList.UnlockList();
{$endif}
end;
end;
if RestServer.Initialized then
ButtonStartStop.Caption := 'Stop server'
else
ButtonStartStop.Caption := 'Start server';
end;
// Get description for AuthMode
function TForm1.GetAuthModeDescription(AM: lAuthenticationMode): string;
begin
case AM of
NoAuthentication:
Result := 'Disabled authentication.';
{
URI:
Result := 'Weak authentication scheme using URL-level parameter';
}
{
SignedURI:
Result := 'Secure authentication scheme using URL-level digital signature - expected format of session_signature is:' + #13 + 'Hexa8(SessionID) + Hexa8(TimeStamp) + ' + #13 +
'Hexa8(crc32(SessionID + HexaSessionPrivateKey Sha256(salt + PassWord) + Hexa8(TimeStamp) + url))';
}
Default:
Result := 'mORMot secure RESTful authentication scheme, this method will use a password stored via safe SHA-256 hashing in the TSQLAuthUser ORM table';
None:
Result := 'mORMot weak RESTful authentication scheme, this method will authenticate with a given username, but no signature' + #13 +
'on client side, this scheme is not called by TSQLRestClientURI.SetUser() method - so you have to write:' + #13 + 'TSQLRestServerAuthenticationNone.ClientSetUser(Client,''User'','''');';
HttpBasic:
Result := 'Authentication using HTTP Basic scheme. This protocol send both name and password as clear (just base-64 encoded) so should only be used over SSL / HTTPS' +
', or for compatibility reasons. Will rely on TSQLRestServerAuthenticationNone for authorization, on client side, this scheme is not called by TSQLRestClientURI.SetUser() ' +
'method - so you have to write: TSQLRestServerAuthenticationHttpBasic.ClientSetUser(Client,''User'',''password'');' + #13 +
'for a remote proxy-only authentication (without creating any mORMot session), you can write: TSQLRestServerAuthenticationHttpBasic.ClientSetUserHttpOnly(Client,''proxyUser'',''proxyPass'');';
{$IFDEF MSWINDOWS}
SSPI:
Result := 'authentication of the current logged user using Windows Security Support Provider Interface (SSPI)' + #13 +
'- is able to authenticate the currently logged user on the client side, using either NTLM or Kerberos - it would allow to safely authenticate on a mORMot server without prompting' +
' the user to enter its password' + #13 + '- if ClientSetUser() receives aUserName as '''', aPassword should be either '''' if you expect NTLM authentication to take place,' +
' or contain the SPN registration (e.g. ''mymormotservice/myserver.mydomain.tld'') for Kerberos authentication.' + #13 +
'- if ClientSetUser() receives aUserName as ''DomainName\UserName'', then authentication will take place on the specified domain, with aPassword as plain password value.';
{$ENDIF}
else
Result := 'Authentication description';
end;
end;
// Changing server protocol
procedure TForm1.ComboBoxProtocolChange(Sender: TObject);
begin
{$IFDEF MSWINDOWS}
LabelHTTPSnote.Visible := (lProtocol(ComboBoxProtocol.ItemIndex) = HTTPsys_SSL);
{$ENDIF}
StartStopServer(Restart);
end;
// Changing server authentication mode
procedure TForm1.ComboBoxAuthenticationChange(Sender: TObject);
begin
StartStopServer(Restart);
end;
// Button clear log
procedure TForm1.ButtonCLSClick(Sender: TObject);
begin
MemoLog.Clear;
end;
// Button show authorization mode description
procedure TForm1.ButtonShowAuthorizationInfoClick(Sender: TObject);
var
AM: lAuthenticationMode;
begin
AM := lAuthenticationMode(ComboBoxAuthentication.ItemIndex);
ShowMessage(GetAuthModeDescription(AM));
end;
// Button start stop server
procedure TForm1.ButtonStartStopClick(Sender: TObject);
begin
StartStopServer();
end;
// Checkbox Enable/Disable logging to memo (slow down performance when enabled)
procedure TForm1.CheckBoxDisableLogClick(Sender: TObject);
begin
if not CheckBoxDisableLog.Checked then
TSQLLog.Family.Level := LOG_VERBOSE
else
TSQLLog.Family.Level := [];
end;
procedure TForm1.ListViewMethodGroupsSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
begin
if Assigned(Item) then
begin
EditAllowGroupNames.Text := Item.SubItems.Strings[0];
EditDenyAllowGroupNames.Text := Item.SubItems.Strings[1];
end;
end;
procedure TForm1.ButtonSaveRoleConfigurationClick(Sender: TObject);
var
Item: TListItem;
begin
Item := ListViewMethodGroups.Selected;
if Assigned(Item) then
begin
Item.SubItems.Strings[0] := EditAllowGroupNames.Text;
Item.SubItems.Strings[1] := EditDenyAllowGroupNames.Text;
StartStopServer(lServerAction.Restart);
end;
end;
procedure TForm1.ListViewUsersSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
begin
if Assigned(Item) then
begin
EditUserName.Text := Item.Caption;
EditUserPassword.Text := Item.SubItems.Strings[0];
EditUserGroup.Text := Item.SubItems.Strings[1];
end;
end;
procedure TForm1.ButtonSaveUsersClick(Sender: TObject);
var
Item: TListItem;
Done: Boolean;
begin
Done := False;
Item := ListViewUsers.Selected;
if Assigned(Item) then
begin
Item.Caption := EditUserName.Text;
Item.SubItems.Strings[0] := EditUserPassword.Text;
Item.SubItems.Strings[1] := EditUserGroup.Text;
Done := True;
end
else if (EditUserName.Text <> '') and (EditUserGroup.Text <> '') then
begin
Item := ListViewUsers.Items.Add();
Item.Caption := EditUserName.Text;
Item.SubItems.Add(EditUserPassword.Text);
Item.SubItems.Add(EditUserGroup.Text);
Done := True;
end
else
ShowMessage('User name and Group must be filled.');
if Done then
begin
EditUserName.Text := '';
EditUserPassword.Text := '';
EditUserGroup.Text := '';
StartStopServer(lServerAction.Restart);
end;
end;
procedure TForm1.RadioGroupAuthorizationPolicyClick(Sender: TObject);
begin
StartStopServer(lServerAction.Restart);
end;
procedure TForm1.ListViewMethodGroupsClick(Sender: TObject);
begin
if ListViewMethodGroups.ItemIndex = -1 then
begin
EditAllowGroupNames.Text := '';
EditDenyAllowGroupNames.Text := '';
end;
end;
procedure TForm1.ListViewUsersClick(Sender: TObject);
begin
if ListViewUsers.ItemIndex = -1 then
begin
EditUserName.Text := '';
EditUserPassword.Text := '';
EditUserGroup.Text := '';
end;
end;
// Delete user
procedure TForm1.ButtonDeleteUserClick(Sender: TObject);
var
SelUserIndex: integer;
begin
SelUserIndex := ListViewUsers.ItemIndex;
if SelUserIndex <> -1 then
begin
ListViewUsers.Items.Delete(SelUserIndex);
EditUserName.Text := '';
EditUserPassword.Text := '';
EditUserGroup.Text := '';
StartStopServer(lServerAction.Restart);
end;
end;
end.

View File

@@ -0,0 +1,90 @@
unit RestServerMethodsUnit;
interface
uses
// RTL
SysUtils,
Classes,
StrUtils,
Forms,
Dialogs,
Controls,
StdCtrls,
ExtCtrls,
// mORMot
mORMot,
mORMotHttpServer,
SynCommons,
// Custom
RestMethodsInterfaceUnit;
type
TCustomRecord = record helper for rCustomRecord
procedure FillResultFromServer();
end;
TRestMethods = class(TInjectableObjectRest, IRestMethods)
public
function HelloWorld(): string;
function Sum(val1, val2: Double): Double;
function GetCustomRecord(): rCustomRecord;
function SendCustomRecord(const CustomResult: rCustomRecord): Boolean;
function SendMultipleCustomRecords(const CustomResult: rCustomRecord; const CustomComplicatedRecord: rCustomComplicatedRecord): Boolean;
function GetMethodCustomResult(): TServiceCustomAnswer; // without default {result:[]}
end;
implementation
{ TCustomResultSrv }
procedure TCustomRecord.FillResultFromServer();
var
i: Integer;
begin
ResultCode := 200;
ResultStr := 'Awesome';
ResultTimeStamp := Now();
SetLength(ResultArray, 3);
for i := 0 to 2 do
ResultArray[i] := 'str_' + i.ToString();
end;
{ TServiceServer }
// [!] ServiceContext can be used from any method to access low level request data
function TRestMethods.HelloWorld(): string;
begin
Result := 'Hello world';
end;
function TRestMethods.Sum(val1, val2: Double): Double;
begin
Result := val1 + val2;
end;
function TRestMethods.GetCustomRecord(): rCustomRecord;
begin
Result.FillResultFromServer();
end;
function TRestMethods.SendCustomRecord(const CustomResult: rCustomRecord): Boolean;
begin
Result := True;
end;
function TRestMethods.SendMultipleCustomRecords(const CustomResult: rCustomRecord; const CustomComplicatedRecord: rCustomComplicatedRecord): Boolean;
begin
Result := True;
end;
function TRestMethods.GetMethodCustomResult(): TServiceCustomAnswer;
begin
Result.Header := 'Content-type: UTF-8';
Result.Content := 'I am custom result, no "result:[]" used.';
Result.Status := 200;
end;
end.

View File

@@ -0,0 +1,417 @@
unit RestServerUnit;
interface
uses
// RTL
SysUtils,
Classes,
StrUtils,
Generics.Collections,
Dialogs,
// mORMot
mORMot,
mORMotHttpServer,
SynBidirSock,
SynCommons,
SynCrtSock,
// Custom
RestMethodsInterfaceUnit,
RestServerMethodsUnit;
type
lProtocol = (HTTP_Socket, HTTPsys, HTTPsys_SSL, HTTPsys_AES, HTTP_WebSocket, WebSocketBidir_JSON, WebSocketBidir_Binary, WebSocketBidir_BinaryAES{$IFDEF MSWINDOWS}, NamedPipe{$ENDIF});
lAuthenticationMode = (NoAuthentication, Default, None, HttpBasic{$IFDEF MSWINDOWS},SSPI{$ENDIF});
lAuthorizationPolicy = (AllowAll, DenyAll, FollowGroupsSettings);
rAuthGroup = record
Name: RawUTF8;
SessionTimeout: integer;
SQLAccessRights: TSQLAccessRights;
end;
rAuthUser = record
LogonName: RawUTF8;
DisplayName: RawUTF8;
PasswordPlain: RawUTF8;
PasswordHashHexa: RawUTF8;
Group: RawUTF8;
end;
rMethodAuthorizationSettings = record
MethodName: RawUTF8;
AllowedGroups: array of RawUTF8;
DeniedGroups: array of RawUTF8;
end;
TRestServerSettings = class
private
fGroupList: TList<rAuthGroup>;
fUserList: TList<rAuthUser>;
fMethodAuthorizationSettings: TList<rMethodAuthorizationSettings>;
public
Protocol: lProtocol;
Port: string;
AuthenticationMode: lAuthenticationMode;
AuthorizationPolicy: lAuthorizationPolicy;
constructor Create();
destructor Destroy(); override;
function AddGroup(AuthGroup: rAuthGroup): boolean;
function AddUser(AuthUser: rAuthUser): boolean;
function AddMethodAuthorizationSettings(MethodAuthorizationSettings: rMethodAuthorizationSettings): boolean;
end;
tRestServer = class
private
fModel: TSQLModel;
fRestServer: TSQLRestServer;
fHTTPServer: TSQLHttpServer;
fServerSettings: TRestServerSettings;
fInitialized: boolean;
procedure ApplyAuthorizationRules(ServiceFactoryServer: TServiceFactoryServer; RestServerSettings: TRestServerSettings);
public
property Initialized: boolean read fInitialized;
constructor Create();
destructor Destroy(); override;
function Initialize(SrvSettings: TRestServerSettings): boolean;
function DeInitialize(): boolean;
end;
const
AppID = '{AA4AC37D-B812-46A7-BEFB-A68167A05BA7}';
var
RestServer: tRestServer;
implementation
{ TServerSettings }
constructor TRestServerSettings.Create();
begin
AuthenticationMode := lAuthenticationMode.NoAuthentication;
AuthorizationPolicy := lAuthorizationPolicy.AllowAll;
fGroupList := TList<rAuthGroup>.Create();
fUserList := TList<rAuthUser>.Create();
fMethodAuthorizationSettings := TList<rMethodAuthorizationSettings>.Create();
end;
destructor TRestServerSettings.Destroy();
begin
fGroupList.Free;
fUserList.Free;
fMethodAuthorizationSettings.Free;
inherited;
end;
function TRestServerSettings.AddGroup(AuthGroup: rAuthGroup): boolean;
begin
Result := True;
// some checks must be implemented here
// ...
fGroupList.Add(AuthGroup);
end;
function TRestServerSettings.AddUser(AuthUser: rAuthUser): boolean;
begin
Result := True;
// some checks must be implemented here
// ...
fUserList.Add(AuthUser);
end;
function TRestServerSettings.AddMethodAuthorizationSettings(MethodAuthorizationSettings: rMethodAuthorizationSettings): boolean;
begin
Result := True;
// some checks must be implemented here
// ...
fMethodAuthorizationSettings.Add(MethodAuthorizationSettings);
end;
{ tRestServer }
constructor tRestServer.Create();
begin
//
end;
destructor tRestServer.Destroy();
begin
DeInitialize();
fServerSettings.Free();
inherited;
end;
procedure tRestServer.ApplyAuthorizationRules(ServiceFactoryServer: TServiceFactoryServer; RestServerSettings: TRestServerSettings);
var
User: TSQLAuthUser;
Group: TSQLAuthGroup;
GroupID: TID;
i, j: integer;
AuthGroup: rAuthGroup;
AuthUser: rAuthUser;
MethodAuthorizationSettings: rMethodAuthorizationSettings;
// IDs: TIDDynArray;
begin
// ID := fRestServer.MainFieldID(TSQLAuthGroup, 'User');
{ TSQLAuthGroup }
// Clear default groups
fRestServer.Delete(TSQLAuthGroup, '');
for i := 0 to RestServerSettings.fGroupList.Count - 1 do
begin
AuthGroup := RestServerSettings.fGroupList.Items[i];
Group := TSQLAuthGroup.Create();
Group.Ident := AuthGroup.Name;
Group.SQLAccessRights := AuthGroup.SQLAccessRights;
Group.SessionTimeout := AuthGroup.SessionTimeout;
// Save object to ORM
fRestServer.Add(Group, True);
// Cleanup
Group.Free;
end;
{ TSQLAuthUser }
// Clear default users
fRestServer.Delete(TSQLAuthUser, '');
for i := 0 to RestServerSettings.fUserList.Count - 1 do
begin
AuthUser := RestServerSettings.fUserList.Items[i];
User := TSQLAuthUser.Create();
User.DisplayName := AuthUser.DisplayName;
User.LogonName := AuthUser.LogonName;
User.PasswordPlain := AuthUser.PasswordPlain;
if AuthUser.PasswordHashHexa <> '' then
User.PasswordHashHexa := AuthUser.PasswordHashHexa;
if AuthUser.Group <> '' then
begin
GroupID := fRestServer.MainFieldID(TSQLAuthGroup, AuthUser.Group);
User.GroupRights := pointer(GroupID);
end;
// Save object to ORM
fRestServer.Add(User, True);
// Cleanup
User.Free;
end;
{
// DEBUG
fRestServer.MainFieldIDs(TSQLAuthGroup, ['Administrators', 'Users'], IDs);
if Length(IDs) = 0 then
ShowMessage('why IDs = []? (((');
// WHY IDs empty?
Group := TSQLAuthGroup.CreateAndFillPrepare(fRestServer, '');
try
while Group.FillOne do
ShowMessage(Group.Ident);
finally
Group.Free;
end;
// DEBUG
}
// Apply Authorization Rules
case RestServerSettings.AuthorizationPolicy of
AllowAll:
ServiceFactoryServer.AllowAll();
DenyAll:
ServiceFactoryServer.DenyAll();
FollowGroupsSettings:
begin
ServiceFactoryServer.DenyAll();
for i := 0 to RestServerSettings.fMethodAuthorizationSettings.Count - 1 do
begin
MethodAuthorizationSettings := RestServerSettings.fMethodAuthorizationSettings.Items[i];
// ServiceFactoryServer.AllowByName([MethodAuthorizationSettings.MethodName], MethodAuthorizationSettings.AllowedGroups); // not work for some reason :(
// ServiceFactoryServer.DenyByName([MethodAuthorizationSettings.MethodName], MethodAuthorizationSettings.DeniedGroups);
for j := 0 to Length(MethodAuthorizationSettings.AllowedGroups) - 1 do
ServiceFactoryServer.AllowByName([MethodAuthorizationSettings.MethodName], MethodAuthorizationSettings.AllowedGroups[j]);
for j := 0 to Length(MethodAuthorizationSettings.DeniedGroups) - 1 do
ServiceFactoryServer.DenyByName([MethodAuthorizationSettings.MethodName], MethodAuthorizationSettings.DeniedGroups[j]);
end;
end;
end;
end;
function tRestServer.Initialize(SrvSettings: TRestServerSettings): boolean;
var
ServiceFactoryServer: TServiceFactoryServer;
{ WebSocketServerRest: TWebSocketServerRest; }
begin
Result := false;
// Destroy current object
if DeInitialize() then
try
// Server initialization (!!! for better understanding, each section contain separate code, later should be refactored)
fServerSettings.Free;
fServerSettings := SrvSettings;
case fServerSettings.AuthenticationMode of
// NoAuthentication
NoAuthentication:
begin
fModel := TSQLModel.Create([], ROOT_NAME);
fRestServer := TSQLRestServerFullMemory.Create(fModel, false);
ServiceFactoryServer := fRestServer.ServiceDefine(TRestMethods, [IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
ServiceFactoryServer.SetOptions([], [optErrorOnMissingParam]);
end;
// TSQLRestServerAuthenticationDefault
Default:
begin
fModel := TSQLModel.Create([], ROOT_NAME);
fRestServer := TSQLRestServerFullMemory.Create(fModel, false { make AuthenticationSchemesCount = 0 } );
ServiceFactoryServer := fRestServer.ServiceDefine(TRestMethods, [IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
fRestServer.AuthenticationRegister(TSQLRestServerAuthenticationDefault); // register single authentication mode
ApplyAuthorizationRules(ServiceFactoryServer, fServerSettings);
ServiceFactoryServer.SetOptions([], [optErrorOnMissingParam]);
end;
// TSQLRestServerAuthenticationNone
None:
begin
fModel := TSQLModel.Create([], ROOT_NAME);
fRestServer := TSQLRestServerFullMemory.Create(fModel, false { make AuthenticationSchemesCount = 0 } );
ServiceFactoryServer := fRestServer.ServiceDefine(TRestMethods, [IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
fRestServer.AuthenticationRegister(TSQLRestServerAuthenticationNone); // register single authentication mode
ApplyAuthorizationRules(ServiceFactoryServer, fServerSettings);
ServiceFactoryServer.SetOptions([], [optErrorOnMissingParam]);
end;
// TSQLRestServerAuthenticationHttpBasic
HttpBasic:
begin
fModel := TSQLModel.Create([], ROOT_NAME);
fRestServer := TSQLRestServerFullMemory.Create(fModel, false { make AuthenticationSchemesCount = 0 } );
ServiceFactoryServer := fRestServer.ServiceDefine(TRestMethods, [IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
fRestServer.AuthenticationRegister(TSQLRestServerAuthenticationHttpBasic); // register single authentication mode
ApplyAuthorizationRules(ServiceFactoryServer, fServerSettings);
ServiceFactoryServer.SetOptions([], [optErrorOnMissingParam]);
end;
// TSQLRestServerAuthenticationSSPI
{$IFDEF MSWINDOWS}
SSPI:
begin
fModel := TSQLModel.Create([], ROOT_NAME);
fRestServer := TSQLRestServerFullMemory.Create(fModel, false { make AuthenticationSchemesCount = 0 } );
ServiceFactoryServer := fRestServer.ServiceDefine(TRestMethods, [IRestMethods], SERVICE_INSTANCE_IMPLEMENTATION);
fRestServer.AuthenticationRegister(TSQLRestServerAuthenticationSSPI); // register single authentication mode
ApplyAuthorizationRules(ServiceFactoryServer, fServerSettings);
ServiceFactoryServer.SetOptions([], [optErrorOnMissingParam]);
end;
{$endif}
else
begin
DeInitialize();
raise Exception.Create('Selected Authentication mode not available in this build.');
end;
end;
// protocol initialization
case fServerSettings.Protocol of
HTTP_Socket:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useHttpSocket);
THttpServer(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
end;
{
// require manual URI registration, we will not use this option in this test project, because this option
// should be used with installation program that will unregister all used URIs during sofware uninstallation.
HTTPsys:
begin
HTTPServer := TSQLHttpServer.Create(AnsiString(Options.Port), [RestServer], '+', useHttpApi);
THttpServer(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := SERVER_CONNECTION_TIMEOUT;
end;
}
HTTPsys:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', HTTP_DEFAULT_MODE);
THttpServer(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
end;
HTTPsys_SSL:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', HTTP_DEFAULT_MODE, 32, TSQLHttpServerSecurity.secSSL);
THttpServer(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
end;
HTTPsys_AES:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', HTTP_DEFAULT_MODE, 32, TSQLHttpServerSecurity.secSynShaAes);
THttpServer(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
end;
HTTP_WebSocket:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useBidirSocket);
TWebSocketServerRest(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
end;
WebSocketBidir_JSON:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useBidirSocket);
TWebSocketServerRest(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
{ WebSocketServerRest := } fHTTPServer.WebSocketsEnable(fRestServer, '', True);
end;
WebSocketBidir_Binary:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useBidirSocket);
TWebSocketServerRest(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
{ WebSocketServerRest := } fHTTPServer.WebSocketsEnable(fRestServer, '', false);
end;
WebSocketBidir_BinaryAES:
begin
fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useBidirSocket);
TWebSocketServerRest(fHTTPServer.HttpServer).ServerKeepAliveTimeOut := CONNECTION_TIMEOUT;
{ WebSocketServerRest := } fHTTPServer.WebSocketsEnable(fRestServer, '2141D32ADAD54D9A9DB56000CC9A4A70', false);
end;
{$IFDEF MSWINDOWS}
NamedPipe:
begin
if not fRestServer.ExportServerNamedPipe(NAMED_PIPE_NAME) then
Exception.Create('Unable to register server with named pipe channel.');
end;
{$ENDIF}
else
begin
DeInitialize();
raise Exception.Create('Selected protocol not available in this build.');
end;
end;
Result := True;
except
on E: Exception do
begin
ShowMessage(E.ToString);
DeInitialize();
end;
end;
fInitialized := Result;
end;
function tRestServer.DeInitialize(): boolean;
begin
Result := True;
try
// if used HttpApiRegisteringURI then remove registration (require run as admin), but seems not work from here
{$IFDEF MSWINDOWS}
if Assigned(fHTTPServer) and (fHTTPServer.HttpServer.ClassType = THttpApiServer) then
THttpApiServer(fHTTPServer.HttpServer).RemoveUrl(ROOT_NAME, fHTTPServer.Port, fServerSettings.Protocol = HTTPsys_SSL, '+');
{$ENDIF}
if Assigned(fHTTPServer) then
FreeAndNil(fHTTPServer);
if Assigned(fRestServer) then
FreeAndNil(fRestServer);
if Assigned(fModel) then
FreeAndNil(fModel);
fInitialized := false;
except
on E: Exception do
begin
ShowMessage(E.ToString);
Result := false;
end;
end;
end;
initialization
RestServer := tRestServer.Create();
finalization
if Assigned(RestServer) then
FreeAndNil(RestServer);
end.

View File

@@ -0,0 +1,915 @@
{
This file is part of the Free Pascal run time library.
Copyright (c) 2014 by Maciej Izak (hnb)
member of the Free Sparta development team (http://freesparta.com)
Copyright(c) 2004-2014 DaThoX
It contains the Free Pascal generics library
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit Generics.Hashes;
{$MODE DELPHI}{$H+}
{$POINTERMATH ON}
{$MACRO ON}
{$COPERATORS ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
interface
uses
Classes, SysUtils;
// Original version of Bob Jenkins Hash
// http://burtleburtle.net/bob/c/lookup3.c
function HashWord(
AKey: PLongWord; //* the key, an array of uint32_t values */
ALength: SizeInt; //* the length of the key, in uint32_ts */
AInitVal: UInt32): UInt32; //* the previous hash, or an arbitrary value */
procedure HashWord2 (
AKey: PLongWord; //* the key, an array of uint32_t values */
ALength: SizeInt; //* the length of the key, in uint32_ts */
var APrimaryHashAndInitVal: UInt32; //* IN: seed OUT: primary hash value */
var ASecondaryHashAndInitVal: UInt32); //* IN: more seed OUT: secondary hash value */
function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32;
procedure HashLittle2(
AKey: Pointer; //* the key to hash */
ALength: SizeInt; //* length of the key */
var APrimaryHashAndInitVal: UInt32; //* IN: primary initval, OUT: primary hash */
var ASecondaryHashAndInitVal: UInt32); //* IN: secondary initval, OUT: secondary hash */
function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32;
procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32);
// hash function from fstl
function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32;
// some other hashes
// http://stackoverflow.com/questions/14409466/simple-hash-functions
// http://www.partow.net/programming/hashfunctions/
// http://en.wikipedia.org/wiki/List_of_hash_functions
// http://www.cse.yorku.ca/~oz/hash.html
// https://code.google.com/p/hedgewars/source/browse/hedgewars/adler32.pas
function Adler32(AKey: Pointer; ALength: SizeInt): UInt32;
function sdbm(AKey: Pointer; ALength: SizeInt): UInt32;
implementation
function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32;
var
i: Integer;
ABuffer: PUInt8 absolute AKey;
begin
Result := 0;
for i := 0 to ALength - 1 do
Inc(Result,ABuffer[i]);
end;
function Adler32(AKey: Pointer; ALength: SizeInt): UInt32;
const
MOD_ADLER = 65521;
var
ABuffer: PUInt8 absolute AKey;
a: UInt32 = 1;
b: UInt32 = 0;
n: Integer;
begin
for n := 0 to ALength -1 do
begin
a := (a + ABuffer[n]) mod MOD_ADLER;
b := (b + a) mod MOD_ADLER;
end;
Result := (b shl 16) or a;
end;
function sdbm(AKey: Pointer; ALength: SizeInt): UInt32;
var
c: PUInt8 absolute AKey;
i: Integer;
begin
Result := 0;
c := AKey;
for i := 0 to ALength - 1 do
begin
Result := c^ + (Result shl 6) + (Result shl 16) {%H-}- Result;
Inc(c);
end;
end;
{ BobJenkinsHash }
{$define mix_abc :=
a -= c; a := a xor (((c)shl(4)) or ((c)shr(32-(4)))); c += b;
b -= a; b := b xor (((a)shl(6)) or ((a)shr(32-(6)))); a += c;
c -= b; c := c xor (((b)shl(8)) or ((b)shr(32-(8)))); b += a;
a -= c; a := a xor (((c)shl(16)) or ((c)shr(32-(16)))); c += b;
b -= a; b := b xor (((a)shl(19)) or ((a)shr(32-(19)))); a += c;
c -= b; c := c xor (((b)shl(4)) or ((b)shr(32-(4)))); b += a
}
{$define final_abc :=
c := c xor b; c -= (((b)shl(14)) or ((b)shr(32-(14))));
a := a xor c; a -= (((c)shl(11)) or ((c)shr(32-(11))));
b := b xor a; b -= (((a)shl(25)) or ((a)shr(32-(25))));
c := c xor b; c -= (((b)shl(16)) or ((b)shr(32-(16))));
a := a xor c; a -= (((c)shl(4)) or ((c)shr(32-(4))));
b := b xor a; b -= (((a)shl(14)) or ((a)shr(32-(14))));
c := c xor b; c -= (((b)shl(24)) or ((b)shr(32-(24))))
}
function HashWord(
AKey: PLongWord; //* the key, an array of uint32_t values */
ALength: SizeInt; //* the length of the key, in uint32_ts */
AInitVal: UInt32): UInt32; //* the previous hash, or an arbitrary value */
var
a,b,c: UInt32;
label
Case0, Case1, Case2, Case3;
begin
//* Set up the internal state */
a := $DEADBEEF + (UInt32(ALength) shl 2) + AInitVal;
b := a;
c := b;
//*------------------------------------------------- handle most of the key */
while ALength > 3 do
begin
a += AKey[0];
b += AKey[1];
c += AKey[2];
mix_abc;
ALength -= 3;
AKey += 3;
end;
//*------------------------------------------- handle the last 3 uint32_t's */
case ALength of //* all the case statements fall through */
3: goto Case3;
2: goto Case2;
1: goto Case1;
0: goto Case0;
end;
Case3: c+=AKey[2];
Case2: b+=AKey[1];
Case1: a+=AKey[0];
final_abc;
Case0: //* case 0: nothing left to add */
//*------------------------------------------------------ report the result */
Result := c;
end;
procedure HashWord2 (
AKey: PLongWord; //* the key, an array of uint32_t values */
ALength: SizeInt; //* the length of the key, in uint32_ts */
var APrimaryHashAndInitVal: UInt32; //* IN: seed OUT: primary hash value */
var ASecondaryHashAndInitVal: UInt32); //* IN: more seed OUT: secondary hash value */
var
a,b,c: UInt32;
label
Case0, Case1, Case2, Case3;
begin
//* Set up the internal state */
a := $deadbeef + (UInt32(ALength shl 2)) + APrimaryHashAndInitVal;
b := a;
c := b;
c += ASecondaryHashAndInitVal;
//*------------------------------------------------- handle most of the key */
while ALength > 3 do
begin
a += AKey[0];
b += AKey[1];
c += AKey[2];
mix_abc;
ALength -= 3;
AKey += 3;
end;
//*------------------------------------------- handle the last 3 uint32_t's */
case ALength of //* all the case statements fall through */
3: goto Case3;
2: goto Case2;
1: goto Case1;
0: goto Case0;
end;
Case3: c+=AKey[2];
Case2: b+=AKey[1];
Case1: a+=AKey[0];
final_abc;
Case0: //* case 0: nothing left to add */
//*------------------------------------------------------ report the result */
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
end;
function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32;
var
a, b, c: UInt32;
u: record case byte of
0: (ptr: Pointer);
1: (i: PtrUint);
end absolute AKey;
k32: ^UInt32 absolute AKey;
k16: ^UInt16 absolute AKey;
k8: ^UInt8 absolute AKey;
label _10, _8, _6, _4, _2;
label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
begin
a := $DEADBEEF + UInt32(ALength) + AInitVal;
b := a;
c := b;
{$IFDEF ENDIAN_LITTLE}
if (u.i and $3) = 0 then
begin
while (ALength > 12) do
begin
a += k32[0];
b += k32[1];
c += k32[2];
mix_abc;
ALength -= 12;
k32 += 3;
end;
case ALength of
12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
8 : begin b += k32[1]; a += k32[0]; end;
7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
6 : begin b += k32[1] and $ffff; a += k32[0]; end;
5 : begin b += k32[1] and $ff; a += k32[0]; end;
4 : begin a += k32[0]; end;
3 : begin a += k32[0] and $ffffff; end;
2 : begin a += k32[0] and $ffff; end;
1 : begin a += k32[0] and $ff; end;
0 : Exit(c); // zero length strings require no mixing
end
end
else
if (u.i and $1) = 0 then
begin
while (ALength > 12) do
begin
a += k16[0] + (UInt32(k16[1]) shl 16);
b += k16[2] + (UInt32(k16[3]) shl 16);
c += k16[4] + (UInt32(k16[5]) shl 16);
mix_abc;
ALength -= 12;
k16 += 6;
end;
case ALength of
12:
begin
c+=k16[4]+((UInt32(k16[5])) shl 16);
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
11:
begin
c+=(UInt32(k8[10])) shl 16; //* fall through */
goto _10;
end;
10:
begin _10:
c+=k16[4];
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
9 :
begin
c+=k8[8]; //* fall through */
goto _8;
end;
8 :
begin _8:
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
7 :
begin
b+=(UInt32(k8[6])) shl 16; //* fall through */
goto _6;
end;
6 :
begin _6:
b+=k16[2];
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
5 :
begin
b+=k8[4]; //* fall through */
goto _4;
end;
4 :
begin _4:
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
3 :
begin
a+=(UInt32(k8[2])) shl 16; //* fall through */
goto _2;
end;
2 :
begin _2:
a+=k16[0];
end;
1 :
begin
a+=k8[0];
end;
0 : Exit(c); //* zero length requires no mixing */
end;
end
else
{$ENDIF}
begin
while ALength > 12 do
begin
a += k8[0];
a += (UInt32(k8[1])) shl 8;
a += (UInt32(k8[2])) shl 16;
a += (UInt32(k8[3])) shl 24;
b += k8[4];
b += (UInt32(k8[5])) shl 8;
b += (UInt32(k8[6])) shl 16;
b += (UInt32(k8[7])) shl 24;
c += k8[8];
c += (UInt32(k8[9])) shl 8;
c += (UInt32(k8[10])) shl 16;
c += (UInt32(k8[11])) shl 24;
mix_abc;
ALength -= 12;
k8 += 12;
end;
case ALength of
12: goto Case12;
11: goto Case11;
10: goto Case10;
9 : goto Case9;
8 : goto Case8;
7 : goto Case7;
6 : goto Case6;
5 : goto Case5;
4 : goto Case4;
3 : goto Case3;
2 : goto Case2;
1 : goto Case1;
0 : Exit(c);
end;
Case12: c+=(UInt32(k8[11])) shl 24;
Case11: c+=(UInt32(k8[10])) shl 16;
Case10: c+=(UInt32(k8[9])) shl 8;
Case9: c+=k8[8];
Case8: b+=(UInt32(k8[7])) shl 24;
Case7: b+=(UInt32(k8[6])) shl 16;
Case6: b+=(UInt32(k8[5])) shl 8;
Case5: b+=k8[4];
Case4: a+=(UInt32(k8[3])) shl 24;
Case3: a+=(UInt32(k8[2])) shl 16;
Case2: a+=(UInt32(k8[1])) shl 8;
Case1: a+=k8[0];
end;
final_abc;
Result := c;
end;
(*
* hashlittle2: return 2 32-bit hash values
*
* This is identical to hashlittle(), except it returns two 32-bit hash
* values instead of just one. This is good enough for hash table
* lookup with 2^^64 buckets, or if you want a second hash if you're not
* happy with the first, or if you want a probably-unique 64-bit ID for
* the key. *pc is better mixed than *pb, so use *pc first. If you want
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
*)
procedure HashLittle2(
AKey: Pointer; //* the key to hash */
ALength: SizeInt; //* length of the key */
var APrimaryHashAndInitVal: UInt32; //* IN: primary initval, OUT: primary hash */
var ASecondaryHashAndInitVal: UInt32); //* IN: secondary initval, OUT: secondary hash */
var
a,b,c: UInt32;
u: record case byte of
0: (ptr: Pointer);
1: (i: PtrUint);
end absolute AKey;
k32: ^UInt32 absolute AKey;
k16: ^UInt16 absolute AKey;
k8: ^UInt8 absolute AKey;
label _10, _8, _6, _4, _2;
label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
begin
//* Set up the internal state */
a := $DEADBEEF + UInt32(ALength) + APrimaryHashAndInitVal;
b := a;
c := b;
c += ASecondaryHashAndInitVal;
{$IFDEF ENDIAN_LITTLE}
if (u.i and $3) = 0 then
begin
while (ALength > 12) do
begin
a += k32[0];
b += k32[1];
c += k32[2];
mix_abc;
ALength -= 12;
k32 += 3;
end;
case ALength of
12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
8 : begin b += k32[1]; a += k32[0]; end;
7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
6 : begin b += k32[1] and $ffff; a += k32[0]; end;
5 : begin b += k32[1] and $ff; a += k32[0]; end;
4 : begin a += k32[0]; end;
3 : begin a += k32[0] and $ffffff; end;
2 : begin a += k32[0] and $ffff; end;
1 : begin a += k32[0] and $ff; end;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end
end
else
if (u.i and $1) = 0 then
begin
while (ALength > 12) do
begin
a += k16[0] + (UInt32(k16[1]) shl 16);
b += k16[2] + (UInt32(k16[3]) shl 16);
c += k16[4] + (UInt32(k16[5]) shl 16);
mix_abc;
ALength -= 12;
k16 += 6;
end;
case ALength of
12:
begin
c+=k16[4]+((UInt32(k16[5])) shl 16);
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
11:
begin
c+=(UInt32(k8[10])) shl 16; //* fall through */
goto _10;
end;
10:
begin _10:
c+=k16[4];
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
9 :
begin
c+=k8[8]; //* fall through */
goto _8;
end;
8 :
begin _8:
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
7 :
begin
b+=(UInt32(k8[6])) shl 16; //* fall through */
goto _6;
end;
6 :
begin _6:
b+=k16[2];
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
5 :
begin
b+=k8[4]; //* fall through */
goto _4;
end;
4 :
begin _4:
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
3 :
begin
a+=(UInt32(k8[2])) shl 16; //* fall through */
goto _2;
end;
2 :
begin _2:
a+=k16[0];
end;
1 :
begin
a+=k8[0];
end;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end;
end
else
{$ENDIF}
begin
while ALength > 12 do
begin
a += k8[0];
a += (UInt32(k8[1])) shl 8;
a += (UInt32(k8[2])) shl 16;
a += (UInt32(k8[3])) shl 24;
b += k8[4];
b += (UInt32(k8[5])) shl 8;
b += (UInt32(k8[6])) shl 16;
b += (UInt32(k8[7])) shl 24;
c += k8[8];
c += (UInt32(k8[9])) shl 8;
c += (UInt32(k8[10])) shl 16;
c += (UInt32(k8[11])) shl 24;
mix_abc;
ALength -= 12;
k8 += 12;
end;
case ALength of
12: goto Case12;
11: goto Case11;
10: goto Case10;
9 : goto Case9;
8 : goto Case8;
7 : goto Case7;
6 : goto Case6;
5 : goto Case5;
4 : goto Case4;
3 : goto Case3;
2 : goto Case2;
1 : goto Case1;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end;
Case12: c+=(UInt32(k8[11])) shl 24;
Case11: c+=(UInt32(k8[10])) shl 16;
Case10: c+=(UInt32(k8[9])) shl 8;
Case9: c+=k8[8];
Case8: b+=(UInt32(k8[7])) shl 24;
Case7: b+=(UInt32(k8[6])) shl 16;
Case6: b+=(UInt32(k8[5])) shl 8;
Case5: b+=k8[4];
Case4: a+=(UInt32(k8[3])) shl 24;
Case3: a+=(UInt32(k8[2])) shl 16;
Case2: a+=(UInt32(k8[1])) shl 8;
Case1: a+=k8[0];
end;
final_abc;
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
end;
procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32);
var
a,b,c: UInt32;
u: record case byte of
0: (ptr: Pointer);
1: (i: PtrUint);
end absolute AKey;
k32: ^UInt32 absolute AKey;
k16: ^UInt16 absolute AKey;
k8: ^UInt8 absolute AKey;
label _10, _8, _6, _4, _2;
label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
begin
//* Set up the internal state */
a := $DEADBEEF + UInt32(ALength shl 2) + APrimaryHashAndInitVal; // delphi version bug? original version don't have "shl 2"
b := a;
c := b;
c += ASecondaryHashAndInitVal;
{$IFDEF ENDIAN_LITTLE}
if (u.i and $3) = 0 then
begin
while (ALength > 12) do
begin
a += k32[0];
b += k32[1];
c += k32[2];
mix_abc;
ALength -= 12;
k32 += 3;
end;
case ALength of
12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
8 : begin b += k32[1]; a += k32[0]; end;
7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
6 : begin b += k32[1] and $ffff; a += k32[0]; end;
5 : begin b += k32[1] and $ff; a += k32[0]; end;
4 : begin a += k32[0]; end;
3 : begin a += k32[0] and $ffffff; end;
2 : begin a += k32[0] and $ffff; end;
1 : begin a += k32[0] and $ff; end;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end
end
else
if (u.i and $1) = 0 then
begin
while (ALength > 12) do
begin
a += k16[0] + (UInt32(k16[1]) shl 16);
b += k16[2] + (UInt32(k16[3]) shl 16);
c += k16[4] + (UInt32(k16[5]) shl 16);
mix_abc;
ALength -= 12;
k16 += 6;
end;
case ALength of
12:
begin
c+=k16[4]+((UInt32(k16[5])) shl 16);
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
11:
begin
c+=(UInt32(k8[10])) shl 16; //* fall through */
goto _10;
end;
10:
begin _10:
c+=k16[4];
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
9 :
begin
c+=k8[8]; //* fall through */
goto _8;
end;
8 :
begin _8:
b+=k16[2]+((UInt32(k16[3])) shl 16);
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
7 :
begin
b+=(UInt32(k8[6])) shl 16; //* fall through */
goto _6;
end;
6 :
begin _6:
b+=k16[2];
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
5 :
begin
b+=k8[4]; //* fall through */
goto _4;
end;
4 :
begin _4:
a+=k16[0]+((UInt32(k16[1])) shl 16);
end;
3 :
begin
a+=(UInt32(k8[2])) shl 16; //* fall through */
goto _2;
end;
2 :
begin _2:
a+=k16[0];
end;
1 :
begin
a+=k8[0];
end;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end;
end
else
{$ENDIF}
begin
while ALength > 12 do
begin
a += k8[0];
a += (UInt32(k8[1])) shl 8;
a += (UInt32(k8[2])) shl 16;
a += (UInt32(k8[3])) shl 24;
b += k8[4];
b += (UInt32(k8[5])) shl 8;
b += (UInt32(k8[6])) shl 16;
b += (UInt32(k8[7])) shl 24;
c += k8[8];
c += (UInt32(k8[9])) shl 8;
c += (UInt32(k8[10])) shl 16;
c += (UInt32(k8[11])) shl 24;
mix_abc;
ALength -= 12;
k8 += 12;
end;
case ALength of
12: goto Case12;
11: goto Case11;
10: goto Case10;
9 : goto Case9;
8 : goto Case8;
7 : goto Case7;
6 : goto Case6;
5 : goto Case5;
4 : goto Case4;
3 : goto Case3;
2 : goto Case2;
1 : goto Case1;
0 :
begin
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
Exit; // zero length strings require no mixing
end;
end;
Case12: c+=(UInt32(k8[11])) shl 24;
Case11: c+=(UInt32(k8[10])) shl 16;
Case10: c+=(UInt32(k8[9])) shl 8;
Case9: c+=k8[8];
Case8: b+=(UInt32(k8[7])) shl 24;
Case7: b+=(UInt32(k8[6])) shl 16;
Case6: b+=(UInt32(k8[5])) shl 8;
Case5: b+=k8[4];
Case4: a+=(UInt32(k8[3])) shl 24;
Case3: a+=(UInt32(k8[2])) shl 16;
Case2: a+=(UInt32(k8[1])) shl 8;
Case1: a+=k8[0];
end;
final_abc;
APrimaryHashAndInitVal := c;
ASecondaryHashAndInitVal := b;
end;
function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32;
var
a, b, c: UInt32;
u: record case byte of
0: (ptr: Pointer);
1: (i: PtrUint);
end absolute AKey;
k32: ^UInt32 absolute AKey;
//k16: ^UInt16 absolute AKey;
k8: ^UInt8 absolute AKey;
label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
begin
a := $DEADBEEF + UInt32(ALength shl 2) + AInitVal; // delphi version bug? original version don't have "shl 2"
b := a;
c := b;
{.$IFDEF ENDIAN_LITTLE} // Delphi version don't care
if (u.i and $3) = 0 then
begin
while (ALength > 12) do
begin
a += k32[0];
b += k32[1];
c += k32[2];
mix_abc;
ALength -= 12;
k32 += 3;
end;
case ALength of
12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
8 : begin b += k32[1]; a += k32[0]; end;
7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
6 : begin b += k32[1] and $ffff; a += k32[0]; end;
5 : begin b += k32[1] and $ff; a += k32[0]; end;
4 : begin a += k32[0]; end;
3 : begin a += k32[0] and $ffffff; end;
2 : begin a += k32[0] and $ffff; end;
1 : begin a += k32[0] and $ff; end;
0 : Exit(c); // zero length strings require no mixing
end
end
else
{.$ENDIF}
begin
while ALength > 12 do
begin
a += k8[0];
a += (UInt32(k8[1])) shl 8;
a += (UInt32(k8[2])) shl 16;
a += (UInt32(k8[3])) shl 24;
b += k8[4];
b += (UInt32(k8[5])) shl 8;
b += (UInt32(k8[6])) shl 16;
b += (UInt32(k8[7])) shl 24;
c += k8[8];
c += (UInt32(k8[9])) shl 8;
c += (UInt32(k8[10])) shl 16;
c += (UInt32(k8[11])) shl 24;
mix_abc;
ALength -= 12;
k8 += 12;
end;
case ALength of
12: goto Case12;
11: goto Case11;
10: goto Case10;
9 : goto Case9;
8 : goto Case8;
7 : goto Case7;
6 : goto Case6;
5 : goto Case5;
4 : goto Case4;
3 : goto Case3;
2 : goto Case2;
1 : goto Case1;
0 : Exit(c);
end;
Case12: c+=(UInt32(k8[11])) shl 24;
Case11: c+=(UInt32(k8[10])) shl 16;
Case10: c+=(UInt32(k8[9])) shl 8;
Case9: c+=k8[8];
Case8: b+=(UInt32(k8[7])) shl 24;
Case7: b+=(UInt32(k8[6])) shl 16;
Case6: b+=(UInt32(k8[5])) shl 8;
Case5: b+=k8[4];
Case4: a+=(UInt32(k8[3])) shl 24;
Case3: a+=(UInt32(k8[2])) shl 16;
Case2: a+=(UInt32(k8[1])) shl 8;
Case1: a+=k8[0];
end;
final_abc;
Result := Int32(c);
end;
end.

View File

@@ -0,0 +1,144 @@
{
This file is part of the Free Pascal run time library.
Copyright (c) 2014 by Maciej Izak (hnb)
member of the Free Sparta development team (http://freesparta.com)
Copyright(c) 2004-2014 DaThoX
It contains the Free Pascal generics library
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit Generics.Helpers;
{$MODE DELPHI}{$H+}
{$MODESWITCH TYPEHELPERS}
interface
uses
Classes, SysUtils;
type
{ TValueAnsiStringHelper }
TValueAnsiStringHelper = record helper for AnsiString
function ToLower: AnsiString; inline;
end;
{ TValuewideStringHelper }
TValueWideStringHelper = record helper for WideString
function ToLower: WideString; inline;
end;
{ TValueUnicodeStringHelper }
TValueUnicodeStringHelper = record helper for UnicodeString
function ToLower: UnicodeString; inline;
end;
{ TValueShortStringHelper }
TValueShortStringHelper = record helper for ShortString
function ToLower: ShortString; inline;
end;
{ TValueUTF8StringHelper }
TValueUTF8StringHelper = record helper for UTF8String
function ToLower: UTF8String; inline;
end;
{ TValueRawByteStringHelper }
TValueRawByteStringHelper = record helper for RawByteString
function ToLower: RawByteString; inline;
end;
{ TValueUInt32Helper }
TValueUInt32Helper = record helper for UInt32
class function GetSignMask: UInt32; static; inline;
class function GetSizedSignMask(ABits: Byte): UInt32; static; inline;
class function GetBitsLength: Byte; static; inline;
const
SIZED_SIGN_MASK: array[1..32] of UInt32 = (
$80000000, $C0000000, $E0000000, $F0000000, $F8000000, $FC000000, $FE000000, $FF000000,
$FF800000, $FFC00000, $FFE00000, $FFF00000, $FFF80000, $FFFC0000, $FFFE0000, $FFFF0000,
$FFFF8000, $FFFFC000, $FFFFE000, $FFFFF000, $FFFFF800, $FFFFFC00, $FFFFFE00, $FFFFFF00,
$FFFFFF80, $FFFFFFC0, $FFFFFFE0, $FFFFFFF0, $FFFFFFF8, $FFFFFFFC, $FFFFFFFE, $FFFFFFFF);
BITS_LENGTH = 32;
end;
implementation
{ TRawDataStringHelper }
function TValueAnsiStringHelper.ToLower: AnsiString;
begin
Result := LowerCase(Self);
end;
{ TValueWideStringHelper }
function TValueWideStringHelper.ToLower: WideString;
begin
Result := LowerCase(Self);
end;
{ TValueUnicodeStringHelper }
function TValueUnicodeStringHelper.ToLower: UnicodeString;
begin
Result := LowerCase(Self);
end;
{ TValueShortStringHelper }
function TValueShortStringHelper.ToLower: ShortString;
begin
Result := LowerCase(Self);
end;
{ TValueUTF8StringHelper }
function TValueUTF8StringHelper.ToLower: UTF8String;
begin
Result := LowerCase(Self);
end;
{ TValueRawByteStringHelper }
function TValueRawByteStringHelper.ToLower: RawByteString;
begin
Result := LowerCase(Self);
end;
{ TValueUInt32Helper }
class function TValueUInt32Helper.GetSignMask: UInt32;
begin
Result := $80000000;
end;
class function TValueUInt32Helper.GetSizedSignMask(ABits: Byte): UInt32;
begin
Result := SIZED_SIGN_MASK[ABits];
end;
class function TValueUInt32Helper.GetBitsLength: Byte;
begin
Result := BITS_LENGTH;
end;
end.

View File

@@ -0,0 +1,236 @@
{
This file is part of the Free Pascal run time library.
Copyright (c) 2014 by Maciej Izak (hnb)
member of the Free Sparta development team (http://freesparta.com)
Copyright(c) 2004-2014 DaThoX
It contains the Free Pascal generics library
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit Generics.MemoryExpanders;
// Memory expanders
{$mode delphi}
{$MACRO ON}
{.$WARN 5024 OFF}
{.$WARN 4079 OFF}
interface
uses
Classes, SysUtils;
type
TProbeSequence = class
public
end;
{ TLinearProbing }
TLinearProbing = class(TProbeSequence)
public
class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline;
const MAX_LOAD_FACTOR = 1;
const DEFAULT_LOAD_FACTOR = 0.75;
end;
{ TQuadraticProbing }
TQuadraticProbing = class(TProbeSequence)
private
class constructor Create;
public
class var C1: UInt32;
class var C2: UInt32;
class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline;
const MAX_LOAD_FACTOR = 0.5;
const DEFAULT_LOAD_FACTOR = 0.5;
end;
{ TDoubleHashing }
TDoubleHashing = class(TProbeSequence)
public
class function Probe(I, {%H-}M, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline;
const MAX_LOAD_FACTOR = 1;
const DEFAULT_LOAD_FACTOR = 0.85;
end;
const
// http://stackoverflow.com/questions/757059/position-of-least-significant-bit-that-is-set
// MultiplyDeBruijnBitPosition[uint32(((numberInt32 and -numberInt32) * $077CB531)) shr 27]
MultiplyDeBruijnBitPosition: array[0..31] of Int32 =
(
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
);
// http://primes.utm.edu/lists/2small/0bit.html
// http://www.math.niu.edu/~rusin/known-math/98/pi_x
// http://oeis.org/A014234/
PrimaryNumbersJustLessThanPowerOfTwo: array[0..31] of UInt32 =
(
0, 1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071,
262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
134217689, 268435399, 536870909, 1073741789, 2147483647
);
// http://oeis.org/A014210
// http://oeis.org/A203074
PrimaryNumbersJustBiggerThanPowerOfTwo: array[0..31] of UInt32 = (
2,3,5,11,17,37,67,131,257,521,1031,2053,4099,
8209,16411,32771,65537,131101,262147,524309,
1048583,2097169,4194319,8388617,16777259,33554467,
67108879,134217757,268435459,536870923,1073741827,
2147483659);
// Fibonacci numbers
FibonacciNumbers: array[0..44] of UInt32 = (
{0,1,1,2,3,}0,5,8,13,21,34,55,89,144,233,377,610,987,
1597,2584,4181,6765,10946,17711,28657,46368,75025,
121393,196418,317811,514229,832040,1346269,
2178309,3524578,5702887,9227465,14930352,24157817,
39088169, 63245986, 102334155, 165580141, 267914296,
433494437, 701408733, 1134903170, 1836311903, 2971215073,
{! not fib number - this is memory limit} 4294967295);
// Largest prime not exceeding Fibonacci(n)
// http://oeis.org/A138184/list
// http://www.numberempire.com/primenumbers.php
PrimaryNumbersJustLessThanFibonacciNumbers: array[0..44] of UInt32 = (
{! not correlated to fib number. For empty table} 0,
5,7,13,19,31,53,89,139,233,373,607,983,1597,
2579,4177,6763,10939,17707,28657,46351,75017,
121379,196387,317797,514229,832003,1346249,
2178283,3524569,5702867,9227443,14930341,24157811,
39088157,63245971,102334123,165580123,267914279,
433494437,701408717,1134903127,1836311879,2971215073,
{! not correlated to fib number - this is prime memory limit} 4294967291);
// Smallest prime >= n-th Fibonacci number.
// http://oeis.org/A138185
PrimaryNumbersJustBiggerThanFibonacciNumbers: array[0..44] of UInt32 = (
{! not correlated to fib number. For empty table} 0,
5,11,13,23,37,59,89,149,233,379,613,
991,1597,2591,4201,6779,10949,17713,28657,46381,
75029,121403,196429,317827,514229,832063,1346273,
2178313,3524603,5702897,9227479,14930387,24157823,
39088193,63245989,102334157,165580147,267914303,
433494437,701408753,1134903179,1836311951,2971215073,
{! not correlated to fib number - this is prime memory limit} 4294967291);
type
{ TCuckooHashingCfg }
TCuckooHashingCfg = class
public
const D = 2;
const MAX_LOAD_FACTOR = 0.5;
class function LoadFactor(M: Integer): Integer; virtual;
end;
TStdCuckooHashingCfg = class(TCuckooHashingCfg)
public
const MAX_LOOP = 1000;
end;
TDeamortizedCuckooHashingCfg = class(TCuckooHashingCfg)
public
const L = 5;
end;
TDeamortizedCuckooHashingCfg_D2 = TDeamortizedCuckooHashingCfg;
{ TDeamortizedCuckooHashingCfg_D4 }
TDeamortizedCuckooHashingCfg_D4 = class(TDeamortizedCuckooHashingCfg)
public
const D = 4;
const L = 20;
const MAX_LOAD_FACTOR = 0.9;
class function LoadFactor(M: Integer): Integer; override;
end;
{ TDeamortizedCuckooHashingCfg_D6 }
TDeamortizedCuckooHashingCfg_D6 = class(TDeamortizedCuckooHashingCfg)
public
const D = 6;
const L = 170;
const MAX_LOAD_FACTOR = 0.99;
class function LoadFactor(M: Integer): Integer; override;
end;
TL5CuckooHashingCfg = class(TCuckooHashingCfg)
public
end;
implementation
{ TDeamortizedCuckooHashingCfg_D6 }
class function TDeamortizedCuckooHashingCfg_D6.LoadFactor(M: Integer): Integer;
begin
Result:=Pred(Round(MAX_LOAD_FACTOR*M));
end;
{ TDeamortizedCuckooHashingCfg_D4 }
class function TDeamortizedCuckooHashingCfg_D4.LoadFactor(M: Integer): Integer;
begin
Result:=Pred(Round(MAX_LOAD_FACTOR*M));
end;
{ TCuckooHashingCfg }
class function TCuckooHashingCfg.LoadFactor(M: Integer): Integer;
begin
Result := Pred(M shr 1);
end;
{ TLinearProbing }
class function TLinearProbing.Probe(I, M, Hash: UInt32): UInt32;
begin
Result := (Hash + I)
end;
{ TQuadraticProbing }
class constructor TQuadraticProbing.Create;
begin
C1 := 1;
C2 := 1;
end;
class function TQuadraticProbing.Probe(I, M, Hash: UInt32): UInt32;
begin
Result := (Hash + C1 * I {%H-}+ C2 * Sqr(I));
end;
{ TDoubleHashingNoMod }
class function TDoubleHashing.Probe(I, M, Hash1: UInt32; Hash2: UInt32): UInt32;
begin
Result := Hash1 + I * Hash2;
end;
end.

View File

@@ -0,0 +1,34 @@
{
This file is part of the Free Pascal run time library.
Copyright (c) 2014 by Maciej Izak (hnb)
member of the Free Sparta development team (http://freesparta.com)
Copyright(c) 2004-2014 DaThoX
It contains the Free Pascal generics library
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit Generics.Strings;
{$mode objfpc}{$H+}
interface
resourcestring
SArgumentOutOfRange = 'Argument out of range';
SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary';
SDictionaryKeyDoesNotExist = 'Dictionary key does not exist';
SItemNotFound = 'Item not found';
implementation
end.

View File

@@ -0,0 +1,533 @@
{%MainUnit generics.collections.pas}
{
This file is part of the Free Pascal run time library.
Copyright (c) 2014 by Maciej Izak (hnb)
member of the Free Sparta development team (http://freesparta.com)
Copyright(c) 2004-2014 DaThoX
It contains the Free Pascal generics library
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$WARNINGS OFF}
type
TEmptyRecord = record // special record for Dictionary TValue (Dictionary as Set)
end;
{ TPair }
TPair<TKey, TValue> = record
public
Key: TKey;
Value: TValue;
class function Create(AKey: TKey; AValue: TValue): TPair<TKey, TValue>; static;
end;
{ TCustomDictionary }
// bug #24283 and #24097 (forward declaration) - should be:
// TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class(TEnumerable<TPair<TKey, TValue> >);
TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract
public type
// workaround... no generics types in generics types
TDictionaryPair = TPair<TKey, TValue>;
PDictionaryPair = ^TDictionaryPair;
PKey = ^TKey;
PValue = ^TValue;
THashFactoryClass = THashFactory;
public
FItemsLength: SizeInt;
FEqualityComparer: IEqualityComparer<TKey>;
FKeys: TEnumerable<TKey>;
FValues: TEnumerable<TValue>;
FMaxLoadFactor: single;
protected
procedure SetCapacity(ACapacity: SizeInt); virtual; abstract;
// bug #24283. workaround for this class because can't inherit from TEnumerable
function DoGetEnumerator: TEnumerator<TDictionaryPair>; virtual; abstract; {override;}
procedure SetMaxLoadFactor(AValue: single); virtual; abstract;
function GetLoadFactor: single; virtual; abstract;
function GetCapacity: SizeInt; virtual; abstract;
public
property MaxLoadFactor: single read FMaxLoadFactor write SetMaxLoadFactor;
property LoadFactor: single read GetLoadFactor;
property Capacity: SizeInt read GetCapacity write SetCapacity;
property Count: SizeInt read FItemsLength;
procedure Clear; virtual; abstract;
procedure Add(constref APair: TPair<TKey, TValue>); virtual; abstract;
strict private // bug #24283. workaround for this class because can't inherit from TEnumerable
function ToArray(ACount: SizeInt): TArray<TDictionaryPair>; overload;
public
function ToArray: TArray<TDictionaryPair>; virtual; final; {override; final; // bug #24283} overload;
constructor Create; virtual; overload;
constructor Create(ACapacity: SizeInt); virtual; overload;
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); virtual; overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); virtual; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload;
destructor Destroy; override;
private
FOnKeyNotify: TCollectionNotifyEvent<TKey>;
FOnValueNotify: TCollectionNotifyEvent<TValue>;
protected
procedure UpdateItemsThreshold(ASize: SizeInt); virtual; abstract;
procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); virtual;
procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); virtual;
procedure PairNotify(constref APair: TPair<TKey, TValue>; ACollectionNotification: TCollectionNotification); inline;
procedure SetValue(var AValue: TValue; constref ANewValue: TValue);
public
property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify;
property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify;
end;
{ TCustomDictionaryEnumerator }
TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerator< T >)
private
FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>;
FIndex: SizeInt;
protected
function DoGetCurrent: T; override;
function GetCurrent: T; virtual; abstract;
public
constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
end;
{ TDictionaryEnumerable }
TDictionaryEnumerable<TDictionaryEnumerator: TObject; // ... inherits from TCustomDictionaryEnumerator. workaround...
T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerable<T>)
private
FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>;
function GetCount: SizeInt;
public
constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
function DoGetEnumerator: TDictionaryEnumerator; override;
function ToArray: TArray<T>; override; final;
property Count: SizeInt read GetCount;
end;
// more info : http://en.wikipedia.org/wiki/Open_addressing
{ TDictionaryEnumerable }
TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
protected
function DoMoveNext: Boolean; override;
end;
TOnGetMemoryLayoutKeyPosition = procedure(Sender: TObject; AKeyPos: UInt32) of object;
TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>)
private type
PItem = ^TItem;
TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
TItemsArray = array of TItem;
private var
FItemsThreshold: SizeInt;
FItems: TItemsArray;
procedure Resize(ANewSize: SizeInt);
function PrepareAddingItem: SizeInt;
protected
function RealItemsLength: SizeInt; virtual;
function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; virtual;
function FindBucketIndex(constref AKey: TKey): SizeInt; overload; inline;
function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; virtual; abstract; overload;
public
type
// Enumerators
TPairEnumerator = class(TOpenAddressingEnumerator<TDictionaryPair, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: TPair<TKey,TValue>; override;
end;
TValueEnumerator = class(TOpenAddressingEnumerator<TValue, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: TValue; override;
end;
TKeyEnumerator = class(TOpenAddressingEnumerator<TKey, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: TKey; override;
end;
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// bug #24283 - workaround related to lack of DoGetEnumerator
function GetEnumerator: TPairEnumerator; reintroduce;
private
function GetKeys: TKeyCollection;
function GetValues: TValueCollection;
private
function GetItem(const AKey: TKey): TValue; inline;
procedure SetItem(const AKey: TKey; const AValue: TValue); inline;
procedure AddItem(var AItem: TItem; constref AKey: TKey; constref AValue: TValue; const AHash: UInt32); inline;
protected
// useful for using dictionary as array
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; virtual;
function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; virtual;
procedure UpdateItemsThreshold(ASize: SizeInt); override;
procedure SetCapacity(ACapacity: SizeInt); override;
// bug #24283 - can't descadent from TEnumerable
function DoGetEnumerator: TEnumerator<TDictionaryPair>; override;
procedure SetMaxLoadFactor(AValue: single); override;
function GetLoadFactor: single; override;
function GetCapacity: SizeInt; override;
public
// many constructors because bug #25607
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
procedure Add(constref APair: TPair<TKey, TValue>); override; overload;
procedure Add(constref AKey: TKey; constref AValue: TValue); overload; inline;
procedure Remove(constref AKey: TKey);
function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
procedure Clear; override;
procedure TrimExcess;
function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
function ContainsKey(constref AKey: TKey): Boolean; inline;
function ContainsValue(constref AValue: TValue): Boolean; overload;
function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload;
property Items[Index: TKey]: TValue read GetItem write SetItem; default;
property Keys: TKeyCollection read GetKeys;
property Values: TValueCollection read GetValues;
procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
end;
TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>)
private type // for workaround Lazarus bug #25613
_TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
protected
procedure NotifyIndexChange(AFrom, ATo: SizeInt); virtual;
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override;
function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload;
end;
// More info and TODO
// https://github.com/OpenHFT/UntitledCollectionsProject/wiki/Tombstones-purge-from-hashtable:-theory-and-practice
TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>)
private
FTombstonesCount: SizeInt;
protected
function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; override;
function RealItemsLength: SizeInt; override;
function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
out AHash: UInt32): SizeInt; virtual; abstract;
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override;
function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; override;
public
property TombstonesCount: SizeInt read FTombstonesCount;
procedure ClearTombstones; virtual;
procedure Clear; override;
end;
TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>)
private type // for workaround Lazarus bug #25613
_TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
protected
function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey;
out AHash: UInt32): SizeInt; override; overload;
function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
out AHash: UInt32): SizeInt; override;
end;
TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>)
private type // for workaround Lazarus bug #25613
_TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
private
R: UInt32;
protected
procedure UpdateItemsThreshold(ASize: SizeInt); override;
function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey;
out AHash: UInt32): SizeInt; override; overload;
function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
out AHash: UInt32): SizeInt; override;
strict protected
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
public // bug #26181 (redundancy of constructors)
constructor Create(ACapacity: SizeInt); override; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
end;
TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
private type // for workaround Lazarus bug #25613
TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
TItemsArray = array of TItem;
private
FMainIndex: SizeInt;
protected
function DoMoveNext: Boolean; override;
public
constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
end;
// more info :
// http://arxiv.org/abs/0903.0391
TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>)
private const // Lookup Result
LR_NIL = -1;
LR_QUEUE = -2;
private type
PItem = ^TItem;
TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValue>;
end;
TValueForQueue = TItem;
TQueueDictionary = class(TOpenAddressingLP<TKey, TValueForQueue, TDelphiHashFactory, TLinearProbing>)
private type // for workaround Lazarus bug #25613
_TItem = record
Hash: UInt32;
Pair: TPair<TKey, TValueForQueue>;
end;
private
FIdx: TList<UInt32>; // list to keep order
protected
procedure NotifyIndexChange(AFrom, ATo: SizeInt); override;
function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): Boolean; override;
public
procedure InsertIntoBack(AItem: Pointer);
procedure InsertIntoHead(AItem: Pointer);
function IsEmpty: Boolean;
function Pop: Pointer;
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
destructor Destroy; override;
end;
// cycle-detection mechanism class
TCDM = class(TOpenAddressingSH<TKey, TEmptyRecord, TDelphiHashFactory, TLinearProbing>);
TItemsArray = array of TItem;
TItemsDArray = array[0..Pred(TCuckooCfg.D)] of TItemsArray;
private var
FQueue: TQueueDictionary; // probably can be optimized - hash TItem give information from TItem.Hash for cuckoo ...
// currently is kept in "TQueueDictionary = class(TOpenAddressingSH<TKey, TItem, ...>"
FCDM: TCDM; // cycle-detection mechanism
FItemsThreshold: SizeInt;
FItems: TItemsDArray;
// sadly there is bug #24848 for class var ...
{class} var
CUCKOO_SIGN, CUCKOO_INDEX_SIZE, CUCKOO_HASH_SIGN: UInt32;
// CUCKOO_MAX_ITEMS_LENGTH: <- to do : calc max length for items based on CUCKOO sign
// maybe some CDM bloom filter?
procedure UpdateItemsThreshold(ASize: SizeInt); override;
procedure Resize(ANewSize: SizeInt);
procedure Rehash(ASizePow2: SizeInt);
function PrepareAddingItem: SizeInt;
protected
function Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; inline; overload;
function Lookup(constref AItems: TItemsDArray; constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; virtual; overload;
public
type
// Enumerators
TPairEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TDictionaryPair, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: TPair<TKey,TValue>; override;
end;
TValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TValue, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: TValue; override;
end;
TKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TKey, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: TKey; override;
end;
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// bug #24283 - workaround related to lack of DoGetEnumerator
function GetEnumerator: TPairEnumerator; reintroduce;
private
function GetKeys: TKeyCollection;
function GetValues: TValueCollection;
private
function GetItem(const AKey: TKey): TValue; inline;
procedure SetItem(const AKey: TKey; const AValue: TValue); overload; inline;
procedure SetItem(constref AValue: TValue; const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); overload;
procedure AddItem(constref AItems: TItemsDArray; constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload;
procedure DoAdd(constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; inline;
function DoRemove(const AHashListOrIndex: PUInt32; ALookupResult: SizeInt;
ACollectionNotification: TCollectionNotification): TValue;
function GetQueueCount: SizeInt;
protected
procedure SetCapacity(ACapacity: SizeInt); override;
// bug #24283 - can't descadent from TEnumerable
function DoGetEnumerator: TEnumerator<TDictionaryPair>; override;
procedure SetMaxLoadFactor(AValue: single); override;
function GetLoadFactor: single; override;
function GetCapacity: SizeInt; override;
strict protected // bug #26181
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
public
// TODO: function TryFlushQueue(ACount: SizeInt): SizeInt;
constructor Create; override; overload;
constructor Create(ACapacity: SizeInt); override; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
destructor Destroy; override;
procedure Add(constref APair: TPair<TKey, TValue>); override; overload;
procedure Add(constref AKey: TKey; constref AValue: TValue); overload;
procedure Remove(constref AKey: TKey);
function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
procedure Clear; override;
procedure TrimExcess;
function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
function ContainsKey(constref AKey: TKey): Boolean; inline;
function ContainsValue(constref AValue: TValue): Boolean; overload;
function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload;
property Items[Index: TKey]: TValue read GetItem write SetItem; default;
property Keys: TKeyCollection read GetKeys;
property Values: TValueCollection read GetValues;
property QueueCount: SizeInt read GetQueueCount;
procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
end;
TDictionaryOwnerships = set of (doOwnsKeys, doOwnsValues);
TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>)
private
FOwnerships: TDictionaryOwnerships;
protected
procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override;
procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override;
public
// can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)"
// because bug #25607
constructor Create(AOwnerships: TDictionaryOwnerships); overload;
constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload;
constructor Create(AOwnerships: TDictionaryOwnerships;
const AComparer: IExtendedEqualityComparer<TKey>); overload;
constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt;
const AComparer: IExtendedEqualityComparer<TKey>); overload;
end;
TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>)
private
FOwnerships: TDictionaryOwnerships;
protected
procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override;
procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override;
public
// can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)"
// because bug #25607
constructor Create(AOwnerships: TDictionaryOwnerships); overload;
constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload;
constructor Create(AOwnerships: TDictionaryOwnerships;
const AComparer: IEqualityComparer<TKey>); overload;
constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt;
const AComparer: IEqualityComparer<TKey>); overload;
end;
// useful generics overloads
TOpenAddressingLP<TKey, TValue, THashFactory> = class(TOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
TOpenAddressingLP<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
TObjectOpenAddressingLP<TKey, TValue, THashFactory> = class(TObjectOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
TObjectOpenAddressingLP<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
// Linear Probing with Tombstones (LPT)
TOpenAddressingLPT<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TLinearProbing>);
TOpenAddressingLPT<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
TOpenAddressingQP<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TQuadraticProbing>);
TOpenAddressingQP<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TQuadraticProbing>);
TOpenAddressingDH<TKey, TValue, THashFactory> = class(TOpenAddressingDH<TKey, TValue, THashFactory, TDoubleHashing>);
TOpenAddressingDH<TKey, TValue> = class(TOpenAddressingDH<TKey, TValue, TDelphiDoubleHashFactory, TDoubleHashing>);
TCuckooD2<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>);
TCuckooD2<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>);
TCuckooD4<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>);
TCuckooD4<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>);
TCuckooD6<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>);
TCuckooD6<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>);
TObjectCuckooD2<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>);
TObjectCuckooD2<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>);
TObjectCuckooD4<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>);
TObjectCuckooD4<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>);
TObjectCuckooD6<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>);
TObjectCuckooD6<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>);
// for normal programmers to normal use =)
TDictionary<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue>);
TObjectDictionary<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue>);
TFastHashMap<TKey, TValue> = class(TCuckooD2<TKey, TValue>);
TFastObjectHashMap<TKey, TValue> = class(TObjectCuckooD2<TKey, TValue>);
THashMap<TKey, TValue> = class(TCuckooD4<TKey, TValue>);
TObjectHashMap<TKey, TValue> = class(TObjectCuckooD4<TKey, TValue>);
var
EmptyRecord: TEmptyRecord;

View File

@@ -0,0 +1,83 @@
/// SOA interface methods definition to circumvent FPC missing RTTI
// - generated at 2016-07-27 09:30:40
unit mORMotRESTFPCInterfaces;
{
WARNING:
This unit has been generated by a mORMot 1.18.2797 server.
Any manual modification of this file may be lost after regeneration.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse Informatique - https://synopse.info
This unit is released under a MPL/GPL/LGPL tri-license,
and therefore may be freely included in any application.
This unit is intended to work on older FPC compilers, which lack of RTTI
for interfaces - see http://bugs.freepascal.org/view.php?id=26774
USAGE:
Add this mORMotRESTFPCInterfaces unit to your uses clause, so that the following
interfaces would be defined as expected by mORMot under FPC:
- IRestMethods
}
interface
{$I Synopse.inc} // needed for setting HASINTERFACERTTI and proper FPC modes
uses
SysUtils,
Classes,
SynCommons,
SynLog,
mORMot,
RestMethodsInterfaceUnit;
implementation
{$ifndef HASINTERFACERTTI} // circumvent old FPC bug of missing RTTI
{ TInterfaceFactoryDefinition }
type
/// define and manage missing interface RTTI for the following interfaces:
// - IRestMethods
TInterfaceFactoryDefinition = class(TInterfaceFactoryGenerated)
protected
procedure AddMethodsFromTypeInfo(aInterface: PTypeInfo); override;
end;
procedure TInterfaceFactoryDefinition.AddMethodsFromTypeInfo(aInterface: PTypeInfo);
begin
if aInterface=TypeInfo(IRestMethods) then begin
AddMethod('HelloWorld',[
ord(smdresult),'Result',TypeInfo(AnsiString)]);
AddMethod('Sum',[
ord(smdconst),'val1',TypeInfo(Double),
ord(smdconst),'val2',TypeInfo(Double),
ord(smdresult),'Result',TypeInfo(Double)]);
AddMethod('GetCustomRecord',[
ord(smdresult),'Result',TypeInfo(rCustomRecord)]);
AddMethod('SendCustomRecord',[
ord(smdconst),'CustomResult',TypeInfo(rCustomRecord),
ord(smdresult),'Result',TypeInfo(Boolean)]);
AddMethod('SendMultipleCustomRecords',[
ord(smdconst),'CustomResult',TypeInfo(rCustomRecord),
ord(smdconst),'CustomComplicatedRecord',TypeInfo(rCustomComplicatedRecord),
ord(smdresult),'Result',TypeInfo(Boolean)]);
AddMethod('GetMethodCustomResult',[
ord(smdresult),'Result',TypeInfo(TServiceCustomAnswer)]);
exit;
end;
end;
initialization
TInterfaceFactoryDefinition.RegisterInterface(TypeInfo(IRestMethods));
{$endif HASINTERFACERTTI}
end.

View File

@@ -0,0 +1,58 @@
program mORMotRESTcl;
{$ifdef Linux}
{$ifdef FPC_CROSSCOMPILING}
{$ifdef CPUARM}
{$linklib GLESv2}
{$endif}
{$linklib libc_nonshared.a}
{$endif}
{$endif}
{$I Synopse.inc}
uses
{$IFDEF FPC}
Interfaces,
{$ENDIF }
Forms,
{$ifndef DELPHI5OROLDER}
mORMotRESTFPCInterfaces,
{$endif}
{$ifdef COMPUTEFPCINTERFACES}
SynCommons,
mORMotWrappers,
{$endif}
RestClientFormUnit in 'RestClientFormUnit.pas' {Form1} ,
RestMethodsInterfaceUnit in 'RestMethodsInterfaceUnit.pas',
RestClientUnit in 'RestClientUnit.pas'
{$ifndef FPC}
,Vcl.Themes
,Vcl.Styles
{$endif}
;
{$ifndef FPC}
{$R *.res}
{$endif}
begin
{$IFDEF DEBUG}
ReportMemoryLeaksOnShutdown := True;
{$ENDIF}
Application.Initialize;
{$ifdef MSWINDOWS}
Application.MainFormOnTaskbar := True;
{$endif}
{$ifndef FPC}
TStyleManager.TrySetStyle('Turquoise Gray');
{$endif}
Application.CreateForm(TForm1, Form1);
Application.Run;
{$ifdef COMPUTEFPCINTERFACES}
ChDir(ExeVersion.ProgramFilePath);
ComputeFPCInterfacesUnit(
['..\..\..\..\..\..\CrossPlatform\templates'],
'..\..\mORMotRESTFPCInterfaces.pas');
{$endif}
end.

View File

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
<PathDelim Value="\"/>
<General>
<Flags>
<MainUnitHasUsesSectionForAllUnits Value="False"/>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="mORMotRESTcl"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<BuildModes Count="2">
<Item1 Name="Default" Default="True"/>
<Item2 Name="Linux">
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="bin\$(TargetCPU)-$(TargetOS)\mORMotRESTcl"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir);generics-collections\src\inc;..\..\..\..\.."/>
<OtherUnitFiles Value="generics-collections\src;..\..\..\..\..;..\..\..\.."/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<SyntaxMode Value="Delphi"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="linux"/>
</CodeGeneration>
<Linking>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
</Item2>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Unit0>
<Filename Value="mORMotRESTcl.dpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="bin\$(TargetCPU)-$(TargetOS)\mORMotRESTcl"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir);generics-collections\src\inc;..\..\..\..\.."/>
<OtherUnitFiles Value="generics-collections\src;..\..\..\..\..;..\..\..\.."/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<SyntaxMode Value="Delphi"/>
</SyntaxOptions>
</Parsing>
<Linking>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@@ -0,0 +1,60 @@
program mORMotRESTsrv;
{$ifdef Linux}
{$ifdef FPC_CROSSCOMPILING}
{$ifdef CPUARM}
{$linklib GLESv2}
{$endif}
{$linklib libc_nonshared.a}
{$endif}
{$endif}
{$I Synopse.inc}
uses
{$IFNDEF FPC}
{$ELSE}
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Interfaces,
{$ENDIF }
Forms,
{$ifndef DELPHI5OROLDER}
mORMotRESTFPCInterfaces,
{$endif}
{$ifdef COMPUTEFPCINTERFACES}
SynCommons,
mORMotWrappers,
{$endif}
RestServerFormUnit in 'RestServerFormUnit.pas' {Form1},
RestServerUnit in 'RestServerUnit.pas',
RestServerMethodsUnit in 'RestServerMethodsUnit.pas',
RestMethodsInterfaceUnit in 'RestMethodsInterfaceUnit.pas'
{$ifndef FPC}
,Vcl.Themes
,Vcl.Styles
{$endif}
;
{$ifndef FPC}
{$R *.res}
{$endif}
begin
{$IFDEF DEBUG}
ReportMemoryLeaksOnShutdown := True;
{$ENDIF}
Application.Initialize;
{$ifdef MSWINDOWS}
Application.MainFormOnTaskbar := True;
{$endif}
Application.CreateForm(TForm1, Form1);
Application.Run;
{$ifdef COMPUTEFPCINTERFACES}
ChDir(ExeVersion.ProgramFilePath);
ComputeFPCInterfacesUnit(
['..\..\..\..\..\..\CrossPlatform\templates'],
'..\..\mORMotRESTFPCInterfaces.pas');
{$endif}
end.

View File

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
<PathDelim Value="\"/>
<General>
<Flags>
<MainUnitHasUsesSectionForAllUnits Value="False"/>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="mORMotRESTsrv"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<BuildModes Count="2">
<Item1 Name="Default" Default="True"/>
<Item2 Name="Linux">
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="bin\$(TargetCPU)-$(TargetOS)\mORMotRESTsrv"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir);generics-collections\src\inc;..\..\..\..\.."/>
<OtherUnitFiles Value="generics-collections\src;..\..\..\..\..;..\..\..\.."/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<SyntaxMode Value="Delphi"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="linux"/>
</CodeGeneration>
<Linking>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
</Item2>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Unit0>
<Filename Value="mORMotRESTsrv.dpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="bin\$(TargetCPU)-$(TargetOS)\mORMotRESTsrv"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir);generics-collections\src\inc;..\..\..\..\.."/>
<OtherUnitFiles Value="generics-collections\src;..\..\..\..\..;..\..\..\.."/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<SyntaxMode Value="Delphi"/>
</SyntaxOptions>
</Parsing>
<Linking>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB