Project

General

Profile

Actions

Building lighttpd on Windows

lighttpd cygwin

lighttpd cygwin is maintained as a set of packages available from cygwin setup.exe and supports nearly all the same features of lighttpd available on unix-like systems. However, extra precautions may need to be taken to protect a _WIN32 system (see below), so you are strongly advised against using lighttpd cygwin in production.

lighttpd native _WIN32

lighttpd development prototype for native _WIN32 may be built static or shared (dll) using autotools or CMake with compiler toolchain mingw (cygwin or msys) or MS Visual Studio.
https://github.com/gstrauss/lighttpd1.4/tree/win32-exp
This is a prototype and you are strongly advised against using lighttpd native _WIN32 in production.

lighttpd native _WIN32 limitations

  • not implemented
    - server.c: no daemonize
    - server.c: no multiple workers
    - server.c: no lighttpd -1 one-shot mode
    - log.c: no syslog option (no Windows Event Log)
  • _WIN32 accepts both / and \ as path separators
    lighttpd code has not been reviewed to normalize _WIN32 \ to /.
    (possible security exposure)
  • _WIN32 environment paths may begin with volume C:/ instead of with /.
    (possible security exposure)
    lighttpd code has not been reviewed to normalize _WIN32 paths.
    lighttpd code assumes in many places that full path begins with /.
    lighttpd might not work across multiple volumes.
  • _WIN32 NTFS alternate data streams not rejected
    (possible security exposure)
    https://redmine.lighttpd.net/issues/1335
  • _WIN32 filesystem functions do not properly handle UTF-8
    (ASCII works when UNICODE is not defined; wide-char works with UNICODE)
    Some UTF-8 to wide-char compat translation funcs provided in fs_win32.[ch],
    but not comprehensive. unlink(), rmdir(), chdir() not provided, or others ...
    (move, replace, etc and other use of _WIN32 ...A() funcs might need to be
    replaced with funcs which convert from UTF-8 to wide-char and use ...W())
  • NTFS fails attempt to rename files atomically if either file is open.
    lighttpd uses this idiom in file caches (deflate.cache-dir disabled on _WIN32)
    lighttpd mod_webdav will fail if a PUT or MOVE is attepted on open an file.
    Many file operations other than read may fail due to this NTFS limitation.
  • lighttpd mod_webdav has not been ported and will have multiple issues,
    including but not limited to atomic file renames, non-ASCII UTF-8 file support
  • lighttpd does not attempt to detect setCaseSensitiveInfo dir attribute
  • ...

MS fuglies

  • SOCKET is (long long unsigned), not (int) as in POSIX file descriptors.
    In practice (sample size n=1), SOCKET appears to be < INT_MAX, except for
    INVALID_SOCKET ((long long unsigned)-1), which casts back to (int)-1.
    While it does not appear necessary to rewrite all code to use SOCKET instead
    of int, it would be nice that IFF true, that it be documented by Microsoft.
    Both the size of the type (64-bit) and the sign (unsigned) is unfriendly to
    porting code. It is suggested that historically SOCKET was a pointer value
    cast to (uintptr_t) (good job with type-safety $MS!). In 32-bit, int and
    pointers are the same size, but not in 64-bit. Perhaps this was changed at
    some point in $MS evolution, but is not well documented, as at least initial
    assignments are smallish integers in 200's and 300's. $MS is able to
    distinguish between file descriptors (int) and (SOCKET) since both are
    supported by SetHandleInformation(), ReadFile(), WriteFile(), so we can
    conclude that $MS did not propagate that logic elsewhere, but probably could.
    ((HANDLE)(uint64_t)sockfd); ((HANDLE)_get_osfhandle(fd)) (e.g. file and pipes)
  • SOCKET and file descriptors are not allowed as lowest available integer,
    so using an array indexed by file descriptors is not a good pattern to apply.
  • select() and WSAPoll() work only with SOCKET, not with files or pipes.
    (see WaitForSingleObject() WaitForMultipleObjects() and related functions)
    (also WSAEventSelect()+WSAWaitForMultipleEvents())
  • select() is not a bitmap; client code around select() often needs to be
    reworked. [1] The macros such as FD_ISSET must be applied to SOCKET, and not
    to arbitrary integer in range from [0,FD_SETSIZE)
    https://devblogs.microsoft.com/oldnewthing/20221102-00/?p=107343
    https://devblogs.microsoft.com/oldnewthing/20161221-00/?p=94985
  • filesystem redirection (stdin/stdout/stderr) to sockets is possible, but:
    - filehandles must be inheritable (default)
    - sockets must be non-overlapped (not default)
    - socket() creates sockets with WSA_FLAG_OVERLAPPED
    - WSASocket() has flag option to omit WSA_FLAG_OVERLAPPED
  • programs generally need SYSTEMROOT defined in the environment to run properly
  • #define WIN32_LEAN_AND_MEAN must be defined before #include <windows.h>
    or else you may get (undesirable) winsock 1.1 header visibility
    https://learn.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application
    For some apps, #include <winsock2.h> is sufficient instead of #include <windows.h>
    Must link with -lws2_32
    #ifdef _MSC_VER
    #pragma comment(lib, "ws2_32.lib")
    #endif
  • connect() on non-blocking socket errors with WSAGetLastError() WSAWOULDBLOCK,
    not EINPROGRESS
  • dup() and dup2() can not be used on SOCKET (see WSADuplicateSocket())
  • dup2() does not return the newfd; incompatible interface
  • stat() on a directory fails if string has trailing / or \
  • stat() returns ENOENT instead of ENOTDIR for CGI PATH_INFO,
    e.g. /real/file.cgi/path/info
  • stat() fails on //?/c:/... paths
  • error codes: WSAGetLastError() for sockets; do not check errno
  • error codes: GetLastError() (except for sockets), and sometimes errno
  • Microsoft NTFS does not allow move/rename file operations on open files.
    (Attempts to do so result in Permission denied; file already in use.)
    A typical POSIX idiom of atomic rename fails on NTFS if either old or new
    file is open. This breaks in lighttpd in numerous places where a cache is
    updated and the new file is kept open during the rename, and then served to
    the client. Forcing the file to be closed and then reopened around the
    rename introduces a race condition in what is ultimately sent to the client.
    (While this race is introduced in mod_deflate cache, mod_webdav and other
    modules might still suffer/fail from this NTFS limitation.)
  • UCRT <sys/types.h> unconditionally defines off_t as long (4 bytes on _WIN32)
    if _CRT_DECLARE_NONSTDC_NAMES is defined. Any application or library which
    supports large files and expects 64-bit off_t needs to take steps to
    workaround this (e.g. define off_t, _off_t, _OFF_T_DEFINED in advance).
  • ...

[1] https://learn.microsoft.com/en-us/windows/win32/winsock/select-and-fd---2

Select, FD_SET, and FD_XXX Macros
Since sockets are not represented by the UNIX-style, small, non-negative integer, the implementation of the select function was changed in Windows Sockets. Each set of sockets is still represented by the FD_SET structure, but instead of being stored as a bitmask, the set is implemented as an array of sockets. To avoid potential problems, applications must adhere to the use of the FD_XXX macros to set, initialize, clear, and check the FD_SET structures.

Updated by gstrauss about 1 year ago · 11 revisions