5. HTTP server
5 HTTP 服务器
5.1 配置
HTTP 服务器(也称为 httpd)处理 HTTP请求,如RFC 2616
少数例外所述,例如网关和代理功能。只要基础机制也这样做,服务器就支持 IPv6。
该服务器实现了许多功能,例如:
- 安全套接字层(SSL)
- Erlang 脚本接口(ESI)
- 通用网关接口(CGI)
- 用户认证(使用 Mnesia,Dets 或纯文本数据库)
- 通用日志文件格式(带或不带 disk_log(3)支持)
- URL 混叠
- 行动映射
- 目录列表
服务器的配置以 Erlang 属性列表的形式提供。为了向后兼容,支持使用 apache 样式配置指令的配置文件。
截至Inets
5.0 HTTP 服务器是一个易于启动/停止和自定义的 Web 服务器,提供了最基本的 Web 服务器功能。Inet 是为嵌入式系统设计的,如果您想要一台功能齐全的 Web 服务器,还有其他 Erlang 开源的替代方案。
几乎所有服务器功能都是使用精心编制的服务器 API 实现的,ErlangWebServerAPI 对此进行了描述。此 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(3)
.为了向后兼容,还支持类似 Apache 的配置文件。
可用的配置属性如下:
httpd_service() -> {httpd, httpd()}
httpd() -> [httpd_config()]
httpd_config() -> {file, file()} |
{proplist_file, file()}
{debug, debug()} |
{accept_timeout, integer()}
debug() -> disable | [debug_options()]
debug_options() -> {all_functions, modules()} |
{exported_functions, modules()} |
{disable, modules()}
modules() -> [atom()]
在此:
{file, file()}
如果您使用一个类似于速度的旧配置文件。
{proplist_file, file()}
包含 Erlang 属性列表的文件,后面是句号,描述 HTTP 服务器配置。
{debug, debug()}
可以对所有函数启用跟踪,或仅在选定模块上启用导出函数。
{accept_timeout, integer()}
设置服务器设置请求连接所需的超时值.
5.2 开始
启动Inets
*
1 > inets:start().
ok
以最低要求的配置启动 HTTP 服务器。如果指定端口0
,则使用任意可用端口,您可以使用函数。info
若要查找所选择的端口号,请执行以下操作:
2 > {ok, Pid} = inets:start(httpd, [{port, 0},
{server_name,"httpd_test"}, {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,"httpd_test"},
{bind_address, {127,0,0,1}},
{server_root,"/tmp"},
{port,59408},
{document_root,"/tmp/htdocs"}]
在不重新启动服务器的情况下重新加载配置:
4 > httpd:reload_config([{port, 59408},
{server_name,"httpd_test"}, {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
必须是由函数报告的 IP 地址,info
并且不能是投入时允许的主机名bind_address
。
5.3 HtAccess-用户配置身份验证
没有服务器管理权限的 web 服务器用户需要管理对其用户本地的 web 页的身份验证,可以使用每个目录运行时可配置的用户身份验证方案。htaccess
它的工作如下:
- 搜索所请求资产路径中的每个目录以查找访问文件(默认为
.htaccess
),这会限制 Web 服务器对请求作出响应的权限。如果找到访问文件,则该文件中的规则将应用于该请求。
- 访问文件中的规则适用于同一目录和子目录中的文件。如果在资产路径中存在多个访问文件,则应用与请求资产最近的访问文件中的规则。
- 若要更改限制使用资产的规则,用户只需对资产所在的目录进行写访问。
- 每个请求都读取请求资产路径中的所有访问文件。这意味着服务器上的负载在
htaccess
被使用了。
- 如果目录受到 HTTP 服务器配置文件中的身份验证指令和
htaccess
文件,必须允许用户通过两种方法访问该文件,才能使请求成功。
访问文件指令
在每个目录下DocumentRoot
或在Alias
用户可以放置访问文件。访问文件是一个纯文本文件,它指定 Web 服务器响应请求之前要考虑的限制。如果请求资产的路径中有多个访问文件,则使用与资产最近的目录中的访问文件中的指令。
“允许”
语法:
Allow
来自子网subnet | from all
默认:
from all
同指令allow
用于服务器配置文件。
“AllowOverride”
语法:
AllowOverRideall | none | Directives
默认:
none
AllowOverRide
指定不允许访问子目录中的文件的参数更改其值。如果参数设置为none
,将不再解析访问文件。
如果只有一个访问文件,则将此参数设置为none
当服务器停止寻找访问文件时,可以减轻服务器的负担。
“AuthGroupfile”
语法:
AuthGroupFile
文件名
默认:
none
AuthGroupFile
指示包含组列表的文件。文件名必须包含文件的绝对路径。文件的格式是每一行一个组,每一行都包含组和组成员的名称,用空格分隔,例如:
GroupName: Member1 Member2 .... MemberN
“AuthName”
语法:
AuthName
Auth域
默认:
none
同指令AuthName
用于服务器配置文件。
“授权类型”
语法:
AuthTypeBasic
默认:
Basic
AuthType
指定要使用的身份验证方案。只实现了使用密码和用户 ID 的 UU 编码进行基本身份验证。
“AuthUserFile”
语法:
AuthUserFile
文件名
默认:
none
AuthUserFile
指示包含用户列表的文件。文件名必须包含文件的绝对路径。用户名和密码没有加密,所以不要将文件与用户放在一个可通过 Web 服务器访问的目录中。文件的格式为每一行一个用户。每一行包含UserName
和Password
例如,用冒号分隔:
UserName:Password
UserName:Password
“否认”
语法:
deny
来自子网subnet | from all
背景:
极限
同指令deny
用于服务器配置文件。
“限制”
语法:<Limit查询方法>
默认:
none
<Limit>和</Limit>用于封装一组仅适用于使用指定方法的请求的指令。如果未指定请求方法,则所有请求方法都将根据限制进行验证。
示例:
<Limit POST GET HEAD>
order allow deny
require group group1
allow from 123.145.244.5
</Limit>
“命令”
语法:
orderallow deny | deny allow
默认:
allow deny
order
定义是否首先执行拒绝或允许控制。
如果订单设置为allow deny
,则首先将用户网络地址控制在允许子集中。如果用户网络地址不在允许的子集中,则拒绝用户获取资产。如果网络地址在允许的子集中,则执行第二个控制。也就是说,用户网络地址不在由参数指定的要拒绝的网络地址的子集中deny
。
如果订单设置为deny allow
,则只有指定位于允许子集中的网络的用户才能成功请求限制区域中的资产。
“要求”
语法:
requiregroup group1 group2... | user user1 user2...
默认:
none
上下文:
限制
了解更多信息,请参阅指令在mod_auth(3)
中require
。
5.4 动态网页
Inets
HTTP 服务器提供了两种创建动态网页的方式,每种方式都有其优点和缺点:
CGI 脚本
通用网关接口(CGI)脚本可以用任何编程语言编写。CGI 脚本是由大多数 Web 服务器标准化和支持的。CGI 脚本的缺点是由于它们的设计,它们是资源密集型的。CGI 要求服务器为每个需要启动的可执行文件分配一个新的操作系统进程。
ESI-函数
Erlang 服务器接口(ESI)函数为执行 Erlang 函数提供了一个紧密有效的接口。另一方面,这个接口是Inets
特定的。
CGI 版本 1.1,RFC 3875
模块mod_cgi
可以CGI scripts
在服务器上执行。匹配 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
被引用的模块必须在代码路径中找到,并且它必须定义一个Function
二元或三元的函数。最好使用 arity 3实现一个函数,因为它允许在生成阶段将网页的块发送给客户端,而不是首先生成整个网页,然后将其发送给客户端。只有出于向后兼容性的原因才能保留用 arity 2实现函数的选项。有关 ESI 回调函数的实现细节,请参阅mod_esi(3)
。
EVAL 计划
评估方案非常简单,并不模仿普通 CGI 的行为。调用 Erlang eval
函数的 URL 具有以下语法:
http://your.server.org/***/Mod:Func(Arg1,...,ArgN)
***取决于如何使用ErlScriptAlias配置指令。
必须在代码路径中找到引用的Mod
模块,并将由该函数返回的数据Func
传回客户端。从函数返回的数据必须采用 CGI 规范中指定的格式。有关 ESI 回调函数的实现细节,请参阅mod_esi(3)
。
注意
评估方案会严重威胁安装 Web 服务器的 Erlang 节点的完整性,例如:
http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[])))
这有效地关闭了 Erlang 节点。因此,请使用 erl 方案,直到此安全漏洞被修复。
今天没有很好的方法来解决这个问题,因此可以在将来的版本中删除评估方案Inets
。
5.5 记录
支持三种类型的日志:传输日志,安全日志和错误日志。事实上的标准通用日志文件格式用于传输和安全日志记录。有许多统计程序可用于分析通用日志文件格式。通用日志文件格式如下所示:
remotehost rfc931 authuser 日期“请求”状态字节
这里:
远程主机
的远程主机
名。rfc931
客户端远程用户名(RFC 931
)。authuser
用于认证的用户名。日期
请求的日期
和时间(RFC 1123
)。“请求”
请求行完全来自客户端(RFC 1945
)。状态
返回给客户端(RFC 1945
)的HTTP状态
码。字节
传输文档的内容长度。
内部服务器错误记录在错误日志文件中。该文件的格式与使用通用日志文件格式的日志相比是一种更无计划的格式,但符合以下语法:
日期
访问路径
失败的远程主机
,原因
:原因
5.6 Erlang Web 服务器 API
处理 HTTP 请求的过程涉及多个步骤,例如:
- 建立连接,发送和接收数据。
- URI 到文件名翻译。
- 认证/访问检查。
- 检索/生成响应。
- 日志记录。
为了提供 HTTP 服务器的请求处理的自定义和可扩展性,大多数这些步骤都由一个或多个模块处理。这些模块可以在运行时替换或删除,并且可以添加新模块。对于每个请求,所有模块都按照服务器配置文件中的模块伪指令指定的顺序遍历。一些部分,主要是与通信相关的步骤,被视为服务器核心功能,并未使用 Erlang Web 服务器 API 实现。在 Erlang 网络服务器 API 中实现的功能描述在下面描述Section Inets Web Server Modules
。
模块可以使用 Erlang 网络服务器 API 模块序列中先前模块生成的数据,或生成连续的 Erlang Web 服务器 API 模块使用的数据。这可能是由于键值元组的内部列表,称为交互数据。
注意
交互数据强制执行模块依赖关系,并且如果可能的话应该避免。这意味着模块属性中的模块顺序非常重要。
API 描述
使用 Erlang Web 服务器 API 实现服务器功能的每个模块都将实现以下回调函数:
do/1
(强制) - 当要处理请求时调用的函数
load/2
store/2
remove/1
只有在引入新的配置指令时才需要后者的功能。有关详情,请参阅httpd(3)
。
5.7 Inets Web 服务器模块
约定是所有实现一些Web服务器功能的模块都有其名称mod_*
。在配置网络服务器时,这些模块的适当选择将出现在模块指令中。请注意,需要考虑一些交互依赖关系,因此模块的顺序不能是随机的。
mod_action - 文件类型/基于方法的脚本执行
无论何时RFC 1945
请求某种类型的文件或 HTTP 方法(请参阅参考资料),此模块都会运行 CGI 脚本。
使用以下 Erlang Web Server API 交互数据:
real_namemod_alias
如果可能,请导出以下 Erlang Web 服务器 API 交互数据:已生成{new_request_uri, RequestURI}
替代方案 .mod_alias
RequestURI
- URL 别名该mod_alias
模块可以将主机文件系统的不同部分映射到文档树中,即创建别名和重定向。如果可能的话,导出以下 Erlang Web 服务器 API 交互数据:是用于 API 函数的参数 .mod_auth - 用户认证模块提供基本的用户认证,使用文本文件,Dets 数据库以及 Mnesia 数据库。使用以下 Erlang Web 服务器 API 交互数据:{real_name, PathData}PathDatamod_alias:path/3mod_auth(3)
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(3)
文档以获取更多信息。由于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 config 伪指令中删除此模块。
使用以下 Erlang Web Server API 交互数据:
real_name
- 从mod_alias
导出以下 Erlang Web Server API 交互数据:{mime_type, MimeType}
传入 URL 的文件后缀映射到MimeType
.mod_disk_log - 使用 Disk_Log.Standard 记录日志使用“Common Logfile Format”和disk_log(3)
。使用以 下Erlang Web Server API 交互数据:
remote_user
-来自mod_auth
mod_esi - Erlang 服务器接口
mod_esi(3)
模块实现了 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 Server API 交互数据:
real_name
- 来自mod_alias
mod_htaccess - 用户可配置访问此模块为每个目录提供用户可配置的访问控制。使用以下 Erlang Web 服务器 API 交互数据:
real_name
-来自mod_alias
导出下列 ErlangWebServerAPI 交互数据:
{remote_user_name, User}
用于身份验证的用户名。
mod_log - 使用文本文件记录。
使用“通用日志文件格式”和文本文件进行标准日志记录。
使用下列 ErlangWebServerAPI 交互数据:
remote_user
- 来自mod_auth
mod_range - 具有范围标题的请求此模块响应对文件的一个或多个范围的请求。这在下载大文件时特别有用,因为可以恢复中断的下载。注意请求文档的多个部分向日志文件报告零大小。使用以下 Erlang Web Server API 交互数据:
real_name
-来自mod_alias
mod_response_control - 带有 If *标题的请求
该模块控制满足请求中的条件。例如,如果内容自上次检索以来未更改,则请求可以指定答案只是感兴趣的。如果内容发生变化,范围请求将被转换为整个文件的请求。
如果客户端发送多个标题字段来限制服务器正确响应,则标准不会指定如何处理。httpd(3)
按照以下顺序控制每个字段,如果其中一个字段与当前状态不匹配,请求将被拒绝并显示正确的响应:
If-modified
If-Unmodified
If-Match
If-Nomatch
使用以下 Erlang Web Server API 交互数据:
real_name
- 从mod_alias
导出以下 Erlang Web 服务器 API 交互数据:
{if_range, send_file}
范围请求的条件未满足。响应不能被视为范围请求,而必须视为普通的获取请求。
mod_security - 安全过滤器
mod_security
模块充当过滤器,用于处理在其中处理的已验证请求mod_auth(3)
。如果用户多次验证失败,它可以限制用户访问指定的时间。它记录失败的身份验证以及阻止用户,并在事件发生时调用可配置的回调模块。
还有一个 API 可以手动阻止或取消阻止用户。此 API 还可以列出被阻止的用户或已在可配置的时间内进行身份验证的用户。
mod_trace - TRACE 请求
mod_trace
负责处理 TRACE 请求。Trace 是 HTTP / 1.1中的一种新的请求方法。跟踪请求的预期用途是用于测试。跟踪响应的主体是响应的 Web 服务器或代理收到的请求消息。