Lwan-это высокопроизводительный и масштабируемый веб-сервер.
Веб -сайт Project содержит более подробную информацию.
| ОС | Архи | Выпускать | Отлаживать | Статический анализ | Тесты |
|---|---|---|---|---|---|
| Linux | x86_64 | Сообщать историю | |||
| FreeBSD 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
Вы можете построить Lwan самостоятельно, использовать изображение контейнера, либо взять пакет из вашего любимого распространения.
Перед установкой LWAN убедитесь, что все зависимости установлены. Все они являются общими зависимостями, обнаруженными в любом распределении GNU/Linux; Имена пакетов будут разными, но не должно быть сложно искать, используя любой инструмент управления пакетами, который используется вашим распространением.
Система сборки будет искать эти библиотеки и включить/ссылку, если она будет доступна.
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (по умолчанию) передается:-DUSE_ALTERNATIVE_MALLOC к cmake со следующими значениями:pacman -S cmake zlibpkg install cmake pkgconfapt-get update && apt-get install git cmake zlib1g-dev pkg-configbrew install cmake pacman -S cmake zlib sqlite luajit mariadb-libs gperftools valgrind mbedtlspkg install cmake pkgconf sqlite3 lua51apt-get update && apt-get install git cmake zlib1g-dev pkg-config lua5.1-dev libsqlite3-dev libmariadb-dev libmbedtls-devbrew install cmake mariadb-connector-c sqlite [email protected] pkg-config ~$ git clone git://github.com/lpereira/lwan
~$ cd lwan
~/lwan$ mkdir build
~/lwan$ cd build
Выбор версии выпуска (без отладки символов, сообщений, некоторых оптимизаций и т. Д.):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
Если вы хотите включить оптимизацию, но все же используйте отладчик, используйте это вместо этого:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
Чтобы отключить оптимизацию и создать более благоприятную отладку версию:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
Это генерирует несколько двоичных файлов:
src/bin/lwan/lwan : главный исполняемый файл Lwan. Может быть выполнен с помощью --help для руководства.src/bin/testrunner/testrunner : содержит код для выполнения тестового пакета ( src/scripts/testsuite.py ).src/samples/freegeoip/freegeoip : образец образец. Требуется SQLite.src/samples/techempower/techempower : код для эталона Web Framework Techempower. Требуется библиотеки SQLite и Mariadb.src/samples/clock/clock : образец часов. Генерирует файл GIF, который всегда показывает локальное время.src/bin/tools/mimegen : создает таблицу типа расширения-имимета. Используется во время процесса сборки.src/bin/tools/bin2hex : генерирует файл C из двоичного файла, подходящий для использования с #include. Используется во время процесса сборки.src/bin/tools/configdump : сбрасывает файл конфигурации, используя API конфигурации чтения. Используется для тестирования.src/bin/tools/weighttp : Перепишите инструмент Benchmarking weighttp HTTP.src/bin/tools/statuslookupgen : генерирует идеальную хеш -таблицу для кодов статуса HTTP и их описания. Используется во время процесса сборки. Прохождение -DCMAKE_BUILD_TYPE=Release позволит некоторым оптимизациям компилятора (например, LTO) и настраивает код для текущей архитектуры.
Важный
Пожалуйста, используйте сборку релиза при сравнительном сравнении . По умолчанию - сборка отладки, которая не только регистрирует все запросы на стандартный вывод, но и делает это при хранении блокировки, серьезно удерживая сервер.
Сборка по умолчанию (то есть не передает -DCMAKE_BUILD_TYPE=Release ) построит версию, подходящую для отладки. Эта версия может использоваться под Valgrind (если присутствуют его заголовки) и включает отладку сообщений, которые разделены в версии выпуска. Отладка сообщения напечатаны для каждого запроса.
На этих сборках можно включить дезинфицирующие средства. Чтобы выбрать, с каким из них создать Lwan, укажите один из следующих вариантов в строке вызова Cmake:
-DSANITIZER=ubsan выбирает не определенное поведение дезинфицирующее средство.-DSANITIZER=address выбирает адресное дезинфицирующее средство.-DSANITIZER=thread Выбирает дезинфицирующее средство потока. Альтернативные распределители памяти также могут быть выбраны. В настоящее время Lwan поддерживает Tcmalloc, Mimalloc и Jemalloc из коробки. Чтобы использовать любого из них, передайте -DALTERNATIVE_MALLOC=name в строку вызова Cmake, используя имена, представленные в разделе «Необязательные зависимости».
Опция -DUSE_SYSLOG=ON может быть передана в Cmake, чтобы также войти в системный журнал в дополнение к стандартному выходу.
Если вы строите Lwan для распределения, может быть целесообразно использовать опцию -DMTUNE_NATIVE=OFF , в противном случае сгенерированный бинар может не работать на некоторых компьютерах.
Поддержка TLS включена автоматически в присутствии подходящей установки MBEDTLS в Linux Systems с заголовками, достаточно новыми для поддержки KTL, но может быть отключена путем передачи -DENABLE_TLS=NO Cmake.
~/lwan/build$ make testsuite
Это составит программу testrunner и выполнить регрессионный набор тестов в src/scripts/testsuite.py .
~/lwan/build$ make benchmark
Это составит testrunner и выполнять эталонный сценарий src/scripts/benchmark.py .
Lwan также может быть построен с типом сборки покрытия, указав -DCMAKE_BUILD_TYPE=Coverage . Это позволяет создавать цель generate-coverage , которая будет запускать testrunner для подготовки отчета о покрытии тестирования с LCOV.
Каждый коммит в этом репозитории вызывает генерацию этого отчета, и результаты публично доступны.
Настройка сервера, редактируя предоставленную lwan.conf ; Формат объясняется подробно ниже.
Примечание
Lwan попытается найти файл конфигурации, основанный в исполняемом имени в текущем каталоге; testrunner.conf будет использоваться для бинарного testrunner , lwan.conf для бинарного lwan и так далее.
Файлы конфигурации загружаются из текущего каталога. Если в этом файле не внесено никаких изменений, запуск LWAN будет обслуживать статические файлы, расположенные в каталоге ./wwwroot . Lwan прослушает порт 8080 на всех интерфейсах.
LWAN обнаружит количество процессоров, увеличит максимальное количество дескрипторов открытых файлов и, как правило, станет лучше всего, чтобы автоматически разместить разумные настройки для среды, в которой она работает. Многие из этих настроек можно настроить в файле конфигурации, но обычно это хорошая идея, чтобы не связываться с ними.
Кончик
Необязательно, двоичный файл lwan может использоваться для одноразового статического файла без какого-либо файла конфигурации. Запустите его с --help помощи в этом.
Lwan использует знакомый key = value . Комментарии поддерживаются символом # (аналогично эг -сценариям Shell, Python и Perl). Вложенные секции могут быть созданы с вьющимися скобками. Раздели могут быть пустыми; В этом случае вьющиеся скобки являются необязательными.
some_key_name эквивалентен some key name в файлах конфигурации (в качестве детали реализации, параметры конфигурации чтения кода будет предоставлена версия только с подчеркиванием).
Кончик
Значения могут содержать переменные среды. Используйте синтаксис ${VARIABLE_NAME} . Значения по умолчанию могут быть указаны с помощью толстой кишки (например, ${VARIABLE_NAME:foo} , который оценивается как ${VARIABLE_NAME} если он установлен, или foo иначе).
sound volume = 11 # This one is 1 louder
playlist metal {
files = '''
/multi/line/strings/are/supported.mp3
/anything/inside/these/are/stored/verbatim.mp3
'''
}
playlist chiptune {
files = """
/if/it/starts/with/single/quotes/it/ends/with/single/quotes.mod
/but/it/can/use/double/quotes.s3m
"""
}
Некоторые примеры можно найти в lwan.conf и techempower.conf .
Константы могут быть определены и повторно используются по всему файлу конфигурации, указав их в разделе constants в любом месте в файле конфигурации. Константа будет доступна только после того, как этот раздел определит определенную константу. Константы могут быть повторно определены. Если константа не определена, ее значение будет получено из переменной среды. Если он не определен ни в одном разделе constants , ни в окружающей среде, Lwan будет прервать соответствующее сообщение об ошибке.
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
Один и тот же синтаксис для значений по умолчанию, указанных выше, действителен здесь (например, указание user_name как ${USER:nobody} установит ${user_name} nobody , если ${USER} не установлен в переменной среды или не является другой постоянной.)
| Тип | Описание |
|---|---|
str | Любой вид текста в свободной форме, обычно специфичный для приложения |
int | Целое число. Диапазон является специфичным для приложения |
time | Временный интервал. См. Таблицу ниже для единиц |
bool | Логическая ценность. См. Таблицу ниже для действительных значений |
Поля времени могут быть указаны с использованием множителей. Можно указать несколько, они просто объединены; Например, «1M 1W» указывает «1 месяц и 1 неделя» (37 дней). В следующей таблице перечислены все известные множители:
| Множитель | Описание |
|---|---|
s | Секунды |
m | Минуты |
h | Часы |
d | Дни |
w | 7-дневные недели |
M | 30-дневные месяцы |
y | 365-дневные годы |
Примечание
Число с множителем, которого нет в этой таблице, игнорируется; Предупреждение выдается при чтении файла конфигурации. Между номером и его множителем не должно существовать места.
| Истинные значения | Ложные значения |
|---|---|
| Любое целое число, отличное от 0 | 0 |
on | off |
true | false |
yes | no |
Как правило, хорошая идея, чтобы Lwan решать лучшие настройки для вашей среды. Однако не каждая среда одинакова, и не все использование могут быть определены автоматически, поэтому предоставляются некоторые параметры конфигурации.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
keep_alive_timeout | time | 15 | Тайм -аут, чтобы сохранить связь |
quiet | bool | false | Установите, чтобы не печатать какие -либо сообщения отладки. Только эффективно в сборе выпуска. |
expires | time | 1M 1w | Стоимость заголовка «истекает». По умолчанию 1 месяц и 1 неделя |
threads | int | 0 | Количество потоков ввода/вывода. По умолчанию (0) - количество онлайн -процессоров |
proxy_protocol | bool | false | Включает протокол прокси. Версии 1 и 2 поддерживаются. Включите эту настройку только при использовании Lwan за прокси, а прокси -сервер поддерживает этот протокол; В противном случае это позволяет любому поддельно подделать IP -адреса происхождения |
max_post_data_size | int | 40960 | Устанавливает максимальное количество размера данных для запросов POST, в байтах |
max_put_data_size | int | 40960 | Устанавливает максимальное количество размера данных для запросов на пут, в байтах |
max_file_descriptors | int | 524288 | Максимальное количество дескрипторов файлов. Должно быть не менее 10 threads |
request_buffer_size | int | 4096 | Запросить длину размера буфера. Если он больше, чем дефолт 4096 , он будет динамически распределен. |
allow_temp_files | str | "" | Используйте временные файлы; Установите для post запросы на почту, put запросы на пункт или all (эквивалентно настройке для post put ) для обоих. |
error_template | str | Шаблон ошибки по умолчанию | Шаблон для кодов ошибок. Смотрите переменные ниже. |
error_template| Переменная | Тип | Описание |
|---|---|---|
short_message | str | Короткое сообщение об ошибке (например, Not found ) |
long_message | str | Длительное сообщение об ошибке (например, The requested resource could not be found on this server ) |
Lwan может отказаться от своих привилегий пользователю в системе и ограничить представление файловой системы Chroot. Хотя это и не пуленепробиваемое, это обеспечивает первый слой безопасности в случае, когда в Лваню есть ошибка.
Чтобы использовать эту функцию, объявите раздел straitjacket (или straightjacket ) и установите некоторые варианты. Это требует, чтобы LWAN был выполнен как root .
Хотя этот раздел может быть записан в любом месте в файле (до тех пор, пока он является объявлением верхнего уровня), если какие -либо каталоги открыты, например, из -за того, что он создает создание модуля serve_files , LWAN отказатся начать. (Эта проверка выполняется только на Linux в качестве защиты для Malconfiguration.)
Кончик
Объявите смирительную рубашку прямо перед разделом site таким образом, что файлы конфигурации и частные данные (например, ключи TLS) находятся недоступны от сервера после того, как инициализация имела место.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
user | str | NULL | Отбросить привилегии на это имя пользователя |
chroot | str | NULL | Путь к chroot() |
drop_capabilities | bool | true | Отбросьте все возможности с помощью CAPSET (2) (под Linux) или залог (2) (под OpenBSD). |
Если необходимо указать пользовательские заголовки для каждого ответа, можно объявить раздел headers в глобальном объеме. Порядок, который кажется этим разделом, не важен.
Например, это объявление:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Будут ли переопределить заголовок Server ( Server: lwan не будет отправлен) и установит Some-Custom-Header со значением, полученным из переменной среды $WITH_THIS_ENVIRONMENT_VARIABLE .
Некоторые заголовки не могут быть переопределены, так как это вызвало бы проблемы при отправке их фактических значений при обслуживании запросов. Они включают, но не ограничены:
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow- Примечание
Названия заголовков также нечувствительны (и сохраняют дела). Переопределение SeRVeR переопределяет заголовок Server , но отправьте его так, как он был записан в файле конфигурации.
Только два слушателя поддерживаются в соответствии с процессом LWAN: HTTP слушатель (раздел listener ) и слушатель HTTPS (раздел tls_listener ). Допускается только один слушатель каждого типа.
Предупреждение
Поддержка TLS является экспериментальной. Хотя он стабилен во время первоначального тестирования, ваш пробег может варьироваться. На данный момент поддерживается только TLSV1.2, но запланирован TLSV1.3.
Примечание
Требуется поддержка TLS? Linux с встроенным или загруженным модулем tls.ko Поддержка других операционных систем может быть добавлена в будущем. FreeBSD кажется возможной, другие операционные системы, похоже, не предлагают аналогичной функции. Для неподдерживаемых операционных систем использование прокси -сервера TLS Terminator, такого как Hitch, является хорошим вариантом.
Как для разделах listener , так и для tls_listener , единственным параметром является адрес интерфейса и порт для прослушивания. Синтаксис прослушивателя - ${ADDRESS}:${PORT} , где ${ADDRESS} может быть * (привязка ко всем интерфейсам), адрес IPv6 (если окружен квадратными скобками), адрес IPv4 или имени хоста. Например, listener localhost:9876 будет прослушать только в интерфейсе lo , порт 9876 .
В то время как раздел listener не берет ключи, раздел tls_listener требует двух: cert и key (каждая указывающая, соответственно, на место на диске, где расположены сертификат TLS и файлы частных ключей) и принимает дополнительные логические ответы hsts , который контролирует, если заголовки Strict-Transport-Security будут отправлены на HTTPS-ответы.
Кончик
Чтобы сгенерировать эти клавиши для целей тестирования, инструмент командной строки OpenSSL можно использовать, например, следующее: openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
Примечание
Рекомендуется, чтобы сразу после раздела tls_listener объявлена смирительная рубашка с опцией chroot , таким образом, что пути к сертификату и ключам недоступны от этого.
Если используется активация SystemD Socket, systemd может быть указан как параметр. (Если указано несколько слушателей из SystemD, можно указать systemd:FileDescriptorName , где FileDescriptorName следует за соглашениями, установленными в документации systemd.socket .)
Примеры:
listener *:8080 # Listen on all interfaces, port 8080, HTTP
tls_listener *:8081 { # Listen on all interfaces, port 8081, HTTPS
cert = /path/to/cert.pem
key = /path/to/key.pem
}
# Use named systemd socket activation for HTTP listener
listener systemd:my-service-http.socket
# Use named systemd socket activation for HTTPS listener
tls_listener systemd:my-service-https.socket {
...
}
Раздел site группирует экземпляры модулей и обработчиков, которые будут отвечать на запросы на заданный префикс URL.
Чтобы направить URL -адреса, Lwan соответствует наибольшему общему префиксу из URI запроса с набором префиксов, указанных в разделе слушателя. То, как будет обрабатываться запрос на конкретный префикс, зависит от того, какой обработчик или модуль был объявлен в разделе слушателя. Хандлеры и модули похожи внутренне; Хандлеры являются просто функциями и не содержат состояния, а модули удерживают состояние (названный экземпляр). Несколько экземпляров модуля могут появиться в разделе слушателя.
Нет специального синтаксиса для прикрепления префикса к обработчику или модулю; Все правила анализатора конфигурации применяются здесь. Используйте ${NAME} ${PREFIX} , чтобы связать путь префикса ${PREFIX} с либо обработчиком с именем ${NAME} (если ${NAME} начинается с & , как с «Адрес» C »или модуль с именем ${NAME} . Пустые разделы могут быть использованы здесь.
Каждый модуль будет иметь свой специфический набор параметров, и они перечислены в следующих разделах. В дополнение к параметрам конфигурации, в объявлении экземпляра модуля может присутствовать специальный раздел authorization . Художники не принимают никаких параметров конфигурации, но могут включать раздел authorization .
Кончик
Выполнение LWAN с аргументом командной строки- --help покажет список встроенных модулей и обработчиков.
Ниже приведена некоторая базовая документация для модулей, поставляемых с Lwan.
Модуль serve_files будет обслуживать статические файлы и автоматически создавать индексы каталогов или служить предварительно сжатым файлам. Обычно он будет стараться изо всех сил для обслуживания файлов самым быстрым способом в соответствии с некоторой эвристикой.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
path | str | NULL | Путь к каталогу, содержащему файлы, которые будут обслуживаться |
index_path | str | index.html | Имя файла, чтобы служить индексом для каталога |
serve_precompressed_path | bool | true | Если $ file.gz существует, меньше и новее, чем $ file, и клиент принимает кодирование gzip , перенесите его |
auto_index | bool | true | Сгенерировать список каталогов автоматически, если нет файла index_path . В противном случае дает 404 |
auto_index_readme | bool | true | Включает содержание файлов README как часть автоматически генерируемого индекса каталога |
directory_list_template | str | NULL | Путь к шаблону усов для списка каталогов; По умолчанию используйте внутренний шаблон |
read_ahead | int | 131702 | Максимальное количество байтов для чтения вперед при кэшировании открытых файлов. Значение 0 отключает ReadaHead. ReadaHead выполняется с помощью потока с низким приоритетом, чтобы не блокировать потоки ввода -вывода, в то время как экстенты файлов читаются из файловой системы. |
cache_for | time | 5s | Время, чтобы сохранить метаданные файла (размер, сжатое содержимое, дескриптор открытия файла и т. Д.) В кэше |
Примечание
Файлы меньше 16 киб будут сжаты в ОЗУ для продолжительности, указанной в настройке cache_for . Lwan всегда будет пытаться сжимать с дефтатом и будет опционально сжимать Brotli и ZSTD (если Lwan был построен с надлежащей поддержкой).
В тех случаях, когда сжатие не будет стоить усилий (например, добавление заголовка Content-Encoding приведет к большему ответу, чем отправка несущественного файла, как правило, в случае с очень небольшими файлами), Lwan не будет тратить время на сжатие файла.
Для файлов, более 16 киб, Lwan не будет пытаться сжать их. В будущих версиях он может сделать это и отправлять ответы с использованием кодирования Chunked, в то время как файл сжимается (конечно, до определенного предела), но сейчас рассматриваются только предварительные файлы (см. Настройки serve_precompressed_path в таблице выше).
Для всех случаев LWAN может попытаться использовать версию GZIPD, если она найдена в файловой системе, и клиент запросил это кодирование.
directory_list_template| Переменная | Тип | Описание |
|---|---|---|
rel_path | str | Путь относительно реального пути корневого каталога |
readme | str | Содержимое First Readme File найдено ( readme , readme.txt , read.me , README.TXT , README ) |
file_list | итератор | Итераты в списке файлов |
file_list.zebra_class | str | odd для нечетных предметов, или even или даже предметов |
file_list.icon | str | Путь к значке для типа файла |
file_list.name | str | Имя файла (сбежалось) |
file_list.type | str | Тип файла (каталог или обычный файл) |
file_list.size | int | Размер файла |
file_list.unit | str | Единица для file_size |
Модуль lua позволит обслуживаться запросами сценариями, написанными на языке программирования LUA. Хотя функциональность, предоставленная этим модулем, довольно спартанская, она может запускать рамки, такие как Sailor.
Сценарии могут быть обслуживались из файлов или встроены в файл конфигурации, а результаты загрузки их, стандартные модули LUA и (необязательно, если использовать Luajit), оптимизация кода будет кэширована на некоторое время.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
default_type | str | text/plain | По умолчанию тип MIME для ответов |
script_file | str | NULL | Путь к сценарию Луа |
cache_period | time | 15s | Время, чтобы штат Луа загружался в памяти |
script | str | NULL | Встроенный сценарий Lua |
Примечание
Сценарии LUA не могут использовать глобальные переменные, так как они могут быть не обслуживаться не только различными потоками, но и состояние будет доступно только в течение количества времени, указанного в опции конфигурации cache_period . Это связано с тем, что каждый поток ввода -вывода в Lwan создаст экземпляр VM Lua (то есть одна структура lua_State для каждого потока ввода/вывода), и каждая коратика Lwan будет породить поток Lua (с lua_newthread() ) за запрос.
Нет необходимости иметь один экземпляр модуля LUA для каждой конечной точки; Один скрипт, встроенный в файл конфигурации или иным образом, может служить множеству различных конечных точек. Предполагается, что сценарии реализуют функции со следующей подписью: handle_${METHOD}_${ENDPOINT}(req) , где ${METHOD} может быть методом HTTP (то есть get , post , head и т. Д.) И ${ENDPOINT} является желаемой конечной точкой для выполнения этой функции. Функция общей handle(req) будет вызвана, если конкретной версии не существует.
Кончик
Используйте root конечную точку для ловушки. Например, обработчик функции handle_get_root() будет вызвана, если для этого запроса не может быть найдено другого обработчика. Если не указан Catchall, сервер вернет ошибку 404 Not Found .
Параметр req указывает на метеористов, который содержит методы получения информации из запроса или для установки ответа, как показано ниже:
req:query_param(param) возвращает параметр запроса (из строки запроса) с ключом param или nil если не найденоreq:post_param(param) возвращает параметр post (только для ${POST} обработчиков) с ключом param или nil , если не найденоreq:set_response(str) Устанавливает ответ на строку strreq:say(str) отправляет кусок ответа (с использованием кодировки в HTTP)req:send_event(event, str) Отправляет событие (с использованием событий Server-Sent)req:cookie(param) возвращает файл cookie с именем param или nil не найденоreq:set_headers(tbl) устанавливает заголовки ответов из таблицы tbl ; Заголовок может быть указан несколько раз с помощью таблицы, а не строки, в таблице значения ( {'foo'={'bar', 'baz'}} ); Должен быть вызван перед отправкой какого -либо ответа с say() или send_event()req:header(name) получает заголовок из запроса с данным именем или nil , если не найденоreq:sleep(ms) приостанавливает текущий обработчик для указанного количества миллисекундовreq:ws_upgrade() возвращает 1 если соединение может быть обновлено до WebSocket; 0 в противном случаеreq:ws_write_text(str) отправляет str через подключение к обновлению WebSocketcocket как текстовое кадрreq:ws_write_binary(str) отправляет str через подключение к обновлению WebSocketCocket в виде двоичного кадраreq:ws_write(str) отправляет str через подключение к обновлению WebSocketcocket в виде текста или двоичного кадра, в зависимости от контента, содержащего только символы ASCII или нетreq:ws_read() Возвращает строку с содержимым последней кадры WebSocket или числом, указывающим статус (enotconn/107 на Linux, если он был отключен; eagain/11 на Linux, если ничего не было доступно; Enomsg/42 на Linux в противном случае). Возвращающаяся стоимость здесь может измениться в будущем для чего-то более похожа на Lua.req:remote_address() возвращает строку с удаленным IP -адресом.req:path() возвращает строку с пути запроса.req:query_string() Возвращает строку по строке запроса (пустая строка, если нет строки запроса).req:body() возвращает тело запроса (Post/Pult запросы).req:request_id() возвращает строку, содержащую идентификатор запроса.req:request_date() возвращает дату, как она будет записана в заголовке Date ответа.req:is_https() возвращает true если этот запрос обслуживается через HTTPS, false в противном случае.req:host() возвращает значение заголовка Host , если он присутствует, иначе nil .req:http_version() возвращает HTTP/1.0 или HTTP/1.1 в зависимости от версии запроса.req:http_method() возвращает строку в верхнем регионе, с помощью метода HTTP (например, "GET" ).req:http_headers() возвращает таблицу со всеми заголовками и их значениями. Функции обработчика могут возвращать либо nil (в этом случае генерируется отклик 200 OK ), либо число, соответствующее коду состояния HTTP. Попытка вернуть неверный код состояния HTTP или что -либо, кроме числа или nil , приведет к брошению ответа 500 Internal Server Error .
В дополнение к метаметодам в параметре req можно также записать сообщения с различными уровнями журнала, вызывая методы из Lwan.log :
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (также прерван Lwan! Использовать с осторожностью)Lwan.log:debug(str) (доступно только в сборках отладки; иначе нет) Примечание
Если Lwan будет построен с поддержкой системного журнала, эти сообщения также будут отправлены в журнал системы, в противном случае они будут напечатаны в стандартную ошибку.
Модуль rewrite будет соответствовать шаблонам в URL -адресах и предоставит возможность либо перенаправить другой URL, либо переписать запрос таким образом, чтобы Lwan обрабатывал запрос, как если бы он был сделан таким образом изначально.
Примечание
Разветвенный от Lua 5.3.1, регулярный двигатель эксплуации может быть не таким насыщенным, как большинство двигателей общего назначения, но был выбран специально потому, что он является детерминированным конечным автоматом в попытке сделать некоторые виды отрицания атак на обслуживание.
Новый URL может быть указан с использованием простого синтаксиса замены текста или использовать сценарии LUA.
Кончик
Сценарии Lua будут содержать те же метаметоды, доступные в протекании req , предоставляемых модулем LUA, поэтому он может быть довольно мощным.
Каждый экземпляр модуля перезаписывания потребует pattern и действия для выполнения при сопоставлении такого шаблона. Паттерны оцениваются в том порядке, который они отображаются в файле конфигурации, и указаны с использованием вложенных разделов в файле конфигурации. Например, рассмотрим следующий пример, где указаны два шаблона:
rewrite /some/base/endpoint {
pattern posts/(%d+) {
# Matches /some/base/endpointposts/2600 and /some/base/endpoint/posts/2600
rewrite_as = /cms/view-post?id=%1
}
pattern imgur/(%a+)/(%g+) {
# Matches /some/base/endpointimgur/gif/mpT94Ld and /some/base/endpoint/imgur/gif/mpT94Ld
redirect_to = https://i.imgur.com/%2.%1
}
}
В этом примере определяется два шаблона: один предоставляет более приятный URL -адрес, который скрыт от пользователя, и другой, который обеспечивает другой способ получить прямую ссылку на изображение, размещенное в популярном службе хостинга изображений (то есть запрос /some/base/endpoint/imgur/mp4/4kOZNYX перераспределяя непосредственно в ресурс в службе Imgur).
Значение rewrite_as или redirect_to также может быть сценариями lua; В этом случае опция expand_with_lua должна быть установлена на true , и вместо того, чтобы использовать простой синтаксис замены текста в качестве примера выше, функция с именем handle_rewrite(req, captures) должна быть определена. Параметр req задокументирован в разделе модуля LUA; Параметр captures представляет собой таблицу, содержащую все захваты, по порядку ( captures[2] эквивалентна %2 в простом синтаксисе текста). Эта функция возвращает новый URL в перенаправление.
У этого модуля нет вариантов само по себе. Параметры указаны в каждом шаблоне.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
rewrite_as | str | NULL | Перепишите URL -адрес с помощью этой шаблона |
redirect_to | str | NULL | Перенаправить новый URL -адрес после этого шаблона |
expand_with_lua | bool | false | Используйте сценарии Lua, чтобы перенаправить или переписать запрос |
Варианты redirect_to и rewrite_as являются взаимоисключающими, и один из них должен быть указан, по крайней мере.
Также можно указать условия, чтобы вызвать переписывание. Чтобы указать один, откройте блок condition , укажите тип условия, а затем параметры для оценки этого условия. Несколько условий могут быть установлены на правило перезаписывания, если есть одно условие для типа:
| Состояние | Может использовать суб. синтаксис | Секция требуется | Параметры | Описание |
|---|---|---|---|---|
cookie | Да | Да | Один key = value | Проверки, если у запроса есть key cookie имеет значение value |
query | Да | Да | Один key = value | Проверки, если запрос имеет key переменной запроса имеет значение value |
post | Да | Да | Один key = value | Проверяет, если в запросе есть key данных, что имеет значение value |
header | Да | Да | Один key = value | Проверки, если key заголовка запроса имеет значение value |
environment | Да | Да | Один key = value | Проверяет, имеет ли key переменной среды значение value |
stat | Да | Да | path , is_dir , is_file | Проверяет, существует ли path в файловой системе, и, необязательно проверяет, is_dir или is_file |
encoding | Нет | Да | deflate , gzip , brotli , zstd , none | Проверяет, принимает ли клиент ответы в определенном кодировании (например, deflate = yes для кодирования дефляции) |
proxied | Нет | Нет | Логический | Проверки, если запрос был прокси по протоколу прокси -сервера |
http_1.0 | Нет | Нет | Логический | Проверки, если запрос сделан с клиентом HTTP/1.0 |
is_https | Нет | Нет | Логический | Проверки, если запрос сделан через HTTPS |
has_query_string | Нет | Нет | Логический | Проверки, если запрос имеет строку запроса (даже если пуст) |
method | Нет | Нет | Название метода | Проверяет, является ли HTTP -метод указанным |
lua | Нет | Нет | Нить | Запуск функции LUA matches(req) внутри строки и проверяет, возвращает ли она true или false |
backref | Нет | Да | Один backref index = value | Проверяет, соответствует ли номер назад номер предоставленного значения |
Может использовать суб. Синтаксис относится к способности ссылаться на соответствующий шаблон, используя тот же синтаксис замещения, который использовался для rewrite as или redirect to действия. Например, condition cookie { some-cookie-name = foo-%1-bar } заменит %1 на первое матч из шаблона, с которым связано это условие.
Примечание
Условия, которые не требуют раздела, должны быть написаны как ключ; Например, condition has_query_string = yes .
Например, если кто-то хочет отправить site-dark-mode.css если есть style cookie с value dark , и отправить site-light-mode.css , иначе можно написать:
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
Другой пример: если кто-то хочет отправить предварительно сжатые файлы, если они существуют в файловой системе, и пользователь запросил их:
pattern (%g+) {
condition encoding { brotli = yes }
condition stat { path = %1.brotli }
rewrite as = %1.brotli
}
pattern (%g+) {
condition encoding { gzip = yes }
condition stat { path = %1.gzip }
rewrite as = %1.gzip
}
pattern (%g+) {
condition encoding { zstd = yes }
condition stat { path = %1.zstd }
rewrite as = %1.zstd
}
pattern (%g+) {
condition encoding { deflate = yes }
condition stat { path = %1.deflate }
rewrite as = %1.deflate
}
Примечание
В общем, это не обязательно, так как модуль обслуживания файла будет делать это автоматически и выбирать наименьший файл, доступный для запрошенного кодирования, но это показывает, что это возможно иметь аналогичную функцию только по конфигурации.
Модуль redirect будет, как говорится в оловом, генерирует 301 Moved permanently (по умолчанию; код может быть изменен, см. Ниже) ответ, согласно параметрам, указанным в его конфигурации. Как правило, вместо этого следует использовать модуль rewrite , поскольку он упаковывает больше функций; Однако этот модуль служит также в качестве примера того, как писать модули LWAN (менее 100 строк кода).
Если опция to не указана, он всегда генерирует ответ 500 Internal Server Error . Указание неверного HTTP -кода или кода, о котором Lwan не знает (см. enum lwan_http_status ), будет создан 301 Moved Permanently .
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
to | str | NULL | Место, чтобы перенаправить на |
code | int | 301 | HTTP -код для выполнения перенаправления |
Модуль response будет генерировать искусственный ответ любого HTTP -кода. В дополнение к тому, чтобы также служить примером того, как написать модуль LWAN, его можно использовать для вырезания пустот из других модулей (например, генерируя 405 Not Allowed ответ для файлов в /.git , если / обслуживается с модулем serve_files ).
Если предоставленный code выходит за пределы кодов ответов, известных Lwan, вместо этого будет отправлена ошибка 404 Not Found .
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
code | int | 999 | Код ответа HTTP |
Запросы прокси модуля fastcgi между клиентом HTTP, подключенным к LWAN, и сервером FASTCGI, доступным LWAN. Это полезно, например, для обслуживания страниц с языка сценариев, таких как PHP.
Примечание
Это предварительная версия этого модуля, и поэтому он не очень оптимизирован, некоторые функции отсутствуют, а некоторые значения, предоставленные для окружающей среды, жестко кодируются.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
address | str | Адрес для подключения к. Может быть путем файла (для сокетов домена Unix), адрес IPv4 ( aaa.bbb.ccc.ddd:port ) или адрес IPv6 ( [...]:port ). | |
script_path | str | Расположение, где расположены сценарии CGI. | |
default_index | str | index.php | Скрипт по умолчанию для выполнения, если он не указан в URI запроса. |
Разделы авторизации могут быть объявлены в любом экземпляре модуля или обработчике и предоставляют способ разрешить выполнение этого запроса с помощью стандартного механизма авторизации HTTP. Чтобы требовать разрешения на доступ к определенному экземпляру или обработчику модуля, объявьте раздел authorization с basic параметром и установите один из его вариантов.
| Вариант | Тип | По умолчанию | Описание |
|---|---|---|---|
realm | str | Lwan | Царство для авторизации. Это обычно отображается в пользовательском интерфейсе пользователя/пароля в браузерах |
password_file | str | NULL | Путь для файла, содержащего имя пользователя и пароли (в прозрачном тексте). Формат файла такой же, как формат файла конфигурации, используемый Lwan |
Предупреждение
Не только пароли хранятся в прозрачном тексте в файле, который должен быть доступен сервером, но и оставаться в памяти в течение нескольких секунд. Избегайте использования этой функции, если это возможно.
Пожалуйста, прочитайте этот раздел (и следуйте ему), если вы планируете внести свой вклад в LWAN. Здесь нет ничего неожиданного; В основном это следует за правилами и ожиданиями многих других проектов FOSS, но каждый ожидает, что все будет немного отличаться друг от друга.
Лван пытается следовать последовательному стилю кодирования на протяжении всего проекта. Если вы рассматриваете возможность внести патч в проект, пожалуйста, уважайте этот стиль, пытаясь сопоставить стиль окружающего кода. В общем:
global_variables_are_named_like_this , даже если они, как правило, редки и должны быть помечены как static (за редкими исключениями)local_var , i , conntypedef для структур редко используется в Lwan#pragma once вместо обычного включения охраныlwan-private.hlwan_lwan_lwan_/* Old C-style comments are preferred */clang-format can be used to format the source code in an acceptable way; a .clang-format file is provided If modifying well-tested areas of the code (eg the event loop, HTTP parser, etc.), please add a new integration test and make sure that, before you send a pull request, all tests (including the new ones you've sent) are working. Tests can be added by modifying src/scripts/testsuite.py , and executed by either invoking that script directly from the source root, or executing the testsuite build target.
Some tests will only work on Linux, and won't be executed on other platforms.
Lwan is automatically fuzz-tested by OSS-Fuzz. To fuzz-test locally, though, one can follow the instructions to test locally.
Currently, there are fuzzing drivers for the request parsing code, the configuration file parser, the template parser, and the Lua string pattern matching library used in the rewrite module.
Adding new fuzzers is trivial:
src/bin/fuzz .${FUZZER_NAME}_fuzzer.cc . Look at the OSS-Fuzz documentation and other fuzzers on information about how to write these.src/fuzz/corpus . Files have to be named corpus-${FUZZER_NAME}-${UNIQUE_ID} . The shared object version of liblwan on ELF targets (eg Linux) will use a symbol filter script to hide symbols that are considered private to the library. Please edit src/lib/liblwan.sym to add new symbols that should be exported to liblwan.so .
Lwan tries to maintain a source history that's as flat as possible, devoid of merge commits. This means that pull requests should be rebased on top of the current master before they can be merged; sometimes this can be done automatically by the GitHub interface, sometimes they need some manual work to fix conflicts. It is appreciated if the contributor fixes these conflicts when asked.
It is advisable to push your changes to your fork on a branch-per-pull request, rather than pushing to the master branch; the reason is explained below.
Please ensure that Git is configured properly with your name (it doesn't really matter if it is your legal name or a nickname, but it should be enough to credit you) and a valid email address. There's no need to add Signed-off-by lines, even though it's fine to send commits with them.
If a change is requested in a pull request, you have two choices:
It is not enforced, but it is recommended to create smaller commits. How commits are split in Lwan is pretty much arbitrary, so please take a look at the commit history to get an idea on how the division should be made. Git offers a plethora of commands to achieve this result: the already mentioned interactive rebase, the -p option to git add , and git commit --amend are good examples.
Commit messages should have one line of summary (~72 chars), followed by an empty line, followed by paragraphs of 80-char lines explaining the change. The paragraphs explaining the changes are usually not necessary if the summary is good enough. Try to write good commit messages.
Lwan is licensed under the GNU General Public License, version 2, or (at your option), any later version. Поэтому:
While Lwan was written originally for Linux, it has been ported to BSD systems as well. The build system will detect the supported features and build support library functions as appropriate.
For instance, epoll has been implemented on top of kqueue, and Linux-only syscalls and GNU extensions have been implemented for the supported systems. This blog post explains the details and how #include_next is used.
It can achieve good performance, yielding about 320000 requests/second on a Core i7 laptop for requests without disk access, and without pipelining.
When disk I/O is required, for files up to 16KiB, it yields about 290000 requests/second ; for larger files, this drops to 185000 requests/second , which isn't too shabby either.
These results, of course, with keep-alive connections, and with weighttp running on the same machine (and thus using resources that could be used for the webserver itself).
Without keep-alive, these numbers drop around 6-fold.
There is an IRC channel ( #lwan ) on Libera. A standard IRC client can be used.
Here's a non-definitive list of third-party stuff that uses Lwan and have been seen in the wild. If you see mentions of Lwan in the media or academia, however small it might be, please contact the author! It'll make her day!
Some other distribution channels were made available as well:
Dockerfile is maintained by @jaxgeller, and is available from the Docker registry.Lwan has been also used as a benchmark:
Mentions in academic journals:
Mentions in magazines:
Mentions in books:
Some talks mentioning Lwan:
Not really third-party, but alas:
Lwan container images are available at ghcr.io/lpereira/lwan. Container runtimes like Docker or Podman may be used to build and run Lwan in a container.
Container images are tagged with release version numbers, so a specific version of Lwan can be pulled.
# latest version
docker pull ghcr.io/lpereira/lwan:latest
# pull a specific version
docker pull ghcr.io/lpereira/lwan:v0.3
Clone the repository and use Containerfile (Dockerfile) to build Lwan with all optional dependencies enabled.
podman build -t lwan .
The image expects to find static content at /wwwroot , so a volume containing your content can be mounted.
docker run --rm -p 8080:8080 -v ./www:/wwwroot lwan
To bring your own lwan.conf , simply mount it at /lwan.conf .
podman run --rm -p 8080:8080 -v ./lwan.conf:/lwan.conf lwan
Podman supports socket activation of containers. This example shows how to run lwan with socket activation and Podman on a Linux host.
Requirements: Podman version 4.5.0 or higher.
sudo useradd test
sudo machinectl shell test@
podman build -t lwan ~/lwan
mkdir -p ~/.config/containers/systemd
mkdir -p ~/.config/systemd/user
listener systemd:my.socket
site {
serve_files / {
path = /web
}
}
[Socket]
ListenStream=8080
[Unit]
After=my.socket
Requires=my.socket
[Container]
Network=none
Image=localhost/lwan
Volume=/home/test/lwan.conf:/lwan.conf:Z
Volume=/home/test/web:/web:Z
:Z is needed on SELinux systems. As lwan only needs to communicate over the socket-activated socket, it's possible to use Network=none . See the article How to limit container privilege with socket activation. mkdir ~/web
echo hello > ~/web/file.txt
systemctl --user daemon-reload
systemctl --user start my.socket
$ curl localhost:8080/file.txt
hello
These are some of the quotes found in the wild about Lwan. They're presented in no particular order. Contributions are appreciated:
"Lwan is like a classic, according to the definition given by Italian -- writer Italo Calvino: you can read it again and again" Antonio Piccolboni
"I read lwan's source code. Especially, the part of using coroutine was very impressive and it was more interesting than a good novel. Thank you for that." -- @patagonia
"For the server side, we're using Lwan, which can handle 100k+ reqs/s. It's supposed to be super robust and it's working well for us." -- @fawadkhaliq
"Insane C thing" -- Michael Sproul
"The best performer is LWAN, a newcomer" -- InfoQ
"I've never had a chance to thank you for Lwan. It inspired me a lot to develop Zewo" -- @paulofariarl
"Let me say that lwan is a thing of beauty. I got sucked into reading the source code for pure entertainment, it's so good. high five " -- @kwilczynski
"mad science" -- jwz
"Nice work with Lwan! I haven't looked that carefully yet but so far I like what I saw. You definitely have the right ideas." -- @thinkingfish
"Lwan is a work of art. Every time I read through it, I am almost always awe-struck." -- @neurodrone
"For Round 10, Lwan has taken the crown" -- TechEmpower
"Jeez this is amazing. Just end to end, rock solid engineering. (...) But that sells this work short." kjeetgill
"I am only a spare time C coder myself and was surprised that I can follow the code. Nice!" cntlzw
"Impressive all and all, even more for being written in (grokkable!) C. Nice work." tpaschalis
"LWAN was a complete failure" dermetfan