[augeas-devel] [PATCH 16/16] Httpd: lens and test for the Apache config

lutter at redhat.com lutter at redhat.com
Wed Dec 23 21:31:05 UTC 2009


From: David Lutterkort <lutter at redhat.com>

The lens needs quite a bit more work; there's a good number of directives
that are not supported yet. It's also not clear that every construct is
parsed into the 'right' kind of tree, i.e. that the resulting tree is
generally useful.
---
 lenses/httpd.aug            |  219 +++++++++++++++++++++++++++++++++++++++++++
 lenses/tests/test_httpd.aug |  104 ++++++++++++++++++++
 tests/Makefile.am           |    1 +
 3 files changed, 324 insertions(+), 0 deletions(-)
 create mode 100644 lenses/httpd.aug
 create mode 100644 lenses/tests/test_httpd.aug

diff --git a/lenses/httpd.aug b/lenses/httpd.aug
new file mode 100644
index 0000000..dceec66
--- /dev/null
+++ b/lenses/httpd.aug
@@ -0,0 +1,219 @@
+module Httpd =
+
+(* FIXME: up to line 737 in httpd.conf *)
+
+(* FIXME: Most of the comparisons in Apache are case insensitive       *)
+(* We do not have a way to express that currently; probably need some *)
+(* additions to the regexp syntax like /[a-z]+/i                      *)
+let sep = Util.del_ws_spc
+let eol = del /[ \t]*\n/ "\n"
+let number = /[0-9]+/
+let on_off = /[Oo](n|ff)/
+let langle = del /[ \t]*</ "<"
+let rangle = Util.del_str ">"
+let modname = /[a-zA-Z0-9._]+/
+let alnum = /[a-zA-Z0-9]+/
+let word = /"([^"\n])*"|'([^'\n])*'|[^'" \t\n]+/
+
+(* A string that does not end with ">" *)
+let secarg = /\"([^\"\n]|\\\\\")*\"|'([^'\n]|\\\\')*'|[^ \t\n]*[^ \t\n>]/
+
+(* A key preceded by optional whitespace *)
+let wskey (k:regexp) = del /[ \t]*/ "" . key k
+
+(* A key value pair KEY VALUE *)
+let kv1 (k:regexp) (v:regexp) =
+  [ wskey k . sep . store v . eol ]
+
+let kv2 (k:regexp) (v:regexp) (l2:string) (v2:regexp) =
+  [ wskey k . sep . store v . [ sep . label "directory" . store word ] . eol ]
+
+(* A key followed by a space-separated list of values *)
+let kv_arr (k:regexp) (l:string) (v:regexp) =
+  [ wskey k . [ sep . label l . store v ]* . eol ]
+
+let kv_val_arr (k:regexp) (v:regexp) (l:string) (v2:regexp) =
+  [ wskey k . sep . store v . [ sep . label l . store v2 ]+ . eol ]
+
+let allow_deny (k:string) =
+  [ wskey k . sep . Util.del_str "from" .
+      [ sep . label "from" . store word ]* . eol ]
+
+let icon =
+   let icon_pair =
+     Util.del_str "(" . [ label "alttext" . store /[^, \t\n]+/ ] .
+     Util.del_str "," . store /[^() \t\n]+/ . Util.del_str ")" in
+   [ label "icon" . (store /[^() \t\n]+/|icon_pair) ]
+
+let comment = [ del /(#.*|[ \t]*)\n/ "#\n" ]
+
+(* Directives with their arguments *)
+
+let accessFileName = kv_arr "AccessFileName" "filename" word
+let addDescription = kv_val_arr "AddDescription" word "file" word
+let addIcon =
+  [ wskey "AddIcon" . sep . icon . [ sep . label "name" . store word ]+ . eol ]
+let addIconByEncodingOrType =
+  [ wskey /AddIconBy(Encoding|Type)/ . sep .
+      icon . [ sep . label "encoding" . store word ]+ . eol ]
+let addLanguage = kv_val_arr "AddLanguage" word "extension" word
+let alias = kv2 "Alias" word "directory" word
+let allow = allow_deny "Allow"
+let allowOverride =
+  let directive_re = /All|None|Limit|Options|FileInfo|AuthConfig|Indexes/ in
+  kv_arr "AllowOverride" "directive" directive_re
+let customLog =
+  [ wskey "CustomLog" . sep . store word .
+      [ sep . label "format" . store word ] .
+      [ sep . key "env" . Util.del_str "=" . store /[^= \t\n]+/ ]? . eol ]
+let davLockDB = kv1 /D[aA][vV]LockDB/ word
+let defaultIcon = kv1 "DefaultIcon" word
+let defaultLanguage = kv1 "DefaultLanguage" word
+let defaultType = kv1 "DefaultType" word
+let deny = allow_deny "Deny"
+let directoryIndex = kv_arr "DirectoryIndex" "url" word
+let documentRoot = kv1 "DocumentRoot" word
+let enableMMAP = kv1 "EnableMMAP" on_off
+let enableSendFile = kv1 "EnableSendFile" on_off
+let errorLog = kv1 "ErrorLog" word
+let extendedStatus = kv1 "ExtendedStatus" on_off
+let forceLanguagePriority =
+  let priority = [ sep . label "priority" . store /None|Prefer|Fallback/] in
+  [ wskey "ForceLanguagePriority" . priority . priority? . eol ]
+let group = kv1 "Group" alnum
+let headerName = kv1 "HeaderName" word
+let hostnameLookups = kv1 "HostnameLookups" on_off
+let include = kv1 "Include" word
+let indexIgnore = kv_arr "IndexIgnore" "ignore" word
+let indexOptions =
+  let opt_re = /[+-]?(FancyIndexing|FoldersFirst|HTMLTable|IconsAreLinks|IgnoreCase|IgnoreClient|ScanHTMLTitles|Suppress(ColumnSorting|Description|HTMLPreamble|Icon|LastModified|Size|Rules)TrackModified|VersionSort|XHTML|ShowForbidden|None)/ in
+  let opt_arg_re = /[+-]?(IconWidth|IconHeight|NameWidth|DescriptionWidth|Type|Charset)/ in
+  [ wskey "IndexOptions" .
+      ( [ sep . key opt_re ]
+        | [ sep . key opt_arg_re . ( Util.del_str "=" . store /[^= \t\n]+/ )? ]
+      )* .
+      eol
+  ]
+let keepAlive = kv1 "KeepAlive" on_off
+let keepAliveTimeout = kv1 "KeepAliveTimeout" number
+let languagePriority = kv_arr "LanguagePriority" "lang" word
+let listen = kv1 "Listen" number
+let loadModule = [ wskey "LoadModule" . sep . store modname .
+                     sep . [ label "path" . store word ] . eol ]
+let logFormat =
+  [ wskey "LogFormat" . sep . store word .
+      [ sep . label "nickname" . store word ]? . eol ]
+let logLevel = kv1 "LogLevel" /emerg|alert|crit|error|warn|notice|info|debug/
+let maxClients = kv1 "MaxClients" number
+let maxKeepAliveRequests = kv1 "MaxKeepAliveRequests" number
+let maxRequestsPerChild = kv1 "MaxRequestsPerChild" number
+let maxSpareServers = kv1 "MaxSpareServers" number
+let minSpareServers = kv1 "MinSpareServers" number
+let maxSpareThreads = kv1 "MaxSpareThreads" number
+let minSpareThreads = kv1 "MinSpareThreads" number
+let mimeMagicFile = kv1 /(MIME|Mime)MagicFile/ word
+let options =
+  let opt_re = /[+-]?(None|Indexes|Includes(NOEXEC|NoExec)?|FollowSymLinks|SymLinksIfOwnerMatch|ExecCGI|MultiViews|RunScripts|All)/ in
+  kv_arr "Options" "option" opt_re
+let order = kv1 "Order" /Allow,Deny|Deny,Allow|Mutual-failure/
+let pidFile = kv1 "PidFile" word
+let readmeName = kv1 "ReadmeName" word
+let scriptAlias = kv2 "ScriptAlias" word "directory" word
+let serverAdmin = kv1 "ServerAdmin" word
+let serverLimit = kv1 "ServerLimit" number
+let serverName = kv1 "ServerName" word
+let serverRoot = kv1 "ServerRoot" word
+let serverSignature = kv1 "ServerSignature" on_off
+let serverTokens =
+  kv1 "ServerTokens" /Major|Minor|Min(imal)?|Prod(uctOnly)?|OS|Full/
+let startServers = kv1 "StartServers" number
+let threadsPerChild = kv1 "ThreadsPerChild" number
+let timeOut = kv1 "Timeout" number
+let typesConfig = kv1 "TypesConfig" word
+let useCanonicalName = kv1 "UseCanonicalName" on_off
+let user = kv1 "User" alnum
+let userDir = kv1 "UserDir" word
+
+let directive = accessFileName
+              | addIcon
+              | addIconByEncodingOrType
+              | addLanguage
+              | alias
+              | allow
+              | allowOverride
+              | customLog
+              | davLockDB
+              | defaultIcon
+              | defaultLanguage
+              | defaultType
+              | deny
+              | directoryIndex
+              | documentRoot
+              | enableMMAP
+              | enableSendFile
+              | errorLog
+              | extendedStatus
+              | forceLanguagePriority
+              | group
+              | headerName
+              | hostnameLookups
+              | include
+              | indexIgnore
+              | indexOptions
+              | keepAlive
+              | keepAliveTimeout
+              | languagePriority
+              | listen
+              | loadModule
+              | logFormat
+              | logLevel
+              | maxClients
+              | maxKeepAliveRequests
+              | maxRequestsPerChild
+              | maxSpareServers
+              | maxSpareThreads
+              | mimeMagicFile
+              | minSpareServers
+              | minSpareThreads
+              | options
+              | order
+              | pidFile
+              | readmeName
+              | serverLimit
+              | serverName
+              | serverRoot
+              | serverSignature
+              | serverTokens
+              | startServers
+              | threadsPerChild
+              | timeOut
+              | typesConfig
+              | useCanonicalName
+              | user
+              | userDir
+
+(* Containers *)
+
+(* A section with one argument in the <Section ..> tag *)
+let sec1 (name:string) (body:lens) =
+  let end_tag = "</" . name . ">" in
+  [ langle . key name . sep . store secarg . rangle . eol .
+      body .
+      del (/[ \t]*/ . end_tag) end_tag  . eol
+  ]
+
+let ifModule (body:lens) = sec1 "IfModule" body
+let directory (body:lens) = sec1 "Directory" body
+let limit (body:lens) = sec1 "Limit" body
+let limitExcept (body:lens) = sec1 "LimitExcept" body
+let files (body:lens) = sec1 "Files" body
+
+(* Limit/LimitExcept sections *)
+(* Files section *)
+
+let rec lns = (directive|comment)*
+            | ifModule lns
+            | directory lns
+            | limit lns
+            | limitExcept lns
+            | files lns
diff --git a/lenses/tests/test_httpd.aug b/lenses/tests/test_httpd.aug
new file mode 100644
index 0000000..6a95a9e
--- /dev/null
+++ b/lenses/tests/test_httpd.aug
@@ -0,0 +1,104 @@
+module Test_httpd =
+
+let directives = "#
+# Timeout: The number of seconds before receives and sends time out.
+#
+Timeout 120
+
+#
+# KeepAlive: Whether or not to allow persistent connections (more than
+# one request per connection). Set to \"Off\" to deactivate.
+#
+KeepAlive Off
+"
+
+let ifmodule = "<IfModule prefork.c>
+StartServers       8
+MinSpareServers    5
+MaxSpareServers   20
+ServerLimit      256
+MaxClients       256
+MaxRequestsPerChild  4000
+</IfModule>\n"
+
+let loadModule = "# Example:
+# LoadModule foo_module modules/mod_foo.so
+#
+LoadModule auth_basic_module modules/mod_auth_basic.so
+LoadModule auth_digest_module modules/mod_auth_digest.so
+LoadModule authn_file_module modules/mod_authn_file.so
+"
+
+test Httpd.lns get directives =
+  {} {} {} { "Timeout" = "120" } {} {} {} {} {} { "KeepAlive" = "Off" }
+
+test Httpd.lns get ifmodule =
+  { "IfModule" = "prefork.c"
+      { "StartServers" = "8" }
+      { "MinSpareServers" = "5" }
+      { "MaxSpareServers" = "20" }
+      { "ServerLimit" = "256" }
+      { "MaxClients" = "256" }
+      { "MaxRequestsPerChild" = "4000" } }
+
+test Httpd.lns get loadModule =
+  {} {} {}
+  { "LoadModule" = "auth_basic_module"
+      { "path" = "modules/mod_auth_basic.so" } }
+  { "LoadModule" = "auth_digest_module"
+      { "path" = "modules/mod_auth_digest.so" } }
+  { "LoadModule" = "authn_file_module"
+      { "path" = "modules/mod_authn_file.so" } }
+
+test Httpd.options get "  Options Indexes +ExecCGI\n" =
+  { "Options"
+      { "option" = "Indexes" }
+      { "option" = "+ExecCGI" } }
+
+test Httpd.customLog get " CustomLog gif-requests.log common env=!gif-image\n" =
+  { "CustomLog" = "gif-requests.log"
+      { "format" = "common" }
+      { "env" = "!gif-image" } }
+
+test Httpd.indexOptions get
+    " IndexOptions +ScanHTMLTitles -IconsAreLinks FancyIndexing Charset=UTF-8\n"
+  =
+  { "IndexOptions"
+    { "+ScanHTMLTitles" }
+    { "-IconsAreLinks" }
+    { "FancyIndexing" }
+    { "Charset" = "UTF-8" } }
+
+test Httpd.addIconByEncodingOrType get
+    "AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip\n" =
+  { "AddIconByEncoding"
+      { "icon" = "/icons/compressed.gif"  { "alttext"  = "CMP" } }
+      { "encoding" = "x-compress" }
+      { "encoding" = "x-gzip" } }
+
+let nested = "<IfModule mod_negotiation.c>
+<IfModule mod_include.c>
+    <Directory \"/var/www/error\">
+        AllowOverride None
+        Options IncludesNoExec
+    </Directory>
+</IfModule>
+</IfModule>\n"
+
+test Httpd.lns get nested =
+  { "IfModule" = "mod_negotiation.c"
+    { "IfModule" = "mod_include.c"
+      { "Directory" = "\"/var/www/error\""
+        { "AllowOverride" { "directive" = "None" } }
+        { "Options" { "option" = "IncludesNoExec" } } } } }
+
+test Httpd.lns put nested after
+  set "/IfModule/IfModule/Directory/Options/option[2]" "ExecCGI" =
+"<IfModule mod_negotiation.c>
+<IfModule mod_include.c>
+    <Directory \"/var/www/error\">
+        AllowOverride None
+        Options IncludesNoExec ExecCGI
+    </Directory>
+</IfModule>
+</IfModule>\n"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a3c2f09..f28dfce 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -28,6 +28,7 @@ lens_tests =			\
   lens-group.sh			\
   lens-json.sh          \
   lens-hosts.sh			\
+  lens-httpd.sh         \
   lens-inetd.sh         \
   lens-inifile.sh		\
   lens-inittab.sh		\
-- 
1.6.5.2




More information about the augeas-devel mailing list