源码学习:web server althttpd (未完待续)

这篇具有很好参考价值的文章主要介绍了源码学习:web server althttpd (未完待续)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

源码路径:

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连接有两个选项:

  1. 可以使用定义了ENABLE_TLS宏并链接到-lssl -lcrypto的althttpd构建,并使用–cert fullchain.pem和–pkey privkey.pem标志启动。
  2. 可以通过外部连接服务(如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” 二进制文件。

所需材料

  1. 从本网站获取althttpd.c源代码文件。
  2. 最新版的OpenSSL发布版的tar包。
  3. 常见的Unix构建工具,如 “make”、“gcc” 等。
  4. OpenSSL构建需要Perl。

构建步骤

  1. 编译静态的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构建过程中,可以边喝茶边等待。
  2. 编译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当作字符串使用

#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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 给深度学习研究生的入门建议(未完待续ing)

    诸神缄默不语-个人CSDN博文目录 本文将系统性介绍深度学习方向(准)研究生可供参考的入门建议。 我的背景是浙江大学人工智能专业在读硕士,研究方向是GNN、NLP、司法智能。 (我的CSDN博文基本涵盖了我所有的深度学习知识,建议参考) 选专业、选学校、选导师之类心态

    2023年04月24日
    浏览(61)
  • 【未完待续】综述:用于视频分割(Video Segmentation)的深度学习

    A Survey on Deep Learning Technique for Video Segmentation 本文回顾视频分割的两条基本研究路线:视频目标分割(object segmentation)和视频语义分割(semantic segmentation)。本文介绍它们各自的task setting、背景概念、感知需求、发展历史以及主要挑战。本文详细概述相关的方法和数据集的代

    2024年02月02日
    浏览(46)
  • SCI一区论文阅读小结之深度学习在气象领域应用(未完待续)

    最近文献调研,发现一个研究相近的师兄最近发的几篇文章给的启发性很高,阅读文献的同时也对这几篇文章做个总结,以防自己忘记,也分享给大家。 首先,甩出这位大佬的RG: https://www.researchgate.net/profile/Xuan-Tong-3/research 最近的研究都是将深度学习应用到气象领域的,比如

    2024年02月21日
    浏览(36)
  • Verilog(未完待续)

    Verilog教程 这个教程写的很好,可以多看看。本篇还没整理完。 什么是FPGA?一种可通过编程来修改其逻辑功能的数字集成电路(芯片) 与单片机的区别?对单片机编程并不改变其地电路的内部结构,只是根据要求实现的功能来编写运行的程序(指令)。 举例:单片机就两个

    2024年03月19日
    浏览(54)
  • Vivado使用记录(未完待续)

    字体大小修改:Setting、Font Quick Start 组包含有 Create Project(创建工程)、 Open Project(打开工程)、 Open Example Project(打开实例工程)。 Tasks 组包含有 Manage IP(管理 IP)、 Open Hardware Manager(打开硬件管理器)、 Xilinx Tcl Store( Tcl 脚本存储库)。 Learning Center 组包含有 Documen

    2024年03月09日
    浏览(45)
  • PHP复习资料(未完待续)

    特别鸣谢:@NLER提供雨课堂数据 (未完待续,请持续关注此板块) 【计科三四】雪课堂PHP期末模拟题:https://ks.wjx.top/vm/tUAmjxq.aspx# 【计科一二】PHP第一章练习题 https://ks.wjx.top/vm/QnjHad4.aspx# 【计科一二】PHP第二章练习题 https://ks.wjx.top/vm/h2FvEVI.aspx# 【计科一二】PHP第三章练习题

    2024年02月07日
    浏览(41)
  • 卫星下行链路预算模型(未完待续)

    1. 接收端天线模型 简单一些,考虑地球同步卫星多波束通信系统,波束指向固定。波束数量为 N b N_b N b ​ . 波束中心在地面的位置可以用经度向量和纬度向量表示: P ⃗ l g = [ l 1 , l 2 , . . . , l N b ] P ⃗ l a = [ a 1 , a 2 , . . . , a N b ] vec{P}_{lg} = [l_1, l_2,...,l_{N_b}] \\\\ vec{P}_{la} = [a_

    2024年02月03日
    浏览(37)
  • [DASCTF7月赛](未完待续~)

    misc Coffee desu! hint1: Strange Protocol 经过搜索,发现这个是一种恶搞协议: 自定义了一些请求方式 结合首页英文: 我们需要使用 BREW 向服务器添加 milktea 返回: 这个协议定义了: Accept-Additions 头,于是我们添加上: milktea [外链图片转存失败,源站可能有防盗链机制,建议将图片保

    2024年02月16日
    浏览(35)
  • Provide/Inject 依赖注入(未完待续)

    父组件传递给子组件数据,通过props,但是需要逐层传递 provide/Inject 的推出就是为了解决这个问题,它提供了一种组件之间共享此类值的方式,不必通过组件树每层级显示地传递props 目的是为了共享那些被 认为对于一个组件树而言是全局的数据 provide 接受两个参数:第一个参

    2024年01月20日
    浏览(39)
  • Java多线程:Lock锁(未完待续)

    在Java中,Lock是一个接口,它提供了比synchronized更高级的线程同步机制。使用Lock接口可以创建更复杂和灵活的同步结构。 Lock接口的常用实现类有ReentrantLock和ReentrantReadWriteLock,它们提供了可重入的互斥锁和读写锁。 相比synchronized来实现同步,使用Lock实现同步主要有以

    2024年02月02日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包