操作
在 Windows 上构建 lighttpd¶
lighttpd 原生 _WIN32¶
lighttpd 原生 _WIN32 可以使用 autotools 或 CMake,配合 mingw(cygwin 或 msys)或 MS Visual Studio 编译器工具链进行静态或共享(dll)构建。
lighttpd 原生 _WIN32 限制¶
- 未实现
- server.c: 不支持守护进程化,但可以作为 Windows 服务安装
- server.c: 不支持多工作进程
- server.c: 不支持 lighttpd -1 一次性模式
- log.c: 不支持 syslog 选项(不支持 Windows 事件日志) - _WIN32 接受
/
和\
作为路径分隔符
lighttpd 代码尚未经过审查,以将 _WIN32 的\
规范化为/
。
(可能存在安全漏洞) - _WIN32 环境变量路径可能以卷标
C:/
开头,而不是/
。
(可能存在安全漏洞)
lighttpd 代码尚未经过审查,以规范化 _WIN32 路径。
lighttpd 代码在许多地方假定完整路径以/
开头。
lighttpd 可能无法跨多个卷工作。 - _WIN32 NTFS 备用数据流未被拒绝
(可能存在安全漏洞)
https://redmine.lighttpd.ac.cn/issues/1335 - _WIN32 文件系统函数未能正确处理 UTF-8
(未定义 UNICODE 时 ASCII 有效;定义 UNICODE 时宽字符有效)
fs_win32.[ch] 中提供了一些 UTF-8 到宽字符的兼容转换函数,
但不全面。unlink()、rmdir()、chdir() 或其他函数未提供...
(move、replace 等以及其他使用 _WIN32 ...A() 函数的地方可能需要
替换为将 UTF-8 转换为宽字符并使用 ...W() 的函数) - 如果任一文件处于打开状态,NTFS 无法原子地重命名文件。
lighttpd 在文件缓存中使用了这种惯用法(deflate.cache-dir 在 _WIN32 上已禁用)
如果对打开的文件尝试 PUT 或 MOVE 操作,lighttpd mod_webdav 将失败。
由于此 NTFS 限制,许多文件操作(除读取外)可能会失败。 - lighttpd mod_webdav 尚未移植,将存在多个问题,
包括但不限于原子文件重命名、非 ASCII UTF-8 文件支持 - lighttpd 不尝试检测 setCaseSensitiveInfo 目录属性
- ...
Microsoft 的怪异之处¶
SOCKET
是(long long unsigned)
,而不是像 POSIX 文件描述符那样的(int)
。
实际上(样本量 n=1),SOCKET
似乎小于INT_MAX
,除了
INVALID_SOCKET
((long long unsigned)-1)
,它会强制转换为(int)-1
。
虽然似乎没有必要将所有代码重写为使用 SOCKET 而不是
int,但如果这是真的,微软能将其文档化会很好。
类型的尺寸(64 位)和符号(unsigned
)都对代码移植不友好。
据推测,历史上SOCKET
是一个指针值
强制转换为(uintptr_t)
(微软在类型安全方面做得好!)。在 32 位系统中,int 和
指针大小相同,但在 64 位系统中则不同。这可能在
微软演进的某个时候发生了变化,但没有得到很好的文档记录,因为至少最初的
赋值是 200 和 300 左右的小整数。微软能够
区分文件描述符(int)
和(SOCKET)
,因为两者都
受SetHandleInformation()
、ReadFile()
、WriteFile()
支持,因此我们可以
得出结论,微软没有在其他地方推广该逻辑,但可能可以。
((HANDLE)(uint64_t)sockfd)
;((HANDLE)_get_osfhandle(fd))
(例如文件和管道)SOCKET
和文件描述符不允许作为最低可用整数,
因此使用文件描述符作为索引的数组不是一个好的应用模式。select()
和WSAPoll()
仅适用于SOCKET
,不适用于文件或管道。
(参见WaitForSingleObject()
WaitForMultipleObjects()
和相关函数)
(另见WSAEventSelect()
+WSAWaitForMultipleEvents()
)select()
不是位图;围绕select()
的客户端代码通常需要
返工。[1]FD_ISSET
等宏必须应用于SOCKET
,而不是
任意整数,范围从 [0,FD_SETSIZE
)。
https://devblogs.microsoft.com/oldnewthing/20221102-00/?p=107343
https://devblogs.microsoft.com/oldnewthing/20161221-00/?p=94985- 文件系统重定向(stdin/stdout/stderr)到套接字是可能的,但
- 文件句柄必须可继承(默认)
- 套接字必须是非重叠的(非默认)
-socket()
创建带WSA_FLAG_OVERLAPPED
标志的套接字
-WSASocket()
有选项可以省略WSA_FLAG_OVERLAPPED
- 程序通常需要在环境中定义
SYSTEMROOT
才能正常运行 - 必须在
#include <windows.h>
之前定义#define WIN32_LEAN_AND_MEAN
否则可能会导致(不希望的)winsock 1.1 头文件可见性
https://learn.microsoft.com/zh-cn/windows/win32/winsock/creating-a-basic-winsock-application
对于某些应用程序,#include <winsock2.h>
就足够了,无需#include <windows.h>
必须链接-lws2_32
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif - 在非阻塞套接字上调用
connect()
失败时,WSAGetLastError()
会返回WSAWOULDBLOCK
,
而不是EINPROGRESS
dup()
和dup2()
不能用于SOCKET
(参见WSADuplicateSocket()
)dup2()
不返回 newfd;接口不兼容- 如果字符串带有末尾的
/
或\
,则对目录执行stat()
会失败 - 对 CGI
PATH_INFO
,stat()
返回ENOENT
而不是ENOTDIR
,
例如 /real/file.cgi/path/info stat()
在 //?/c:/... 路径上失败- 错误代码:套接字使用
WSAGetLastError()
;不要检查errno
- 错误代码:
GetLastError()
(套接字除外),有时是errno
- Microsoft NTFS 不允许对打开的文件进行移动/重命名操作。
(尝试这样做会导致“权限拒绝;文件已在使用中”。)
如果旧文件或新文件处于打开状态,POSIX 中典型的原子重命名惯用法在 NTFS 上会失败。
这在 lighttpd 的许多地方造成了问题,例如缓存更新时,新文件在重命名期间保持打开状态,然后提供给
客户端。强制文件在重命名前后关闭再重新打开会引入竞态条件,影响最终发送给客户端的内容。
客户端。强制关闭文件然后在大约重命名时重新打开
重命名引入了一个竞态条件,这最终会发送给客户端。
(虽然这种竞态条件出现在 mod_deflate 缓存中,但 mod_webdav 和其他
模块可能仍会受此 NTFS 限制影响/失败。) - 如果定义了
_CRT_DECLARE_NONSTDC_NAMES
,UCRT 的<sys/types.h>
会无条件地将off_t
定义为 long(在_WIN32
上是 4 字节)。任何应用程序或库如果
如果定义了_CRT_DECLARE_NONSTDC_NAMES
。任何应用程序或库如果
支持大文件并期望 64 位off_t
,都需要采取措施来
解决此问题(例如提前定义off_t
,_off_t
,_OFF_T_DEFINED
)。 - ...
[1] https://learn.microsoft.com/en-us/windows/win32/winsock/select-and-fd---2
Select、FD_SET 和 FD_XXX 宏
由于套接字并非以 UNIX 风格的小型非负整数表示,Windows Sockets 中 select 函数的实现有所改变。每组套接字仍由 FD_SET 结构表示,但不再以位掩码形式存储,而是作为套接字数组实现。为避免潜在问题,应用程序必须遵循使用 FD_XXX 宏来设置、初始化、清除和检查 FD_SET 结构。
lighttpd cygwin¶
lighttpd cygwin 作为一组软件包通过 cygwin setup.exe
提供维护,支持 lighttpd 在类 Unix 系统上几乎所有相同的功能。然而,可能需要采取额外预防措施来保护 _WIN32 系统(参见下文),因此强烈建议您不要在生产环境中使用 lighttpd cygwin。