diff --git a/Config/Server.xml b/Config/Server.xml
index 6c02a4ea..c5a8fa65 100644
--- a/Config/Server.xml
+++ b/Config/Server.xml
@@ -8,7 +8,6 @@
0
0
0
- 0
0
diff --git a/Core/Boot.dyalog b/Core/Boot.dyalog
index e8d3b3f4..d18ceeb9 100644
--- a/Core/Boot.dyalog
+++ b/Core/Boot.dyalog
@@ -4,663 +4,663 @@
:section Startup/Shutdown
- ∇ {msroot}Run root
- :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
- AppRoot←folderize root ⍝ application (website) root
- Load AppRoot ⍝ load essential objects
- ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
- Configure ms
- ms.Run
- ∇
-
- ∇ {msroot}RunWC2 root
- :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
- AppRoot←folderize root ⍝ application (website) root
- Load AppRoot ⍝ load essential objects
- !!!ms←1 Init ConfigureServer AppRoot ⍝ read configuration and create server instance
- Configure ms
- {ms.Run}
- ∇
-
- ∇ Load AppRoot;filterOut;files;HTML;f;failed;dir;name;file;folder;callingEnv
+ ∇ {msroot}Run root
+ :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
+ AppRoot←folderize root ⍝ application (website) root
+ Load AppRoot ⍝ load essential objects
+ ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
+ Configure ms
+ ms.Run
+ ∇
+
+ ∇ {msroot}RunWC2 root
+ :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
+ AppRoot←folderize root ⍝ application (website) root
+ Load AppRoot ⍝ load essential objects
+ !!!ms←1 Init ConfigureServer AppRoot ⍝ read configuration and create server instance
+ Configure ms
+ {ms.Run}
+ ∇
+
+ ∇ Load AppRoot;filterOut;files;HTML;f;failed;dir;name;file;folder;callingEnv
⍝ Load required objects for MiServer
-
- HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0
-
- :If 0=#.⎕NC'Files' ⋄ ⎕SE.SALT.Load MSRoot,'Utils/Files -target=#' ⋄ :EndIf
-
- filterOut←{⍺←'' ⋄ ⍺{0∊⍴⍺:⍵ ⋄ ⍺{∊¨↓⍵⌿⍨~⍵[;2]∊eis ⍺}↑⎕NPARTS¨⍵}⊃#.Files.Dir ⍵,'/*.dyalog'}
-
- files←'Boot'filterOut MSRoot,'Core'
- files,←'Files'filterOut MSRoot,'Utils' ⍝ find utility libraries
- files,←filterOut MSRoot,'Extensions'
-
-
- failed←''
- :For f :In files
- {326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}⎕SE.SALT.Load f,' -target=#' ⍝ do not reload already loaded spaces
- :EndFor
-
- :For file :In failed
- disperror ⎕SE.SALT.Load∊'"',file,'" -target=#'
- :EndFor
-
-
- HTML←'_JQ' '_JS'{⍵[⍋⍺⍳(↑⎕NPARTS¨⍵)[;2]]}filterOut MSRoot,'HTML' ⍝ prioritize loading of _JQ and _JS
-
- #.SupportedHtml5Elements.Build_html_namespace
-
+
+ HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0
+
+ :If 0=#.⎕NC'Files' ⋄ ⎕SE.SALT.Load MSRoot,'Utils/Files -target=#' ⋄ :EndIf
+
+ filterOut←{⍺←'' ⋄ ⍺{0∊⍴⍺:⍵ ⋄ ⍺{∊¨↓⍵⌿⍨~⍵[;2]∊eis ⍺}↑⎕NPARTS¨⍵}⊃#.Files.Dir ⍵,'/*.dyalog'}
+
+ files←'Boot'filterOut MSRoot,'Core'
+ files,←'Files'filterOut MSRoot,'Utils' ⍝ find utility libraries
+ files,←filterOut MSRoot,'Extensions'
+
+
+ failed←''
+ :For f :In files
+ {326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}⎕SE.SALT.Load f,' -target=#' ⍝ do not reload already loaded spaces
+ :EndFor
+
+ :For file :In failed
+ disperror ⎕SE.SALT.Load∊'"',file,'" -target=#'
+ :EndFor
+
+
+ HTML←'_JQ' '_JS'{⍵[⍋⍺⍳(↑⎕NPARTS¨⍵)[;2]]}filterOut MSRoot,'HTML' ⍝ prioritize loading of _JQ and _JS
+
+ #.SupportedHtml5Elements.Build_html_namespace
+
⍝↓↓↓ Some controls may require controls in other folders.
⍝ So we attempt to load everything, and keep track of what failed
⍝ and then go back and try to load the failed controls again their
- failed←''
- :For f :In HTML
- (folder name)←2↑⎕NPARTS f
- disperror ⎕SE.SALT.Load f,' -target=#'
- :If #.Files.DirExists dir←folder,name,'/'
- dir∘{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂⍺(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}¨⎕SE.SALT.Load dir,'* -target=#.',name
- :EndIf
- :EndFor
-
- :For (f file) :In failed
- disperror ⎕SE.SALT.Load∊'"',file,'" -target=#.',f
- :EndFor
-
- LoadFromFolder MSRoot,'Loadable'
-
- 'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
- #.Pages.(MiPage RESTfulPage)←#.(MiPage RESTfulPage)
-
- 'CachedPages'#.⎕NS'' ⍝ Container for cached pages
-
- BuildEAWC ⍝ build the Easy As ⎕WC namespace
-
+ failed←''
+ :For f :In HTML
+ (folder name)←2↑⎕NPARTS f
+ disperror ⎕SE.SALT.Load f,' -target=#'
+ :If #.Files.DirExists dir←folder,name,'/'
+ dir∘{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂⍺(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}¨⎕SE.SALT.Load dir,'* -target=#.',name
+ :EndIf
+ :EndFor
+
+ :For (f file) :In failed
+ disperror ⎕SE.SALT.Load∊'"',file,'" -target=#.',f
+ :EndFor
+
+ LoadFromFolder MSRoot,'Loadable'
+
+ 'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
+ #.Pages.(MiPage RESTfulPage)←#.(MiPage RESTfulPage)
+
+ 'CachedPages'#.⎕NS'' ⍝ Container for cached pages
+
+ BuildEAWC ⍝ build the Easy As ⎕WC namespace
+
⍝ Now load any code from the MiSite
-
- :If ~0∊⍴AppRoot
- :Trap 22
- :For class :In filterOut AppRoot,'Code' ⍝ Classes in application folder
- disperror ⎕SE.SALT.Load class,' -target=#'
- :EndFor
-
- :If #.Files.DirExists AppRoot,'/Code/Templates/'
- disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
- :EndIf
- :EndTrap
- :EndIf
- ∇
-
- ∇ Cleanup
- #.⎕EX¨classes
- #.⎕EX¨utils
- #.⎕EX'Pages'
- #.⎕EX'CachedPages'
- #.⎕EX¨'MiServer' 'HTTPRequest'
- ∇
-
- ∇ ms←{HtmlRenderer}Init Config;path;class;classes;e;res;mask
+
+ :If ~0∊⍴AppRoot
+ :Trap 22
+ :For class :In filterOut AppRoot,'Code' ⍝ Classes in application folder
+ disperror ⎕SE.SALT.Load class,' -target=#'
+ :EndFor
+
+ :If #.Files.DirExists AppRoot,'/Code/Templates/'
+ disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
+ :EndIf
+ :EndTrap
+ :EndIf
+ ∇
+
+ ∇ Cleanup
+ #.⎕EX¨classes
+ #.⎕EX¨utils
+ #.⎕EX'Pages'
+ #.⎕EX'CachedPages'
+ #.⎕EX¨'MiServer' 'HTTPRequest'
+ ∇
+
+ ∇ ms←{HtmlRenderer}Init Config;path;class;classes;e;res;mask
⍝ Create instances of MiServer, Session and Authentication Handlers
-
- HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0 ⍝ using HTMLRenderer?
-
- :If 0≠HtmlRenderer
- ms←⎕NS''
- ms.Config←Config
- ms.Log←{⎕←⍵}
- :Else
- ms←⎕NEW(#⍎Config.ClassName)Config
- path←MSRoot,'Extensions/'
-
- :If 0≠⍴Config.SessionHandler
- class←⎕SE.SALT.Load path,Config.SessionHandler
- ms.SessionHandler←⎕NEW class ms
- :EndIf
-
- :If 0≠⍴Config.Authentication
- class←⎕SE.SALT.Load path,Config.Authentication
- ms.Authentication←⎕NEW class ms
- :EndIf
-
- :If 0≠⍴Config.SupportedEncodings
- {}⎕SE.SALT.Load path,'ContentEncoder'
- :For e :In Config.SupportedEncodings
- class←⎕SE.SALT.Load path,e
- ms.Encoders,←⎕NEW class
- :EndFor
- :If ∨/mask←0≠1⊃¨res←ms.Encoders.Init
- 2 ms.Log'Content Encoding Initialization failed for:',∊' ',¨mask/ms.Encoders.Encoding
- ms.Encoders←(~mask)/ms.Encoders
- :EndIf
- :EndIf
- Config.UseContentEncoding∧←0≠⍴ms.Encoders
-
- :If 0≠⍴Config.Logger
- class←⎕SE.SALT.Load path,Config.Logger
- ms.Logger←⎕NEW class ms
- :EndIf
- :EndIf
- ∇
-
- ∇ End;classes;z;m
+
+ HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0 ⍝ using HTMLRenderer?
+
+ :If 0≠HtmlRenderer
+ ms←⎕NS''
+ ms.Config←Config
+ ms.Log←{⎕←⍵}
+ :Else
+ ms←⎕NEW(#⍎Config.ClassName)Config
+ path←MSRoot,'Extensions/'
+
+ :If 0≠⍴Config.SessionHandler
+ class←⎕SE.SALT.Load path,Config.SessionHandler
+ ms.SessionHandler←⎕NEW class ms
+ :EndIf
+
+ :If 0≠⍴Config.Authentication
+ class←⎕SE.SALT.Load path,Config.Authentication
+ ms.Authentication←⎕NEW class ms
+ :EndIf
+
+ :If 0≠⍴Config.SupportedEncodings
+ {}⎕SE.SALT.Load path,'ContentEncoder'
+ :For e :In Config.SupportedEncodings
+ class←⎕SE.SALT.Load path,e
+ ms.Encoders,←⎕NEW class
+ :EndFor
+ :If ∨/mask←0≠1⊃¨res←ms.Encoders.Init
+ 2 ms.Log'Content Encoding Initialization failed for:',∊' ',¨mask/ms.Encoders.Encoding
+ ms.Encoders←(~mask)/ms.Encoders
+ :EndIf
+ :EndIf
+ Config.UseContentEncoding∧←0≠⍴ms.Encoders
+
+ :If 0≠⍴Config.Logger
+ class←⎕SE.SALT.Load path,Config.Logger
+ ms.Logger←⎕NEW class ms
+ :EndIf
+ :EndIf
+ ∇
+
+ ∇ End;classes;z;m
⍝ Clean up the workspace
-
- :If 9=⎕NC'ms'
- :Trap 0
- ms.End
- :EndTrap
- {}try'⎕EX⍕⊃⊃⎕CLASS ms.SessionHandler'
- {}try'⎕EX⍕⊃⊃⎕CLASS ms.Authentication'
- {}try'⎕EX⍕⊃⊃⎕CLASS ms.Logger'
- {}try'⎕EX⍕¨∪∊ ⎕CLASS¨ms.Encoders'
- ⎕EX⍕⊃⊃⎕CLASS ms
- ⎕EX'ms'
- :EndIf
-
- :If 9=⎕NC'SQA'
- {}try'SQA.Close''.'''
- :EndIf
-
- :If 0≠⍴classes←↓#.⎕NL 9.4
- :AndIf 0≠⍴classes←(m←2=⊃∘⍴¨z←⎕CLASS¨#⍎¨classes)/classes
- :AndIf 0≠⎕NC'#.MiPage'
- classes←(#.MiPage≡¨2 1∘⊃¨m/z)/classes
- #.⎕EX↑⍕¨classes ⍝ Erase loaded classes
- :EndIf
-
- ⎕EX'#.MiPage'
- ⎕EX'AppRoot'
- {}try'#.DRC.Close ''.'''
- ⎕EX'#.DRC'
- #.Conga.UnloadSharedLib
- {}⎕WA
- ∇
-
- ∇ BuildEAWC;src;sources;fields;source;list;mask;refs;target
+
+ :If 9=⎕NC'ms'
+ :Trap 0
+ ms.End
+ :EndTrap
+ {}try'⎕EX⍕⊃⊃⎕CLASS ms.SessionHandler'
+ {}try'⎕EX⍕⊃⊃⎕CLASS ms.Authentication'
+ {}try'⎕EX⍕⊃⊃⎕CLASS ms.Logger'
+ {}try'⎕EX⍕¨∪∊ ⎕CLASS¨ms.Encoders'
+ ⎕EX⍕⊃⊃⎕CLASS ms
+ ⎕EX'ms'
+ :EndIf
+
+ :If 9=⎕NC'SQA'
+ {}try'SQA.Close''.'''
+ :EndIf
+
+ :If 0≠⍴classes←↓#.⎕NL 9.4
+ :AndIf 0≠⍴classes←(m←2=⊃∘⍴¨z←⎕CLASS¨#⍎¨classes)/classes
+ :AndIf 0≠⎕NC'#.MiPage'
+ classes←(#.MiPage≡¨2 1∘⊃¨m/z)/classes
+ #.⎕EX↑⍕¨classes ⍝ Erase loaded classes
+ :EndIf
+
+ ⎕EX'#.MiPage'
+ ⎕EX'AppRoot'
+ {}try'#.DRC.Close ''.'''
+ ⎕EX'#.DRC'
+ #.Conga.UnloadSharedLib
+ {}⎕WA
+ ∇
+
+ ∇ BuildEAWC;src;sources;fields;source;list;mask;refs;target
⍝ Build the Easy As ⎕WC namespace from core classes and its own source
⍝ Also build the #._ namespace with shortcuts
- sources←#._html #._SF #._JQ #._DC #._JS
+ sources←#._html #._SF #._JQ #._DC #._JS
⍝ fields←''
- '_'#.⎕NS''
- :For source :In sources
- list←source.⎕NL ¯9.4
- list←list/⍨'_'≠⊃¨list
- :If ∨/mask←0≠refs←source{6::0 ⋄ (⍕⍺){('.'∊1↓s↓r)<⍺≡(s←⍴⍺)↑r←⍕⍵}t←⍺⍎⍵:t ⋄ 0}¨list
- #._⍎∊(mask/list){'⋄',⍺,'←',⍕⍵}¨mask/refs
- :EndIf
- :EndFor
- ∇
-
- ∇ {root}LoadFromFolder path;type;name;nsName;parts;ns
+ '_'#.⎕NS''
+ :For source :In sources
+ list←source.⎕NL ¯9.4
+ list←list/⍨'_'≠⊃¨list
+ :If ∨/mask←0≠refs←source{6::0 ⋄ (⍕⍺){('.'∊1↓s↓r)<⍺≡(s←⍴⍺)↑r←⍕⍵}t←⍺⍎⍵:t ⋄ 0}¨list
+ #._⍎∊(mask/list){'⋄',⍺,'←',⍕⍵}¨mask/refs
+ :EndIf
+ :EndFor
+ ∇
+
+ ∇ {root}LoadFromFolder path;type;name;nsName;parts;ns
⍝ Loads an APL "project" folder
- root←{6::⍵ ⋄ root}#
- :For name type :In ↓{⍵[⍒⍵[;2];]}⍉↑0 1 #.Files.Dir path,'/*'
- nsName←∊1↓parts←1 ⎕NPARTS name
- :If 1=type ⍝ directory?
- :Select ⊃root.⎕NC nsName
- :Case 9 ⋄ ns←⍕root⍎nsName
- :Case 0 ⋄ ns←nsName root.⎕NS''
- :Else ⋄ Log'"',name,'" is not a namespace'
- :EndSelect
- ⎕SE.SALT.Load name,'/* -target=',⍕ns
- :Else
- :If ~∨/∊(⎕NSI,¨'.')⍷⍨¨⊂'.',(2⊃1 ⎕NPARTS name),'.' ⍝ don't load it if we're being called from it
- ⎕SE.SALT.Load name,' -target=',⍕root
- :EndIf
- :EndIf
- :EndFor
- ∇
+ root←{6::⍵ ⋄ root}#
+ :For name type :In ↓{⍵[⍒⍵[;2];]}⍉↑0 1 #.Files.Dir path,'/*'
+ nsName←∊1↓parts←1 ⎕NPARTS name
+ :If 1=type ⍝ directory?
+ :Select ⊃root.⎕NC nsName
+ :Case 9 ⋄ ns←⍕root⍎nsName
+ :Case 0 ⋄ ns←nsName root.⎕NS''
+ :Else ⋄ Log'"',name,'" is not a namespace'
+ :EndSelect
+ ⎕SE.SALT.Load name,'/* -target=',⍕ns
+ :Else
+ :If ~∨/∊(⎕NSI,¨'.')⍷⍨¨⊂'.',(2⊃1 ⎕NPARTS name),'.' ⍝ don't load it if we're being called from it
+ ⎕SE.SALT.Load name,' -target=',⍕root
+ :EndIf
+ :EndIf
+ :EndFor
+ ∇
:endsection
:section Configuration
- ∇ Configure ms
- ConfigureDatasources ms
- ConfigureVirtual ms
- ConfigureResources ms
- ConfigureContentTypes ms
- ConfigureLogger ms
- ms AddConfiguration'MappingHandlers'
- ∇
-
- ∇ ms AddConfiguration name;conf
- conf←ReadConfiguration name
- {ms.Config⍎name,'←⍵'}conf
- ∇
-
- ∇ r←ns Setting pars;name;num;default;mask
+ ∇ Configure ms
+ ConfigureDatasources ms
+ ConfigureVirtual ms
+ ConfigureResources ms
+ ConfigureContentTypes ms
+ ConfigureLogger ms
+ ms AddConfiguration'MappingHandlers'
+ ∇
+
+ ∇ ms AddConfiguration name;conf
+ conf←ReadConfiguration name
+ {ms.Config⍎name,'←⍵'}conf
+ ∇
+
+ ∇ r←ns Setting pars;name;num;default;mask
⍝ returns setting from a config style namespace or provides a default if it doesn't exist
⍝ pars - name [num] [default]
⍝ ns - namespace reference
⍝ name - name of the setting
⍝ num - 1 if setting is numeric scalar, (,1) if numeric vector is allowed, 0 otherwise
⍝ default - default value if not found
- pars←eis pars
- (name num)←2↑pars,(⍴pars)↓'' 0 ''
- :If 2<⍴pars ⋄ default←3⊃pars
- :Else ⋄ default←(1+num)⊃''⍬
- :EndIf
- r←(⍴ns)⍴⊂default
- :If ∨/mask←0≠⊃¨ns.⎕NC⊂name
- (mask/r)←(((⍴⍴num)∘tonum)⍣(⊃num))¨(mask/ns).⍎⊂name
- :EndIf
- :If 0=⍴⍴r ⋄ r←⊃r ⋄ :EndIf
- ∇
-
- ∇ config←{element}ReadConfiguration type;serverconfig;file;siteconfig;thing;ind;mask
+ pars←eis pars
+ (name num)←2↑pars,(⍴pars)↓'' 0 ''
+ :If 2<⍴pars ⋄ default←3⊃pars
+ :Else ⋄ default←(1+num)⊃''⍬
+ :EndIf
+ r←(⍴ns)⍴⊂default
+ :If ∨/mask←0≠⊃¨ns.⎕NC⊂name
+ (mask/r)←(((⍴⍴num)∘tonum)⍣(⊃num))¨(mask/ns).⍎⊂name
+ :EndIf
+ :If 0=⍴⍴r ⋄ r←⊃r ⋄ :EndIf
+ ∇
+
+ ∇ config←{element}ReadConfiguration type;serverconfig;file;siteconfig;thing;ind;mask
⍝ Attempt to read configuration file
⍝ 1) from server root MSRoot
⍝ 2) from site root AppRoot
⍝ merging the two if they both exist - site settings overrule server settings
- config←''
- :If #.Files.Exists file←MSRoot,'Config/',type,'.xml'
- config←serverconfig←(#.XML.ToNS #.Files.ReadText file)⍎type
- :EndIf
- :If #.Files.Exists file←AppRoot,'Config/',type,'.xml'
- siteconfig←(#.XML.ToNS #.Files.ReadText file)⍎type
- :If 0∊⍴config
- config←siteconfig
- :ElseIf 0=⎕NC'element'
- {}{try'serverconfig.',⍵,'←siteconfig.',⍵}¨siteconfig.⎕NL ¯2
- :Else ⍝ element specifies the element(s) to search on
- :For thing :In siteconfig
- :If 0≠thing.⎕NC element
- :If ∨/mask←0≠∊serverconfig.⎕NC⊂element
- :If (+/mask)|≡⍵),⍵} ⍝ Enclose if simple
-
- tonum←{⍺←0
- 1∊⍺:tonumvec ⍵
- w←⍵ ⋄ ((w='-')/w)←'¯'
- ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
-
- ∇ r←tonumvec v;to;minus;digits;c;mask
+ pars←eis pars
+ (name num)←2↑pars,(⍴pars)↓'' 0 ''
+ :If 2<⍴pars ⋄ default←3⊃pars
+ :Else ⋄ default←(1+num)⊃''⍬
+ :EndIf
+ r←(⍴ns)⍴⊂default
+ :If ∨/mask←0≠⊃¨ns.⎕NC⊂name
+ (mask/r)←(((⍴⍴num)∘tonum)⍣(⊃num))¨(mask/ns).⍎⊂name
+ :EndIf
+ :If 0=⍴⍴r ⋄ r←⊃r ⋄ :EndIf
+ ∇
+
+ eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
+
+ tonum←{⍺←0
+ 1∊⍺:tonumvec ⍵
+ w←⍵ ⋄ ((w='-')/w)←'¯'
+ ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
+
+ ∇ r←tonumvec v;to;minus;digits;c;mask
⍝ tonum vector version
⍝ allows for specific of ranges and comma or space delimited numbers
⍝ tonumvec '8080-8090' or '5,7-9,11-15'
- r←⍬
- ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
- to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
- v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
- minus←'-'=v
- digits←v∊⎕D,'.'
- ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
- ((' '=v)/v)←','
- (('-'=v)/v)←⊂' to '
- :Trap 0
- :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
- r,←⍎∊c
- :EndFor
- :Else
- ⎕SIGNAL 11
- :EndTrap
- ∇
-
- :Endclass
+ r←⍬
+ ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
+ to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
+ v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
+ minus←'-'=v
+ digits←v∊⎕D,'.'
+ ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
+ ((' '=v)/v)←','
+ (('-'=v)/v)←⊂' to '
+ :Trap 0
+ :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
+ r,←⍎∊c
+ :EndFor
+ :Else
+ ⎕SIGNAL 11
+ :EndTrap
+ ∇
+
+ :Endclass
:endsection
:section Utilities
- disperror←{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:⎕←⍵}
- isWin←'Win'≡3↑1⊃#.⎕WG'APLVersion'
- fileSep←'/\'[1+isWin]
- isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
+ disperror←{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:⎕←⍵}
+ isWin←'Win'≡3↑1⊃#.⎕WG'APLVersion'
+ fileSep←'/\'[1+isWin]
+ isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
MSRoot←{('.'=⊃⍵)∨isRelPath ⍵:'.',fileSep,⍵ ⋄ ⍵}{(1-⌊/'/\'⍳⍨⌽⍵)↓⍵}⎕WSID
- tonum←{⍺←0
- 1∊⍺:tonumvec ⍵
- w←⍵ ⋄ ((w='-')/w)←'¯'
- ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
- try←{0::'' ⋄⍎⍵}
- empty←{0∊⍴⍵}
- notEmpty←~∘empty
- eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
- isRef←{(0∊⍴⍴⍵)∧326=⎕DR ⍵}
- folderize←{19 22::⍵,'/'↓⍨'/\'∊⍨¯1↑⍵ ⋄ ∊1 ⎕NPARTS⊃{⍺,(⍵=1)/'/'}/0 1 ⎕NINFO ⍵} ⍝ append trailing file separator unless empty and left arg←1
- makeSitePath←{folderize ⍺{((isRelPath ⍵)/⍺),⍵},(2×'./'≡2↑⍵)↓⍵}
- subdirs←{⊃{(⍵=1)/⍺}/0 1(⎕NINFO⍠1)⍵,'/*'}
- Log←{⎕←⍵}
-
- ∇ {r}←AutoStatus setting
+ tonum←{⍺←0
+ 1∊⍺:tonumvec ⍵
+ w←⍵ ⋄ ((w='-')/w)←'¯'
+ ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
+ try←{0::'' ⋄⍎⍵}
+ empty←{0∊⍴⍵}
+ notEmpty←~∘empty
+ eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
+ isRef←{(0∊⍴⍴⍵)∧326=⎕DR ⍵}
+ folderize←{19 22::⍵,'/'↓⍨'/\'∊⍨¯1↑⍵ ⋄ ∊1 ⎕NPARTS⊃{⍺,(⍵=1)/'/'}/0 1 ⎕NINFO ⍵} ⍝ append trailing file separator unless empty and left arg←1
+ makeSitePath←{folderize ⍺{((isRelPath ⍵)/⍺),⍵},(2×'./'≡2↑⍵)↓⍵}
+ subdirs←{⊃{(⍵=1)/⍺}/0 1(⎕NINFO⍠1)⍵,'/*'}
+ Log←{⎕←⍵}
+
+ ∇ {r}←AutoStatus setting
⍝ Set Dyalog/Windows AutoStatus setting
- :If r←isWin
- :Trap 0
- :If setting≠r←⎕SE.mb.tools.status_error.Checked
- 1 ⎕NQ ⎕SE.mb.tools.status_error'Select'
- :EndIf
- :EndTrap
- :EndIf
- ∇
-
- ∇ r←tonumvec v;to;minus;digits;c;mask
+ :If r←isWin
+ :Trap 0
+ :If setting≠r←⎕SE.mb.tools.status_error.Checked
+ 1 ⎕NQ ⎕SE.mb.tools.status_error'Select'
+ :EndIf
+ :EndTrap
+ :EndIf
+ ∇
+
+ ∇ r←tonumvec v;to;minus;digits;c;mask
⍝ tonum vector version
⍝ allows for specific of ranges and comma or space delimited numbers
⍝ tonumvec '8080-8090' or '5,7-9,11-15'
- r←⍬
- ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
- to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
- v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
- minus←'-'=v
- digits←v∊⎕D,'.'
- ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
- ((' '=v)/v)←','
- (('-'=v)/v)←⊂' to '
- :Trap 0
- :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
- r,←⍎∊c
- :EndFor
- :Else
- ⎕SIGNAL 11
- :EndTrap
- ∇
-
- ∇ r←SubstPath r
- r←(#.Strings.subst∘('%ServerRoot%'(¯1↓MSRoot)))r
- r←(#.Strings.subst∘('%SiteRoot%'(¯1↓AppRoot)))r
- ∇
-
- ∇ r←isRunning
- :Trap r←0
- r←ms.TID∊⎕TNUMS
- :EndTrap
- ∇
-
- ∇ r←Oops;dmx;ends;xsi
+ r←⍬
+ ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
+ to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
+ v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
+ minus←'-'=v
+ digits←v∊⎕D,'.'
+ ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
+ ((' '=v)/v)←','
+ (('-'=v)/v)←⊂' to '
+ :Trap 0
+ :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
+ r,←⍎∊c
+ :EndFor
+ :Else
+ ⎕SIGNAL 11
+ :EndTrap
+ ∇
+
+ ∇ r←SubstPath r
+ r←(#.Strings.subst∘('%ServerRoot%'(¯1↓MSRoot)))r
+ r←(#.Strings.subst∘('%SiteRoot%'(¯1↓AppRoot)))r
+ ∇
+
+ ∇ r←isRunning
+ :Trap r←0
+ r←ms.TID∊⎕TNUMS
+ :EndTrap
+ ∇
+
+ ∇ r←Oops;dmx;ends;xsi
⍝ debugging framework to bubble up to user's code when rendering fails
- r←'⎕SIGNAL 811'
- ends←{(,⍺)≡(-⍴,⍺)↑⍵}
- :If {0::0 ⋄ #.HtmlPage∊∊⎕CLASS ⍵}⊃⊃⎕RSI
- r←'⎕TRAP←(800 ''C'' ''→FAIL'')(811 ''E'' ''⎕SIGNAL 801'')(813 ''E'' ''⎕SIGNAL 803'')(812 ''S'')(85 ''N'')(0 ''S'')'
- ⎕←''
- ⎕←'*** MiServer Debug ***'
- ⎕←↑⎕DMX.DM
- ⎕←''
- ⎕←' ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
- ⎕←' or Press Ctrl-Enter to invoke debugger'
- :Else
- :Select ⎕DMX.EN
- :Case 801
- xsi←⎕XSI
- :If '.HandleMSP'ends 2⊃xsi
- r←'→FAIL'
- :ElseIf '.HandleMSP'ends 4⊃xsi
- :If '.Render'ends 3⊃xsi
- r←'⎕SIGNAL 813'
- :EndIf
- :ElseIf '.HandleMSP'ends 3⊃xsi
- :If '.Wrap'ends 2⊃xsi
- r←'⎕SIGNAL 813'
- :EndIf
- :Else
- r←'⎕SIGNAL 811'
- :EndIf
- :Case 803
- r←'⎕SIGNAL 812'
- ⎕←'Press Ctrl-Enter to invoke debugger'
- :Else
- :Trap 0
- dmx←⎕DMX
- ⎕←'*** MiServer Debug ***'
- ⎕←'' 'occurred at:',⍪dmx.(EM(2⊃DM))
- ⎕←'' 'SI Stack is ',(⍕¯1+⍴⎕XSI),' levels deep'
- ⎕←''
- :EndTrap
- ⎕←' ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
- ⎕←' ⎕SIGNAL 801 ⍝ to cut back and debug'
- r←''
- :EndSelect
- :EndIf
- ∇
+ r←'⎕SIGNAL 811'
+ ends←{(,⍺)≡(-⍴,⍺)↑⍵}
+ :If {0::0 ⋄ #.HtmlPage∊∊⎕CLASS ⍵}⊃⊃⎕RSI
+ r←'⎕TRAP←(800 ''C'' ''→FAIL'')(811 ''E'' ''⎕SIGNAL 801'')(813 ''E'' ''⎕SIGNAL 803'')(812 ''S'')(85 ''N'')(0 ''S'')'
+ ⎕←''
+ ⎕←'*** MiServer Debug ***'
+ ⎕←↑⎕DMX.DM
+ ⎕←''
+ ⎕←' ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
+ ⎕←' or Press Ctrl-Enter to invoke debugger'
+ :Else
+ :Select ⎕DMX.EN
+ :Case 801
+ xsi←⎕XSI
+ :If '.HandleMSP'ends 2⊃xsi
+ r←'→FAIL'
+ :ElseIf '.HandleMSP'ends 4⊃xsi
+ :If '.Render'ends 3⊃xsi
+ r←'⎕SIGNAL 813'
+ :EndIf
+ :ElseIf '.HandleMSP'ends 3⊃xsi
+ :If '.Wrap'ends 2⊃xsi
+ r←'⎕SIGNAL 813'
+ :EndIf
+ :Else
+ r←'⎕SIGNAL 811'
+ :EndIf
+ :Case 803
+ r←'⎕SIGNAL 812'
+ ⎕←'Press Ctrl-Enter to invoke debugger'
+ :Else
+ :Trap 0
+ dmx←⎕DMX
+ ⎕←'*** MiServer Debug ***'
+ ⎕←'' 'occurred at:',⍪dmx.(EM(2⊃DM))
+ ⎕←'' 'SI Stack is ',(⍕¯1+⍴⎕XSI),' levels deep'
+ ⎕←''
+ :EndTrap
+ ⎕←' ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
+ ⎕←' ⎕SIGNAL 801 ⍝ to cut back and debug'
+ r←''
+ :EndSelect
+ :EndIf
+ ∇
:endsection
:EndNamespace
diff --git a/Core/HTTPRequest.dyalog b/Core/HTTPRequest.dyalog
index fdf2b3b5..da3fcd53 100644
--- a/Core/HTTPRequest.dyalog
+++ b/Core/HTTPRequest.dyalog
@@ -10,10 +10,15 @@
SC,←(500 'Internal Server Error')(501 'Not Implemented')(503 'Service Unavailable')
SC←↑SC
+⍝ Shared Fields
+
+ :Field Public Shared DecodeBuffers←1 ⍝ have Conga decode HTTP message buffers by default (MiServer sets this on initialization)
+ :Field Public Shared Server ⍝ reference back to the server
+
⍝ Fields related to the Request
:Field Public Instance Complete←0 ⍝ do we have a complete request?
- :Field Public Instance Input←''
+ :Field Public Instance URI←'' ⍝ the complete URI (page + query string)
:Field Public Instance Headers←0 2⍴⊂'' ⍝ HTTPRequest header fields (plus any supplied from HTTPTrailer event)
:Field Public Instance Method←'' ⍝ HTTP method (GET, POST, PUT, etc)
:Field Public Instance Page←'' ⍝ Requested URI
@@ -28,7 +33,6 @@
:Field Public Instance HTTPVersion←''
:Field Public Instance Cookies←0 2⍴⊂''
:Field Public Instance Session←''
- :Field Public Instance Server←⎕NS ''
:Field Public Instance CloseConnection←0
⍝ Fields related to the Response
@@ -42,7 +46,7 @@
ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
inf←{∨/⍵⍷⍺:'' ⋄ ⍵} ⍝ if not found
begins←{⍺≡(⍴⍺)↑⍵}
- split←{p←(⍺⍷⍵)⍳1 ⋄ ((p-1)↑⍵)(p↓⍵)} ⍝ Split ⍵ on first occurrence of ⍺
+ split←{p←(⍺⍷⍵)⍳1 ⋄ ((p-≢⍺)↑⍵)(p↓⍵)} ⍝ Split ⍵ on first occurrence of ⍺
∇ r←eis w
:Access public shared
@@ -52,17 +56,34 @@
∇ Make args;query;cookies
:Access Public Instance
:Implements Constructor
- ⍝ args [1] HTTP method [2] URI [3] HTTP version [4] 2-column headers
+ ⍝ args is either:
+ ⍝ [1] HTTP method [2] URI [3] HTTP version [4] 2-column headers
+ ⍝ character vector if Conga could not parse the HTTP header or if DecodeBuffers is turned off
+
+ args←eis args
+ :If 1=≢args ⍝ single arg means Conga did not or could not decode
+ :Trap 999
+ :If DecodeBuffers ⍝ this is a header that Conga failed to parse
+ 1 Server.Log'Bad HTTP header received:',⊃args ⍝ may want to improve this if bad header is very long
+ :EndIf
+ (Method URI HTTPVersion Headers)←ParseHead⊃args
+ :Else
+ 1 Fail 400
+ →0
+ :EndTrap
+ :Else
+ (Method URI HTTPVersion Headers)←args
+ :EndIf
- (Method Input HTTPVersion Headers)←args
Headers[;1]←#.Strings.lc Headers[;1] ⍝ header names are case insensitive
+ Headers←CombineHeaders Headers ⍝ combine any multiple header entries
Method←#.Strings.lc Method
Response←⎕NS''
Response.(Status StatusText Headers File HTML HTMLHead PeerAddr NoWrap Bytes MSec)←200 'OK'(0 2⍴⊂'')0 '' '' '' 0(0 0)(⎕AI[3])
Host←GetHeader'host'
- Page query←'?'split Input
+ Page query←'?'split URI
Page←PercentDecode Page
:If '/'≠⊃Page ⍝!!! need to update this to deal with absolute URI's, see https://tools.ietf.org/html/rfc7230#section-5.3.2
@@ -75,7 +96,7 @@
:If ~0∊⍴cookies←GetHeader'cookie'
cookies←CookieSplit cookies
:If ~0∊⍴cookies←(2=⊃∘⍴¨cookies)/cookies
- Cookies←↑{(' '~⍨1⊃⍵)(DeCode 2⊃⍵)}¨cookies
+ Cookies←↑{(' '~⍨1⊃⍵)(PercentDecode 2⊃⍵)}¨cookies
:EndIf
:EndIf
:If 'get'≡Method
@@ -84,24 +105,69 @@
:EndIf
∇
+ ∇ (method uri version headers)←ParseHead head;start;ind;header;t
+ ⍝ manually parse HTTP head
+ (start header)←1(↑{⍺ ⍵}↓)(⊂'')~⍨{2↓¨⍵⊂⍨NL⍷⍵}NL,head ⍝ split start line from headers
+ ⎕SIGNAL 999/⍨3≠≢t←start⊆⍨' '≠start←∊start ⍝ start line should be method uri httpversion
+ (method uri version)←t
+ ⎕SIGNAL 999/⍨~∧/':'∊¨header ⍝ a valid header must have a ':'
+ headers←↑{dltb¨':'split ⍵}¨header
+ ∇
+
∇ ProcessBody args
:Access public
Body←args
FinalizeRequest
∇
- ∇ ProcessChunk args
+ ∇ ProcessChunk args;len;chunk
:Access public
- ⍝ args is [1] chunk content [2] chunk-extension name/value pairs (which we don't expect and won't process)
- Body,←1⊃args
+ ⍝ args is either
+ ⍝ [1] chunk content [2] chunk-extension name/value pairs (which we don't expect and won't process)
+ ⍝ or a character vector if either DecodeBuffers=0 or Conga failed to parse the chunk
+ :If DecodeBuffers=1=≡,args ⍝ if we're Decoding buffers, we expect a nested arg, if not, we expect a simple vector
+ 1 Fail 400
+ →0
+ :EndIf
+
+ :If DecodeBuffers
+ Body,←1⊃args
+ :Else
+ :Trap 0
+ (len chunk)←NL split args
+ Body,←(hex len)↑chunk
+ :Else
+ 1 Fail 400
+ :EndTrap
+ :EndIf
∇
- ∇ ProcessTrailer args;inds;mask
+ ∇ ProcessTrailer args;inds;mask;len;trailer;forbidden
:Access public
- args[;1]←#.Strings.lc args[;1]
- mask←(≢Headers)≥inds←Headers[;1]⍳args[;1]
- Headers[mask/inds;2]←mask/args[;2]
- Headers⍪←(~mask)⌿args
+ ⍝ args is either
+ ⍝ 2-column matrix of
+ ⍝ or a character vector if either DecodeBuffers=0 or Conga failed to parse the chunk
+ :If 1=≢⍴args
+ (len trailer)←NL split args
+ :If len≢,'0'
+ 1 Fail 400
+ →0
+ :EndIf
+ args←0 2⍴⊂''
+ :If ~0∊⍴trailer
+ args←↑{dltb¨':'split ⍵}¨(⊂'')~⍨{2↓¨⍵⊂⍨NL⍷⍵}NL,trailer
+ :EndIf
+ :EndIf
+ :If ~0∊⍴args
+ args[;1]←#.Strings.lc args[;1]
+ ⍝ The following is an attempt to comply with https://tools.ietf.org/html/rfc7230#section-4.1.2
+ ⍝ However, there doesn't seem to be a definitive list of the forbidden trailer fields, so, we do our best...
+ forbidden←'age' 'authorization' 'cache-control' 'content-encoding' 'content-length' 'content-range' 'content-type' 'date' 'expect' 'expires' 'host' 'location' 'max-forwards' 'pragma' 'proxy-authenticate' 'proxy-authorization' 'range' 'retry-after' 'set-cookie' 'te' 'trailer' 'transfer-encoding' 'vary' 'warning' 'www-authenticate'
+ args⌿←~args[;1]∊forbidden
+ mask←(≢Headers)≥inds←Headers[;1]⍳args[;1]
+ Headers[mask/inds;2]←mask/args[;2]
+ Headers⍪←(~mask)⌿args
+ :EndIf
FinalizeRequest
∇
@@ -132,10 +198,22 @@
CloseConnection←'close'≡GetHeader'connection'
∇
+ ∇ headers←CombineHeaders headers;inds;hdrs;hdr;i
+ :Access public shared
+ ⍝ combines any headers that may occur more than once
+ :If ~0∊⍴inds←⍸1<≢¨2⌷[2]hdrs←{⍺ ⍵}⌸headers[;1]
+ :For (hdr i) :In ↓hdrs[inds;]
+ headers[⊃i;2]←⊂¯1↓∊headers[i;2],¨',;'[1+'cookie'≡∊hdr]
+ :EndFor
+ headers←headers[⊃¨hdrs[;2];]
+ :EndIf
+ ∇
+
+
∇ Wipe
:Access public
⍝ clear out all request data
- Input←''
+ URI←''
Headers←''
Method←''
Page←''
@@ -147,7 +225,6 @@
Data←⍬
Cookies←''
Session←''
- Server←⎕NS''
Response←⎕NS''
∇
@@ -174,13 +251,11 @@
:EndIf
∇
-
∇ r←GetArgument name
:Access Public Instance
r←name GetFromTable Arguments
∇
-
∇ r←GetData name
:Access Public Instance
r←name GetFromTableCS Data
@@ -342,6 +417,7 @@
:EndIf
:EndFor
:EndIf
+ Complete←CloseConnection←1
∇
∇ {code}Redirect location
@@ -439,7 +515,7 @@
∇
∇ SetContentType x;ct
- :Access public instance
+ :Access public instance
⍝ Sets response content-type header
⍝ x is either a file name or extension (if it contains a period), in which case we attempt to look up the appropriate content-type
⍝ or the actual setting for content-type
@@ -543,36 +619,27 @@
:endsection
- Split←{(⎕IO ⎕ML)←0 3 ⍝ Split Http syntax.
- ⍺←'?&=' ⋄ (,⍺){ ⍝ Default separator chars.
- 0=⍴⍺:⍵ ⍝ No separator chars left: finished.
- sepr←↑⍺ ⍝ First char is separator char.
- (1↓⍺)∘∇∘{ ⍝ Recursively split at each sub level.
- ⍵~sepr ⍝ Separator removed.
- }¨(1+⍵=sepr)⊂⍵ ⍝ String partitioned at separator char.
- }⍵ ⍝ Http string.
+ dltb←{ ⍝ delete leading/trailing blanks
+ (⌽∘{⍵/⍨∨\⍵≠' '}⍣2)⍵
}
- CookieSplit←{(⎕IO ⎕ML)←0 3 ⍝ Split cookies
- {{db←{⍵/⍨∨\⍵≠' '} ⋄ ⌽db⌽db ⍵}¨⍵⊂⍨~<\'='=⍵}¨⍵⊂⍨⍵≠';'}
-
-
- DeCode←{(⎕IO ⎕ML)←0 3 ⍝ Decode Special chars in HTML string.
- hex←'0123456789ABCDEF' ⍝ Hex chars.
- { ⍝ Convert numbers.
- v f←⎕VFI ⍵ ⍝ Check for numbers.
- ~∧/v:⍵ ⍝ Not all numbers: char vec.
- 1=⍴f:↑f ⋄ f ⍝ Numeric scalar or vector.
- }∊{ ⍝ Enlist of segments.
- '%'≠↑⍵:⍵ ⍝ 1st seg may not contain special char.
- (⎕UCS 16⊥hex⍳1↓3↑⍵),3↓⍵ ⍝ Hex code replaced with corresp. ⎕AV char.
- }¨(1+⍵='%')⊂,⍵ ⍝ Segments split at '%' char.
- }
+ ∇ r←CookieSplit w
+ :Access public shared
+ r←{ ⍝ Split cookies
+ {dltb¨'='split ⍵}¨⍵⊆⍨⍵≠';'
+ }w
+ ∇
+ ∇ r←hex w;i;⎕IO
+ :Access public shared
+ ⎕IO←0
+ ⎕SIGNAL 11/⍨31∨.APLVersion)/⊂('EN' 11)('Message' 'Dyalog v16.0 or later is required to use HTMLRenderer-based features')
+ →0↓⍨0∊⍴r←(17>APLVersion)/⊂('EN' 11)('Message' 'Dyalog v17.0 or later is required to use HTMLRenderer-based features')
Props←⎕NS''
_Config←#.Boot.ms.Config
_PageName←3⊃⎕SI,⊂'WC2Page'
@@ -66,7 +66,7 @@
∇ run arg
:Access public
- _Renderer←⎕NEW'HTMLRenderer'(('Coord'Coord)('Size' Size)('Event'('onHTTPRequest' '__CallbackFn'))('URL'_PageName))
+ _Renderer←⎕NEW'HTMLRenderer'(('Coord'Coord)('Size' Size)('Event'('onHTTPRequest' '__CallbackFn'))('URL'_PageName)('InterceptedURLs' (1 2⍴'*' 1)))
:If ~0∊⍴props←_Renderer.PropList∩Props.⎕NL ¯2
{_Renderer⍎⍺,'←⍵'}/¨{⍵(Props⍎⍵)}¨props
:EndIf
@@ -92,7 +92,7 @@
∇ r←__CallbackFn args;ext;mimeType;filename;url;mask;cbdata;request;int;handler;content
- :Access public
+ :Access public
∘∘∘
r←args
→0⍴⍨0∊⍴8⊃args
diff --git a/Utils/HtmlUtils.dyalog b/Utils/HtmlUtils.dyalog
index 99b6d27a..c4949630 100644
--- a/Utils/HtmlUtils.dyalog
+++ b/Utils/HtmlUtils.dyalog
@@ -161,13 +161,13 @@
∇ r←HtmlSafeText txt;i;m;u;ucs;s
⍝ make text HTML "safe"
r←,⎕FMT txt
- i←'&<>"#'⍳r
- i-←(i=1)∧1↓(i=5),0 ⍝ mark & that aren't
- m←i∊⍳4
+ i←'&<>"''#'⍳r
+ i-←(i=1)∧1↓(i=6),0 ⍝ mark & that aren't
+ m←i∊⍳5
u←127'⎕FMT u/ucs
r←∊r
∇
diff --git a/miserver.dws b/miserver.dws
index dcd9885e..3b79d7a5 100644
Binary files a/miserver.dws and b/miserver.dws differ