LWAN es un servidor web de alto rendimiento y escalable .
El sitio web del proyecto contiene más detalles.
| Sistema operativo | Arco | Liberar | Depurar | Análisis estático | Pruebas |
|---|---|---|---|---|---|
| Linux | x86_64 | Historial de informes | |||
| FreeBSD 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
Puede construir LWAN usted mismo, usar una imagen de contenedor o tomar un paquete de su distribución favorita.
Antes de instalar LWAN, asegúrese de instalar todas las dependencias. Todas ellas son dependencias comunes que se encuentran en cualquier distribución GNU/Linux; Los nombres de los paquetes serán diferentes, pero no debería ser difícil buscar utilizando cualquier herramienta de administración de paquetes que use su distribución.
El sistema de compilación buscará estas bibliotecas y habilitará/enlace si está disponible.
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (predeterminado):-DUSE_ALTERNATIVE_MALLOC a cmake con los siguientes valores: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
Seleccionar una versión de lanzamiento (sin símbolos de depuración, mensajes, habilitar algunas optimizaciones, etc.):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
Si desea habilitar las optimizaciones pero aún usa un depurador, use esto en su lugar:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
Para deshabilitar las optimizaciones y construir una versión más amigable para la depuración:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
Esto generará algunos binarios:
src/bin/lwan/lwan : el ejecutable principal de LWAN. Puede ejecutarse con --help para orientación.src/bin/testrunner/testrunner : contiene código para ejecutar el conjunto de pruebas ( src/scripts/testsuite.py ).src/samples/freegeoip/freegeoip : Implementación de muestra FreegeOIP. Requiere sqlite.src/samples/techempower/techempower : Código para el punto de referencia del marco web TechEmPower. Requiere bibliotecas SQLite y MariadB.src/samples/clock/clock : muestra de reloj. Genera un archivo GIF que siempre muestra la hora local.src/bin/tools/mimegen : construye la tabla de tipo de extensión-mime. Utilizado durante el proceso de compilación.src/bin/tools/bin2hex : genera un archivo C a partir de un archivo binario, adecuado para usar con #include. Utilizado durante el proceso de compilación.src/bin/tools/configdump : descarga un archivo de configuración utilizando la API del lector de configuración. Utilizado para las pruebas.src/bin/tools/weighttp : Reescribe de la herramienta Benchmarking HTTP weighttp .src/bin/tools/statuslookupgen : genera una tabla hash perfecta para los códigos de estado HTTP y sus descripciones. Utilizado durante el proceso de compilación. Pasar -DCMAKE_BUILD_TYPE=Release habilitará algunas optimizaciones del compilador (como LTO) y ajustará el código para la arquitectura actual.
Importante
Utilice la compilación de lanzamiento cuando se compare . El valor predeterminado es la compilación de depuración, que no solo registra todas las solicitudes a la salida estándar, sino que lo hace mientras mantiene un bloqueo, manteniendo presionada severamente el servidor.
La construcción predeterminada (es decir, no pasar -DCMAKE_BUILD_TYPE=Release ) construirá una versión adecuada para fines de depuración. Esta versión se puede usar bajo Valgrind (si sus encabezados están presentes) e incluye mensajes de depuración que se despojan en la versión de lanzamiento. Los mensajes de depuración se imprimen para todas y cada una de las solicitudes.
En estas compilaciones, los desinfectantes se pueden habilitar. Para seleccionar con cuál construir LWAN, especifique una de las siguientes opciones a la línea de invocación CMake:
-DSANITIZER=ubsan selecciona el desinfectante de comportamiento indefinido.-DSANITIZER=address selecciona el desinfectante de la dirección.-DSANITIZER=thread selecciona el desinfectante de subprocesos. También se pueden seleccionar asignadores de memoria alternativos. Actualmente, LWAN es compatible con TCMalloc, Mimalloc y Jemalloc fuera de la caja. Para usar cualquiera de ellos, pase -DALTERNATIVE_MALLOC=name a la línea de invocación Cmake, utilizando los nombres proporcionados en la sección "Dependencias opcionales".
La opción -DUSE_SYSLOG=ON se puede pasar a CMake para iniciar sesión también al registro del sistema, además de la salida estándar.
Si está construyendo LWAN para una distribución, podría ser aconsejable usar la opción -DMTUNE_NATIVE=OFF , de lo contrario, el binario generado puede no ejecutarse en algunas computadoras.
El soporte TLS está habilitado automáticamente en presencia de una instalación de MBEDTLS adecuada en sistemas Linux con encabezados lo suficientemente nuevos como para admitir KTLS, pero se puede deshabilitar pasando -DENABLE_TLS=NO a CMake.
~/lwan/build$ make testsuite
Esto compilará el programa testrunner y ejecutará la suite de prueba de regresión en src/scripts/testsuite.py .
~/lwan/build$ make benchmark
Esto compilará testrunner y ejecutará el script de referencia src/scripts/benchmark.py .
LWAN también se puede construir con el tipo de construcción de cobertura especificando -DCMAKE_BUILD_TYPE=Coverage . Esto permite el objetivo de marca generate-coverage , que ejecutará testrunner para preparar un informe de cobertura de prueba con LCOV.
Cada compromiso en este repositorio desencadena la generación de este informe, y los resultados están disponibles públicamente.
Configure el servidor editando el lwan.conf proporcionado; El formato se explica en detalles a continuación.
Nota
LWAN intentará encontrar un archivo de configuración basado en el nombre ejecutable en el directorio actual; testrunner.conf se utilizará para el testrunner binario, lwan.conf para el binario lwan , y así sucesivamente.
Los archivos de configuración se cargan desde el directorio actual. Si no se realizan cambios en este archivo, la ejecución de LWAN servirá archivos estáticos ubicados en el directorio ./wwwroot . Lwan escuchará en el puerto 8080 en todas las interfaces.
LWAN detectará el número de CPU, aumentará el número máximo de descriptores de archivos abiertos y generalmente hará todo lo posible para autodetectar la configuración razonable para el entorno en el que se ejecuta. Muchas de estas configuraciones se pueden ajustar en el archivo de configuración, pero generalmente es una buena idea no meterse con ellos.
Consejo
Opcionalmente, el binario lwan se puede usar para un archivo estático de un solo disparo que sirve sin ningún archivo de configuración. Ejecutarlo con --help para obtener ayuda en eso.
LWAN utiliza una sintaxis de archivo de configuración de key = value Familiar. Los comentarios son compatibles con el personaje # (similar a los scripts de eg Shell, Python y Perl). Las secciones anidadas se pueden crear con soportes rizados. Las secciones pueden estar vacías; En este caso, los soportes rizados son opcionales.
some_key_name es equivalente a some key name en los archivos de configuración (como detalle de implementación, las opciones de configuración de lectura de código solo recibirán la versión con subrayos).
Consejo
Los valores pueden contener variables de entorno. Use la sintaxis ${VARIABLE_NAME} . Los valores predeterminados se pueden especificar con un colon foo ${VARIABLE_NAME:foo} ${VARIABLE_NAME} .
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
"""
}
Algunos ejemplos se pueden encontrar en lwan.conf y techempower.conf .
Las constantes se pueden definir y reutilizar en todo el archivo de configuración especificándolas en una sección constants en cualquier lugar del archivo de configuración. Una constante estará disponible solo después de esa sección define una constante particular. Las constantes se pueden volver a definir. Si no se define una constante, su valor se obtendrá de una variable de entorno. Si no se define en una sección constants , o en el entorno, LWAN abortará con un mensaje de error apropiado.
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
La misma sintaxis para los valores predeterminados especificados anteriormente es válido aquí (por ejemplo, especificar que user_name sea ${USER:nobody} establecerá ${user_name} a nobody si ${USER} no está configurado en la variable de entorno o no es otra constante).
| Tipo | Descripción |
|---|---|
str | Cualquier tipo de texto de forma libre, generalmente específica de la aplicación |
int | Número entero. El rango es específico de la aplicación |
time | Intervalo de tiempo. Ver la tabla a continuación para ver las unidades |
bool | Valor booleano. Consulte la tabla a continuación para obtener valores válidos |
Los campos de tiempo se pueden especificar usando multiplicadores. Se pueden especificar múltiples, solo se suman; Por ejemplo, "1M 1W" especifica "1 mes y 1 semana" (37 días). La siguiente tabla enumera todos los multiplicadores conocidos:
| Multiplicador | Descripción |
|---|---|
s | Artículos de segunda clase |
m | Minutos |
h | Horas |
d | Días |
w | Semana de 7 días |
M | Meses de 30 días |
y | 365 días |
Nota
Se ignora un número con un multiplicador que no está en esta tabla; Se emite una advertencia mientras lee el archivo de configuración. No deben existir espacios entre el número y su multiplicador.
| Valores verdaderos | Valores falsos |
|---|---|
| Cualquier número entero diferente a 0 | 0 |
on | off |
true | false |
yes | no |
En general, es una buena idea dejar que Lwan decida la mejor configuración para su entorno. Sin embargo, no todos los entornos son el mismo, y no todos los usos se pueden decidir automáticamente, por lo que se proporcionan algunas opciones de configuración.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
keep_alive_timeout | time | 15 | Tiempo de espera para mantener viva una conexión |
quiet | bool | false | Establecer en True para no imprimir ningún mensaje de depuración. Solo efectivo en las construcciones de lanzamiento. |
expires | time | 1M 1w | Valor del encabezado "expirado". El valor predeterminado es 1 mes y 1 semana |
threads | int | 0 | Número de hilos de E/S. Predeterminado (0) es el número de CPU en línea |
proxy_protocol | bool | false | Habilita el protocolo proxy. Las versiones 1 y 2 son compatibles. Solo habilite esta configuración si usa LWAN detrás de un proxy, y el proxy admite este protocolo; De lo contrario, esto permite a cualquiera falsificar las direcciones IP de origen |
max_post_data_size | int | 40960 | Establece el número máximo de tamaño de datos para las solicitudes de publicación, en bytes |
max_put_data_size | int | 40960 | Establece el número máximo de tamaño de datos para las solicitudes de put, en bytes |
max_file_descriptors | int | 524288 | Número máximo de descriptores de archivos. Necesita ser al menos 10 veces threads |
request_buffer_size | int | 4096 | Solicite la longitud del tamaño del búfer. Si es mayor que el valor predeterminado de 4096 , se asignará dinámicamente. |
allow_temp_files | str | "" | Usar archivos temporales; Establecer en post para las solicitudes de publicación, put las solicitudes de PUT o all (equivalente a configurar para post put ) para ambos. |
error_template | str | Plantilla de error predeterminada | Plantilla para códigos de error. Ver variables a continuación. |
error_template| Variable | Tipo | Descripción |
|---|---|---|
short_message | str | Mensaje de error corto (por ejemplo, Not found ) |
long_message | str | Mensaje de error largo (por ejemplo, The requested resource could not be found on this server ) |
Lwan puede eliminar sus privilegios a un usuario en el sistema y limitar su vista del sistema de archivos con un chroot. Si bien no es a prueba de balas, esto proporciona una primera capa de seguridad en el caso de que hay un error en LWAN.
Para usar esta función, declare una sección de straitjacket (o straightjacket ) y establezca algunas opciones. Esto requiere que LWAN se ejecute como root .
Aunque esta sección se puede escribir en cualquier parte del archivo (siempre que sea una declaración de nivel superior), si algún director de directorios está abierto, debido a que EG instanciando el módulo serve_files , LWAN se negará a comenzar. (Esta verificación solo se realiza en Linux como una salvaguardia para MalConfiguration).
Consejo
Declare un Jacket de la Estria de Estrucia justo antes de una sección site de tal manera que los archivos de configuración y los datos privados (por ejemplo, las claves TLS) estén fuera del alcance del servidor después de que se haya producido la inicialización.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
user | str | NULL | Dejar privilegios a este nombre de usuario |
chroot | str | NULL | Camino a chroot() |
drop_capabilities | bool | true | Deje caer todas las capacidades con Capset (2) (en Linux), o compromiso (2) (debajo de OpenBSD). |
Si es necesario especificar encabezados personalizados para cada respuesta, uno puede declarar una sección headers en el alcance global. El orden que aparece esta sección no es importante.
Por ejemplo, esta declaración:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Ambos anularán el encabezado Server ( Server: lwan no se enviará) y establecerá Some-Custom-Header con el valor obtenido de la variable de entorno $WITH_THIS_ENVIRONMENT_VARIABLE .
Algunos encabezados no pueden ser anulados, ya que eso causaría problemas al enviar sus valores reales mientras realiza solicitudes. Estos incluyen, pero no se limitan a:
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow- Nota
Los nombres de los encabezados también son insensibles al caso (y preservan los casos). SeRVeR primordial anulará el encabezado Server , pero lo envíe de la forma en que se escribió en el archivo de configuración.
Solo dos oyentes son compatibles con el proceso LWAN: el oyente HTTP (sección listener ) y el oyente HTTPS (sección tls_listener ). Solo se permite un oyente de cada tipo.
Advertencia
El soporte de TLS es experimental. Aunque es estable durante las pruebas iniciales, su kilometraje puede variar. Solo TLSV1.2 es compatible en este punto, pero se planifica TLSV1.3.
Nota
¿El soporte de TLS requiere? Linux con el módulo tls.ko incorporado o cargado. El soporte para otros sistemas operativos se puede agregar en el futuro. FreeBSD parece posible, otros sistemas operativos no parecen ofrecer características similares. Para los sistemas operativos no compatibles, el uso de un proxy TLS Terminator, como enganche, es una buena opción.
Tanto para las secciones listener y tls_listener , el único parámetro es la dirección de la interfaz y el puerto para escuchar. La sintaxis del oyente es ${ADDRESS}:${PORT} , donde ${ADDRESS} puede ser * (vinculante a todas las interfaces), una dirección IPv6 (si está rodeada de soportes cuadrados), una dirección IPv4 o un nombre de host. Por ejemplo, listener localhost:9876 solo escucharía en la interfaz lo , puerto 9876 .
Si bien una sección listener no toma claves, una sección tls_listener requiere dos: cert y key (cada uno apuntando, respectivamente, a la ubicación en el disco donde se ubican el certificado TLS y los archivos de clave privados) y toma una clave hsts booleana opcional, que controla si los encabezados Strict-Transport-Security se enviarán en las respuestas HTTPS.
Consejo
Para generar estas claves para fines de prueba, la herramienta de línea de comandos OpenSSL se puede usar como la siguiente: openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
Nota
Se recomienda que se declare un chaleco de fuerza con una opción chroot justo después de una sección tls_listener , de tal manera que las rutas al certificado y la clave estén fuera de alcance desde ese punto.
Si se utiliza la activación del socket Systemd, systemd se puede especificar como un parámetro. (Si se especifican múltiples oyentes de Systemd, se puede especificar systemd:FileDescriptorName , donde FileDescriptorName sigue las convenciones establecidas en la documentación systemd.socket ).
Ejemplos:
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 {
...
}
Una sección site agrupa instancias de módulos y manejadores que responderán a las solicitudes a un prefijo de URL dado.
Para enrutar URL, LWAN coincide con el prefijo común más grande del URI de solicitud con un conjunto de prefijos especificados en la sección del oyente. Cómo se manejará una solicitud a un prefijo en particular depende de qué manejador o módulo se haya declarado en la sección del oyente. Los manejadores y los módulos son similares internamente; Los manejadores son simplemente funciones y no tienen estado, y los módulos tienen estado (instancia nombrada). Múltiples instancias de un módulo pueden aparecer en una sección del oyente.
No hay una sintaxis especial para unir un prefijo a un controlador o módulo; Todas las reglas de análisis de configuración se aplican aquí. Use ${NAME} ${PREFIX} para vincular la ruta de prefijo ${PREFIX} a un controlador llamado ${NAME} (si ${NAME} comienza con & , como con la dirección "del operador") de C, o un módulo llamado ${NAME} . Las secciones vacías se pueden usar aquí.
Cada módulo tendrá su conjunto específico de opciones, y se enumeran en las siguientes secciones. Además de las opciones de configuración, una sección authorization especial puede estar presente en la declaración de una instancia de módulo. Los manejadores no toman ninguna opción de configuración, pero pueden incluir la sección authorization .
Consejo
Ejecutar LWAN con el argumento de línea de comandos --help mostrará una lista de módulos y manejadores incorporados.
La siguiente es una documentación básica para los módulos enviados con LWAN.
El módulo serve_files servirá archivos estáticos y creará automáticamente índices de directorio o servirá a archivos precompresados. En general, hará todo lo posible para servir archivos de la manera más rápida posible según algunas heurísticas.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
path | str | NULL | Ruta a un directorio que contiene archivos a servir |
index_path | str | index.html | Nombre del archivo para servir como índice para un directorio |
serve_precompressed_path | bool | true | Si $ file.gz existe, es más pequeño y más nuevo que el archivo $, y el cliente acepta la codificación gzip , transfiéralo, transferirlo |
auto_index | bool | true | Genere una lista de directorio automáticamente si no hay archivo index_path presente. De lo contrario, produce 404 |
auto_index_readme | bool | true | Incluye el contenido de los archivos ReadMe como parte del índice de directorio generado automáticamente |
directory_list_template | str | NULL | Ruta a una plantilla de bigote para la lista de directorio; Por defecto, use una plantilla interna |
read_ahead | int | 131702 | Cantidad máxima de bytes para leer antes al almacenar en caché los archivos abiertos. Un valor de 0 deshabilita ReadAhead. ReadAhead se realiza mediante un hilo de baja prioridad para no bloquear los subprocesos de E/S mientras se leen los extensiones de archivos desde el sistema de archivos. |
cache_for | time | 5s | Tiempo para mantener metadatos de archivo (tamaño, contenido comprimido, descriptor de archivo abierto, etc.) en caché |
Nota
Los archivos más pequeños de 16KIB se comprimirán en RAM para la duración especificada en la configuración cache_for . LWAN siempre intentará comprimir con Deflate, y opcionalmente se comprimirá con Brotli y ZSTD (si LWAN se ha construido con el soporte adecuado).
En los casos en que la compresión no valdría la pena (por ejemplo, agregar el encabezado Content-Encoding daría como resultado una respuesta mayor que enviar el archivo sin comprimir, generalmente el caso para archivos muy pequeños), Lwan no pasará tiempo comprimiendo un archivo.
Para archivos mayores de 16KIB, LWAN no intentará comprimirlos. En versiones futuras, podría hacer esto y enviar respuestas utilizando la codificación de fragmentos mientras el archivo se comprime (hasta cierto límite, por supuesto), pero por ahora, solo se consideran archivos precomprimidos (ver serve_precompressed_path en la tabla anterior).
Para todos los casos, LWAN podría intentar usar la versión GZipped si se encuentra en el sistema de archivos y el cliente solicitó esta codificación.
directory_list_template| Variable | Tipo | Descripción |
|---|---|---|
rel_path | str | Ruta relativa a la ruta real del directorio raíz |
readme | str | Contenido del primer archivo ReadMe encontrado ( readme , readme.txt , read.me , README.TXT , README ) |
file_list | iterador | Itera en la lista de archivos |
file_list.zebra_class | str | odd para artículos impares, o even o incluso elementos |
file_list.icon | str | Ruta al icono para el tipo de archivo |
file_list.name | str | Nombre del archivo (escapado) |
file_list.type | str | Tipo de archivo (directorio o archivo regular) |
file_list.size | int | Tamaño de archivo |
file_list.unit | str | Unidad para file_size |
El módulo lua permitirá que las solicitudes sean cumplidas por scripts escritos en el lenguaje de programación LUA. Aunque la funcionalidad proporcionada por este módulo es bastante espartana, puede ejecutar marcos como Sailor.
Los scripts se pueden servir desde archivos o incrustados en el archivo de configuración, y los resultados de cargarlos, los módulos LUA estándar y (opcionalmente, si se usa lujit), la optimización del código se almacenará en caché por un tiempo.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
default_type | str | text/plain | Tipo mime predeterminado para respuestas |
script_file | str | NULL | Camino al guión de lua |
cache_period | time | 15s | Es hora de mantener el estado de Lua cargado en la memoria |
script | str | NULL | Script Lua en línea |
Nota
Los scripts de Lua no pueden usar variables globales, ya que pueden no solo ser atendidas por diferentes subprocesos, sino que el estado estará disponible solo por la cantidad de tiempo especificada en la opción de configuración cache_period . Esto se debe a que cada hilo de E/S en LWAN creará una instancia de una VM Lua (es decir, una estructura lua_State para cada hilo de E/S), y cada Coroutine de Lwan generará un hilo LUA (con lua_newthread() ) por solicitud.
No hay necesidad de tener una instancia del módulo LUA para cada punto final; Un solo script, integrado en el archivo de configuración o de otro tipo, puede atender muchos puntos finales diferentes. Se supone que los scripts implementan funciones con la siguiente firma: handle_${METHOD}_${ENDPOINT}(req) , donde ${METHOD} puede ser un método http (es decir, get , post , head , etc.), y ${ENDPOINT} es el punto final deseado para ser manejado por esa función. Se llamará a una función handle(req) si la versión específica no existe.
Consejo
Use el punto final root para una Catchall. Por ejemplo, la función del controlador handle_get_root() se llamará si no se pudiera encontrar otro controlador para esa solicitud. Si no se especifica Catchall, el servidor devolverá un error 404 Not Found .
El parámetro req apunta a un metatible que contiene métodos para obtener información de la solicitud, o para establecer la respuesta, como se ve a continuación:
req:query_param(param) Devuelve el parámetro de consulta (desde la cadena de consulta) con la clave param , o nil si no se encuentrareq:post_param(param) Devuelve el parámetro post (solo para controladores ${POST} ) con la clave param , o nil si no se encuentrareq:set_response(str) establece la respuesta a la cadena strreq:say(str) envía un fragmento de respuesta (utilizando la codificación fragmentada en HTTP)req:send_event(event, str) envía un evento (usando eventos de servidor)req:cookie(param) Devuelve la cookie llamada param , o no se encuentra nilreq:set_headers(tbl) establece los encabezados de respuesta de la tabla tbl ; Se puede especificar un encabezado varias veces usando una tabla, en lugar de una cadena, en el valor de la tabla ( {'foo'={'bar', 'baz'}} ); debe llamarse antes de enviar cualquier respuesta con say() o send_event()req:header(name) obtiene el encabezado de la solicitud con el nombre de pila o nil si no se encuentrareq:sleep(ms) detiene el controlador actual para la cantidad especificada de milisegundosreq:ws_upgrade() Devuelve 1 si la conexión podría actualizarse a un WebSocket; 0 de lo contrarioreq:ws_write_text(str) envía str a través de la conexión actualizada de WebSocket como marco de textoreq:ws_write_binary(str) envía str a través de la conexión actualizada de WebSocket como marco binarioreq:ws_write(str) envía str a través de la conexión actualizada de WebSocket como texto o marco binario, dependiendo del contenido que contenga solo caracteres ASCII o noreq:ws_read() Devuelve una cadena con el contenido del último marco de WebSocket, o un número que indica un estado (enotconn/107 en Linux si se ha desconectado; Eagain/11 en Linux si no había nada disponible; Enomsg/42 en Linux de otra manera). El valor de retorno aquí podría cambiar en el futuro por algo más como Lua.req:remote_address() Devuelve una cadena con la dirección IP remota.req:path() Devuelve una cadena con la ruta de solicitud.req:query_string() Devuelve una cadena con la cadena de consulta (cadena vacía si no hay cadena de consulta presente).req:body() Devuelve el cuerpo de solicitud (solicitudes de publicación/put).req:request_id() Devuelve una cadena que contiene la ID de solicitud.req:request_date() Devuelve la fecha, ya que se escribirá en el encabezado de respuesta Date .req:is_https() devuelve true si esta solicitud se da servicio a través de https, false de lo contrario.req:host() Devuelve el valor del encabezado Host si está presente, de lo contrario nil .req:http_version() devuelve HTTP/1.0 o HTTP/1.1 dependiendo de la versión de solicitud.req:http_method() Devuelve una cadena, en mayúsculas, con el método HTTP (por ejemplo, "GET" ).req:http_headers() Devuelve una tabla con todos los encabezados y sus valores. Las funciones del controlador pueden nil (en cuyo caso, se genera una respuesta 200 OK ), o un número que coincida con un código de estado HTTP. Intentar devolver un código de estado HTTP no válido o cualquier otra cosa que no sea un número o nil dará como resultado una respuesta 500 Internal Server Error .
Además de los metamethods en el parámetro req , también se puede registrar mensajes con diferentes niveles de registro llamando a métodos de Lwan.log :
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (también abortará Lwan! Usar con precaución)Lwan.log:debug(str) (solo disponible en depuración construye; no-op de lo contrario) Nota
Si LWAN está construido con soporte de syslog, estos mensajes también se enviarán al registro del sistema, de lo contrario se imprimirán al error estándar.
El módulo rewrite coincidirá con los patrones en URL y dará la opción de redirigir a otra URL, o reescribir la solicitud de manera que Lwan manejará la solicitud como si se hiciera de esa manera originalmente.
Nota
Bifurado de Lua 5.3.1, el motor de expresiones regular puede no ser tan lleno de características como la mayoría de los motores de propósito general, pero se ha elegido específicamente porque es un autómata finito determinista en un intento de hacer imposible algunos tipos de negación de ataques de servicio.
La nueva URL se puede especificar utilizando una sintaxis de sustitución de texto simple o usar scripts Lua.
Consejo
Los scripts de Lua contendrán los mismos metametodos disponibles en el metatable req proporcionado por el módulo LUA, por lo que puede ser bastante poderoso.
Cada instancia del módulo de reescritura requerirá un pattern y la acción para ejecutar cuando dicho patrón coincida. Los patrones se evalúan en el orden en que aparecen en el archivo de configuración y se especifican utilizando secciones anidadas en el archivo de configuración. Por ejemplo, considere el siguiente ejemplo, donde se especifican dos patrones:
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
}
}
Este ejemplo define dos patrones, uno que proporciona una URL más agradable que está oculta al usuario, y otro que proporciona una forma diferente de obtener un enlace directo a una imagen alojada en un servicio de alojamiento de imágenes popular (es decir, solicitar /some/base/endpoint/imgur/mp4/4kOZNYX redirigirá directamente a un recurso en el servicio Imgur).
El valor de rewrite_as o redirect_to también puede ser Lua Scripts; En cuyo caso, la opción expand_with_lua debe establecerse en true , y, en lugar de usar la sintaxis de sustitución de texto simple como el ejemplo anterior, se debe definir una función llamada handle_rewrite(req, captures) . El parámetro req se documenta en la sección del módulo LUA; El parámetro captures es una tabla que contiene todas las capturas, en orden (es decir, captures[2] es equivalente a %2 en la sintaxis de sustitución de texto simple). Esta función devuelve la nueva URL para redirigir.
Este módulo no tiene opciones por sí mismo. Las opciones se especifican en cada patrón.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
rewrite_as | str | NULL | Reescribe la URL después de este patrón |
redirect_to | str | NULL | Redirigir a una nueva URL después de este patrón |
expand_with_lua | bool | false | Use scripts de lua para redirigir o reescribir una solicitud |
Las opciones redirect_to y rewrite_as son mutuamente excluyentes, y una de ellas debe especificarse al menos.
También es posible especificar condiciones para activar una reescritura. Para especificar uno, abra un bloque condition , especifique el tipo de condición y luego los parámetros para que se evalúe esa condición. Se pueden establecer múltiples condiciones por regla de reescritura siempre que haya una condición por tipo:
| Condición | Puede usar sust. sintaxis | Sección requerida | Parámetros | Descripción |
|---|---|---|---|---|
cookie | Sí | Sí | Una sola key = value | Comprobaciones si la solicitud tiene key de cookie tiene value de valor |
query | Sí | Sí | Una sola key = value | Comprobaciones si la solicitud de la solicitud de consulta tiene key de value |
post | Sí | Sí | Una sola key = value | Comprobaciones si la solicitud tiene key de datos posterior tiene value de valor |
header | Sí | Sí | Una sola key = value | Comprobaciones si key del encabezado de solicitud tiene value de valor |
environment | Sí | Sí | Una sola key = value | Comprueba si key de la variable de entorno tiene value de valor |
stat | Sí | Sí | path , is_dir , is_file | Comprueba si path existe en el sistema de archivos, y opcionalmente verifica si is_dir o is_file |
encoding | No | Sí | deflate , gzip , brotli , zstd , none | Comprueba si el cliente acepta respuestas en una codificación determinada (por ejemplo, deflate = yes para la codificación de desinflado) |
proxied | No | No | Booleano | Comprobaciones si la solicitud se ha proxenado a través del protocolo proxy |
http_1.0 | No | No | Booleano | Comprueba si la solicitud se realiza con un cliente HTTP/1.0 |
is_https | No | No | Booleano | Verificación si la solicitud se realiza a través de HTTPS |
has_query_string | No | No | Booleano | Comprueba si la solicitud tiene una cadena de consulta (incluso si está vacía) |
method | No | No | Nombre del método | Verifica si el método HTTP es el especificado |
lua | No | No | Cadena | Ejecuta matches(req) Inside String y verifica si devuelve true o false |
backref | No | Sí | Un solo backref index = value | Comprueba si el número de backref coincide con el valor proporcionado |
Puede usar sust. La sintaxis se refiere a la capacidad de hacer referencia al patrón coincidente utilizando la misma sintaxis de sustitución utilizada para rewrite as o redirect to las acciones. Por ejemplo, condition cookie { some-cookie-name = foo-%1-bar } sustituirá %1 con la primera coincidencia del patrón con el que está relacionada con esta condición.
Nota
Las condiciones que no requieren una sección deben escribirse como clave; Por ejemplo, condition has_query_string = yes .
Por ejemplo, si uno quiere enviar site-dark-mode.css si hay una cookie style con el valor dark y enviar site-light-mode.css de lo contrario, uno puede escribir:
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
Otro ejemplo: si uno quiere enviar archivos precomprimidos si existen en el sistema de archivos y el usuario los solicitó:
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
}
Nota
En general, esto no es necesario, ya que el módulo de servicio del archivo lo hará automáticamente y elegirá el archivo más pequeño disponible para la codificación solicitada, pero esto muestra que es posible tener una característica similar solo por configuración.
El módulo redirect , como dice, en la lata, generará un 301 Moved permanently (por defecto; el código se puede cambiar, ver a continuación) respuesta, de acuerdo con las opciones especificadas en su configuración. En general, el módulo rewrite debe usarse en su lugar, ya que incluye más funciones; Sin embargo, este módulo también sirve como un ejemplo de cómo escribir módulos LWAN (menos de 100 líneas de código).
Si no se especifica la opción to , siempre genera una respuesta 500 Internal Server Error . Especificar un código HTTP no válido, o un código que LWAN no conoce (ver enum lwan_http_status ), producirá una respuesta 301 Moved Permanently .
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
to | str | NULL | La ubicación para redirigir a |
code | int | 301 | El código HTTP para realizar una redirección |
El módulo response generará una respuesta artificial de cualquier código HTTP. Además de servir también como un ejemplo de cómo escribir un módulo LWAN, se puede usar para forjar vacíos de otros módulos (por ejemplo, generando una respuesta 405 Not Allowed para archivos en /.git , si / se sirve con el módulo serve_files ).
Si el code suministrado cae fuera de los códigos de respuesta conocidos por LWAN, se enviará un error 404 Not Found .
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
code | int | 999 | Un código de respuesta HTTP |
Las solicitudes del módulo fastcgi solicitan entre el cliente HTTP que se conecta a LWAN y un servidor FASTCGI accesible por LWAN. Esto es útil, por ejemplo, para servir páginas de un lenguaje de secuencias de comandos como PHP.
Nota
Esta es una versión preliminar de este módulo, y como tal, no está bien optimizado, faltan algunas características y algunos valores proporcionados al entorno están codificados.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
address | str | Dirección para conectarse. Puede ser una ruta de archivo (para sockets de dominio Unix), dirección IPv4 ( aaa.bbb.ccc.ddd:port ) o dirección IPv6 ( [...]:port ). | |
script_path | str | Ubicación donde se encuentran los scripts CGI. | |
default_index | str | index.php | Script predeterminado para ejecutar si no se especifica en el URI de solicitud. |
Las secciones de autorización se pueden declarar en cualquier instancia o controlador de módulo, y proporciona una forma de autorizar el cumplimiento de esa solicitud a través del mecanismo de autorización HTTP estándar. Para requerir la autorización para acceder a una determinada instancia o controlador de módulo, declare una sección authorization con un parámetro basic y establezca una de sus opciones.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
realm | str | Lwan | Reino para la autorización. Esto generalmente se muestra en la interfaz de usuario de usuario/contraseña en los navegadores |
password_file | str | NULL | Ruta para un archivo que contiene nombre de usuario y contraseñas (en texto claro). El formato de archivo es el mismo que el formato de archivo de configuración utilizado por LWAN |
Advertencia
No solo las contraseñas se almacenan en texto claro en un archivo al que el servidor debería acceder a que el servidor, sino que se mantendrán en la memoria durante unos segundos. Evite usar esta función si es posible.
Lea esta sección (y síguela) si planea contribuir a LWAN. No hay nada inesperado aquí; Esto sigue principalmente las reglas y expectativas de muchos otros proyectos de FOSS, pero cada uno espera que las cosas sean un poco diferentes entre sí.
Lwan intenta seguir un estilo de codificación constante en todo el proyecto. Si está considerando contribuir con un parche al proyecto, respete este estilo tratando de hacer coincidir el estilo del código circundante. En general:
global_variables_are_named_like_this , aunque tienden a ser raros y deben marcarse como static (con raras excepciones)local_var , i , conntypedef para estructuras rara vez se usan en LWAN#pragma once en lugar de lo habitual incluir Guard Hackerylwan-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. Por lo tanto:
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