parent
3cd06f49ad
commit
f482877825
@ -0,0 +1 @@
|
||||
*.html
|
@ -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
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
File diff suppressed because one or more lines are too long
@ -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>
|
@ -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>
|
@ -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 < 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>
|