查看源代码 HTTP 服务器
配置
HTTP 服务器,也称为 httpd,按照 RFC 2616 中的描述处理 HTTP 请求,但有一些例外,例如网关和代理功能。只要底层机制也支持,服务器就支持 IPv6。
服务器实现了许多功能,例如
- 安全套接字层 (SSL)
- Erlang 脚本接口 (ESI)
- 通用网关接口 (CGI)
- 用户身份验证(使用 Mnesia、Dets 或纯文本数据库)
- 通用日志文件格式(支持或不支持 disk_log(3))
- URL 别名
- 操作映射
- 目录列表
服务器的配置以 Erlang 属性列表的形式提供。
从 Inets
5.0 版本开始,HTTP 服务器是一个易于启动/停止和自定义的 Web 服务器,提供了最基本的 Web 服务器功能。Inets 专为嵌入式系统设计,如果您需要功能齐全的 Web 服务器,还有其他 Erlang 开源替代方案。
几乎所有服务器功能都是使用专门设计的服务器 API 实现的,该 API 在 Erlang Web 服务器 API 中进行了描述。此 API 可用于增强核心服务器功能,例如自定义日志记录和身份验证。
以下内容应放入 Erlang 节点应用程序配置文件中,以便在应用程序启动时启动 HTTP 服务器
[{inets, [{services, [{httpd, [{proplist_file,
"/var/tmp/server_root/conf/8888_props.conf"}]},
{httpd, [{proplist_file,
"/var/tmp/server_root/conf/8080_props.conf"}]}]}]}].
服务器使用 Erlang 属性列表进行配置。有关可用属性,请参阅 httpd
。
可用的配置属性如下
httpd_service() -> {httpd, httpd()}
httpd() -> [httpd_config()]
httpd_config() -> {proplist_file, file()}
{debug, debug()} |
{accept_timeout, integer()}
debug() -> disable | [debug_options()]
debug_options() -> {all_functions, modules()} |
{exported_functions, modules()} |
{disable, modules()}
modules() -> [atom()]
这里
{proplist_file, file()}
- 包含 Erlang 属性列表的文件,后跟句点,描述 HTTP 服务器配置。{debug, debug()}
- 可以启用所有函数或选定模块上导出的函数的跟踪。{accept_timeout, integer()}
- 设置服务器建立请求连接所需的超时值。
入门
启动 Inets
1> inets:start().
ok
使用最少的必需配置启动 HTTP 服务器。如果指定端口 0
,则使用任意可用的端口,您可以使用函数 info
来查找选择的端口号
2> {ok, Pid} = inets:start(httpd, [{port, 0}, {server_root,"/tmp"},
.. {document_root,"/tmp/htdocs"}, {bind_address, "localhost"}]).
{ok, 0.79.0}
调用 info
3> httpd:info(Pid).
[{mime_types,[{"html","text/html"},{"htm","text/html"}]},
{server_name,"machine.local"},
{bind_address, {127,0,0,1}},
{server_root,"/tmp"},
{port,59408},
{document_root,"/tmp/htdocs"},
{ipfamily,inet}]
重新加载配置,而无需重新启动服务器
4> httpd:reload_config([{port, 59408},
.. {server_root,"/tmp/www_test"}, {document_root,"/tmp/www_test/htdocs"},
.. {bind_address, "localhost"}], non_disturbing).
ok.
注意
port
和bind_address
无法更改。尝试在重新加载期间访问服务器的客户端会收到服务暂时不可用的响应。
5> httpd:info(Pid, [server_root, document_root]).
[{server_root,"/tmp/www_test"},{document_root,"/tmp/www_test/htdocs"}]
6> ok = inets:stop(httpd, Pid).
替代方案
6> ok = inets:stop(httpd, {{127,0,0,1}, 59408}).
请注意,bind_address
必须是函数 info
报告的 IP 地址,而不能是输入 bind_address
时允许的主机名。
动态网页
Inets
HTTP 服务器提供了两种创建动态网页的方法,每种方法都有其自身的优点和缺点
CGI 脚本 - 通用网关接口 (CGI) 脚本可以用任何编程语言编写。CGI 脚本是标准化的,并且大多数 Web 服务器都支持它们。CGI 脚本的缺点是由于其设计而导致资源密集型。CGI 要求服务器为需要启动的每个可执行文件派生一个新的 OS 进程。
ESI 函数 - Erlang 服务器接口 (ESI) 函数提供了一个与 Erlang 函数执行的紧密高效的接口。另一方面,此接口是
Inets
特有的。
CGI 版本 1.1,RFC 3875
模块 mod_cgi
允许在服务器上执行 CGI 脚本。与 ScriptAlias 配置指令的定义匹配的文件被视为 CGI 脚本。CGI 脚本由服务器执行,其输出返回给客户端。
CGI 脚本响应包括消息头和消息体,两者之间用空行分隔。消息头包含一个或多个头字段。消息体可以为空。
示例
"Content-Type:text/plain\nAccept-Ranges:none\n\nsome very
plain text"
服务器解释消息头,并且大多数消息头被转换为 HTTP 头,并与消息体一起发送回客户端。
对 CGI-1.1 的支持是根据 RFC 3875 实现的。
ESI
Erlang 服务器接口由模块 mod_esi
实现。
ERL 方案
erl 方案旨在模仿普通的 CGI,但没有额外的开销。调用 Erlang erl
函数的 URL 具有以下语法(正则表达式)
http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo)
*** 取决于 ErlScriptAlias 配置指令的使用方式。
所引用的模块 Module
必须在代码路径中找到,并且它必须定义一个 arity 为二或三的函数 Function
。最好实现一个 arity 为三的函数,因为它允许在生成阶段将网页块发送给客户端,而不是先生成整个网页然后将其发送给客户端。保留实现 arity 为二的函数选项仅出于向后兼容性原因。有关 ESI 回调函数的实现细节,请参阅 mod_esi
。
日志记录
支持三种类型的日志:传输日志、安全日志和错误日志。事实上的标准通用日志文件格式用于传输和安全日志记录。有许多统计程序可用于分析通用日志文件格式。通用日志文件格式如下所示
remotehost rfc931 authuser [date] "request" status bytes
这里
remotehost - 远程主机名。
rfc931 - 客户端远程用户名 (RFC 931)。
authuser - 用于身份验证的用户名。
[date] - 请求的日期和时间 (RFC 1123)。
"request" - 请求行与来自客户端的完全一致 (RFC 1945)。
status - 返回给客户端的 HTTP 状态代码 (RFC 1945)。
bytes - 传输的文档的内容长度。
内部服务器错误记录在错误日志文件中。此文件的格式比使用通用日志文件格式的日志更随意,但符合以下语法
[date] 对 path 的访问对 remotehost 失败,原因:reason
Erlang Web 服务器 API
处理 HTTP 请求的过程涉及多个步骤,例如
- 建立连接,发送和接收数据。
- URI 到文件名转换。
- 身份验证/访问检查。
- 检索/生成响应。
- 日志记录。
为了提供 HTTP 服务器请求处理的自定义和可扩展性,大多数步骤由一个或多个模块处理。这些模块可以在运行时替换或删除,并且可以添加新的模块。对于每个请求,所有模块都按照服务器配置文件中模块指令指定的顺序遍历。某些部分,主要是与通信相关的步骤,被认为是服务器核心功能,并且未使用 Erlang Web 服务器 API 实现。有关 Erlang Web 服务器 API 实现的功能的描述,请参阅 “Inets Web 服务器模块”部分。
一个模块可以使用 Erlang Web 服务器 API 模块序列中以前的模块生成的数据,或者生成供连续的 Erlang Web 服务器 API 模块使用的数据。这归功于称为交互数据的内部键值元组列表。
注意
交互数据强制执行模块依赖关系,如果可能,应避免使用。这意味着模块属性中模块的顺序非常重要。
API 描述
每个使用 Erlang Web 服务器 API 实现服务器功能的模块都应实现以下回调函数
do/1
(强制性) - 当要处理请求时调用的函数load/2
store/2
remove/1
只有当要引入新的配置指令时才需要后面的函数。有关详细信息,请参阅 httpd
。
Inets Web 服务器模块
约定是所有实现某些 Web 服务器功能的模块都具有名称 mod_*
。配置 Web 服务器时,这些模块的适当选择应存在于模块指令中。请注意,有一些交互依赖关系需要考虑,因此模块的顺序不能是随机的。
mod_action - 基于文件类型/方法的脚本执行
每当请求特定类型的文件或 HTTP 方法(请参阅 RFC 1945)时,此模块都会运行 CGI 脚本。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
。
如果可能,导出以下 Erlang Web 服务器 API 交互数据
{new_request_uri, RequestURI}
- 生成了替代的RequestURI
。
mod_alias - URL 别名
mod_alias
模块可以使主机文件系统的不同部分映射到文档树中,即创建别名和重定向。
如果可能,导出以下 Erlang Web 服务器 API 交互数据
{real_name, PathData}
-PathData
是用于 API 函数mod_alias:path/3
的参数。
mod_auth - 用户身份验证
mod_auth
模块提供了使用文本文件、Dets 数据库以及 Mnesia 数据库进行基本用户身份验证的功能。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
导出以下 Erlang Web 服务器 API 交互数据
{remote_user, User}
- 用于身份验证的用户名。
Mnesia 作为身份验证数据库
如果使用 Mnesia 作为存储方法,则必须在启动 HTTP 服务器之前启动 Mnesia。首次启动 Mnesia 时,必须先创建模式和表,然后再启动 Mnesia。此处提供了一个简单的模块示例,其中包含两个用于创建和启动 Mnesia 的函数。函数 first_start/0
用于首次启动。它创建模式和表。start/0
用于连续启动。start/0
启动 Mnesia 并等待表初始化。此函数只能在已创建模式和表时使用。
-module(mnesia_test).
-export([start/0,load_data/0]).
-include_lib("mod_auth.hrl").
first_start() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(httpd_user,
[{type, bag},
{disc_copies, [node()]},
{attributes, record_info(fields,
httpd_user)}]),
mnesia:create_table(httpd_group,
[{type, bag},
{disc_copies, [node()]},
{attributes, record_info(fields,
httpd_group)}]),
mnesia:wait_for_tables([httpd_user, httpd_group], 60000).
start() ->
mnesia:start(),
mnesia:wait_for_tables([httpd_user, httpd_group], 60000).
要创建 Mnesia 表,我们使用在 mod_auth.hrl
中定义的两个记录,因此必须包含该文件。first_start/0
创建一个模式,指定数据库将驻留在哪些节点上。然后它启动 Mnesia 并创建表。第一个参数是表的名称,第二个参数是有关如何创建表的选项列表,请参阅 mnesia
文档以获取更多信息。由于 mod_auth_mnesia
的实现为每个用户保存一行,因此类型必须为 bag
。创建模式和表后,使用函数 mnesia:start/0
启动 Mnesia 并等待表加载。如果指定了,Mnesia 将使用启动时指定的 mnesia_dir
目录,否则 Mnesia 将使用当前目录。出于安全原因,请确保 Mnesia 表存储在 HTTP 服务器的文档树之外。如果它们放置在它保护的目录中,客户端可以下载这些表。只有 Dets 和 Mnesia 存储方法允许将动态用户数据写入磁盘。plain
是一种只读方法。
mod_cgi - CGI 脚本
此模块处理 CGI 脚本的调用。
mod_dir - 目录
如果客户端发送的是目录而不是文件的请求,则此模块会生成 HTML 目录列表(Apache 样式)。如果不需要目录列表,则必须从 Modules 配置指令中删除此模块。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
导出以下 Erlang Web 服务器 API 交互数据
{mime_type, MimeType}
- 输入 URL 的文件后缀映射到MimeType
。
mod_disk_log - 使用 Disk_Log 进行日志记录。
使用“通用日志文件格式”和 disk_log
进行标准日志记录。
使用以下 Erlang Web 服务器 API 交互数据
remote_user
- 来自mod_auth
mod_esi - Erlang 服务器接口
mod_esi
模块实现了 Erlang 服务器接口 (ESI),为执行 Erlang 函数提供了紧密而高效的接口。
使用以下 Erlang Web 服务器 API 交互数据
remote_user
- 来自mod_auth
导出以下 Erlang Web 服务器 API 交互数据
{mime_type, MimeType}
- 输入 URL 的文件后缀映射到MimeType
mod_get - 常规 GET 请求
此模块负责处理对常规文件的 GET 请求。对文件部分内容的 GET 请求由 mod_range
处理。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
mod_head - 常规 HEAD 请求
此模块负责处理对常规文件的 HEAD 请求。对动态内容的 HEAD 请求由负责动态内容的每个模块处理。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
mod_log - 使用文本文件进行日志记录。
使用“通用日志文件格式”和文本文件进行标准日志记录。
使用以下 Erlang Web 服务器 API 交互数据
remote_user
- 来自mod_auth
mod_range - 带有 Range 标头的请求
此模块响应对文件的一个或多个范围的请求。这在下载大型文件时尤其有用,因为可以恢复中断的下载。
请注意,对文档的多个部分的请求会向日志文件报告零大小。
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
mod_response_control - 带有 If* 标头的请求
此模块控制请求中的条件是否得到满足。例如,请求可以指定,如果自上次检索以来内容未更改,则响应才是感兴趣的。如果内容已更改,则范围请求将转换为对整个文件的请求。
如果客户端发送多个限制服务器响应权限的标头字段,则标准未指定如何处理。 httpd
按以下顺序控制每个字段,如果其中一个字段与当前状态不匹配,则会拒绝该请求并给出适当的响应
If-modified
If-Unmodified
If-Match
If-Nomatch
使用以下 Erlang Web 服务器 API 交互数据
real_name
- 来自mod_alias
导出以下 Erlang Web 服务器 API 交互数据
{if_range, send_file}
- 未满足范围请求的条件。响应不得被视为范围请求,而必须被视为普通的 GET 请求。
mod_security - 安全过滤器
mod_security
模块用作 mod_auth
中处理的已验证请求的过滤器。它提供了在用户多次未能通过身份验证时,限制用户在指定时间内访问的可能性。它会记录失败的身份验证以及对用户的阻止,并且当事件发生时,它会调用可配置的回调模块。
还有一个 API 可以手动阻止或取消阻止用户。此 API 还可以列出被阻止的用户或在可配置的时间内通过身份验证的用户。
mod_trace - TRACE 请求
mod_trace
负责处理 TRACE 请求。Trace 是 HTTP/1.1 中的一种新请求方法。trace 请求的预期用途是用于测试。trace 响应的主体是响应 Web 服务器或代理接收到的请求消息。
从命令行提供文件
httpd 包括从命令行快速提供文件的功能。最简单的形式是,erl -S httpd
将在 localhost 上提供本地目录中的文件。
--port
- 设置要绑定的端口。默认为8000
。--bind
- 设置要侦听的绑定地址。默认为127.0.0.1
。DIRECTORY - 设置要从中提供数据的目录。默认为当前目录。
例如,要从端口 4000
上的目录 test_results
提供文件
erl -S httpd serve --port 4000 test_results
有关所有选项的完整参考,请运行 erl -S httpd serve --help
。