主配置

每个Web服务器的核心都是配置文件。它控制着整个行为,因此必须强大,但同时又要足够简单,以便轻松获得所需的结果。
在lighttpd配置中,你控制它如何响应请求。为了实现这一点,你必须表达某种逻辑——就像在编程语言中一样。

基本语法

lighttpd 2.0 配置文件的语法与各种编程语言有些相似——有点像混合体。但别害怕,它真的非常简单。没有涉及指针:)
基本构成块是变量函数调用条件

布尔值

有两种布尔值

  • true (真)
  • false (假)

整数

整数存储为64位有符号整数(最大值约为 9 * 10^18)。有三种基本使用方式

  • 十进制整数:以任何非零数字开头,例如 128
  • 八进制整数:以零开头,例如 0644
  • 十六进制整数:以 0x 开头,例如 0xff

这三种类型都可以带有一个后缀,表示数字乘以的因子

  • 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

对于大小,基本单位是字节;对于时间间隔,基本单位是秒。

字符串

有4种指定字符串的方式

  • 'hello world'
  • "hello world"
  • e'hello world'
  • e"hello world"

基本转义规则对于这两种方式都是相同的

  • "\n" 是换行符,"\r" 是回车符,"\t" 是制表符
  • "\\" 是一个 \"\"" 是一个双引号 ""\'" 是一个单引号 '
  • 如果符号不用于终止字符串,则单引号/双引号的转义是可选的,即 '\"' = '"'"\'" = "'"
  • "\xNN":NN 必须是十六进制字符,字符串将被替换为解码后的8位值,作为一个单字节
  • 所有其他 \ 的出现都**不会**从字符串中移除。

e'..'e"..." 变体不允许最后一种情况的任何出现。

允许所有其他字符(字符串被解析为8位二进制;lighttpd2 通常不关心编码)。

列表

列表是其他值的有序集合

  • [1, true, "foo"] (简单列表)
  • [] (空列表)
  • [1] (包含一个元素的列表)
  • [[1,2],[3,4],5] (嵌套列表)
  • [1,2,] (最后一个值也可以有一个末尾的 ,)
  • (1, 2, 3) (包含多个元素的替代括号)

请注意,(1) 不是一个列表;括号也可以用于分组表达式,例如 1*(2+3),因此建议只使用 [] 来指定列表。

键值对列表

键值对列表将键与值关联起来(两者都可以是任何类型);语法是

  • [ "a" => 1, "b" => 10 ]

与普通列表一样,最后一个值也可以有一个末尾的 ,。你不能在一个列表中混合简单值和键值关联(嵌套它们可以)。

key => value 运算符也只是 [key, value] 列表的语法糖,上面的例子与以下相同

  • [ [a, 1], [b, 10] ]

这尤其意味着键值对列表也是有序的,尽管它们在某些函数调用内部可以被转换为哈希表。

表达式

有一些运算符可用于某些值类型

  • 对于整数:+, -, */
  • 对于字符串:+ (连接两个字符串)
  • 对于列表:+ (追加列表)

此外,你可以将字符串和布尔值转换为整数,并将任何值转换为字符串

  • cast(int) "256" (只支持十进制表示,不带上面那样的后缀)
  • cast(int) true (true 映射到 1false 映射到 0)
  • cast(string) 5

表达式可以分组以覆盖默认的结合性

  • 3 * (1 + 2) 对比 3 * 1 + 2 等等

动作块

动作块由一系列函数调用(在动作上下文中)和条件语句组成;它被视为一个值,即可以赋值给变量并用作函数调用的参数。

动作块还可以包含变量赋值和设置块/函数调用,这些不属于“动作块值”本身,而是在解析配置时进行评估。

语法是

{ log "hello world"; if req.path =$ ".hidden" { static; } }

完整的配置也是一个动作块(但没有周围的大括号);条件语句也使用动作块作为其分支。

每个不是条件分支的动作块都会启动一个新的嵌套变量作用域;

变量

变量名以字母字符(a-zA-Z)或下划线 _ 开头,后跟字母数字字符、下划线 _ 和点 .;关键字不允许作为变量名。

变量存储值,并且变量名可以代替实际值使用;后来对变量的修改对之前的用途没有影响(即,只在**解析**配置时进行评估)。

变量使用 = 赋值(并以 ; 终止)

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

默认情况下,变量赋值会覆盖现有变量(在其先前的作用域中),如果变量不存在,则在局部作用域中创建一个新变量(即,它将仅在当前作用域和嵌套后代中可用)。

你可以通过在赋值前加上 local 前缀,在局部作用域中显式创建一个新变量(隐藏父作用域中同名的变量)

local wwwpath = "/var/www/example.com";

你也可以通过在赋值前加上 global 前缀,在全局作用域中创建变量。
主配置已经在一个嵌套作用域中(即**不是**全局作用域)。全局作用域在配置加载后不会被销毁,并且可以在延迟配置加载(例如将来从 SQL 加载)中使用。

如果在某个上下文中使用变量名,它将始终使用最近作用域中的定义。

示例

此示例说明了变量在解析配置时进行评估。

foo = "bar";
if req.path == "/somepath" {
	foo = "baz";
}

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

示例

此示例说明了作用域。

foo = "bar";
php = {
	local foo = "baz";
	# in this block (and nested blocks within) foo is now "baz"
	# ...
};
# foo is now "bar" again

特殊变量

sys.* 变量是只读的。目前以下 sys.* 变量可用

  • sys.pid: lighttpd 的进程ID
  • sys.cwd: 当前工作目录
  • sys.env.X: 名称为 X 的系统环境变量 (对于任何 X)

函数调用

有三种类型的函数调用

  • 动作
  • 设置
  • 选项

动作只能在动作(块)上下文中使用,设置只能在设置(块)上下文中使用,而选项可以在两者中使用。

设置上下文以关键字 setup 启动,后跟单个设置函数调用或一个设置块。

设置函数调用在它们出现时立即运行(它们用于“设置”Web服务器环境,例如监听TCP套接字和设置默认选项),而动作函数调用为每个请求运行(将请求URL映射到物理路径、处理请求、修改默认选项)。

动作、设置和选项由模块提供。

包含文件

包含文件在语法上类似于函数调用,但由配置解析器直接处理。它们只允许在动作上下文中使用,因为它们在其使用位置插入对动作块的引用。

有三种类型的包含

  • include "/etc/lighttpd/vhosts/*.conf";: 包含文件,就像主配置本身一样;路径可以包含通配符
  • include_shell "/etc/lighttpd/config_generator.sh";: 运行指定的命令,并将其输出解析为配置文件
  • include_lua "/etc/lighttpd/complex.lua": 包含一个 Lua 配置文件。要执行的单个动作必须在全局 actions 变量中返回(或留空以不执行任何操作)。另请参阅 lua.handler

包含文件也会创建一个新的嵌套作用域。

调试打印

与包含文件类似,__print 是一个特殊函数,但可在动作和设置上下文中使用。它以“debug”日志级别记录其参数的字符串值。

条件

条件(来自Lighttpd 1.x)等同于大多数编程语言中的“if”语句。也有“else”和“elseif”的等价物。
它们通过对条件变量和在运行时(针对每个请求)评估的值进行比较来创建。
条件可以嵌套,你可以使用 andor 运算符将它们分组。and 的绑定比 or 强,尽管更推荐使用括号将它们分组。

示例

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";
		}
	}
}

语法

基本语法形式是

  • if <expr> { ... }
  • if <expr> { ... } else { ... }
  • if <expr> { ... } else if <expr2> { ... }
  • if <expr> { ... } else if <expr2> { ... } ... (继续使用 elseelse if)

条件表达式 <expr>

  • (<expr>)
  • <expr1> and <expr2>
  • <expr1> or <expr1> (<expr1> and <expr2> or <expr3> = (<expr1> and <expr2>) or <expr3>; and 优先级更高)
  • <condvar> <op> <value>,其中 <condvar> 是条件变量(类型与布尔值不同),<op> 是条件运算符,<value> 是字符串或数字。
  • <condvar>!<condvar> 用于布尔条件变量。

条件变量

有三类条件变量

  • request.xyz (可缩写为 req.xyz)
  • physical.xyz (可缩写为 phys.xyz)
  • response.xyz (可缩写为 resp.xyz)
变量 描述
request.localip 客户端连接的监听套接字的 IP 地址(Unix 套接字的文件名)
request.localport 监听套接字的端口号,Unix 套接字为 -1
request.remoteip 客户端的 IP 地址
request.remoteport 客户端的端口号,Unix 套接字为 -1
request.path 请求 URL 的*路径*部分。不包括查询字符串。
request.raw_path 请求 URL 的原始*路径*(未 URL 解码,未简化),包括查询字符串。
request.host 请求的主机名
request.scheme 请求的方案。“http” 或 “https”
request.query 请求 URL 的*查询字符串*
request.method 请求的方法。“GET”、“POST”、“HEAD” 等。
request.length 整数。例如 POST 方法的内容长度
request.header[“name”] 请求头 *name*,例如 request.header[“referer”]
request.is_handled 布尔条件,指示请求是否已有处理程序(static, fastcgi..)
request.environment[“name”] (或简写 request.env[“name”])CGI 环境变量
physical.path 要服务的文件的物理路径。例如文档根目录 + 路径
physical.exists 布尔条件,指示请求的文件(普通文件、目录甚至特殊文件)是否存在
physical.size 整数。请求文件的大小。如果文件不存在则为 -1
physical.is_dir 布尔条件,指示请求的文件是否是目录
physical.is_file 布尔条件,指示请求的文件是否是普通文件(例如,不是 Unix 套接字等)
physical.docroot 文档根目录
physical.pathinfo 路径信息
response.status 响应状态码(阻止请求直到响应头可用)
response.header[“name”] 响应头(阻止请求直到响应头可用)

条件运算符

操作符 描述 操作符 描述
== 比较两个值是否相等 != 不等于
<= 小于或等于 < 小于
>= 大于或等于 > 大于
=~ 正则表达式匹配 !~ 正则表达式不匹配
=^ 前缀匹配 !^ 前缀不匹配
=$ 后缀匹配 !$ 后缀不匹配
=/ CIDR 匹配 !/ CIDR 不匹配