Browse Source

[doc] document core config and modules in tree

personal/stbuehler/wip
Stefan Bühler 6 years ago
parent
commit
f482877825
70 changed files with 4358 additions and 748 deletions
  1. +10
    -0
      COPYING
  2. +1
    -0
      doc/.gitignore
  3. +41
    -0
      doc/Makefile.am
  4. +7
    -0
      doc/bootstrap-theme.min.css
  5. +7
    -0
      doc/bootstrap.min.css
  6. +7
    -0
      doc/bootstrap.min.js
  7. +632
    -0
      doc/compile.rb
  8. +336
    -0
      doc/core_config.xml
  9. +10
    -0
      doc/core_fetch.xml
  10. +53
    -0
      doc/core_pattern.xml
  11. +21
    -0
      doc/core_regex.xml
  12. +170
    -0
      doc/doc_schema.xsd
  13. +6
    -0
      doc/jquery-1.10.1.min.js
  14. +68
    -0
      doc/mod_access.xml
  15. +69
    -0
      doc/mod_accesslog.xml
  16. +144
    -0
      doc/mod_auth.xml
  17. +51
    -0
      doc/mod_balance.xml
  18. +40
    -0
      doc/mod_cache_disk_etag.xml
  19. +122
    -0
      doc/mod_core.lua.xml
  20. +28
    -0
      doc/mod_debug.xml
  21. +115
    -0
      doc/mod_deflate.xml
  22. +71
    -0
      doc/mod_dirlist.xml
  23. +58
    -0
      doc/mod_expire.xml
  24. +37
    -0
      doc/mod_fastcgi.xml
  25. +38
    -0
      doc/mod_flv.xml
  26. +34
    -0
      doc/mod_fortune.xml
  27. +155
    -0
      doc/mod_gnutls.xml
  28. +117
    -0
      doc/mod_limit.xml
  29. +96
    -0
      doc/mod_lua.xml
  30. +108
    -0
      doc/mod_memcached.xml
  31. +119
    -0
      doc/mod_openssl.xml
  32. +88
    -0
      doc/mod_progress.xml
  33. +20
    -0
      doc/mod_proxy.xml
  34. +77
    -0
      doc/mod_redirect.xml
  35. +83
    -0
      doc/mod_rewrite.xml
  36. +23
    -0
      doc/mod_scgi.xml
  37. +80
    -0
      doc/mod_secdownload.lua.xml
  38. +45
    -0
      doc/mod_status.xml
  39. +78
    -0
      doc/mod_throttle.xml
  40. +50
    -0
      doc/mod_userdir.xml
  41. +93
    -0
      doc/mod_vhost.xml
  42. +801
    -0
      doc/plugin_core.xml
  43. +128
    -0
      doc/style.css
  44. +2
    -2
      src/main/config_parser.rl
  45. +6
    -4
      src/main/connection.c
  46. +0
    -33
      src/modules/mod_access.c
  47. +0
    -20
      src/modules/mod_accesslog.c
  48. +0
    -48
      src/modules/mod_auth.c
  49. +0
    -17
      src/modules/mod_balance.c
  50. +0
    -17
      src/modules/mod_cache_disk_etag.c
  51. +0
    -21
      src/modules/mod_debug.c
  52. +0
    -30
      src/modules/mod_deflate.c
  53. +0
    -43
      src/modules/mod_dirlist.c
  54. +0
    -38
      src/modules/mod_expire.c
  55. +1
    -17
      src/modules/mod_fastcgi.c
  56. +0
    -30
      src/modules/mod_flv.c
  57. +0
    -22
      src/modules/mod_fortune.c
  58. +8
    -35
      src/modules/mod_gnutls.c
  59. +0
    -42
      src/modules/mod_limit.c
  60. +1
    -23
      src/modules/mod_lua.c
  61. +0
    -28
      src/modules/mod_memcached.c
  62. +0
    -35
      src/modules/mod_openssl.c
  63. +0
    -50
      src/modules/mod_progress.c
  64. +1
    -15
      src/modules/mod_proxy.c
  65. +1
    -42
      src/modules/mod_redirect.c
  66. +0
    -45
      src/modules/mod_rewrite.c
  67. +1
    -15
      src/modules/mod_scgi.c
  68. +0
    -27
      src/modules/mod_status.c
  69. +0
    -19
      src/modules/mod_userdir.c
  70. +0
    -30
      src/modules/mod_vhost.c

+ 10
- 0
COPYING View File

@@ -22,3 +22,13 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.




The doc/ directory contains some assets by 3rd parties:
From doc/bootstrap* (Apache License 2.0):
Copyright 2013 Twitter, Inc.
Licensed under http://www.apache.org/licenses/LICENSE-2.0
From doc/jquery-1.10.1.min.js (MIT License):
(c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license

+ 1
- 0
doc/.gitignore View File

@@ -0,0 +1 @@
*.html

+ 41
- 0
doc/Makefile.am View File

@@ -1 +1,42 @@
dist_man8_MANS=lighttpd2.8 lighttpd2-worker.8
EXTRA_DIST=\
jquery-1.10.1.min.js \
bootstrap.min.js \
bootstrap.min.css \
bootstrap-theme.min.css \
style.css \
doc_schema.xsd \
core_config.xml \
core_fetch.xml \
core_pattern.xml \
core_regex.xml \
mod_accesslog.xml \
mod_access.xml \
mod_auth.xml \
mod_balance.xml \
mod_cache_disk_etag.xml \
mod_core.lua.xml \
mod_debug.xml \
mod_deflate.xml \
mod_dirlist.xml \
mod_expire.xml \
mod_fastcgi.xml \
mod_flv.xml \
mod_fortune.xml \
mod_gnutls.xml \
mod_limit.xml \
mod_lua.xml \
mod_memcached.xml \
mod_openssl.xml \
mod_progress.xml \
mod_proxy.xml \
mod_redirect.xml \
mod_rewrite.xml \
mod_scgi.xml \
mod_secdownload.lua.xml \
mod_status.xml \
mod_throttle.xml \
mod_userdir.xml \
mod_vhost.xml \
plugin_core.xml \
compile.rb

+ 7
- 0
doc/bootstrap-theme.min.css
File diff suppressed because it is too large
View File


+ 7
- 0
doc/bootstrap.min.css
File diff suppressed because it is too large
View File


+ 7
- 0
doc/bootstrap.min.js
File diff suppressed because it is too large
View File


+ 632
- 0
doc/compile.rb View File

@@ -0,0 +1,632 @@
#!/usr/bin/ruby

require 'rubygems'
require 'nokogiri'

require 'bluecloth'
require 'redcloth'

HTML_TEMPLATE='''
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="bootstrap-theme.min.css">
<link rel="stylesheet" href="style.css">
<script src="jquery-1.10.1.min.js"></script>
<script src="bootstrap.min.js"></script>
</head>
<body data-spy="scroll" data-target=".bs-sidebar" data-offset="30"><div class="container"><div class="row">

<!-- TOC -->
<div class="col-md-3" role="complementary"><div class="bs-sidebar hidden-print toc" id="sidebar" data-spy="affix" data-offset-top="0" data-offset-bottom="30"></div></div>

<!-- MAIN -->
<div class="col-md-9" role="main" id="main"></div></div>

</div></div></body>
</html>
'''

class Documentation
XPATH_NAMESPACES = { 'd' => 'urn:lighttpd.net:lighttpd2/doc1' }
TAB_WIDTH = 4

def initialize(basename)
@html_doc = Nokogiri::HTML::Document.parse(HTML_TEMPLATE)
@html_main = @html_doc.xpath('//div[@role="main"]')[0]
@html_toc = @html_doc.css('#sidebar')[0]
@title = nil

@depth = 0
@uniqueid = 0
@current_toc = @toc = []

@basename = basename

@actions = []
@setups = []
@options = []
end

def render_main
Nokogiri::XML::Builder.with(@html_main) do |html|
@html = html
yield
@html = nil
end
end

def title
@title
end

def title=(value)
@title = value
@html_doc.xpath('/html/head/title')[0].inner_html = value
end

def to_html_fragment
@html_main.inner_html
end

def to_html
@html_doc.to_html
end

def toc
@toc
end

def actions
@actions
end

def setups
@setups
end

def options
@options
end

def _store_toc(html, toc, rootToc = false)
return unless toc.length > 0
html.ul(:class => rootToc ? "nav bs-sidenav" : "nav" ) {
toc.each do |anchor, title, subtoc, cls|
html.li(:class => cls || '') {
html.a({:href => '#' + anchor}, title)
_store_toc(html, subtoc)
}
end
}
end

def store_toc
Nokogiri::HTML::Builder.with(@html_toc) do |html|
_store_toc(html, @toc, true)
end
end

def _unique_anchor(title)
id = @uniqueid
@uniqueid += 1
("%02x-" % id) + title.downcase.gsub(/[^a-z0-9]+/, '_')
end

# although '.' and ':' are allowed in anchors (IDs), they
# don't work in CSS selectors like: #anchor:foo and #anchor.bar
def escape_anchor(anchor)
anchor.gsub(/[.:]/, '-')
end

def nest(title, anchor = nil, cls = nil)
attribs = {}
have_toc = nil != @current_toc
anchor = (@depth < 3 and have_toc) ? _unique_anchor(title) : nil if anchor == '#'
if anchor
anchor = (anchor == '') ? @basename : @basename + '__' + anchor
anchor = escape_anchor(anchor)
attribs[:id] = anchor
end

use_toc = have_toc && anchor
old_toc = @current_toc
@current_toc = use_toc ? [] : nil

@depth += 1
if cls
@html.div(:class => cls) {
@html.send("h#{@depth}", attribs, title)
yield
}
else
@html.send("h#{@depth}", attribs, title)
yield
end
@depth -= 1

old_toc << [ anchor, title, @current_toc, cls ] if use_toc
@current_toc = old_toc
return anchor
end

## Code formatting
def _count_indent(line)
/^[ \t]*/.match(line)[0].each_char.reduce(0) do |i, c|
c == ' ' ? i + 1 : (i + TAB_WIDTH) - (i % TAB_WIDTH)
end
end
def _remove_indent(indent, line)
p = 0
l = line.length
i = 0
while i < indent && p < l
case line[p]
when ' '
i += 1
when "\t"
i = (i + TAB_WIDTH) - (i % TAB_WIDTH)
else
return line[p..-1]
end
p += 1
end
return line[p..-1]
end
def _format_code(code)
lines = code.rstrip.lines
real_lines = lines.grep(/\S/)
return '' if real_lines.length == 0

indent = real_lines.map { |l| _count_indent l }.min

code = lines.map { |line| _remove_indent(indent, line).rstrip }.join("\n") + "\n"
code.gsub(/\A\n+/, "").gsub(/\n\n+/, "\n\n").gsub(/\n+\Z/, "\n")
end


def _parse_code(xml)
@html.pre { @html.code { @html << _format_code(xml.inner_html) } }
end

def _parse_markdown(xml)
md = _format_code(xml.inner_html)
@html << BlueCloth.new(md).to_html
end

def _parse_textile(xml)
tx = _format_code(xml.inner_html)
@html << RedCloth.new(tx).to_html
end

def _parse_html(xml)
@html << xml.inner_html
end

def _parse_description(xmlParent)
return unless xmlParent
xml = xmlParent.xpath('d:description[1]', XPATH_NAMESPACES)[0]
return unless xml

xml.children.each do |child|
if child.text?
@html.p child.content.strip
elsif ['html','textile','markdown'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid description element ' + child.name
end
end
end

def <=>(other)
ordername <=> other.ordername
end

def ordername=(value)
@ordername = value
end

def ordername
@ordername || @basename
end

def basename
@basename
end

def filename
basename + '.html'
end

def write_disk(output_directory)
puts "Writing #{output_directory}: #{filename}"
File.open(File.join(output_directory, self.filename), "w") { |f| f.write self.to_html }
end
end


class ModuleDocumentation < Documentation
def initialize(filename, xml)
super(File.basename(filename, '.xml'))

self.title = basename
render_main { _parse_module(xml.root) }

store_toc
end

def _parse_short(xmlParent, makeDiv = false)
return unless xmlParent
xml = xmlParent.xpath('d:short[1]', XPATH_NAMESPACES)[0]
return unless xml
text = xml.content.strip
return unless text

if makeDiv
@html.p.short text
else
@html.text text
end
text
end

def _parse_parameters(xml)
@html.dl {
xml.xpath('d:parameter', XPATH_NAMESPACES).each do |param|
@html.dt param['name']
child = param.element_children[0]
if child.name == 'short'
@html.dd { _parse_short param }
elsif child.name == 'table'
@html.dd {
@html.text "A key-value table with the following entries:"
@html.dl {
child.element_children.each do |entry|
@html.dt entry['name']
@html.dd { _parse_short entry }
end
}
}
end
end
}
end

def _parse_default(xml)
@html.div(:class => 'default') {
@html.text "Default value: "

child = xml.element_children[0]
@html.span({:class => child.name}, child.content.strip)
}
end

def _parse_aso(xml, type)
name = xml['name']
raise "#{type} requires a name" unless name
parameter_names = xml.xpath('d:parameter', XPATH_NAMESPACES).map { |p| p['name'] }
parameter_names = ['value'] if parameter_names.length == 0 and type == 'option'

title = "#{name} (#{type})"
anchor = "#{type}_#{name}"
cls = "aso #{type}"
short = nil

anchor = nest(title, anchor, cls) {
short = _parse_short(xml, true)

@html.pre(:class => "template") {
@html.span.key name
if parameter_names.length == 1
@html.text ' '
@html.span.param parameter_names[0]
@html.text ';'
elsif parameter_names.length > 0
@html.text ' ('
first = true
parameter_names.each do |pname|
@html.text ', ' unless first
first = false
@html.span.param pname
end
@html.text ');'
else
@html.text ';'
end
}

if type == 'option'
_parse_default(xml.xpath('d:default', XPATH_NAMESPACES)[0])
else
_parse_parameters(xml) if parameter_names.length > 0
end

_parse_description(xml)
xml.xpath('d:example', XPATH_NAMESPACES).each do |child|
_parse_example(child)
end
}

[name, filename + '#' + anchor, short, self]
end

def _parse_action(xml)
@actions << _parse_aso(xml, 'action')
end

def _parse_setup(xml)
@setups << _parse_aso(xml, 'setup')
end

def _parse_option(xml)
@options << _parse_aso(xml, 'option')
end

def _parse_example(xml)
nest(xml['title'] || 'Example', xml['anchor'], 'example') {
_parse_description(xml)

config = xml.xpath('d:config[1]', XPATH_NAMESPACES)
_parse_code(config[0])
}
end

def _parse_section(xml)
title = xml['title']
raise 'section requires a title' unless title

nest(title, xml['anchor'] || '#', 'section') {
xml.children.each do |child|
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['action','setup','option','html','textile','markdown','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
end
end
}
end

def _parse_module(xml)
raise 'unexpected root node' if xml.name != 'module'

self.title = xml['title'] || self.title
self.ordername = xml['order']

nest(title, '', 'module') {
@html.p {
@html.text (basename + ' ')
@short = _parse_short(xml, false)
}
_parse_description(xml)

xml.element_children.each do |child|
if ['action','setup','option','example','section'].include? child.name
self.send('_parse_' + child.name, child)
elsif ['short', 'description'].include? child.name
nil # skip
else
raise 'invalid module element ' + child.name
end
end
}
end

def short
@short
end

def link(html_builder)
html_builder.a({:href => self.filename + '#' + escape_anchor(self.basename)}, self.basename)
end
end

class ChapterDocumentation < Documentation
def initialize(filename, xml)
super(File.basename(filename, '.xml'))

render_main { _parse_chapter(xml.root) }

store_toc
end

def _parse_example(xml)
nest(xml['title'] || 'Example', xml['anchor'], 'example') {
_parse_description(xml)

config = xml.xpath('d:config[1]', XPATH_NAMESPACES)
_parse_code(config[0])
}
end

def _parse_section(xml)
title = xml['title']
raise 'section requires a title' unless title

nest(title, xml['anchor'] || '#', 'section') {
xml.children.each do |child|
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['html','textile','markdown','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
end
end
}
end

def _parse_chapter(xml)
raise 'unexpected root node' if xml.name != 'chapter'

self.title = xml['title']
raise 'chapter requires a title' unless self.title
self.ordername = xml['order']

nest(self.title, '', 'chapter') {
_parse_description(xml)

xml.element_children.each do |child|
if ['example','section'].include? child.name
self.send('_parse_' + child.name, child)
elsif ['description'].include? child.name
nil # skip
else
raise 'invalid chapter element ' + child.name
end
end
}
end
end

class ModuleIndex < Documentation
def modules_table(modules)
nest('Modules', 'modules') {
@html.table(:class => 'table table-striped') {
@html.tr {
@html.th "name"
@html.th "description"
}
modules.each do |mod|
next unless mod.is_a? ModuleDocumentation
@html.tr {
@html.td {
mod.link(@html)
}
@html.td mod.short
}
end
}

}
end

def aso_html_table(name, list)
nest(name.capitalize + 's', name + 's') {
@html.table(:class => 'table table-striped aso') {
@html.tr {
@html.th "name"
@html.th "module"
@html.th "description"
}
list.each do |id, href, short, mod|
@html.tr {
@html.td {
@html.a({:href => href}, id)
}
@html.td {
mod.link(@html)
}
@html.td short
}
end
}
@html.text "none" unless list.length > 0
}
end

def initialize(modules)
super('index_modules')

actions = []
setups = []
options = []
modules.each do |mod|
actions += mod.actions
setups += mod.setups
options += mod.options
end

render_main do
nest('Modules overview', '', 'index_modules') {
modules_table(modules)
aso_html_table('action', actions.sort)
aso_html_table('setup', setups.sort)
aso_html_table('option', options.sort)
}
end

self.title = "lighttpd2 - all in one"
store_toc
end
end

class AllPage < Documentation
def fix_link(a)
href = a['href']
return unless href
m = /^([^#]*)(#.*)$/.match(href)
a['href'] = m[2] if m && @href_map[m[1]]
end

def initialize(pages)
super('all')

@href_map = {}
pages.each do |page|
@href_map[page.filename] = true
end

render_main do
pages.each do |page|
@html << page.to_html_fragment
@toc += page.toc
end
end

@html_doc.xpath('//a').each do |a|
fix_link(a)
end

self.title = "lighttpd2 - all in one"
store_toc
end
end


def loadXML(filename)
xml = Nokogiri::XML(File.read filename) do |config|
config.strict.nonet
end

if xml.root.name == 'module'
ModuleDocumentation.new(filename, xml)
elsif xml.root.name == 'chapter'
ChapterDocumentation.new(filename, xml)
end
end


if __FILE__ == $0
output_directory = ARGV[0] || '.'

if not system("xmllint --noout --schema doc_schema.xsd *.xml 2>&1")
STDERR.puts "Couldn't validate XML files"
exit 1
end

pages = []

Dir["*.xml"].each do |file|
puts "Compiling #{file}"
pages << loadXML(file)
end

pages.sort!
pages << ModuleIndex.new(pages)


pages.sort!
pages << AllPage.new(pages)

pages.sort!
pages.each { |page| page.write_disk(output_directory) }
end

+ 336
- 0
doc/core_config.xml View File

@@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Main Configuration">

<description>
<textile>
At the heart of each webserver is the configuration file. It controls the whole behaviour and therefore has to be mighty but at the same time easy enough to get the desired results without hassle.
In the lighttpd config you control how it reacts on requests. To achieve this you have to express some kind of logic - just like in a programming language.
</textile>
</description>

<section title="Basic Syntax">
<textile><![CDATA[
The syntax for the lighttpd 2.0 configuration file is somewhat similar to various programming languages - kind of a mixture. But don't be afraid, it is really simple. No pointers involved :)
The basic blocks are "values":#core_config__values, "variables":#core_config__variables, "function calls":#core_config__funcalls and "conditions":#core_config__conditions.
]]></textile>
</section>

<section title="Values" anchor="values">
<section title="Boolean">
<textile><![CDATA[
There are two boolean values:
* @true@
* @false@
]]></textile>
</section>

<section title="Integers">
<textile><![CDATA[
Integers are stored as signed ints with 64 bits (max value around 9 * 10^18). There are three basic ways to use them:
* decimal integers: starting with any non zero digit, like @128@
* octal integers: starting with a zero, like @0644@
* hexadecimal integers: starting with @0x@ like @0xff@

All three can have a suffix representing a factor the number gets multiplied with:
* @byte@: @1@
* @kbyte@: @1024@
* @mbyte@: @1024*1024@
* @gbyte@: @1024*1024*1024@
* @tbyte@: @1024*1024*1024*1024@
* @pbyte@: @1024*1024*1024*1024*1024@
* @bit@: @1 / 8@
* @kbit@: @1024 / 8@
* @mbit@: @1024*1024 / 8@
* @gbit@: @1024*1024*1024 / 8@
* @tbit@: @1024*1024*1024*1024 / 8@
* @pbit@: @1024*1024*1024*1024*1024 / 8@
* @sec@: @1@
* @min@: @60@
* @hours@: @3600@
* @days@: @24*3600@

For sizes the base unit is byte, and for intervalls seconds.
]]></textile>
</section>

<section title="Strings">
<textile><![CDATA[
There are 4 ways to specify a string:
* @'hello world'@
* @"hello world"@
* @e'hello world'@
* @e"hello world"@

The basic escape rules are the same for both:
* @"\n"@ is a newline, @"\r"@ a carriage return, @"\t"@ a tab stop
* @"\\"@ is one @\@, @"\""@ is one double quote @"@ and @"\'"@ a single quote @'@
* escaping single/double quote is optional if the symbol is not used to terminate the string, i.e. @'\"'@ = @'"'@ and @"\'"@ = @"'"@
* @"\xNN"@: NN must be hexadecimal characters, and the string is replaced with the decoded 8-bit value as a single byte
* All other @\@ occurences are *not* removed from the string.

The @e'..'@ and @e"..."@ variant do not allow any occurences of the last kind to happen.

All other characters are allowed (the strings are parsed as 8-bit binary; lighttpd2 usually doesn't care about the encoding).
]]></textile>
</section>

<section title="Lists">
<textile><![CDATA[
Lists are ordered collections of other values:
* @[1, true, "foo"]@ (simple list)
* @[]@ (empty list)
* @[1]@ (list with one element)
* @[[1,2],[3,4],5]@ (nested lists)
* @[1,2,]@ (final value can have a trailing @,@ too)
* @(1, 2, 3)@ (alternative brackets with more than one element)

Note that @(1)@ is not a list; parentheses can also be used to group expressions as in @1*(2+3)@, and it is therefore recommend to use only @[]@ to specify lists.
]]></textile>
</section>

<section title="Key-Value Lists">
<textile><![CDATA[
Key-value lists associates keys to values (both can be of any type); the syntax is:
* @[ "a" => 1, "b" => 10 ]@

As with normal lists the final value can have a trailing @,@ too. You cannot mix simple values and key-value associations in one list (nesting them works).

The @key => value@ operator is also only syntactic sugar for a @[key, value]@ list, the above example is the same as:
* @[ [a, 1], [b, 10] ]@

This especially means that key-value lists are ordered too, although they can get converted to hash tables in certain function calls internally.
]]></textile>
</section>

<section title="Expressions">
<textile><![CDATA[
There are operators available for some value types:
* for integers: @+@, @-@, @*@ and @/@
* for strings: @+@ (concatenate two strings)
* for lists: @+@ (append lists)

Also you can cast strings and booleans to integers and any value to strings:
* @cast(int) "256"@ (only supports decimal representations and no suffix like above)
* @cast(int) true@ (@true@ maps to @1@ and @false@ to @0@)
* @cast(string) 5@

Expressions can be grouped to override default association:
* @3 * (1 + 2)@ vs. @3 * 1 + 2@ and so on
]]></textile>
</section>

<section title="Action Blocks">
<textile><![CDATA[
An action block consists of a list of function calls (in action context) and conditionals; it is treated like a value, i.e. can be assigned to variables and used as parameter in function calls.

Action blocks can also contain variable assignments and setup blocks/function calls, which are not part of the "action block value" itself, but are evaluated while parsing the config.

The syntax is:

<pre>
{
log "hello world";
if req.path =$ ".hidden" { static; }
}
</pre>

The complete config is an action block too (but without the surrounding curly braces); conditionals also use action blocks for their branches.

Each action block that is not a condition branch starts a new nested variable scope;
]]></textile>
</section>

<section title="Variables" anchor="variables">
<textile><![CDATA[
Variable names start with a alphabetic character (@a-z@ and @A-Z@) or an underscore @_@, and are followed by alphanumeric characters, underscores @_@ and dots @.@; keywords are not allowed as variable names.

Variables store values, and the name can be used in place of an actual value; later modifications to a variable have no influence on previous uses (i.e. are only evaluated while *parsing* the config).

Variables are assigned values with @=@ (and a terminating @;@)

<pre>
my_types = [".txt" => "text/html"];
php = {
if phys.path =$ ".php" { fastcgi "unix:/var/run/lighttpd/php.sock"; }
};
</pre>

By default variables assignment overwrites an existing variable (in its previous scope) or, if it doesn't exist, creates a new one in the local scope (i.e. it will only be available in the current scope and nested descendants).

You can explicitly create a new variable in the local scope (hiding variables in parent scopes with the same name) by prefixing the assignment with @local@:
@local wwwpath = "/var/www/example.com";

You can also create variables in the global scope by prefixing the assignment with @global@.
The main config already is in a nested scope (i.e. *not* the global scope). The global scope is not destroyed after config loading, and can be used in delayed config loading (say from SQL in the future).

If a variable name is used in a context it will always use the definition from the nearest scope.
]]></textile>

<example>
<description>
This example illustrates that variables are evaluated while parsing the config.
</description>
<config>
foo = "bar";
if req.path == "/somepath" {
foo = "baz";
}

# at this point foo will ALWAYS contain "baz" no matter if "/somepath" was requested or not
</config>
</example>

<example>
<description>
This example illustrates scoping.
</description>
<config>
foo = "bar";
php = {
local foo = "baz";
# in this block (and nested blocks within) foo is now "baz"
# ...
};
# foo is now "bar" again
</config>
</example>
</section>

<section title="Special Variables">
<textile><![CDATA[
@sys.*@ variables are readonly. right now the following @sys.*@ variables are available:
* @sys.pid@: the process id of lighttpd
* @sys.cwd@: the current working directory
* @sys.env.X@: the system environment variable with name @X@ (for any @X@)
]]></textile>
</section>
</section>

<section title="Function calls" anchor="funcalls">
<textile><![CDATA[
There are three types of function calls:
* actions
* setups
* options

Actions can only be used in action (block) context, setups only in setup (block) context, and options can be used in both.

A setup context is started with the keyword @setup@, and is followed by either a single setup function call or a setup block.

Setup function calls are run immediately when they occur (they are used to "setup" the webserver environment, like listening on TCP sockets, and setting default options), while action function calls are run for each request (mapping request urls to physical paths, handling requests, modifying the default options).

The actions, setups and options are provided by the "modules":index_modules.html#index_modules.
]]></textile>

<section title="Includes">
<textile><![CDATA[
Includes are similar to function calls in the syntax, but are directly handled by the config parser. They are only allowed in action context, as they insert a reference to an action block at the point they are used.

There are three types of includes:
* @include "/etc/lighttpd/vhosts/*.conf";@: include files like the main config itself; the path can contain wildcards
* @include_shell "/etc/lighttpd/config_generator.sh";@: runs the specified command, and parses the output of it as config file
* @include_lua "/etc/lighttpd/complex.lua"@: includes a Lua config file

Includes also create a new nested scope.
]]></textile>
</section>

<section title="Debug Print">
<textile><![CDATA[
Similar to includes @__print@ is a special function, but is available in action and setup context. It logs the string values of its parameter(s) with log level "debug".
]]></textile>
</section>
</section>

<section title="Conditions" anchor="conditions">
<textile><![CDATA[
Conditions (known from Lighttpd 1.x) are the equivalent to "if"s in most programming languages. There are also "else" and "elseif" equivalents.
They are created by a comparison of a condition variable and a value that is evaluated at runtime (for each request).
Conditions can be nested, and you can group them with @and@ and @or@ operators. @and@ binds stronger than @or@, although it is preferred to group them with parentheses.
]]></textile>

<example>
<config>
if req.host == "mydomain.tld" {
if req.path == "/" or req.path == "/index.html" {
static;
} else if req.path == "/upload" {
if req.content_length > 100mbyte {
access.deny;
} else {
proxy "127.0.0.1:8080";
}
}
}
</config>
</example>

<section title="Syntax">
<textile><![CDATA[
The basic syntax forms are:
* @if <expr> { ... }@
* @if <expr> { ... } else { ... }@
* @if <expr> { ... } else if <expr2> { ... }@
* @if <expr> { ... } else if <expr2> { ... } ...@ (continue with @else@ or @else if@)

A condition expression @<expr>@ is:
* @(<expr>)@
* @<expr1> and <expr2>@
* @<expr1> or <expr1>@ (@<expr1> and <expr2> or <expr3>@ = @(<expr1> and <expr2>) or <expr3>@; @and@ has higher precedence)
* @<condvar> <op> <value>@ for @<condvar>@ being a condition variable (with a type different from boolean), @<op>@ an condition operator and @<value>@ a string or a number.
* @<condvar>@ or @!<condvar>@ for a boolean condition variable.
]]></textile>
</section>

<section title="Condition variables" anchor="condition_vars">
<textile><![CDATA[
There are three categories of condvars:
* request.xyz (can be abbreviated by req.xyz)
* physical.xyz (can be abbreviated by phys.xyz)
* response.xyz (can be abbreviated by resp.xyz)

table(table table-striped).
|_. variable |_. description |
| req.localip | ip address of the listing socket, the client connected to (filename for unix sockets) |
| req.localport | port number of the listening socket, -1 for unix sockets |
| req.remoteip | ip address of the client |
| req.remoteport | port number of the client, -1 for unix sockets |
| req.path | the _path_ part of the requested url. not including the querystring. |
| req.host | requested hostname |
| req.scheme | scheme of the request. "http" or "https" |
| req.query | the _querystring_ of the requested url |
| req.method | method of the request. "GET", "POST", "HEAD" etc. |
| req.length | integer. length of the content for e.g. POST methods |
| req.header["name"] | request header _name_ e.g. req.header["referer"] |
| req.is_handled | boolean condition, does request already have a handler (static, fastcgi..) |
| req.env["name"] | (short for req.environment["name"]) CGI environment |
| | |
| phys.path | physical path of the file to be served. e.g. document root + path |
| phys.exists | boolean condition, indicates whether the requested file exist |
| phys.size | integer. size of the requested file. -1 if file doesn't exist |
| phys.is_dir | boolean condition, indicates whether the requested file is a directory |
| phys.is_file | boolean condition, indicates whether the requested file is a normal file (e.g. no unix socket etc) |
| phys.docroot | document root |
| phys.pathinfo | pathinfo |
| | |
| resp.status | response status code (blocks request until response header is available) |
| resp.header["name"] | response header (blocks request until response header is available) |
]]></textile>
</section>

<section title="Condition operators">
<textile><![CDATA[
table(table table-striped).
|_. op |_. description |_. op |_. description |
| <notextile>==</notextile> | compares two values on equality | != | negative == |
| <= | less than or equal | < | less than |
| >= | greater than or equal | > | greater than |
| =~ | regular expression match | !~ | negative =~ |
| =^ | prefix match | !^ | negative =^ |
| =$ | suffix match | !$ | negative =$ |
| =/ | cidr match | !/ | negative =/ |
]]></textile>
</section>

</section>
</chapter>

+ 10
- 0
doc/core_fetch.xml View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Fetch API">
<description>
<textile>
The Fetch API provides a common interface between lighttpd modules to lookup entries in a database. Both lookup key and data are simple (binary) strings.

So far only a "fetch.files_static":plugin_core.html#plugin_core__setup_fetch-files_static is providing a database, and only "gnutls":mod_gnutls.html#mod_gnutls__setup_gnutls is using it to lookup SNI certificates.
</textile>
</description>
</chapter>

+ 53
- 0
doc/core_pattern.xml View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Patterns">
<description>
<textile>
The lighttpd config supports "patterns" in various places (docroot, redirect, rewrite, env.set, ...); and they share the following structure.

There are two kinds of "captures" available; one from the action itself (like captures from a regular expression in redirect/rewrite, or the labels from the hostname in docroot), and the captures from the previous matching regular expression conditional in the action stack. If there was no capture of the selected kind the values will be empty strings.
</textile>
</description>

<section title="Syntax">
<textile>
A pattern is a string consisting of the following parts:
* simple text. can contain special characters $ and % only when they are escaped with \ - remember, that the \ has to be escaped too for the config, so you'll probably have to use \\ to escape. You are allowed to escape ? too (used for special "split" in rewrite).
* "%" capture references (previous matching regular expression conditional); either followed by a single digit, or a range (see below for range syntax)
* "$" capture references (depends on action); either followed by a single digit, or a range (see below for range syntax)
* "%" references to "condition variables":core_config.html#core_connfig__condition_vars, for example: @%{req.path}@; the conditional can be prefixed with "enc:" (@%{enc:req.path}@), in which case the value will be urlencoded.
</textile>
</section>

<section title="Ranges">
<textile><![CDATA[
Ranges can either be a single element @[n]@ (@n@ can have more than one digit), a closed range @[n-m]@ or an open range @[n-]@ or @[-m]@;
the open end is always replaced with "G_MAXUINT" (a very big positive integer). ranges can be "reversed", i.e. @n > m@.

There are now two different ways ranges are used:
* ranges of regular expression captures: the captures are just inserted for all values in the range; if the range is reversed, it starts with the highest index in the range.
* ranges of labels in a hostname: similar to the first range, but the inserted labels are separated by a "."; the index 0 stands for "complete hostname", and ranges including 0 are reduced to the complete hostname; the labels are numbered from top-level, and the range is intepreted reversed (just have a look at the examples, and it will be clear).
]]></textile>
</section>

<example title="Example: simple redirect">
<config>
redirect "http://%{req.host}%{req.path}?redirected=1";
</config>
</example>

<example title="Example: docroot">
<description>
<textile>
* a request to "http://example.com/project/trunk" would lead to docroot "/var/www/project/trunk/htdocs"
* a request to "http://sub.example.com/" would lead to docroot "/var/www/vhosts/com/sub.example"
</textile>
</description>
<config>
if req.path =~ "^/project/([^/]*)" {
docroot "/var/www/projects/%1/htdocs";
} else {
docroot "/var/www/vhosts/$1/${2-}/htdocs";
}
</config>
</example>
</chapter>

+ 21
- 0
doc/core_regex.xml View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Regular expressions">
<description>
<textile>
lighttpd2 uses the "Perl-compatible regular expressions" implementation from GLib, see their "Regular expression syntax":https://developer.gnome.org/glib/stable/glib-regex-syntax.html documentation.

The config format has different ways to provide strings (you can quote with either @'@ or @"@; the character used to quote has to be escaped with @\@ if used inside the string).
The simple (standard) way @"text"@ has the following escape rules:
* @"\n"@ is a newline, @"\r"@ a carriage return, @"\t"@ a tab stop
* @"\\"@ is one @\@, @"\""@ is one double quote @"@ and @"\'"@ a single quote @'@
* escaping single/double quote is optional if the symbol is not used to terminate the string, i.e. @'\"'@ = @'"'@ and @"\'"@ = @"'"@
* @"\xNN"@: NN must be hexadecimal characters, and the string is replaced with the decoded 8-bit value as a single byte
* All other @\@ occurences are *not* removed from the string.

This way is the preferred one for regular expressions; only to actually match a @\@ you have to do additional escaping (@"\\\\"@; @"\x5C"@ = @"\\"@ is not working), and @\\@ is usually not doing what you wanted (matching a digit: @"\\d"@ = @"\d"@). All other escape rules are compatible with what pcre is doing.

The second way is to place an @e@ before the string like this: @e"text"@. It has the same rules like the normal string, but does not allow unknown escape sequences (the last rule above).
To match a digit with pcre this way you'd have to write @e"\\d"@ instead of @"\d"@.
</textile>
</description>
</chapter>

+ 170
- 0
doc/doc_schema.xsd View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:d="urn:lighttpd.net:lighttpd2/doc1"
targetNamespace="urn:lighttpd.net:lighttpd2/doc1"
elementFormDefault="qualified">

<!-- root nodes -->
<element name="module" type="d:ModuleType" />
<element name="chapter" type="d:ChapterType" />

<complexType name="ModuleType">
<sequence>
<!-- a module requires a short description -->
<element name="short" type="string" />
<!-- optional description to print below ToC -->
<element name="description" type="d:DescriptionType" minOccurs="0" />

<!-- a module cannot contain more simple text; include it in a section -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="action" type="d:ActionSetupType" />
<element name="setup" type="d:ActionSetupType" />
<element name="option" type="d:OptionType" />

<element name="example" type="d:ExampleType" />
<element name="section" type="d:ModuleSectionType" />
</choice>
</sequence>

<!-- optional title, defaults to module name, which is the basename -->
<attribute name="title" type="token" use="optional" />
<!-- order modules / chapters. defaults to name -->
<attribute name="order" type="Name" use="optional" />
</complexType>

<complexType name="ActionSetupParameterTypeParameterTableEntryType">
<sequence>
<element name="short" type="string" />
</sequence>
<attribute name="name" type="Name" use="required" />
</complexType>

<complexType name="ActionSetupParameterTypeParameterTableType">
<sequence>
<element name="entry" type="d:ActionSetupParameterTypeParameterTableEntryType" maxOccurs="unbounded" />
</sequence>
</complexType>

<complexType name="ActionSetupParameterType">
<choice>
<element name="short" type="string" />
<element name="table" type="d:ActionSetupParameterTypeParameterTableType" />
</choice>

<!-- a parameter requires a name -->
<attribute name="name" type="Name" use="required" />
</complexType>

<complexType name="ActionSetupType">
<sequence>
<element name="short" type="string" />
<element name="parameter" type="d:ActionSetupParameterType" minOccurs="0" maxOccurs="unbounded" />
<element name="description" type="d:DescriptionType" minOccurs="0" />
<element name="example" type="d:ExampleType" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="Name" use="required" />
</complexType>

<complexType name="OptionParameterType">
<!-- a parameter requires a name -->
<attribute name="name" type="Name" use="required" />
</complexType>

<complexType name="OptionDefaultType">
<choice>
<element name="text" type="string" />
<element name="value" type="string" />
</choice>
</complexType>

<complexType name="OptionType">
<sequence>
<element name="short" type="string" />
<!-- has exactly one parameter, defaults to name "value" -->
<element name="parameter" type="d:OptionParameterType" minOccurs="0" />
<element name="default" type="d:OptionDefaultType" />
<element name="description" type="d:DescriptionType" minOccurs="0" />
<element name="example" type="d:ExampleType" minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="Name" use="required" />
</complexType>

<complexType name="DescriptionType" mixed="true">
<sequence>
<choice minOccurs="0" maxOccurs="unbounded">
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
</choice>
</sequence>
</complexType>

<complexType name="ExampleType">
<sequence>
<element name="description" type="d:DescriptionType" minOccurs="0" />
<element name="config" type="string" />
</sequence>
<!-- title defaults to "Example" -->
<attribute name="title" type="token" use="optional" />
<!-- set anchor (id attribute of <h*> node); set to '#' to generate a unique anchor; if no anchor is set no ToC entry is created -->
<attribute name="anchor" type="token" use="optional" />
</complexType>

<complexType name="ModuleSectionType" mixed="true">
<sequence>
<choice minOccurs="0" maxOccurs="unbounded">
<element name="action" type="d:ActionSetupType" />
<element name="setup" type="d:ActionSetupType" />
<element name="option" type="d:OptionType" />

<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:ModuleSectionType" />
</choice>
</sequence>
<!-- a section requires a title -->
<attribute name="title" type="token" use="required" />
<!-- set anchor (id attribute of <h*> node); set to '#' to generate a unique anchor; if no anchor is set no ToC entry is created -->
<attribute name="anchor" type="token" use="optional" />
</complexType>



<complexType name="ChapterType">
<sequence>
<!-- optional description to print below ToC -->
<element name="description" type="d:DescriptionType" minOccurs="0" />

<!-- a chapter cannot contain more simple text; include it in a section -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="example" type="d:ExampleType" />
<element name="section" type="d:ChapterSectionType" />
</choice>
</sequence>

<!-- a chapter requires a title -->
<attribute name="title" type="token" use="required" />
<!-- order modules / chapters. defaults to name -->
<attribute name="order" type="Name" use="optional" />
</complexType>

<complexType name="ChapterSectionType" mixed="true">
<sequence>
<choice minOccurs="0" maxOccurs="unbounded">
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:ChapterSectionType" />
</choice>
</sequence>
<!-- a section requires a title -->
<attribute name="title" type="token" use="required" />
<!-- set anchor (id attribute of <h*> node); set to '#' to generate a unique anchor; if no anchor is set no ToC entry is created -->
<attribute name="anchor" type="token" use="optional" />
</complexType>

</schema>

+ 6
- 0
doc/jquery-1.10.1.min.js
File diff suppressed because it is too large
View File


+ 68
- 0
doc/mod_access.xml View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>
lets you filter clients by IP address.
</short>

<action name="access.deny">
<short>denies access by returning a 403 status code</short>
</action>

<action name="access.check">
<short>allows or denies access based on client IP address</short>
<parameter name="rules">
<short>A key value list mapping "access" and/or "deny" keys to a list of CIDR addresses or "all".</short>
</parameter>
<description>
Checks the client IP address agains the rules. Default is to deny all addresses. The most precise matching rule defines the result ("192.168.100.0/24" takes precedence over "192.168.0.0/16"; similar to routing tables); if the same CIDR is in both lists the second action is taken. "all" is a synonym for "0.0.0.0/0" and "::/0", matching all IPv4 and IPv6 addresses.
</description>
<example title="Example: restrict access to local network" anchor="#">
<description>
Limit access to clients from the local network. The deny rule isn't strictly required, as the default is to deny anyway. The smaller CIDR strings for the local networks override the global deny rule.
</description>
<config>
setup {
module_load "mod_access";
}

access.check (
"allow" => ("127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"),
"deny" => ("all")
);
</config>
</example>
<example title="Example: restrict access to subnet with exception" anchor="#">
<description>
Limit access to clients from "192.168.10.0/24", but deny access to "192.168.10.1". As "192.168.10.1" (equivalent to "192.168.10.1/32") is a more precise match it overwrites the allow rule for the subnet "192.168.10.0/24" containing it.
</description>
<config>
setup {
module_load "mod_access";
}

access.check (
"allow" => ("192.168.10.0/24"),
"deny" => ("192.168.10.1")
);
</config>
</example>
</action>

<option name="access.redirect_url">
<short>url to redirect to if access was denied (not implemented yet)</short>
<parameter name="url" />
<default><text>not set</text></default>
<description>
<textile>
*NOT IMPLEMENTED YET*
</textile>
</description>
</option>

<option name="access.log_blocked">
<short>whether to log when access was denied (with log level "info")</short>
<parameter name="url" />
<default><value>false</value></default>
</option>

</module>

+ 69
- 0
doc/mod_accesslog.xml View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>logs requests handled by lighttpd to files, pipes or syslog. The format of the logs can be customized using printf-style placeholders.</short>

<option name="accesslog.format">
<short>defines the log format</short>
<parameter name="format" />
<default><value>"%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""</value></default>
<description>
<textile><![CDATA[
Some format specifiers take a mandatory key, enclosed in curly braces between percent sign and the actual specifier. CLF means "common log format", if a value is zero, a '-' is used instead.

table(table table-striped).
|_. specifier |_. description |
| %% | Percent sign itself |
| %a | Remote IP-address |
| %A | Local IP-address|
| %b | Size of response in bytes, excluding HTTP headers (CLF) |
| %B | Size of response in bytes, excluding HTTP headers |
| %{foobar}C | (*not implemented yet*) Contents of cookie @foobar@ of the request |
| %D | Time taken to serve the request in microseconds |
| %{foobar}e | Contents of the request environment variable @foobar@ |
| %f | Path to physical file |
| %h | Remote IP-address (same as @%a@) |
| %{foobar}i | Contents of request header @foobar@ |
| %m | Request method (GET, POST, etc) |
| %{foobar}o | Contents of response header @foobar@ |
| %p | Local port |
| %q | Querystring |
| %r | First line of request (GET /foo.html?bar HTTP/1.1) |
| %s | Response status code |
| %t | Time/date the request was received in standard english format |
| %T | Time taken to serve the request in seconds |
| %u | Authed user (from mod_auth). Same as @%{REMOTE_USER}e@ |
| %U | Request path (not including querystring) |
| %v | Server name as set through the @server.name@ option or the request hostname of @server.name@ is not set |
| %V | Request hostname |
| %X | Connection status after response: "X" if aborted before completed, "+" if keepalive, "-" if no keepalive |
| %I | Bytes received including HTTP headers and request body |
| %O | Bytes sent including HTTP headers and response body |

Modifiers right after the percent sign like Apache provides them, are not supported. "<" or ">" are ignored, everything else results in a parse error. Specifiers supported by Apache but not lighty: %l, %n, %P
]]></textile>
</description>
<example>
<config>
accesslog.format "%h %V %u %t \"%r\" %>s %b";
</config>
</example>
</option>

<option name="accesslog">
<short>defines the log target</short>
<parameter name="target" />
<default><text>logging disabled</text></default>
<description>
<html>Enable logging by setting a log target. Supports the same log targets as <a href="plugin_clore.html#plugin_core__action_log">log</a>.</html>
</description>
<example>
<config>
setup {
module_load "mod_accesslog";

accesslog "/var/log/lighttpd/access.log";
}
</config>
</example>
</option>
</module>

+ 144
- 0
doc/mod_auth.xml View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>requires authentication from clients using a username and password. It supports the basic (digest not yet) authentication method as well as plaintext, htpasswd and htdigest backends.</short>

<description>
<textile>
*IMPORTANT NOTE*: You need to put the auth actions before generating content! If a content handler is already active (like php or static or dirlist), auth will be ignored!

* Basic:
The "basic" method transfers the username and the password in cleartext over the network (base64 encoded) and might result in security problems if not used in conjunction with an encrypted communication channel between client and server.
It is recommend to use https in conjunction with basic authentication.

* Digest (not supported yet):
The "digest" method only transfers a hashed value over the network which performs a lot of work to harden the authentication process in insecure networks (like the internet).
The "digest" method doesn't work with the htpasswd backend, only with plaintext and htdigest.

*NOTE*: The digest method is broken in Internet Explorer &lt; 7. Use basic instead if this is a problem for you. (not supported for now anyway)
</textile>
</description>


<action name="auth.plain">
<short>requires authentication using a plaintext file</short>
<parameter name="options">
<table>
<entry name="method">
<short>"basic" or "digest" - for now only "basic" is supported, but you still have to specify it.</short>
</entry>
<entry name="realm">
<short>the realm name to send in the "Need authentication" response to the browser; used in the hash for htdigest too.</short>
</entry>
<entry name="file">
<short>the filename of the backend data</short>
</entry>
<entry name="ttl">
<short>(optional) after how many seconds lighty reloads the password file if it got changed and is needed again (defaults to 10 seconds)</short>
</entry>
</table>
</parameter>
<description>
requires authentication using a plaintext file containing user:password pairs seperated by newlines (\n).
</description>
</action>

<action name="auth.htpasswd">
<short>requires authentication using a htpasswd file</short>
<parameter name="options">
<table>
<entry name="method">
<short>only "basic" is supported</short>
</entry>
<entry name="realm">
<short>the realm name to send in the "Need authentication" response to the browser; used in the hash for htdigest too.</short>
</entry>
<entry name="file">
<short>the filename of the backend data</short>
</entry>
<entry name="ttl">
<short>(optional) after how many seconds lighty reloads the password file if it got changed and is needed again (defaults to 10 seconds)</short>
</entry>
</table>
</parameter>
<description>
<textile>
* requires authentication using a htpasswd file containing user:encrypted_password pairs seperated by newlines (\n)
* passwords are encrypted using crypt(3), use the htpasswd binary from apache to manage the file
** hashes starting with "$apr1$" ARE supported (htpasswd -m)
** hashes starting with "{SHA}" ARE supported (followed by sha1_base64(password), htpasswd -s)
</textile>
</description>
</action>

<action name="auth.htdigest">
<short>requires authentication using a htdigest file</short>
<parameter name="options">
<table>
<entry name="method">
<short>"basic" or "digest" - for now only "basic" is supported, but you still have to specify it.</short>
</entry>
<entry name="realm">
<short>the realm name to send in the "Need authentication" response to the browser; used in the hash for htdigest too.</short>
</entry>
<entry name="file">
<short>the filename of the backend data</short>
</entry>
<entry name="ttl">
<short>(optional) after how many seconds lighty reloads the password file if it got changed and is needed again (defaults to 10 seconds)</short>
</entry>
</table>
</parameter>
<description>
<textile>
* requires authentication using a htdigest file containing user:realm:hashed_password tuples seperated by newlines (\n)
* the hashes are bound to the realm, so you can't change the realm without resetting the passwords
* passwords are saved as (modified) md5 hashes:
@md5hex(username + ":" + realm + ":" + password)@
</textile>
</description>
</action>

<action name="auth.deny">
<short>handles request with "401 Unauthorized"</short>
</action>

<option name="auth.debug">
<short>enable debug output</short>
<default><value>false</value></default>
</option>

<example>
<config>
setup {
module_load "mod_auth";
}

#/members/ is for known users only
if request.path =^ "/members/" {
auth.plain [ "method" => "basic", "realm" => "members only", "file" => "/etc/lighttpd/users.txt"];
if req.env["REMOTE_USER"] !~ "^(admin1|user2|user3)$" { auth.deny; }
}
</config>
</example>

<example>
<description>
<textile>
You can use @auth.require_user@ from the mod_lua plugin "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2/tree/contrib/core.lua for the REMOTE_USER check too:
</textile>
</description>

<config>
setup {
module_load ("mod_auth", "mod_lua");
lua.plugin "contrib/core.lua";
}

#/members/ is for known users only
if request.path =^ "/members/" {
auth.plain [ "method" => "basic", "realm" => "members only", "file" => "/etc/lighttpd/users.txt"];
auth.require_user ("admin1", "user2", "user3");
}
</config>
</example>
</module>

+ 51
- 0
doc/mod_balance.xml View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>balances between different backends.</short>

<description>
Using an action from mod_balance also activates a backlog: lighttpd2 will then put requests in a backlog if no backend is available and try again later.

Be careful: the referenced actions may get executed more than once (until one is successful!), so don't loop rewrites in them or something similar.
</description>

<action name="balance.rr">
<short>balance between actions (list or single action) with Round-Robin</short>
<parameter name="actions">
<short>the actions to balance between</short>
</parameter>
<description>
Round-Robin (rr) the requests are distributed equally over all backends.
</description>
<example>
<config>
balance.rr { fastcgi "127.0.0.1:9090"; };
</config>
</example>
<example>
<config>
balance.rr ({ fastcgi "127.0.0.1:9090"; }, { fastcgi "127.0.0.1:9091"; });
</config>
</example>
</action>

<action name="balance.sqf">
<short>balance between actions (list or single action) with SQF</short>
<parameter name="actions">
<short>the actions to balance between</short>
</parameter>
<description>
Shortest-Queue-First (sqf) is similar to Round-Robin and prefers the backend with the shortest wait-queue.
</description>
<example>
<config>
balance.sqf { fastcgi "127.0.0.1:9090"; };
</config>
</example>
</action>

<option name="balance.debug">
<short>enable debug output</short>
<default><value>false</value></default>
</option>

</module>

+ 40
- 0
doc/mod_cache_disk_etag.xml View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>caches generated content on disk if an etag response header is set; if the backend sends an already cached etag, the backend is closed and the file is sent directly.</short>

<description>
<textile>
Please note: This will not skip the backend, as it will need at least the reponse headers.

*Hint:*
Use a cron-job like the following to remove old cached data, e.g. in crontab daily:
<pre>
find /var/cache/lighttpd/cache_etag/ -type f -mtime +2 -exec rm -r {} \;
</pre>

*Hint:*
Have a look at mod_deflate to see this module in action.
</textile>
</description>

<action name="cache.disk.etag">
<short>cache responses based on the ETag response header</short>
<parameter name="path">
<short>directory to store the cached results in</short>
</parameter>
<description>
This blocks action progress until the response headers are done (i.e. there has to be a content generator before it (like fastcgi/dirlist/static file).
You could insert it multiple times of course (e.g. before and after deflate).
</description>

<example>
<config>
setup {
module_load "mod_cache_disk_etag";
}

cache.disk.etag "/var/lib/lighttpd/cache_etag"
</config>
</example>
</action>
</module>

+ 122
- 0
doc/mod_core.lua.xml View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1" title="mod_core (lua)" order="mod_lua:core">
<short>provides some useful helpers written in lua</short>

<section title="Install">
<textile>
By default distributions (and @make install@) should provide the necessary files; but you can always find them in the "contrib":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib folder:
* @core.lua@
* @core__cached_html.lua@
* @core__xsendfile.lua@

That way you can modify them for your own needs if you have to (although it is recommended to change the names of the files and the actions, so you don't get conflicts).

lighttpd should search for @core.lua@ in the correct (install) locations, so you don't need the absolute path here.
</textile>
</section>

<action name="core.wsgi">
<short>Splits the url into SCRIPT_NAME (the subdirectory a web application is mounted at) and PATH_INFO (the path the web application should route)</short>
<parameter name="prefix">
<short>URL prefix ("subdirectory") the web application is mounted at</short>
</parameter>
<parameter name="action">
<short>action block connecting to the wsgi backend</short>
</parameter>
<description>
<textile>
See "Howto WSGI":https://redmine.lighttpd.net/projects/lighttpd2/wiki/Howto_WSGI for an example.

WSGI applications expect the url to be split into @SCRIPT_NAME@ and @PATH_INFO@ (CGI environment variables); @SCRIPT_NAME@ is their "application root", and @PATH_INFO@ the requsted resource in the application.
By default, lighttpd uses an empty @PATH_INFO@ (unless you used the "pathinfo;" action, but this doesn't help as we're not dealing with static files here).

*Important*: WSGI is an "extension" of CGI; it doesn't specify a transport protocol, you can use it with plain CGI, FastCGI or SCGI (or anything else that supports the basic CGI protocol)
</textile>
</description>

<example>
<description>
<textile>
Example: Trac in @/trac@, listening via FastCGI on @unix:/var/run/lighttpd/trac.socket@.
</textile>
</description>
<config>
setup {
module_load ("mod_lua", "mod_fastcgi");
lua.plugin "core.lua";
}
core.wsgi ( "/trac", { fastcgi "unix:/var/run/lighttpd/trac.socket"; } );
</config>
</example>
</action>

<action name="core.cached_html">
<short>try to find a file for the current url with ".html" suffix, if we couldn't find a static file for the url yet and the url doesn't already have the ".html" suffix.</short>
<example>
<config>
setup {
module_load "mod_lua";
lua.plugin "core.lua";
}
docroot "/some/dynamic/app/public";
core.cached_html;
if physical.is_file {
header.add ("X-cleanurl", "hit");
} else {
header.add ("X-cleanurl", "miss");
fastcgi "/var/run/lighttpd/dynamic-app.sock";
}
</config>
</example>
</action>

<action name="core.xsendfile">
<short>provides a simple X-Sendfile feature; send a "X-Sendfile: /path/to/file" response header from your backend</short>
<parameter name="docroot">
<short>(optional) doc-root the files has be in</short>
</parameter>
<example>
<config>
setup {
module_load ("mod_lua", "mod_fastcgi");
lua.plugin "core.lua";
}
fastcgi "/var/run/lighttpd/dynamic-app.sock";
core.xsendfile "/some/dynamic/app/";
</config>
</example>
</action>

<action name="auth.require_user">
<short>require a specific authenticated user</short>
<parameter name="userlist">
<short>list of usernames to allow</short>
</parameter>
<description>
<textile>
This helper constructs a regular expression to match against request.environment["REMOTE_USER"], so the example below is the same as:

<pre>
auth.plain [ "method" => "basic", "realm" => "test", "file" => "/etc/lighttpd2/test.plain" ];
if req.env["REMOTE_USER"] !~ "^(foo1|foo2)$" { auth.deny; }
</pre>

This action uses lua only to create the action, no lua is executed to handle requests in this case.

Be careful: the empty username matches unauthenticated users.
</textile>
</description>
<example>
<config>
setup {
module_load "mod_lua";
lua.plugin "core.lua";
}
auth.plain [ "method" => "basic", "realm" => "test", "file" => "/etc/lighttpd2/test.plain" ];
auth.require_user ("foo1", "foo2");
</config>
</example>


</action>
</module>

+ 28
- 0
doc/mod_debug.xml View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>
offers various utilities to aid you debug a problem.
</short>

<action name="debug.show_connections">
<short>shows a page similar to the one from mod_status, listing all active connections</short>
<description>
By specifying one or more "connection ids" via querystring (parameter "con"), one can request additional debug output for specific connections.
</description>
<example>
<config>
if req.path == "/debug/connections" { debug.show_connections; }
</config>
</example>
</action>

<action name="debug.profiler_dump">
<short>dumps all allocated memory to the profiler output file if profiling enabled</short>

<description>
<textile>
lighttpd2 needs to be compiled with profiler support, and profiling has to be enabled by setting the environment variable @LIGHTY_PROFILE_MEM@ to the path of a target log file.
</textile>
</description>
</action>
</module>

+ 115
- 0
doc/mod_deflate.xml View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>mod_deflate compresses content on the fly</short>

<action name="deflate">
<short>waits for response headers; if the response can be compressed deflate adds a filter to compress it</short>
<parameter name="options">
<table>
<entry name="encodings">
<short>supported method, depends on whats compiled in (default: "deflate,gzip,bzip2")</short>
</entry>
<entry name="blocksize">
<short>blocksize is the number of kilobytes to compress at one time, it allows the webserver to do other work (network I/O) in between compression (default: 4096)</short>
</entry>
<entry name="output-buffer">
<short>output-buffer is a per connection buffer for compressed output, it can help decrease the response size (fewer chunks to encode). If it is set to zero a shared buffer will be used. (default: 4096)</short>
</entry>
<entry name="compression-level">
<short>0-9: lower numbers means faster compression but results in larger files/output, high numbers might take longer on compression but results in smaller files/output (depending on files ability to be compressed), this option is used for all selected encoding variants (default: 1)</short>
</entry>
</table>
</parameter>
<example>
<config>
deflate [ "encodings" => "deflate,gzip,bzip2", "blocksize" => 4096, "output-buffer" => 4096, "compression-level" => 1 ];
</config>
</example>
<example>
<config>
deflate [ "compression-level" => 6 ];
</config>
</example>
</action>

<option name="deflate.debug">
<short>enable debug output</short>
<default><value>false</value></default>
</option>

<section title="Notes">
<textile>
*Important*: As deflate; waits for the response headers, you must handle the request before it (see below how to check whether the request is handled).
If the request is not handled, you will get a "500 - Internal error" and a message in the error.log.

Does not compress:
* response status: 100, 101, 204, 205, 206, 304
* already compressed content
* if more than one etag response header is sent
* if no common encoding is found

Supported encodings
* gzip, deflate (needs zlib)
* bzip2 (needs bzip2)

* Modifies etag response header (if present)
* Adds "Vary: Accept-Encoding" response header
* Resets Content-Length header
</textile>
</section>

<example title="Simple config" anchor="#">
<config>
setup {
module_load "mod_deflate";
}

# define our own "action"
do_deflate = {
# make sure static files get handled before we try deflate
static;
# we can only wait for response headers if we already have a request handler! (static only handles GET/HEAD requests)
if request.is_handled {
# limit content-types we want to compress -> see mimetypes
if response.header["Content-Type"] =~ "^(.*/javascript|text/.*)(;|$)" {
deflate;
}
}
};

# now add do_deflate; in places where you want deflate. for example at the end of your config:

do_deflate;
</config>
</example>

<example title="Extended config (makes use of mod_cache_disk_etag)" anchor="#">
<config>
setup {
module_load ("mod_deflate", "mod_cache_disk_etag");
}

# define our own "action"
do_deflate = {
# make sure static files get handled before we try deflate
static;
# we can only wait for response headers if we already have a request handler! (static only handles GET/HEAD requests)
if request.is_handled {
# limit content-types we want to compress -> see mimetypes
if response.header["Content-Type"] =~ "^(.*/javascript|text/.*)(;|$)" {
deflate;
# the following block needs mod_cache_disk_etag (and is optional)
# only cache compressed result of static files
if physical.is_file {
cache.disk.etag "/var/cache/lighttpd/cache_etag";
}
}
}
};

# now add do_deflate; in places where you want deflate. for example at the end of your config:

do_deflate;
</config>
</example>
</module>

+ 71
- 0
doc/mod_dirlist.xml View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>lists files inside a directory. The output can be customized in various ways from style via css to excluding certain entries.</short>

<action name="dirlist">
<short>lists file in a directory</short>
<parameter name="options">
<table>
<entry name="css">
<short>string: url to external stylesheet (default: inline internal css)</short>
</entry>
<entry name="hide-dotfiles">
<short>boolean: hide entries beginning with a dot (default: true)</short>
</entry>
<entry name="hide-tildefiles">
<short>boolean: hide entries ending with a tilde (~), often used for backups (default: true)</short>
</entry>
<entry name="hide-directories">
<short>boolean: hide directories from the directory listing (default: false)</short>
</entry>
<entry name="include-header">
<short>boolean: include HEADER.txt above the directory listing (default: false)</short>
</entry>
<entry name="hide-header">
<short>boolean: hide HEADER.txt from the directory listing (default: false)</short>
</entry>
<entry name="encode-header">
<short>boolean: html-encode HEADER.txt (if included), set to false if it contains real HTML (default: true)</short>
</entry>
<entry name="include-readme">
<short>boolean: include README.txt below the directory listing (default: true)</short>
</entry>
<entry name="hide-readme">
<short>boolean: hide README.txt from the directory listing (default: false)</short>
</entry>
<entry name="encode-readme">
<short>boolean: html-encode README.txt (if included), set to false if it contains real HTML (default: true)</short>
</entry>
<entry name="exclude-suffix">
<short>list of strings: hide entries that end with one of the strings supplied (default: empty list)</short>
</entry>
<entry name="exclude-prefix">
<short>list of strings: hide entries that begin with one of the strings supplied (default: empty list)</short>
</entry>
<entry name="debug">
<short>boolean: output debug information to log (default: false)</short>
</entry>
<entry name="content-type">
<short>string: content-type to return in HTTP response headers (default: "text/html; charset=utf-8")</short>
</entry>
</table>
</parameter>

<example>
<description>
shows a directory listing including the content of HEADER.txt above the list and hiding itself from it; also hides all files ending in ".bak"
</description>
<config>
setup {
module_load ("mod_dirlist");
}

if req.path =^ "/files/" {
dirlist ("include-header" => true, "hide-header" => true, "hide->suffix" => (".bak"));
}
</config>
</example>
</action>

</module>


+ 58
- 0
doc/mod_expire.xml View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>add "Expires" and "Cache-Control" headers to the response</short>

<description>
<textile><![CDATA[
This allows you to control client-side caching of responses based on a simple rule/formula.
If a response is cached using an "Expires" and "Cache-Control" header, then the client will not issue a new request for it until the date specified by the header is reached.

Adding expire headers to static content like css, javascript, images or similar, can greatly reduce the amount of requests you get and therefor save resources.
Use "modification" as @<base>@ if your content changes in specific intervals like every 15 minutes.
]]></textile>
</description>

<action name="expire">
<short>adds an "Expires" header to the response</short>
<parameter name="rule">
<short>the rule to calculate the "Expires" header value with</short>
</parameter>
<description>
<textile><![CDATA[
The rule/formula used here, complies with the one mod_expire for Apache uses:

<pre>
<base> [plus] (<num> <type>)+
</pre>

* @<base>@ is one of "access", "now" or "modification"; "now" being equivalent to "access".
* "@plus@" is optional and does nothing.
* @<num>@ is any positive integer.
* @<type>@ is one of "seconds, "minutes", "hours", "days", "weeks", "months" or "years".

The trailing "s" in @<type>@ is optional.

If you use "modification" as @<base>@ and the file does not exist or cannot be accessed, mod_expire will do nothing and request processing will go on.