源码路径:
https://sqlite.org/althttpd/dir?ci=tip (推荐)
https://github.com/jesrui/althttpd/tree/master (旧版本)
althttpd.md文件翻译
https://sqlite.org/althttpd/file?name=althttpd.md&ci=tip
althttpd简介:
轻量级 web 服务器,设计宗旨是追求简洁、安全和低资源占用。
设计理念 Design Philosophy
设计理念
Althttpd通常是通过xinetd、systemd或类似的工具启动的。针对每个传入的连接启动一个单独的进程,该进程完全专注于为该连接提供服务。单个althttpd进程将处理同一连接上的一个或多个HTTP请求。当连接关闭时,althttpd进程退出。
Althttpd还可以独立运行。Althttpd本身监听端口80以接收传入的HTTP请求(或端口443以接收传入的HTTPS请求),然后复制自身以处理每个传入的连接。每个连接仍然使用单独的进程处理。唯一的区别是连接处理程序进程现在是由主althttpd实例启动的,而不是由xinetd或systemd启动。
Althttpd没有配置文件。所有配置都使用一些命令行参数处理。这有助于保持配置的简单性,并减轻了通过错误配置的Web服务器引入安全漏洞的担忧。
由于每个althttpd进程只需为单个连接提供服务,althttpd是单线程的。此外,每个进程的生命周期仅为单个连接的持续时间,这意味着althttpd不需要过多担心内存泄漏。这些设计因素有助于保持althttpd源代码的简洁性,便于进行安全审计和分析。
用于服务TLS连接有两个选项:
- 可以使用定义了ENABLE_TLS宏并链接到-lssl -lcrypto的althttpd构建,并使用–cert fullchain.pem和–pkey privkey.pem标志启动。
- 可以通过外部连接服务(如stunnel4)启动althttpd,向althttpd传递-https 1标志,告诉它通过该服务“间接”以HTTPS模式运行。
首选第一种选项(使用内置TLS)。
TLS:传输层安全性协议(TLS),是一种网络协议,用于保护在两个通信应用程序之间传输的数据的安全性和隐私性。TLS的前身是安全套接层协议(SSL)。TLS和SSL之间存在一些技术差异,但它们的基本目标相同。
TLS的主要功能包括:数据加密、身份验证、数据完整性。
HTTPS(超文本传输安全协议)是在HTTP协议上使用TLS的变体,用于加密和保护Web页面的数据传输。
源代码
althttpd的完整源代码包含在一个单独的C代码文件中,没有依赖于标准C库之外的任何内容,只有在选择ENABLE_TLS选项时才需要OpenSSL。源代码文件命名为"althttpd.c"。要构建和安装althttpd,请运行以下命令:
gcc -Os -o /usr/bin/althttpd althttpd.c
-Os 用于指定编译器优化代码大小而非执行速度。
althttpd的源代码有详细的注释,并且易于访问。对于特殊需求,定制化应该相对容易。
要使用libssl构建带有内置TLS支持的althttpd:
gcc -Os -o /usr/bin/althttpd -fPIC -DENABLE_TLS althttpd.c -lssl -lcrypto
SQLite网站使用静态构建,因此在服务器上不需要安装OpenSSL。
创建使用Xinetd的设置
以下是sqlite.org上/etc/xinetd.d/http文件的完整内容,配置althttpd以在IPv4和IPv6上提供未加密的HTTP请求。您可以将其用作创建自己安装的模板。
如果您更愿意使用systemd,有一个附近的单独教程介绍如何执行该操作。但是,即使只是为了更好地了解althttpd的期望,您仍然可以查看此处的说明。
service http
{
port = 80
flags = IPv4
socket_type = stream
wait = no
user = root
server = /usr/bin/althttpd
server_args = -logfile /logs/http.log -root /home/www -user www-data
bind = 45.33.6.223
}
service http
{
port = 80
flags = REUSE IPv6
bind = 2600:3c00::f03c:91ff:fe96:b959
socket_type = stream
wait = no
user = root
server = /usr/bin/althttpd
server_args = -logfile /logs/http.log -root /home/www -user www-data
}
关键观察点在于端口80上的每个传入TCP/IP连接都会启动一个/usr/bin/althttpd的副本,并带有一些额外的参数,这些参数等于web服务器的配置。
请注意,althttpd作为超级用户运行。这不是必需的,但如果这样做,然后althttpd将自身移动到Web文档层次结构的根(例如,示例中的/home/www)的chroot监狱中,然后在读取任何内容之前放弃所有超级用户权限。-user选项告诉althttpd在进入chroot监狱后变为用户www-data。
-root选项(始终应为绝对路径)告诉althttpd在哪里找到文档层次结构。在sqlite.org的情况下,所有内容都是从/home/www提供的。在此文档层次结构的顶级是一堆以".website"结尾的目录。每个这样的目录都是一个单独的网站。目录的选择基于传入HTTP请求的Host参数。sqlite.org的目录部分列表如下:
3dcanvas_tcl_lang_org.website
3dcanvas_tcl_tk.website
androwish_org.website
canvas3d_tcl_lang_org.website
canvas3d_tcl_tk.website
cvstrac_org.website
default.website
fossil_scm_com.website
fossil_scm_hwaci_com.website
fossil_scm_org.website
system_data_sqlite_org.website
wapp_tcl_lang_org.website
wapp_tcl_tk.website
www2_alt_mail_net.website
www_androwish_org.website
www_cvstrac_org.website
www_fossil_scm_com.website
www_fossil_scm_org.website
www_sqlite_org.website
对于每个传入的HTTP请求,althttpd获取请求头中Host参数的文本,将其转换为小写,并将所有ASCII字母数字之外的字符更改为"_"。结果确定用于内容的子目录。如果没有匹配项,则将使用"default.website"目录作为回退。
例如,如果Host参数是"www.SQLite.org",则将其名称转换为"www_sqlite_org.website",并且这是用于提供内容的目录。如果Host参数是"fossil-scm.org",则使用"fossil_scm_org.website"目录。通常,两个或更多名称指向同一个网站。例如,fossil-scm.org、www.fossil-scm.org、fossil-scm.com和www.fossil-scm.com都是同一个网站。在这种情况下,通常只有一个目录是真正的目录,其他目录是符号链接。
在仅托管单个网站的最小安装中,只需有一个名为"default.website"的子目录即可。
在*.website目录内,要提供的文件由HTTP请求URI选择。标记为可执行的文件将作为CGI运行。以".scgi"结尾并且内容形式为"SCGI hostname port"的非可执行文件将SCGI请求中继到hostname:port。所有其他非可执行文件都按原样提供。
如果请求URI指定了*.website内部的目录名称,则althttpd会附加"/home"、“/index.html"和”/index.cgi",按顺序查找匹配项。
如果URI的前缀与可执行文件的名称相匹配,那么该文件将作为CGI运行。对于按原样传递的内容,MIME类型是通过使用编译到althttpd中的表从文件扩展名推断出来的。
支持使用Xinetd的HTTPS
从版本2.0(2022-01-16)开始,althttpd可以选择支持TLS加密连接。使用Xinetd设置HTTPS网站与HTTP网站非常相似。xinetd的适当配置是/etc/xinetd.d目录中名为"https"的单个文件,内容如下:
service https
{
port = 443
flags = IPv4
socket_type = stream
wait = no
user = root
server = /usr/bin/althttpd
server_args = -logfile /logs/http.log -root /home/www -user www-data -cert /etc/letsencrypt/live/sqlite.org/fullchain.pem -pkey /etc/letsencrypt/live/sqlite.org/privkey.pem
bind = 45.33.6.223
}
service https
{
port = 443
flags = REUSE IPv6
bind = 2600:3c00::f03c:91ff:fe96:b959
socket_type = stream
wait = no
user = root
server = /usr/bin/althttpd
server_args = -logfile /logs/http.log -root /home/www -user www-data -cert /etc/letsencrypt/live/sqlite.org/fullchain.pem -pkey /etc/letsencrypt/live/sqlite.org/privkey.pem
}
当然,您需要调整路径名和IP地址,使其适合您的特定安装。
这个https配置文件与之前的http配置文件相同,只有一些变化:
将服务名称从 “http” 更改为 “https”
将端口号从80更改为443
添加-cert和-pkey选项给althttpd,以便它知道在哪里找到适当的证书和私钥。
创建新的https配置文件后,只需重新启动xinetd(通常使用命令 “/etc/init.d/xinetd restart”),您的现有网站将立即具有HTTPS版本。
设置HTTPS使用Stunnel4
早期版本的althttpd不支持加密。在althttpd上加密网站的推荐方式是使用stunnel4。这些建议现在已经改变。我们现在建议您将althttpd更新到2.0版本或更高版本,并使用前一节中描述的xinetd技术。此部分保留供历史参考。
在sqlite.org网站上,/etc/stunnel/stunnel.conf文件的相关行是:
cert = /etc/letsencrypt/live/sqlite.org/fullchain.pem
key = /etc/letsencrypt/live/sqlite.org/privkey.pem
[https]
accept = :::443
TIMEOUTclose = 0
exec = /usr/bin/althttpd
execargs = /usr/bin/althttpd -logfile /logs/http.log -root /home/www -user www-data -https 1
这个设置与xinetd设置非常相似。一个关键的区别是使用"-https 1"选项告诉althttpd连接是加密的。这很重要,这样althttpd将知道为CGI程序设置HTTPS环境变量。
配置xinetd和stunnel4同时运行althttpd是可以的。事实上,这是SQLite.org网站的运行方式。对http://sqlite.org/的请求经过xinetd,对https://sqlite.org/的请求经过stunnel4。
独立运行
在作者的桌面工作站上,他的主目录下有一个子目录名为~/www/default.website。该子目录包含一组文件和CGI脚本。可以通过运行以下命令让althttpd在那里提供内容:
althttpd -root ~/www -port 8080
"-port 8080"选项告诉althttpd在独立模式下运行,监听端口8080。
althttpd的作者只在测试时使用独立模式。对于基于SSL的生产用途,建议使用内置的TLS支持或stunnel4。
带有HTTPS的独立运行
如果althttpd使用TLS支持构建,则可以使用以下选项告诉它以HTTPS模式运行:
althttpd -root ~/www --port 8043 --cert unsafe-builtin
此选项使用编译的自签名SSL证书,极不安全,仅用于测试目的。使用–cert选项指定自己的PEM格式SSL证书。–cert的参数可以是SSL私钥(通常命名为"privkey.pem")和证书链(通常命名为"fullchain.pem")的串联。或者,–cert可以指向只有fullchain.pem文件,而独立的–pkey选项可以指向privkey.pem文件。
使用自己的证书:
althttpd -root ~/www --port 8043 --cert fullchain.pem --pkey privkey.pem
请注意,证书在althttpd降低根权限之前就会被读取,因此证书可能存在于althttpd进程将在其下运行的非根用户无法访问的地方。
独立模式用于网站开发和测试
如果您在一个目录中有构成网站的各种HTML、JavaScript、CSS和其他资源文件,并且希望轻松测试这些文件,您可以输入以下命令:
althttpd --page index.html
在上述命令中,"index.html"是初始HTML页面的名称。此命令以独立模式启动althttpd,侦听它可以找到的第一个可用端口,并绑定到回环IP地址(127.0.0.1)。它还会自动在您的Web浏览器中打开一个新标签页,并指向"index.html"页面。
如果您在远程系统上开发网站,可以使用以下方式启动:
althttpd --popup
“–popup"选项与”–page"类似,但不限制IP地址为回环地址,并且不尝试启动新的Web浏览器标签页。
安全特性
为了防范恶意行为,althttpd对将提供的文件名称施加了一些限制。在请求URI中,除了字母数字和", -./:~“之外的所有字符都会转换为单个”_“。此外,如果请求URI的任何路径元素以”.“或”-“开头,althttpd都会始终返回404 Not Found错误。因此,只要文件名以”.“或”-"开头,就可以将辅助文件(例如由CGI使用的数据库或其他内容)放在文档层次结构中。
当althttpd返回404时,它会尝试确定请求是否恶意,如果它认为是,则可以选择临时阻止客户端的IP。
一个例外:尽管althttpd通常对任何以".“开头的路径元素的请求返回404 Not Found,但它允许以”/.well-known/“开头的URI请求。”/.well-known/“下面的文件或目录名称允许以”.“或”-“开头(但不允许以”…"开头)。此例外是为了允许LetsEncrypt验证对网站的拥有权。
基本身份验证
如果在内容层次结构的任何位置都存在名为"-auth"的文件,那么所有同级文件和所有低级目录中的文件都需要HTTP基本身份验证,其定义由"-auth"文件的内容确定。“-auth"文件是纯文本和面向行的。空行和以”#"开头的行将被忽略。其他行的含义如下:
-
http-redirect
如果存在http-redirect行,则会导致所有HTTP请求重定向为HTTPS请求。"-auth"文件按顺序读取和处理,因此在"http-redirect"行下面的行对于http请求永远不会被看到或处理。
-
https-only
如果存在https-only行,则表示仅允许HTTPS请求。任何HTTP请求都会导致404 Not Found错误。https-only行通常出现在http-redirect行之后。
-
realm NAME
此形式的单行建立基本身份验证的 “realm”。Web浏览器通常将realm名称显示为要求用户名和密码的对话框的标题。
-
user NAME LOGIN:PASSWORD
有多个用户行,每个有效用户对应一行。LOGIN:PASSWORD参数定义用户必须键入的用户名和密码以访问网站。密码是明文的 - HTTP基本身份验证不是最安全的身份验证机制。成功登录后,NAME被存储在REMOTE_USER环境变量中,以便可以被CGI脚本访问。NAME和LOGIN通常相同,但也可以不同。
-
anyone
如果遇到"anyone"行,表示允许任何请求,即使没有提供用户名和密码。这行与"http-redirect"组合使用,可以使所有普通HTTP请求在不要求登录凭据的情况下重定向到HTTPS。
基本身份验证示例
http://www.sqlite.org/ 网站在顶级目录下包含一个"-auth"文件,内容如下:
http-redirect
anyone
这个"-auth"文件导致所有HTTP请求都被重定向到HTTPS,而无需进一步的登录。(试试:访问 http://sqlite.org/ 并验证是否被重定向到 https://sqlite.org/。)
在 https://fossil-scm.org/private/ 存在一个"-auth"文件,内容如下:
realm Access To All Fossil Repositories
http-redirect
user drh drh:xxxxxxxxxxxxxxxx
当然,密码不是一行 “x” 字符。这演示了"-auth"文件的典型用法。对于单个用户,只要用户通过HTTPS而不是HTTP进入,就授予对"private"子目录中内容的访问权限。强烈建议对所有基本身份验证使用 “http-redirect” 行,因为密码包含在请求头中,如果通过HTTP发送请求,可能会被坏人截取和窃取。
日志文件
如果在althttpd命令行上给出了"-logfile"选项,那么对于每个HTTP请求,都会向指定的文件追加一行。日志文件采用RFC4180规定的逗号分隔值(CSV)格式。在源代码中有一条注释解释输出行中每个字段的含义。
日志文件是CSV格式的,因此可以使用类似下面的脚本将其轻松导入到SQLite中进行分析:
CREATE TABLE log(
date TEXT, /* 时间戳 */
ip TEXT, /* 源IP地址 */
url TEXT, /* 请求URI */
ref TEXT, /* 引用者 */
code INT, /* 结果代码,例如:200,404 */
nIn INT, /* 请求中的字节数 */
nOut INT, /* 回复中的字节数 */
t1 INT, t2 INT, /* 处理时间(用户、系统)毫秒 */
t3 INT, t4 INT, /* CGI脚本时间(用户、系统)毫秒 */
t5 INT, /* 墙钟时间,毫秒 */
nreq INT, /* 此请求的序列号 */
agent TEXT, /* 用户代理 */
user TEXT, /* 远程用户 */
n INT, /* 在SCRIPT_NAME中的URL字节数 */
lineno INT /* 生成日志条目的源代码行 */
);
.mode csv
.import httplog.csv log
在"-logfile"选项上的文件名可能包含strftime()扩展的基于时间的字符。因此,要使每天使用一个新的日志文件,可以使用类似以下的内容:
-logfile /var/logs/althttpd/httplog-%Y%m%d.csv
客户端IP阻止
如果在althttpd中包含了–ipshun DIRECTORY选项,DIRECTORY是从chroot jail内部访问的绝对路径(以"/"开头),并且客户端的IP地址作为该目录中的一个文件出现,那么althttpd可能返回503 Service Unavailable而不是处理请求。
如果文件大小为零字节,则始终返回503。因此,您可以"touch"一个文件,其名称是IP地址,永久封禁该客户端。
如果文件大小为N字节,则如果文件的修改时间在300*N秒之前,则在N秒内返回503。换句话说,每字节文件封禁客户端五分钟。
如果althttpd收到一个将导致404 Not Found的请求,并且在检查REQUEST_URI时该请求看起来很可疑,那么会自动创建封禁文件。例如,任何包含/…/的请求都被视为黑客企图。还检查其他常见的漏洞探测。可能会根据经验增加漏洞探测列表。
封禁文件在5分钟/字节后自动取消链接。
封禁文件最初大小为1字节。但如果封禁到期,然后在5分钟每字节的块文件大小之前到达新请求,则文件将增加一个字节,并且修改时间将被重置。
5分钟的封禁时间可以在构建时通过传递 -DBANISH_TIME=N 进行配置,其中 N 是默认为 300 的秒数。
GZip内容压缩
Althttpd对服务器端内容压缩有基本支持,通常将文件的传输成本减少一半以上。与将压缩库添加到althttpd不同,它依赖于网站开发人员提供压缩和未压缩形式的内容。
在提供文件时,如果客户端支持gzip压缩,并且找到了同名文件加上 .gz 扩展名,就会向客户端提供已gzip压缩的文件,并附带响应头表示已经gzip压缩。对于用户来说,原始请求的文件似乎是压缩的。但实际上,在幕后,提供的是不同的文件。
请注意,此功能仅适用于静态文件,而不适用于CGI。
static-build.md文件翻译
https://sqlite.org/althttpd/file?name=static-build.md&ci=tip
构建独立二进制文件
本文档描述了如何在Linux(或类似的类Unix操作系统)上构建一个完全自包含的、静态链接的 “althttpd” 二进制文件。
所需材料
- 从本网站获取althttpd.c源代码文件。
- 最新版的OpenSSL发布版的tar包。
- 常见的Unix构建工具,如 “make”、“gcc” 等。
- OpenSSL构建需要Perl。
构建步骤
-
编译静态的OpenSSL库。
- 解压OpenSSL的tar包。
- 将顶层目录重命名为 “openssl”。
- 进入 openssl 目录。
- 运行以下命令:
./config no-ssl3 no-weak-ssl-ciphers no-shared no-threads --openssldir=/usr/lib/ssl CFLAGS=-Os make -e
- 在OpenSSL构建过程中,可以边喝茶边等待。
-
编译althttpd。
- 返回到存放althttpd.c源文件的顶层目录。
- 运行:
gcc -I./openssl/include -Os -Wall -Wextra -DENABLE_TLS \ -o althttpd althttpd.c -L./openssl -lssl -lcrypto -ldl
- 上述命令构建了一个二进制文件,该文件静态链接到OpenSSL,但仍然动态链接到系统库。要使二进制文件完全静态,请添加 -static 选项。
- 在上述gcc命令中,您还可以选择添加 -DALTHTTPD_VERSION=‘…’,引号中的文本是将报告给CGI脚本的althttpd版本号。
使用二进制文件
以上就是所有需要做的。编译后,只需将生成的二进制文件移动到服务器的 /usr/bin 目录即可。
linode-systemd.md文件翻译
https://sqlite.org/althttpd/file?name=linode-systemd.md&ci=tip
使用Althttpd、Linode和Systemd设置网站
以下是我最近(2024年1月16日)在廉价的Linode VPS上使用Alhttpd、Let’s Encrypt和systemd设置网站的笔记。
如果对本文档有建议或错误报告,请在论坛上发表留言。
1.0 创建Linode帐户并启动VPS
在Linode上创建帐户并启动新的VPS。您可以以每月5美元的价格获得它们。在这个示例中,我使用了一个每月12美元的Linode,这可能有点过剩。我可能会在某个时候降级到每月5美元的计划。
我选择了Ubuntu 23.10作为操作系统,因为那是当时最新的Ubuntu版本。使用您更熟悉的其他Linux发行版。
2.0 注册您的域名
我构建的系统使用了我已经拥有的域名的子域,因此我不必购买新的域名。如果不是这种情况,请立即购买域名。如何执行此操作的详细信息超出了本文档的范围,但网络上有许多好的教程。
将您的域名指向Linode名称服务器。根据您要执行的操作,进行所需的任何DNS条目。Linode提供了一个出色且易于使用的界面。
3.0 初始系统配置
以root身份登录到您的VPS。我需要升级和安装新软件,如下所示:
apt update
apt upgrade
apt install letsencrypt
4.0 创建Web服务器用户和主目录
应该已经有一个名为 “www-data” 的用户。(检查 /etc/passwd 文件。)这是我用于Web服务的用户。
我还手动编辑了 /etc/passwd 文件,将用户 www-data 的默认shell从 /usr/bin/nologin 更改为 /bin/bash。这个更改允许我运行 “su www-data” 命令以成为 www-data 用户,同时操作属于该用户的文件。但这一步是完全可选的。
为该用户在 /home/www 创建一个主目录。创建一个名为 /home/www/default.website 的子目录。将子目录的所有者更改为 www-data。命令序列如下:
mkdir -p /home/www/default.website
chown www-data /home/www/default.website
创建一个名为 /home/www/default.website/index.html 的文件,该文件由 www-data 用户可读,并放入一些HTML内容作为占位符。也许像这样:
<h1>Hello, World</h1>
<p>If you can see this, that means the web server is running.</p>
添加任何您想要的其他内容。但是,请注意,具有执行权限位的文件将被视为CGI运行。因此,请确保 *.website 文件夹中的任何文件或子文件夹中的文件都不可执行,除非您确实打算将它们作为CGI运行。
5.0 为althttpd构建二进制文件
我在我的桌面Linux机器上构建了一个静态链接的althttpd二进制文件(使用这些说明),并使用scp将静态二进制文件传输到VPS。在 /usr/bin/ 安装静态二进制文件。
6.0 用于HTTP服务的Systemd设置
首先,您需要启动简单的HTTP(未加密)服务,因为这是从Let’s Encrypt获取证书的先决条件。创建一个文件 /etc/systemd/system/http.socket,内容如下:
[Unit]
Description=HTTP socket
[Socket]
Accept=yes
ListenStream=80
NoDelay=true
[Install]
WantedBy=sockets.target
然后创建另一个文件,命名为 /etc/systemd/system/http@.service,内容如下:
[Unit]
Description=HTTP socket server
After=network-online.target
[Service]
WorkingDirectory=/home/www
ExecStart=/usr/bin/althttpd -root /home/www -
user www-data
StandardInput=socket
[Install]
WantedBy=multi-user.target
文件名中的 “@” 不是拼写错误。由于某种原因,systemd似乎需要它。我不知道细节。
最后,使用以下命令启动新服务:
systemctl daemon-reload
systemctl enable http.socket
systemctl start http.socket
此时,您应该能够将Web浏览器指向VPS的端口80,并看到在步骤4.0末尾安装的占位HTML。您还可以使用以下命令检查服务的状态或关闭服务:
systemctl status http.socket
systemctl stop http.socket
7.0 获取Let’s Encrypt证书
为了使用HTTPS,您需要一个证书。使用类似以下的命令获取证书:
letsencrypt certonly --webroot -w /home/www/default.website -d your-domain.org
当然,将您的实际域名替换为 “your-domain.org”。如果您希望Web服务器为多个域提供服务,可以使用多个 “-d” 选项。有关详细信息,请参阅letsencrypt文档。
我相信此命令设置了域将自动更新证书。您不应该再次运行此命令。如果以后发现我错了,我将回来更正这段文字。
您的证书将在以下文件中找到:
/etc/letsencrypt/live/your-domain.org/fullchain.pem
/etc/letsencrypt/live/your-domain.org/privkey.pem
确保 “privkey.pem” 文件保持安全。这是您的私钥。这是Web服务器用于向陌生人验证您服务器身份的内容。
8.0 激活HTTPS
现在您有了证书,您可以创建额外的systemd配置条目,以在端口443上接收TLS HTTPS请求。首先创建一个名为 /etc/systemd/system/https.socket 的文件,内容如下:
[Unit]
Description=HTTPS socket
[Socket]
Accept=yes
ListenStream=443
NoDelay=true
[Install]
WantedBy=sockets.target
然后创建另一个文件,命名为 /etc/systemd/system/https@.service,内容如下:
[Unit]
Description=HTTPS socket server
After=network-online.target
[Service]
WorkingDirectory=/home/www
ExecStart=/usr/bin/althttpd -root /home/www -user www-data -cert /etc/letsencrypt/live/your-domain.org/fullchain.pem -pkey /etc/letsencrypt/live/your-domain.org/privkey.pem
StandardInput=socket
[Install]
WantedBy=multi-user.target
这两个文件与步骤6.0中为HTTP服务创建的文件非常相似。关键区别:
- 文件名中说 “https” 而不是 “http”。
- TCP端口是443而不是80。
- althttpd 命令有额外的 -cert 和 -pkey 参数来标识您的证书。
创建这两个文件后,运行:
systemctl daemon-reload
systemctl enable https.socket
systemctl start https.socket
执行完这些步骤后,您应该能够使用TLS加密的连接浏览您的网站。
9.0 记录Web流量
如果要记录Web流量(建议),请创建一个名为 /home/www/log 的新目录,并将所有者更改为 www-data。然后编辑步骤6.0和8.0中创建的 *.service 文件,将 “–log /log/http.log” 一词添加到 “ExecStart=…” 行中。
请注意,文件名为 “/log/http.log”,而不是 “/home/www/log/http.log”。这是因为althttpd在执行任何其他操作之前会对 /home/www 目录进行chroot。这是一个安全功能,可防止CGI脚本中的错误危害您的系统。由于chroot,从althttpd的角度来看, “/home/www/log/http.log” 文件实际上将被称为 “/log/http.log”。
在进行这些更改后,运行:
systemctl restart http.socket
systemctl restart https.socket
10.0 其他配置更改
现在,您可以尝试其他配置更改。添加像 “–ipshun /ipshun” 这样的选项是推荐的,以帮助消除恶意爬虫。您还可以在 “/home/www” 下添加特定域的新 “*.website” 文件夹。有关指导和建议,请参阅其他althttpd文档。本教程应足以帮助您入门。
Makefile文件解释
default: althttpd althttpsd # 构建目标
SHELL = /bin/bash # 指定shell
VERSION_NUMBER = 2.0
CC=cc # c编译器
CFLAGS=-Os -Wall -Wextra
# 检查是否安装了 fossil(版本控制工具),如果安装了,则使用 fossil 更新版本。
manifest: # 只是一个标签
@if which fossil > /dev/null; then \
fossil update --nosync current; \
else \
echo "fossil binary not found. Version hash/time might be incorrect."
fi
# 定义一个目标 manifest.uuid,它依赖于 manifest。这个目标的规则是空的
manifest.uuid: manifest
# We do the version-setting CPPFLAGS this way, instead of via
# $(shell...), for the sake of portability with BSD Make. Some hoops
# have to be jumped through to get the escaping "just right," though.
# 读取 manifest.uuid 文件中的版本信息和哈希,并将其写入名为 version 的文件中。
version: Makefile manifest.uuid
@hash=`cut -c1-12 manifest.uuid`; \
time=`sed -n 2p manifest | cut -d' ' -f2`; \
{ echo -n "ALTHTTPD_VERSION=\""; \
echo '$(VERSION_NUMBER)' "[$$hash] [$$time]\""; \
} > $@
# 使用从 version 文件中读取的标志进行编译
althttpd: althttpd.c version
@flags="`cat version`"; set -x; \
$(CC) $(CFLAGS) "-D$$flags" -o althttpd althttpd.c
# 相对于althttpd,加了TLS、SSL、crypto、-fPIC
althttpsd: althttpd.c version
@flags="`cat version`"; set -x; \
$(CC) $(CFLAGS) "-D$$flags" -fPIC -o althttpsd -DENABLE_TLS althttpd.c -lssl -lcrypto
# 静态编译,避免运行时依赖问题。
static-althttpd: althttpd.c version
@flags="`cat version`"; set -x; \
$(CC) $(CFLAGS) "-D$$flags" -static -o althttpd althttpd.c
# 静态编译,避免运行时依赖问题。
static-althttpsd: althttpd.c version
@flags="`cat version`"; set -x; \
$(CC) $(CFLAGS) "-D$$flags" -static -fPIC -o althttpsd -DENABLE_TLS althttpd.c -lssl -lcrypto -lpthread -ldl
clean:
rm -f althttpd althttpsd version
extract-error-numbers.tcl 文件解释
tcl脚本,用于从althttpd.c源文件中提取每个日志条目末尾的错误号。
比较类似python、bash脚本
#!/usr/bin/tclsh # 指定脚本的解释器,Tcl解释器。
# Run this script to extract the error numbers that appear at the end of each log file entry from the source text.
# 运行这个脚本可以从源文本中提取日志条目末尾的错误号。
set in [open althttpd.c r] # 打开名为 althttpd.c 的文件用于读取(r表示只读),文件句柄in。
while {![eof $in]} { # 直到文件结束
set line [gets $in] # 从文件中读取一行,并将其存储在变量 line 中。
# 使用正则表达式匹配来检查是否在当前行中存在特定的日志格式。
# 如果匹配成功,将匹配到的错误号存储在变量 num 中,将日志消息存储在变量 msg 中。
if {[regexp {(\d+)[^0-9]+/\* LOG: (.*) \*/} $line all num msg]} {
# 如果匹配成功,打印一个SQL语句,将错误号和消息插入到名为 xref 的表中。
puts "INSERT INTO xref VALUES($num,'$msg');"
}
}
close $in # 关闭文件句柄,释放文件资源。
static-ssl.mk 文件解释
用于构建静态链接 OpenSSL 版本的 althttpd 的 Makefile 文件。
# 2022-02-16:
# This makefile is used by the author (drh) to build versions of
# althttpd that are statically linked against OpenSSL. The resulting
# binaries power sqlite.org, fossil-scm.org, and other machines.
# 说明该 Makefile 由作者(drh)用于构建静态链接 OpenSSL 版本的 althttpd,用于驱动 sqlite.org、fossil-scm.org 等网站。
# This is not a general-purpose makefile. But perhaps you can adapt it to your specific needs.
#
default: althttpd # 默认目标是构建 althttpd 可执行文件。
VERSION_NUMBER ?= 2.0 # 定义变量
# 定义变量 VERSION_HASH,使用 shell 命令从 manifest.uuid 文件中提取前12个字符。
VERSION_HASH ?= $(shell cut -c1-12 manifest.uuid)
# 定义变量 VERSION_TIME,使用 shell 命令从 manifest 文件的第二行提取时间。
VERSION_TIME ?= $(shell sed -n 2p manifest | cut -d' ' -f2)
# 定义变量 ALTHTTPD_VERSION,包含版本号、哈希和时间。
ALTHTTPD_VERSION ?= "$(VERSION_NUMBER) [$(VERSION_HASH)] $(VERSION_TIME)"
# 在预处理阶段添加一个宏定义,将 ALTHTTPD_VERSION 的值传递给 C 源文件。
CPPFLAGS += -DALTHTTPD_VERSION='$(ALTHTTPD_VERSION)'
# 如果安装了fossil ,则使用 fossil 更新版本。
manifest:
@if which fossil > /dev/null; then \
fossil update --nosync current; \
else \
echo "fossil binary not found. Version hash/time might be incorrect."
fi
manifest.uuid: manifest
# 指定 OpenSSL 的安装路径
OPENSSLDIR = /home/drh/fossil/release-build/compat/openssl
# 指定链接 OpenSSL 库时需要的库选项。
OPENSSLLIB = -L$(OPENSSLDIR) -lssl -lcrypto -ldl
# 启用 TLS 并指定 OpenSSL 头文件路径
CPPFLAGS += -I$(OPENSSLDIR)/include -DENABLE_TLS
CPPFLAGS += -Wall -Wextra
CFLAGS = -Os
althttpd: althttpd.c manifest
gcc $(CPPFLAGS) $(CFLAGS) -o althttpd althttpd.c $(OPENSSLLIB)
clean:
rm -f althttpd
源码中将ALTHTTPD_VERSION当作字符串使用文章来源:https://www.toymoban.com/news/detail-810237.html
#define SERVER_SOFTWARE "althttpd " ALTHTTPD_VERSION
#define SERVER_SOFTWARE_TLS SERVER_SOFTWARE ", " OPENSSL_VERSION_TEXT
static char *zServerSoftware = 0;/* Software name and version info */
puts(SERVER_SOFTWARE_TLS);
zServerSoftware = useHttps==2 ? SERVER_SOFTWARE_TLS : SERVER_SOFTWARE;
althttpd.c 源码分析
althttpd.c 源码注释部分翻译
注释描述了一个小型、简单、独立的 HTTP 服务器的源代码文件。文章来源地址https://www.toymoban.com/news/detail-810237.html
/*
** 2001-09-15
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This source code file implements a small, simple, stand-alone HTTP server.
** 小型、简单、独立的 HTTP 服务器
** Features: 特点
**
** * Launched from inetd/xinetd/systemd, or as a stand-alone server
** * One process per TCP/IP connection
** * Deliver static content or run CGI or SCGI
** * Virtual sites based on the "Host:" property of the HTTP header
** * Runs in a chroot jail
** * Unified log file in a CSV format
** * Small code base (this 1 file) to facilitate security auditing
** * Simple setup - no configuration files to misconfigure
可以从 inetd/xinetd/systemd 启动,也可以作为独立服务器。
每个 TCP/IP 连接一个进程。
提供静态内容或运行 CGI 或 SCGI。
基于 HTTP 头的 "Host:" 属性的虚拟站点。
在 chroot jail 中运行。
统一的 CSV 格式日志文件。
小型代码库,有助于进行安全审计。
简单设置 - 无需配置文件。
**
** This file implements a small and simple but secure and effective web
** server. There are no frills. Anything that could be reasonably
** omitted has been.
该文件实现了一个小巧简单、安全有效的 Web 服务器。没有多余的装饰,一切可以合理省略的都已省略。
**
** Setup rules: 设置规则
**
** (1) Launch as root from inetd/systemd like this:
** 作为 root 从 inetd/systemd 启动,使用如下命令:
** althttpd -logfile logfile -root /home/www -user nobody
**
** It will automatically chroot to /home/www and become user "nobody".
** The logfile name should be relative to the chroot jail.
** 它将自动进入 chroot jail,切换到 /home/www 目录,并成为用户 "nobody"。
日志文件名应相对于 chroot jail。
** (2) Directories of the form "*.website" (ex: www_sqlite_org.website)
** contain content. The directory is chosen based on the HTTP_HOST
** request header. If there is no HTTP_HOST header or if the
** corresponding host directory does not exist, then the
** "default.website" is used.
形如 "*.website" 的目录(例如:www_sqlite_org.website)包含内容。
目录的选择基于 HTTP_HOST 请求头。如果没有 HTTP_HOST 请求头,
或者对应的主机目录不存在,则使用 "default.website"。
**
** In stand-alone mode (when the --port, --page, or --popup options are
** used) if neither the HTTP_HOST.website nor "default.website"
** directories exist, then files are served directly from the
** directory from which althttpd was launched. This makes it easy
** to run a quick web-server for testing purposes using commands like:
** 在独立模式下(当使用 --port、--page 或 --popup 选项时),
如果既不存在 HTTP_HOST.website 目录,也不存在 "default.website" 目录,
那么文件将直接从启动 althttpd 的目录提供服务。
这样可以轻松地通过诸如以下命令运行快速的用于测试目的的 Web 服务器:
** althttpd --page fiddle.html
** althttpd --popup
** althttpd --port 8080..8090
**
** In one-connection mode (when launched from xinetd or similar)
** an error is raised if "default.website" does not exist.
在单连接模式下(从 xinetd 或类似的工具启动时),如果 "default.website" 不存在,将引发错误。
**
** If the HTTP_HOST header contains any charaters other than
** [a-zA-Z0-9_.,*~/] then a 403 error is generated.
如果 HTTP_HOST 请求头包含除 [a-zA-Z0-9_.,*~/] 之外的任何字符,将生成 403 错误。
**
** (3) Any file or directory whose name begins with "." or "-" is ignored,
** except if the URL begins with "/.well-known/" then initial "." and
** "-" characters are allowed, but not initial "..". The exception is
** for RFC-5785 to allow letsencrypt or certbot to generate a TLS cert
** using webroot.
任何文件或目录名以 "." 或 "-" 开头的都会被忽略,但如果 URL 以 "/.well-known/" 开头,
则允许使用初始的 "." 和 "-" 字符,但不允许使用初始的 ".."。
这是为了 RFC-5785 的例外,以允许 letsencrypt 或 certbot 使用 webroot 生成 TLS 证书。
**
** (4) Characters other than [0-9a-zA-Z,-./:_~] and any %HH characters
** escapes in the filename are all translated into "_". This is
** a defense against cross-site scripting attacks and other mischief.
文件名中除了 [0-9a-zA-Z,-./:_~] 和任何 %HH 字符转义之外的字符都会被翻译成 "_”。
这是为了防范跨站脚本攻击和其他恶意行为。
**
** (5) Executable files are run as CGI. Files whose name ends with ".scgi"
** trigger an SCGI request (see item 9 below). All other files
** are delivered as is.
可执行文件会被作为 CGI 运行。
文件名以 ".scgi" 结尾的会触发一个 SCGI 请求(参见下面的第9项)。
所有其他文件将按原样传送。
**
** (6) If a file named "-auth" exists in the same directory as the file to
** be run as CGI/SCGI or to be delivered, then it contains information
** for HTTP Basic authorization. See file format details below.
如果与将要作为 CGI/SCGI 运行或传送的文件位于同一目录中存在一个名为 "-auth" 的文件,
那么它包含了 HTTP 基本授权的信息。请参阅下面的文件格式详细信息。
**
** (7) To run as a stand-alone server, simply add the "-port N" command-line
** option to define which TCP port to listen on. If the argument is
** "--port N1..N2" then TCP ports between N1 and N2 are scanned looking
** for one that is open and the first open port is used.
要作为独立服务器运行,只需添加 "-port N" 命令行选项来定义要监听的 TCP 端口。
如果参数是 "--port N1..N2",则会扫描 N1 和 N2 之间的 TCP 端口,找到第一个开放的端口并使用。
**
** (8) For static content, the mimetype is determined by the file suffix
** using a table built into the source code below. If you have
** unusual content files, you might need to extend this table.
对于静态内容,文件的 MIME 类型由下面源代码中内置的表格来确定,它使用文件后缀。
如果你有不寻常的内容文件,可能需要扩展这个表格。
**
** (9) Content files that end with ".scgi" and that contain text of the
** form "SCGI hostname port" will format an SCGI request and send it
** to hostname:port, then relay back the reply. Error behavior is
** determined by subsequent lines of the .scgi file. See SCGI below
** for details.
以 ".scgi" 结尾的内容文件,如果包含形如 "SCGI hostname port" 的文本,
将格式化一个 SCGI 请求并将其发送到 hostname:port,然后中继回响应。
错误行为由 .scgi 文件的后续行决定。有关详细信息,请参阅下面的 SCGI 部分。
**
** (10) If compiled with -DENABLE_TLS and linked against OpenSSL and
** launched with a --cert option to identify a certificate file, then
** TLS is used to encrypt the connection.
如果使用 -DENABLE_TLS 进行编译,并链接到 OpenSSL,
并使用 --cert 选项启动以确定证书文件,则会使用 TLS 加密连接。
**
** (11) When requesting static file NAME and if the HTTP request includes
** "Accept-Encoding: gzip" and if file "NAME.gz" exists on disk, then
** return NAME.gz with an "Content-encoding: gzip" header
在请求静态文件 NAME 时,如果 HTTP 请求包含 "Accept-Encoding: gzip",
并且磁盘上存在文件 "NAME.gz",那么返回 NAME.gz 并带有 "Content-Encoding: gzip" 头。
**
** Command-line Options:命令行选项
**
** --root DIR Defines the directory that contains the various
** $HOST.website subdirectories, each containing web content
** for a single virtual host. If launched as root and if
** "--user USER" also appears on the command-line and if
** "--jail 0" is omitted, then the process runs in a chroot
** jail rooted at this directory and under the userid USER.
** This option is required for xinetd launch but defaults
** to "." for a stand-alone web server. DIR should always
** be an absolute path, else child processes might misbehave.
定义包含各种 $HOST.website 子目录的目录,每个子目录包含单个虚拟主机的 Web 内容。
**
** --port N Run in standalone mode listening on TCP port N, or from
** --port N1..N2 the first available TCP port in the range from N1 to N2.
以独立模式运行,监听 TCP 端口 N 或 N1 到 N2 范围内的第一个可用端口。
**
** --user USER Define the user under which the process should run if
** originally launched as root. This process will refuse to
** run as root (for security). If this option is omitted and
** the process is launched as root, it will abort without
** processing any HTTP requests.
定义进程应以哪个用户身份运行。
**
** --logfile FILE Append a single-line, CSV-format, log file entry to FILE
** for each HTTP request. FILE should be a full pathname.
** The FILE name is interpreted inside the chroot jail. The
** FILE name is expanded using strftime() if it contains
** at least one '%' and is not too long.
为每个 HTTP 请求在 FILE 中追加一行 CSV 格式的日志条目。
**
** --ipshun DIR If the remote IP address is also the name of a file
** in DIR that has size N bytes and where either N is zero
** or the m-time of the file is less than N time-units ago
** then that IP address is being shunned and no requests
** are processed. The time-unit is a compile-time option
** (BANISH_TIME) that defaults to 300 seconds. If this
** happens, the client gets a 503 Service Unavailable
** reply. Furthermore, althttpd will create ip-shunning
** files following a 404 Not Found error if the request
** URI is an obvious hack attempt. The ip-shunning file
** will also be created if a CGI returns status code 418.
如果远程 IP 地址在 DIR 中的文件名中,该文件大小为 N 字节,
且 N 为零或文件的修改时间少于 N 时间单位,则该 IP 地址被屏蔽。
**
** --https BOOLEAN Indicates that input is coming over SSL and is being
** decoded upstream, perhaps by stunnel. This option
** does *not* activate built-in TLS support. Use --cert
** for that.
指示输入是否通过 SSL,但不激活内置 TLS 支持。
**
** --page NAME Come up in stand-alone mode, and then try to launch a
** web-browser pointing to the NAME document after the
** listening socket has been created. This option
** implies --loopback and "--port 8080..8100".
在独立模式下启动,并尝试在创建监听套接字后启动一个指向 NAME 文档的 Web 浏览器。
**
** --popup Launch a stand-alone web server to use for testing.
** This option implies "--port 8080..8100". This option
** is similar to "--page NAME" except that it does not
** try to launch a web-browser and does not force the
** connection into --loopback mode. Use this when
** running a test web-server on a remote host via ssh.
启动一个用于测试的独立 Web 服务器。
**
** --loopback Only accept loop-back TCP connections (connections
** originating from the same host). This is the
** default if --root is omitted.
仅接受回环 TCP 连接。
**
** --family ipv4 Only accept input from IPV4 or IPV6, respectively.
** --family ipv6 These options are only meaningful if althttpd is run
** as a stand-alone server.
仅接受 IPV4 或 IPV6 输入。
**
** --jail BOOLEAN Indicates whether or not to form a chroot jail if
** initially run as root. The default is true, so the only
** useful variant of this option is "--jail 0" which prevents
** the formation of the chroot jail.
指示是否形成 chroot jail。
**
** --max-age SEC The value for "Cache-Control: max-age=%d". Defaults to
** 120 seconds.
"Cache-Control: max-age=%d" 的值,默认为 120 秒。
**
** --max-cpu SEC Maximum number of seconds of CPU time allowed per
** HTTP connection. Default 30 (build option:
** -DMAX_CPU=integer). 0 means no limit.
每个 HTTP 连接允许的最大 CPU 时间。
**
** --debug BOOLEAN Disables input timeouts. This is useful for debugging
** when inputs are being typed in manually.
禁用输入超时,用于调试。
**
** --enable-sab Add new lines to the HTTP reply header that are
** prerequisites for SharedArrayBuffer. These are the lines:
** Cross-Origin-Embedder-Policy: require-corp
** Cross-Origin-Opener-Policy: same-origin
为 SharedArrayBuffer 添加新行到 HTTP 回复头。
**
**
** Additional command-line options available when compiling with ENABLE_TLS:
**
** --cert FILE The TLS certificate, the "fullchain.pem" file
**
** --pkey FILE The TLS private key, the "privkey.pem" file. May be
** omitted if the --cert file is the concatenation of
** the fullchain.pem and the privkey.pem.
**
**
** Command-line options can take either one or two initial "-" characters.
** So "--debug" and "-debug" mean the same thing, for example.
**
**
** Security Features: 安全特性
**
** (1) This program automatically puts itself inside a chroot jail if
** it can and if not specifically prohibited by the "--jail 0"
** command-line option. The root of the jail is the directory that
** contains the various $HOST.website content subdirectories.
自动将自身置于 chroot jail 中。
**
** (2) No input is read while this process has root privileges. Root
** privileges are dropped prior to reading any input (but after entering
** the chroot jail, of course). If root privileges cannot be dropped
** (for example because the --user command-line option was omitted or
** because the user specified by the --user option does not exist),
** then the process aborts with an error prior to reading any input.
在具有 root 特权时不读取任何输入。
**
** (3) The length of an HTTP request is limited to MAX_CONTENT_LENGTH bytes
** (default: 250 million). Any HTTP request longer than this fails
** with an error. (Build option: -DMAX_CONTENT_LENGTH=integer)
限制 HTTP 请求长度。
**
** (4) There are hard-coded time-outs on each HTTP request. If this process
** waits longer than the timeout for the complete request, or for CGI
** to finish running, then this process aborts. (The timeout feature
** can be disabled using the --debug command-line option.)
对每个 HTTP 请求设置硬编码的超时。
**
** (5) If the HTTP_HOST request header contains characters other than
** [0-9a-zA-Z,-./:_~] then the entire request is rejected.
针对 HTTP_HOST 请求头进行字符检查。
**
** (6) Any characters in the URI pathname other than [0-9a-zA-Z,-./:_~]
** are converted into "_". This applies to the pathname only, not
** to the query parameters or fragment.
转换 URI 路径中的特殊字符。
**
** (7) If the first character of any URI pathname component is "." or "-"
** then a 404 Not Found reply is generated. This prevents attacks
** such as including ".." or "." directory elements in the pathname
** and allows placing files and directories in the content subdirectory
** that are invisible to all HTTP requests, by making the first
** character of the file or subdirectory name "-" or ".".
防止 URI 路径以 "." 或 "-" 开头。
**
** (8) The request URI must begin with "/" or else a 404 error is generated.
请求 URI 必须以 "/" 开头。
**
** (9) This program never sets the value of an environment variable to a
** string that begins with "() {".
**
** (10) If the --ipshun option is used, specific IP addresses can be
** temporarily block for abusive behavior.
不设置环境变量值以 "(){..." 开头。
**
** (11) If a CGI reports status code 418 ("I'm a teapot", rfc2324) and if
** the --ipshun option is used, then the IP address is temporarily
** blocked.
使用 --ipshun 选项可临时阻止特定 IP 地址。
**
** Security Auditing: 安全审计
**
** This webserver mostly only serves static content. Any security risk will
** come from CGI and SCGI. To check an installation for security, then, it
** makes sense to focus on the CGI and SCGI scripts.
此 Web 服务器主要用于提供静态内容。任何安全风险都将来自 CGI 和 SCGI。
因此,要检查安装的安全性,重点应放在 CGI 和 SCGI 脚本上。
**
** To locate all CGI files: 查找所有 CGI 文件的方法:
**
** find *.website -executable -type f -print
** OR: find *.website -perm +0111 -type f -print
**
** The first form of the "find" command is preferred, but is only supported
** by GNU find. On a Mac, you'll have to use the second form.
"find" 命令的第一种形式更受欢迎,但仅受 GNU find 支持。在 Mac 上,您必须使用第二种形式。
**
** To find all SCGI files: 查找所有 SCGI 文件的方法:
**
** find *.website -name '*.scgi' -type f -print
**
** If any file is a security concern, it can be disabled on a live
** installation by turning off read permissions:
如果任何文件引起安全担忧,可以通过关闭读取权限在生产环境中禁用该文件:
**
** chmod 0000 file-of-concern
**
** SCGI Specification Files: SCGI 规范文件:
**
** Content files (files without the execute bit set) that end with ".scgi"
** specify a connection to an SCGI server. The format of the .scgi file
** follows this template:
不包含执行位的内容文件(以 ".scgi" 结尾的文件)指定与 SCGI 服务器的连接。 .scgi 文件的格式如下:
**
** SCGI hostname port
** fallback: fallback-filename
** relight: relight-command
**
** The first line specifies the location and TCP/IP port of the SCGI
** server that will handle the request. Subsequent lines determine
** what to do if the SCGI server cannot be contacted. If the
** "relight:" line is present, then the relight-command is run using
** system() and the connection is retried after a 1-second delay. Use
** "&" at the end of the relight-command to run it in the background.
** Make sure the relight-command does not generate output, or that
** output will become part of the SCGI reply. Add a ">/dev/null"
** suffix (before the "&") to the relight-command if necessary to
** suppress output. If there is no relight-command, or if the relight
** is attempted but the SCGI server still cannot be contacted, then
** the content of the fallback-filename file is returned as a
** substitute for the SCGI request. The mimetype is determined by the
** suffix on the fallback-filename. The fallback-filename would
** typically be an error message indicating that the service is
** temporarily unavailable.
第一行指定 SCGI 服务器的位置和 TCP/IP 端口,随后的行确定如果无法联系 SCGI 服务器将采取什么措施。如果存在 "relight:" 行,则使用 system() 运行 relight-command,并在 1 秒延迟后重试连接。在 relight-command 的末尾使用 "&" 以在后台运行它。确保 relight-command 不会生成输出,否则输出将成为 SCGI 回复的一部分。如果没有 relight-command,或者尝试 relight 但 SCGI 服务器仍无法联系,则将 fallback-filename 文件的内容作为 SCGI 请求的替代返回。 mimetype 由 fallback-filename 的后缀确定。 fallback-filename 通常是指示服务暂时不可用的错误消息。
**
** Basic Authorization: 基本授权
**
** If the file "-auth" exists in the same directory as the content file
** (for both static content and CGI) then it contains the information used
** for basic authorization. The file format is as follows:
如果文件 "-auth" 存在于与内容文件(对于静态内容和 CGI 文件都是如此)相同的目录中,则其中包含用于基本授权的信息。文件格式如下:
**
** * Blank lines and lines that begin with '#' are ignored
** * "http-redirect" forces a redirect to HTTPS if not there already
** * "https-only" disallows operation in HTTP
** * "user NAME LOGIN:PASSWORD" checks to see if LOGIN:PASSWORD
** authorization credentials are provided, and if so sets the
** REMOTE_USER to NAME.
** * "realm TEXT" sets the realm to TEXT.
空白行和以 '#' 开头的行将被忽略。
"http-redirect" 强制将请求重定向到 HTTPS(如果尚未进行)。
"https-only" 不允许在 HTTP 中操作。
"user NAME LOGIN:PASSWORD" 检查是否提供了 LOGIN:PASSWORD 授权凭据,如果是,则将 REMOTE_USER 设置为 NAME。
"realm TEXT" 将 realm 设置为 TEXT。
**
** There can be multiple "user" lines.
If no "user" line matches, the request fails with a 401 error.
可以有多个 "user" 行。如果没有匹配的 "user" 行,则请求将以 401 错误失败。
** Because of security rule (7), there is no way for the content of the "-auth"
** file to leak out via HTTP request.
由于安全规则(7),不存在通过 HTTP 请求泄漏 "-auth" 文件内容的途径。
*/
头文件部分
#include <stdio.h>
#include <ctype.h>
#include <syslog.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <time.h>
#include <sys/times.h>
#include <netdb.h>
#include <errno.h>
#include <sys/resource.h>
#include <signal.h>
#include <dirent.h>
#ifdef linux
#include <sys/sendfile.h>
#endif
#include <assert.h>
常量部分
/*
** Configure the server by setting the following macros and recompiling.
通过设置以下宏并重新编译来配置服务器。
*/
#ifndef DEFAULT_PORT
#define DEFAULT_PORT "80" /* Default TCP port for HTTP ,HTTP 的默认 TCP 端口 */
#endif
#ifndef MAX_CONTENT_LENGTH
#define MAX_CONTENT_LENGTH 250000000 /* Max length of HTTP request content,HTTP 请求内容的最大长度 */
#endif
#ifndef MAX_CPU
#define MAX_CPU 30 /* Max CPU cycles in seconds,CPU 循环的最大秒数 */
#endif
#ifndef ALTHTTPD_VERSION
#define ALTHTTPD_VERSION "2.0"
#endif
#ifndef BANISH_TIME
#define BANISH_TIME 300 /* How long to banish for abuse (sec) ,封禁滥用的时长(秒)*/
#endif
#ifndef SERVER_SOFTWARE
# define SERVER_SOFTWARE "althttpd " ALTHTTPD_VERSION
#endif
#ifndef SERVER_SOFTWARE_TLS
# ifdef ENABLE_TLS
# define SERVER_SOFTWARE_TLS SERVER_SOFTWARE ", " OPENSSL_VERSION_TEXT
# else
# define SERVER_SOFTWARE_TLS SERVER_SOFTWARE
# endif
#endif
/*
** Clock ID to use for clock_gettime(), for use in collecting the
** wall-clock processing time using clock_gettime() (which is
** signal-safe). We use clock_gettime(), rather than gmtime(), for
** measuring request wall-clock time because it's signal-safe.
** See discussion at:
** https://sqlite.org/althttpd/forumpost/4dc31619341ce947
用于 clock_gettime() 的时钟标识,用于使用 clock_gettime()(它是信号安全的)收集墙钟处理时间。
我们使用 clock_gettime() 而不是 gmtime() 来测量请求的墙钟时间,因为它是信号安全的。详见讨论:
*/
#ifdef _POSIX_MONOTONIC_CLOCK
# define ALTHTTPD_CLOCK_ID CLOCK_MONOTONIC
#else
# define ALTHTTPD_CLOCK_ID CLOCK_REALTIME
/* noting that this can jump if the system time changes
注意,如果系统时间更改,这可能会跳跃 */
#endif
程序数据集
/*
** We record most of the state information as global variables. This
** saves having to pass information to subroutines as parameters, and
** makes the executable smaller...
我们将大部分状态信息记录为全局变量。
这样可以避免将信息作为参数传递给子程序,也使得可执行文件更小...
*/
static const char *zRoot = 0; /* Root directory of the website 网站的根目录*/
static char *zPostData= 0; /* POST data POST请求的数据*/
static int nPostData = 0; /* Number of bytes of POST data POST数据的字节数*/
static char *zProtocol = 0; /* The protocol being using by the browser 浏览器使用的协议*/
static char *zMethod = 0; /* The method. Must be GET HTTP请求的方法,必须为GET*/
static char *zScript = 0; /* The object to retrieve 要获取的对象*/
static char *zRealScript = 0; /* The object to retrieve. Same as zScript except might have "/index.html" appended
要获取的对象,与zScript相同,可能附加了"/index.html"。*/
static char *zRequestUri = 0; /* Sanitized request uri 经过处理的请求URI。*/
static char *zHome = 0; /* The directory containing content 包含内容的目录。*/
static char *zQueryString = 0; /* The query string on the end of the name 名称末尾的查询字符串。 */
static char *zFile = 0; /* The filename of the object to retrieve 要获取的对象的文件名。*/
static int lenFile = 0; /* Length of the zFile name, zFile名称的长度。*/
static char *zDir = 0; /* Name of the directory holding zFile 包含zFile的目录的名称。*/
static char *zPathInfo = 0; /* Part of the pathname past the file 文件之后路径的一部分。*/
static char *zAgent = 0; /* What type if browser is making this query 发起此查询的浏览器类型。*/
static char *zServerName = 0; /* The name after the http:// "http://"后的名称。*/
static char *zServerPort = 0; /* The port number 端口号。*/
static char *zServerSoftware = 0;/* Software name and version info 软件名称和版本信息。*/
static char *zCookie = 0; /* Cookies reported with the request 请求中报告的Cookies。*/
static char *zHttpHost = 0; /* Name according to the web browser 根据Web浏览器的名称。*/
static char *zRealPort = 0; /* The real TCP port when running as daemon 作为守护进程运行时的实际TCP端口。*/
static char *zRemoteAddr = 0; /* IP address of the request 请求的IP地址。*/
static char *zReferer = 0; /* Name of the page that refered to us 引用我们的页面的名称。*/
static char *zAccept = 0; /* What formats will be accepted 将接受的格式。*/
static char *zAcceptEncoding =0; /* gzip or default 接受的编码格式,可以是gzip或默认。*/
static char *zContentLength = 0; /* Content length reported in the header 报头中报告的内容长度。*/
static char *zContentType = 0; /* Content type reported in the header 报头中报告的内容类型。*/
static char *zQuerySuffix = 0; /* The part of the URL after the first ? 第一个问号之后的URL的一部分。*/
static char *zAuthType = 0; /* Authorization type (basic or digest) 授权类型(基本或摘要)。*/
static char *zAuthArg = 0; /* Authorization values 授权值。*/
static char *zRemoteUser = 0; /* REMOTE_USER set by authorization module 由授权模块设置的REMOTE_USER。 */
static char *zIfNoneMatch= 0; /* The If-None-Match header value 表示If-None-Match头的值。*/
static char *zIfModifiedSince=0; /* The If-Modified-Since header value 表示If-Modified-Since头的值。*/
static char *zHttpScheme = "http";/* HTTP_SCHEME CGI variable 表示HTTP_SCHEME CGI变量,默认为"http"。*/
static char *zHttps = 0; /* HTTPS CGI variable 表示HTTPS CGI变量。*/
static int nIn = 0; /* Number of bytes of input 输入的字节数。*/
static int nOut = 0; /* Number of bytes of output 输出的字节数。*/
static char zReplyStatus[4]; /* Reply status code 回复的状态码。*/
static int statusSent = 0; /* True after status line is sent 在发送状态行后为真。*/
static const char *zLogFile = 0; /* Log to this file 记录日志到这个文件。*/
static char zExpLogFile[500] = {0}; /* %-expanded log file name 表示%扩展的日志文件名。*/
static const char *zIPShunDir=0; /* Directory containing hostile IP addresses 包含敌对IP地址的目录。*/
static int debugFlag = 0; /* True if being debugged如果正在调试,则为真。 */
static struct timeval beginTime; /* Time when this process starts 进程启动的时间。*/
static struct timespec tsBeginTime; /* clock_gettime() when request processing starts 请求处理开始时的clock_gettime()。*/
static int closeConnection = 0; /* True to send Connection: close in reply 为真时在回复中发送Connection: close。*/
static int nRequest = 0; /* Number of requests processed 处理的请求数量。*/
static int omitLog = 0; /* Do not make logfile entries if true 如果为真,则不生成日志文件条目。*/
static int useHttps = 0; /* 0=HTTP, 1=external HTTPS (stunnel), 2=builtin TLS support
使用的协议,0表示HTTP,1表示外部HTTPS(通过stunnel),2表示内置的TLS支持。*/
static int useTimeout = 1; /* True to use times 为真时使用时间。*/
static int nTimeoutLine = 0; /* Line number where timeout was set 设置超时的行号。 */
static int standalone = 0; /* Run as a standalone server (no inetd)为真时作为独立服务器运行(无inetd)。 */
static int ipv6Only = 0; /* Use IPv6 only */
static int ipv4Only = 0; /* Use IPv4 only */
static struct rusage priorSelf; /* Previously report SELF time 表示之前报告的SELF时间。*/
static struct rusage priorChild; /* Previously report CHILD time 之前报告的CHILD时间。*/
/*static struct timespec tsSelf;*/
static int mxAge = 120; /* Cache-control max-age 表示Cache-control的最大年龄。*/
static char *default_path = "/bin:/usr/bin"; /* Default PATH variable 默认的PATH变量。*/
static char *zScgi = 0; /* Value of the SCGI env variable 表示SCGI环境变量的值*/
static int rangeStart = 0; /* Start of a Range: request 表示Range请求的起始位置。*/
static int rangeEnd = 0; /* End of a Range: request 表示Range请求的结束位置。*/
static int maxCpu = MAX_CPU; /* Maximum CPU time per process 每个进程的最大CPU时间。 */
static int enableSAB = 0; /* Add reply header to enable SharedArrayBuffer
是否添加响应头以启用SharedArrayBuffer。
SharedArrayBuffer是一种在Web Workers之间共享数据的机制。*/
static int inSignalHandler = 0; /* True if running a signal handler 是否正在运行信号处理程序。*/
到了这里,关于源码学习:web server althttpd (未完待续)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!