Lwan est un serveur Web haute performance et évolutif .
Le site Web du projet contient plus de détails.
| OS | Cambre | Libérer | Déboguer | Analyse statique | Tests |
|---|---|---|---|---|---|
| Linux | x86_64 | Signaler l'historique | |||
| FreeBSD 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
Vous pouvez soit construire Lwan vous-même, utiliser une image de conteneur ou prendre un package à partir de votre distribution préférée.
Avant d'installer LWAN, assurez-vous que toutes les dépendances sont installées. Tous sont des dépendances communes trouvées dans toute distribution GNU / Linux; Les noms de packages seront différents, mais il ne devrait pas être difficile de rechercher en utilisant l'outil de gestion des packages utilisé par votre distribution.
Le système de construction recherchera ces bibliothèques et activera / lien si disponible.
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (par défaut) est passé:-DUSE_ALTERNATIVE_MALLOC pour CMake avec les valeurs suivantes: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
Sélection d'une version de version (pas de symboles de débogage, messages, activer certaines optimisations, etc.):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
Si vous souhaitez activer les optimisations mais que vous utilisez toujours un débogueur, utilisez-le à la place:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
Pour désactiver les optimisations et créer une version plus conviviale de débogage:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
Cela générera quelques binaires:
src/bin/lwan/lwan : L'exécutable principal LWAN. Peut être exécuté avec --help pour obtenir des conseils.src/bin/testrunner/testrunner : contient du code pour exécuter la suite de tests ( src/scripts/testsuite.py ).src/samples/freegeoip/freegeoip : Implémentation de l'échantillon FreegeOIP. Nécessite sqlite.src/samples/techempower/techempower : Code pour la référence Web Framework de TecheMpower. Nécessite des bibliothèques SQLite et MariADB.src/samples/clock/clock : échantillon d'horloge. Génère un fichier GIF qui affiche toujours l'heure locale.src/bin/tools/mimegen : construit la table de type extension-mime. Utilisé pendant le processus de construction.src/bin/tools/bin2hex : génère un fichier C à partir d'un fichier binaire, adapté à une utilisation avec #include. Utilisé pendant le processus de construction.src/bin/tools/configdump : vide un fichier de configuration à l'aide de l'API de lecteur de configuration. Utilisé pour les tests.src/bin/tools/weighttp : réécriture de l'outil d'analyse comparative HTTP weighttp .src/bin/tools/statuslookupgen : génère une table de hachage parfaite pour les codes d'état HTTP et leurs descriptions. Utilisé pendant le processus de construction. Passing -DCMAKE_BUILD_TYPE=Release permettra certaines optimisations du compilateur (telles que LTO) et régleront le code pour l'architecture actuelle.
Important
Veuillez utiliser la version de version lors de l'analyse comparative . La valeur par défaut est la version de débogage, qui enregistre non seulement toutes les demandes de la sortie standard, mais le fait tout en maintenant une serrure, maintenant gravement le serveur.
La version par défaut (c'est-à-dire ne pas passer -DCMAKE_BUILD_TYPE=Release ) créera une version adaptée à des fins de débogage. Cette version peut être utilisée sous Valgrind (si ses en-têtes sont présents) et comprend des messages de débogage qui sont éliminés dans la version de version. Les messages de débogage sont imprimés pour chaque demande.
Sur ces versions, les désinfectants peuvent être activés. Pour sélectionner avec lequel construire LWAN, spécifiez l'une des options suivantes à la ligne d'invocation CMake:
-DSANITIZER=ubsan Sélectionne le désinfectant de comportement non défini.-DSANITIZER=address sélectionne le désinfectant d'adresse.-DSANITIZER=thread Sélectionne le désinfectant du thread. Les allocateurs de mémoire alternatifs peuvent également être sélectionnés. Lwan prend actuellement en charge TCMalloc, Mimalloc et Jemalloc hors de la boîte. Pour utiliser l'un d'eux, passez -DALTERNATIVE_MALLOC=name sur la ligne d'invocation CMake, en utilisant les noms fournis dans la section "dépendances facultatives".
L'option -DUSE_SYSLOG=ON peut être transmise à CMake pour se connecter également au journal système en plus de la sortie standard.
Si vous construisez LWAN pour une distribution, il pourrait être sage d'utiliser l'option -DMTUNE_NATIVE=OFF , sinon le binaire généré peut ne pas fonctionner sur certains ordinateurs.
La prise en charge TLS est activée automatiquement en présence d'une installation MBEDTLS appropriée sur les systèmes Linux avec des en-têtes suffisamment nouveaux pour prendre en charge KTLS, mais peut être désactivé en passant -DENABLE_TLS=NO à Cmake.
~/lwan/build$ make testsuite
Cela compilera le programme testrunner et exécutera la suite de tests de régression dans src/scripts/testsuite.py .
~/lwan/build$ make benchmark
Cela compilera testrunner et exécutera le script de benchmark src/scripts/benchmark.py .
Lwan peut également être construit avec le type de construction de couverture en spécifiant -DCMAKE_BUILD_TYPE=Coverage . Cela permet à la generate-coverage Make Target, qui exécutera testrunner pour préparer un rapport de couverture de test avec LCOV.
Chaque engagement dans ce référentiel déclenche la génération de ce rapport et les résultats sont accessibles au public.
Configurez le serveur en modifiant le lwan.conf fourni; Le format est expliqué dans les détails ci-dessous.
Note
LWAN essaiera de trouver un fichier de configuration basé dans le nom exécutable dans le répertoire actuel; testrunner.conf sera utilisé pour le testrunner Binary, lwan.conf pour le binaire lwan , etc.
Les fichiers de configuration sont chargés à partir du répertoire actuel. Si aucune modification n'est apportée à ce fichier, l'exécution de LWAN servira des fichiers statiques situés dans le répertoire ./wwwroot . Lwan écoutera sur le port 8080 sur toutes les interfaces.
LWAN détectera le nombre de processeurs, augmentera le nombre maximal de descripteurs de fichiers ouverts et fera généralement de son mieux pour automatiquement des paramètres raisonnables pour l'environnement sur lequel il fonctionne. Beaucoup de ces paramètres peuvent être modifiés dans le fichier de configuration, mais c'est généralement une bonne idée de ne pas jouer avec eux.
Conseil
Facultativement, le binaire lwan peut être utilisé pour le service de fichiers statique unique sans aucun fichier de configuration. Exécutez-le avec --help pour obtenir de l'aide à ce sujet.
Lwan utilise une syntaxe de fichier de configuration key = value Familière. Les commentaires sont pris en charge avec le caractère # (similaire à EG Shell Scripts, Python et Perl). Des sections imbriquées peuvent être créées avec des supports bouclés. Les sections peuvent être vides; Dans ce cas, les supports bouclés sont facultatifs.
some_key_name équivaut à some key name dans les fichiers de configuration (en tant que détail d'implémentation, les options de configuration de lecture de code ne seront données que la version avec des soulignements).
Conseil
Les valeurs peuvent contenir des variables d'environnement. Utilisez la syntaxe ${VARIABLE_NAME} . Les valeurs par défaut peuvent être spécifiées avec un colon (par exemple ${VARIABLE_NAME:foo} , qui évalue ${VARIABLE_NAME} si elle est définie, ou foo sinon).
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
"""
}
Certains exemples peuvent être trouvés dans lwan.conf et techempower.conf .
Les constantes peuvent être définies et réutilisées dans le fichier de configuration en les spécifiant dans une section constants n'importe où dans le fichier de configuration. Une constante ne sera disponible qu'après que cette section définit une constante particulière. Les constantes peuvent être redéfinies. Si une constante n'est pas définie, sa valeur sera obtenue à partir d'une variable d'environnement. S'il n'est pas défini dans l'une ou l'autre section constants , ou dans l'environnement, Lwan s'abortera avec un message d'erreur approprié.
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
La même syntaxe pour les valeurs par défaut spécifiées ci-dessus est valide ici (par exemple, spécifiant user_name pour être ${USER:nobody} ne définira ${user_name} à nobody si ${USER} n'est pas défini dans la variable d'environnement ou n'est pas une autre constante.)
| Taper | Description |
|---|---|
str | Tout type de texte de forme libre, généralement spécifique à l'application |
int | Numéro entier. La plage est spécifique à l'application |
time | Intervalle de temps. Voir le tableau ci-dessous pour les unités |
bool | Valeur booléenne. Voir le tableau ci-dessous pour les valeurs valides |
Les champs de temps peuvent être spécifiés à l'aide de multiplicateurs. Plusieurs peuvent être spécifiés, ils sont simplement additionnés; Par exemple, "1m 1W" spécifie "1 mois et 1 semaine" (37 jours). Le tableau suivant répertorie tous les multiplicateurs connus:
| Multiplicateur | Description |
|---|---|
s | Secondes |
m | Minutes |
h | Heures |
d | Jours |
w | Semaines de 7 jours |
M | Mois de 30 jours |
y | 365 jours |
Note
Un nombre avec un multiplicateur non dans ce tableau est ignoré; Un avertissement est émis lors de la lecture du fichier de configuration. Aucun espace ne doit exister entre le nombre et son multiplicateur.
| Vraies valeurs | Fausses valeurs |
|---|---|
| Tout nombre entier différent de 0 | 0 |
on | off |
true | false |
yes | no |
C'est généralement une bonne idée de laisser Lwan décider des meilleurs paramètres pour votre environnement. Cependant, tous les environnements ne sont pas les mêmes, et toutes les utilisations ne peuvent pas être décidées automatiquement, donc certaines options de configuration sont fournies.
| Option | Taper | Défaut | Description |
|---|---|---|---|
keep_alive_timeout | time | 15 | Timeout pour garder une connexion vivante |
quiet | bool | false | Définir TRUE pour ne pas imprimer de messages de débogage. Seule efficace dans les constructions de libération. |
expires | time | 1M 1w | Valeur de l'en-tête "Expire". La valeur par défaut est 1 mois et 1 semaine |
threads | int | 0 | Nombre de fils d'E / S. Par défaut (0) est le nombre de processeurs en ligne |
proxy_protocol | bool | false | Active le protocole proxy. Les versions 1 et 2 sont prises en charge. Activez uniquement ce paramètre si vous utilisez LWAN derrière un proxy, et le proxy prend en charge ce protocole; Sinon, cela permet à quiconque de parcourir les adresses IP d'origine |
max_post_data_size | int | 40960 | Définit le nombre maximum de taille de données pour les demandes de poste, en octets |
max_put_data_size | int | 40960 | Définit le nombre maximum de taille de données pour les demandes de put, en octets |
max_file_descriptors | int | 524288 | Nombre maximum de descripteurs de fichiers. Doit être au moins 10 fois threads |
request_buffer_size | int | 4096 | Demande la longueur de taille du tampon. S'il est supérieur à la valeur par défaut de 4096 , il sera alloué dynamiquement. |
allow_temp_files | str | "" | Utiliser des fichiers temporaires; Défini sur post pour les demandes de publication, les demandes de put ou all (équivalentes à la définition post put ) pour les deux. |
error_template | str | Modèle d'erreur par défaut | Modèle pour les codes d'erreur. Voir les variables ci-dessous. |
error_template| Variable | Taper | Description |
|---|---|---|
short_message | str | Message d'erreur court (par exemple, Not found ) |
long_message | str | Message d'erreur long (par exemple, The requested resource could not be found on this server ) |
LWAN peut abandonner ses privilèges à un utilisateur dans le système et limiter sa vue de système de fichiers avec un chroot. Bien qu'il ne soit pas à l'épreuve des balles, cela offre une première couche de sécurité dans le cas où il y a un bug dans LWAN.
Afin d'utiliser cette fonctionnalité, déclarez une section de straitjacket (ou straightjacket ) et définissez certaines options. Cela nécessite que Lwan soit exécuté sous forme root .
Bien que cette section puisse être écrite n'importe où dans le fichier (tant qu'il s'agit d'une déclaration de haut niveau), si des répertoires sont ouverts, en raison de l'instanciation du module serve_files , Lwan refusera de commencer. (Ce chèque n'est effectué que sur Linux en tant que sauvegarde pour la malconfiguration.)
Conseil
Déclarez une camisole de force juste avant une section site de manière à ce que les fichiers de configuration et les données privées (par exemple les touches TLS) soient hors de portée du serveur une fois l'initialisation.
| Option | Taper | Défaut | Description |
|---|---|---|---|
user | str | NULL | Déposez les privilèges à ce nom d'utilisateur |
chroot | str | NULL | Path to chroot() |
drop_capabilities | bool | true | Déposez toutes les capacités avec CapSet (2) (sous Linux), ou Pledge (2) (sous OpenBSD). |
S'il est nécessaire de spécifier des en-têtes personnalisés pour chaque réponse, on peut déclarer une section headers dans la portée globale. L'ordre sur lequel cette section apparaît n'est pas important.
Par exemple, cette déclaration:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Se remplacera à la fois l'en-tête Server ( Server: lwan ne sera pas envoyé) et définira Some-Custom-Header avec la valeur obtenue à partir de la variable d'environnement $WITH_THIS_ENVIRONMENT_VARIABLE .
Certains en-têtes ne peuvent pas être remplacés, car cela entraînerait des problèmes lors de l'envoi de leurs valeurs réelles lors de l'entretien des demandes. Ceux-ci incluent mais sans s'y limiter:
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow- Note
Les noms d'en-tête sont également insensibles à la casse (et préservant du cas). SeRVeR primordial remplacera l'en-tête Server , mais l'envoie de la façon dont il a été écrit dans le fichier de configuration.
Seuls deux auditeurs sont pris en charge par processus LWAN: l'écouteur HTTP (section listener ) et l'écouteur HTTPS (section tls_listener ). Un seul auditeur de chaque type est autorisé.
Avertissement
Le support TLS est expérimental. Bien qu'il soit stable lors des tests initiaux, votre kilométrage peut varier. Seul TLSV1.2 est pris en charge à ce stade, mais TLSV1.3 est prévu.
Note
Le support TLS nécessite? Linux avec le module tls.ko intégré ou chargé. Une prise en charge d'autres systèmes d'exploitation peut être ajoutée à l'avenir. FreeBSD semble possible, d'autres systèmes d'exploitation ne semblent pas offrir une fonctionnalité similaire. Pour les systèmes d'exploitation non pris en charge, l'utilisation d'un proxy TLS Terminator tel que Hitch est une bonne option.
Pour les sections listener et tls_listener , le seul paramètre est l'adresse et le port d'interface sur lesquels écouter. La syntaxe de l'écoute est ${ADDRESS}:${PORT} , où ${ADDRESS} peut être * (liaison à toutes les interfaces), une adresse IPv6 (si elle est entourée de crochets), une adresse IPv4 ou un nom d'hôte. Par exemple, listener localhost:9876 n'écouterait que dans l'interface lo , port 9876 .
Bien qu'une section listener ne prenne pas de clés, une section tls_listener en nécessite deux: cert et key (chacune pointant, respectivement, à l'emplacement sur le disque où le certificat TLS et les fichiers de clés privés se trouvent) et prend une clé hsts Boolean en option, qui contrôle si des en-têtes Strict-Transport-Security seront condamnées aux réponses HTTPS.
Conseil
Pour générer ces clés à des fins de test, l'outil de ligne de commande OpenSSL peut être utilisé comme celui qui suit: openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
Note
Il est recommandé qu'un camion-colatement avec une option chroot soit déclaré juste après une section tls_listener , de telle manière que les chemins vers le certificat et la clé sont hors de portée à partir de ce moment.
Si l'activation du socket systemd est utilisée, systemd peut être spécifié en tant que paramètre. (Si plusieurs écouteurs de SystemD sont spécifiés, systemd:FileDescriptorName peut être spécifié, où FileDescriptorName suit les conventions définies dans la documentation systemd.socket .)
Exemples:
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 {
...
}
Une section site regroupe les instances de modules et de gestionnaires qui répondront aux demandes à un préfixe URL donné.
Afin d'acheter des URL, LWAN correspond au plus grand préfixe commun de l'URI de demande avec un ensemble de préfixes spécifiés dans la section Écouteur. La façon dont une demande à un préfixe particulier sera traitée dépend du gestionnaire ou du module a été déclaré dans la section de l'auditeur. Les gestionnaires et les modules sont similaires en interne; Les gestionnaires ne sont que des fonctions et ne contiennent aucun état, et les modules maintiennent l'état (instance nommée). Plusieurs instances d'un module peuvent apparaître dans une section auditeur.
Il n'y a pas de syntaxe spéciale pour attacher un préfixe à un gestionnaire ou un module; Toutes les règles de l'analyseur de configuration s'appliquent ici. Utilisez ${NAME} ${PREFIX} pour lier le chemin de préfixe ${PREFIX} vers un gestionnaire nommé ${NAME} (if ${NAME} commence par & , comme par "l'adresse de" opérateur), ou un module nommé ${NAME} . Des sections vides peuvent être utilisées ici.
Chaque module aura son ensemble spécifique d'options et ils sont répertoriés dans les sections suivantes. En plus des options de configuration, une section authorization spéciale peut être présente dans la déclaration d'une instance de module. Les gestionnaires ne prennent aucune option de configuration, mais peuvent inclure la section authorization .
Conseil
L'exécution de LWAN avec l'argument de ligne de commande --help affichera une liste de modules et de gestionnaires intégrés.
Ce qui suit est une documentation de base pour les modules expédiés avec LWAN.
Le module serve_files servira des fichiers statiques et créera automatiquement des indices de répertoire ou servira des fichiers pré-compressés. Il fera généralement de son mieux pour servir les fichiers de la manière la plus rapide possible selon certaines heuristiques.
| Option | Taper | Défaut | Description |
|---|---|---|---|
path | str | NULL | Chemin vers un répertoire contenant des fichiers à servir |
index_path | str | index.html | Nom de fichier pour servir d'index pour un répertoire |
serve_precompressed_path | bool | true | Si $ file.gz existe, est plus petit et plus récent que le fichier $ et que le client accepte le codage gzip , transférez-le |
auto_index | bool | true | Générez automatiquement une liste de répertoires si aucun fichier index_path ne présente. Sinon, donne 404 |
auto_index_readme | bool | true | Comprend le contenu des fichiers ReadMe dans le cadre de l'index du répertoire généré automatiquement |
directory_list_template | str | NULL | Chemin vers un modèle de moustache pour la liste des répertoires; Par défaut, utilisez un modèle interne |
read_ahead | int | 131702 | Montant maximum d'octets à lire à l'avance lors de la mise en cache des fichiers ouverts. Une valeur de 0 désactive leadahead. ReadaHead est effectué par un thread à faible priorité pour ne pas bloquer les threads d'E / S pendant que les étendues de fichiers sont lues à partir du système de fichiers. |
cache_for | time | 5s | Il est temps de conserver les métadonnées de fichiers (taille, contenu compressé, descripteur de fichier ouvrir, etc.) en cache |
Note
Les fichiers inférieurs à 16KIB seront compressés dans RAM pour la durée spécifiée dans le paramètre cache_for . Lwan essaiera toujours de compresser avec dégonfler et se compressera éventuellement avec Brotli et ZSTD (si Lwan a été construit avec un support approprié).
Dans les cas où la compression ne valait pas l'effort (par exemple, l'ajout de l'en-tête Content-Encoding entraînerait une réponse plus importante que l'envoi du fichier non compressé, généralement le cas pour de très petits fichiers), LWAN ne passera pas de temps à compresser un fichier.
Pour les fichiers supérieurs à 16KIB, LWAN n'essaiera pas de les compresser. Dans les versions futures, il pourrait le faire et envoyer des réponses en utilisant le codage en morceaux pendant que le fichier est compressé (jusqu'à une certaine limite, bien sûr), mais pour l'instant, seuls les fichiers précompressés (voir serve_precompressed_path paramètre dans le tableau ci-dessus) sont considérés.
Pour tous les cas, LWAN pourrait essayer d'utiliser la version gzippé si cela se trouve dans le système de fichiers et que le client a demandé ce codage.
directory_list_template| Variable | Taper | Description |
|---|---|---|
rel_path | str | Chemin par rapport au répertoire racine réel Chemin |
readme | str | Contenu du premier fichier readme trouvé ( readme , readme.txt , read.me , README.TXT , README ) |
file_list | itérateur | Iterates sur la liste des fichiers |
file_list.zebra_class | str | odd pour les articles étranges, ou even ou même les articles |
file_list.icon | str | Chemin vers l'icône du type de fichier |
file_list.name | str | Nom de fichier (échappé) |
file_list.type | str | Type de fichier (répertoire ou fichier régulier) |
file_list.size | int | Taille de fichier |
file_list.unit | str | Unité pour file_size |
Le module lua permettra des demandes à entretenir par des scripts écrits dans le langage de programmation LUA. Bien que les fonctionnalités fournies par ce module soient assez spartiates, il est capable d'exécuter des cadres tels que Sailor.
Les scripts peuvent être servis à partir de fichiers ou intégrés dans le fichier de configuration et les résultats de leur chargement, les modules LUA standard et (éventuellement, si vous utilisez Luajit), l'optimisation du code sera mise en cache pendant un certain temps.
| Option | Taper | Défaut | Description |
|---|---|---|---|
default_type | str | text/plain | Type de mime par défaut pour les réponses |
script_file | str | NULL | Chemin vers le script LUA |
cache_period | time | 15s | Il est temps de garder Lua State chargé en mémoire |
script | str | NULL | Script Lua en ligne |
Note
Les scripts LUA ne peuvent pas utiliser les variables globales, car ils peuvent non seulement être entretenus par différents threads, mais l'état ne sera disponible que pour la durée spécifiée dans l'option cache_period . En effet, chaque fil d'E / S dans LWAN créera une instance d'un VM Lua (c'est-à-dire un lua_State Struct pour chaque thread d'E / S), et chaque Lwan Coroutine engendrera un thread Lua (avec lua_newthread() ) par demande.
Il n'est pas nécessaire d'avoir une instance du module LUA pour chaque point de terminaison; Un seul script, intégré dans le fichier de configuration ou autrement, peut servir de nombreux points de terminaison différents. Les scripts sont censés implémenter des fonctions avec la signature suivante: handle_${METHOD}_${ENDPOINT}(req) , où ${METHOD} peut être une méthode HTTP (c'est-à-dire get , post , head , etc.), et ${ENDPOINT} est le point de terminaison souhaité à gérer par cette fonction. Une fonction handle(req) sera appelée si la version spécifique n'existe pas.
Conseil
Utilisez le point de terminaison root pour un capot. Par exemple, la fonction de gestionnaire handle_get_root() sera appelée si aucun autre gestionnaire n'a pu être trouvé pour cette demande. Si aucun Catchall n'est spécifié, le serveur renverra une erreur 404 Not Found .
Le paramètre req pointe vers un métatable qui contient des méthodes pour obtenir des informations à partir de la demande, ou pour définir la réponse, comme indiqué ci-dessous:
req:query_param(param) nil le paramètre de requête (à partir de la chaîne de requête) avec la clé paramreq:post_param(param) renvoie le paramètre de post (uniquement pour ${POST} gestionnaires) avec la clé param , ou nil si elle n'est pas trouvéereq:set_response(str) définit la réponse à la chaîne str la chaînereq:say(str) envoie un morceau de réponse (en utilisant un codage en morceaux en http)req:send_event(event, str) envoie un événement (en utilisant des événements de serveur)req:cookie(param) renvoie le cookie nommé param , ou nil n'est pas trouvéreq:set_headers(tbl) définit les en-têtes de réponse de la table tbl ; Un en-tête peut être spécifié plusieurs fois en utilisant une table, plutôt qu'une chaîne, dans la valeur du tableau ( {'foo'={'bar', 'baz'}} ); doit être appelé avant d'envoyer une réponse avec say() ou send_event()req:header(name) obtient l'en-tête de la demande avec le nom donné ou nil si cela n'est pas trouvéreq:sleep(ms) fait une pause du gestionnaire actuel pour la quantité spécifiée de millisecondesreq:ws_upgrade() renvoie 1 si la connexion pouvait être mise à niveau vers un WebSocket; 0 autrementreq:ws_write_text(str) envoie str via la connexion mise à niveau Web en tant que cadre de textereq:ws_write_binary(str) envoie str via la connexion à remise en binaire en tant que cadre binairereq:ws_write(str) envoie str via la connexion mise à niveau Web en tant que texte ou cadre binaire, en fonction du contenu contenant uniquement des caractères ASCII ou nonreq:ws_read() renvoie une chaîne avec le contenu de la dernière trame WebSocket, ou un nombre indiquant un statut (EnotConn / 107 sur Linux s'il a été déconnecté; Eagain / 11 sur Linux si rien n'était disponible; Enomsg / 42 sur Linux sinon). La valeur de retour ici pourrait changer à l'avenir pour quelque chose de plus semblable à Lua.req:remote_address() renvoie une chaîne avec l'adresse IP distante.req:path() renvoie une chaîne avec le chemin de demande.req:query_string() renvoie une chaîne avec la chaîne de requête (chaîne vide si aucune chaîne de requête ne présente).req:body() renvoie le corps de la demande (requêtes de poste / put).req:request_id() renvoie une chaîne contenant l'ID de demande.req:request_date() renvoie la date car elle sera écrite dans l'en-tête de réponse Date .req:is_https() renvoie true si cette demande est entretenue via https, false autrement.req:host() renvoie la valeur de l'en-tête Host si elle est présente, sinon nil .req:http_version() renvoie HTTP/1.0 ou HTTP/1.1 selon la version de la demande.req:http_method() renvoie une chaîne, en majuscule, avec la méthode http (par exemple "GET" ).req:http_headers() renvoie une table avec tous les en-têtes et leurs valeurs. Les fonctions de gestionnaire peuvent renvoyer nil (auquel cas, une réponse 200 OK est générée), ou un nombre correspondant à un code d'état HTTP. Tenter de renvoyer un code d'état HTTP non valide ou autre chose qu'un nombre ou nil entraînera une réponse 500 Internal Server Error lancement.
En plus des métaméthodes du paramètre req , on peut également enregistrer des messages avec différents niveaux de journalisation en appelant des méthodes de Lwan.log :
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (Abortera également Lwan! Utiliser avec prudence)Lwan.log:debug(str) (uniquement disponible dans les builds de débogage; No-opératoire autrement) Note
Si LWAN est construit avec la prise en charge de Syslog, ces messages seront également envoyés au journal système, sinon ils seront imprimés à l'erreur standard.
Le module rewrite correspondra aux modèles dans les URL et donnera la possibilité de rediriger vers une autre URL, ou de réécrire la demande d'une manière que Lwan gère la demande comme si elle était faite de cette manière à l'origine.
Note
Firisée de LUA 5.3.1, le moteur d'expresion ordinaire n'est peut-être pas aussi plein de caractéristiques que les moteurs les plus généraux, mais a été choisi spécifiquement parce qu'il s'agit d'un automate fini déterministe dans le but de rendre impossible des attaques de déni de service.
La nouvelle URL peut être spécifiée à l'aide d'une syntaxe de substitution de texte simple ou utilise des scripts LUA.
Conseil
Les scripts LUA contiendront les mêmes métaméthodes disponibles dans le métatable req fourni par le module LUA, donc il peut être assez puissant.
Chaque instance du module de réécriture nécessitera un pattern et l'action pour exécuter lorsque ce modèle est apparié. Les modèles sont évalués dans l'ordre dans lequel ils apparaissent dans le fichier de configuration et sont spécifiés à l'aide de sections imbriquées dans le fichier de configuration. Par exemple, considérez l'exemple suivant, lorsque deux modèles sont spécifiés:
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
}
}
Cet exemple définit deux modèles, l'un fournissant une URL plus agréable qui est cachée à l'utilisateur, et une autre fournissant une façon différente d'obtenir un lien direct vers une image hébergée sur un service d'hébergement d'image populaire (c'est-à-dire en demande /some/base/endpoint/imgur/mp4/4kOZNYX redirectionra directement à une ressource dans le service Imgur).
La valeur de rewrite_as ou redirect_to peut également être des scripts LUA; Dans ce cas, l'option expand_with_lua doit être définie sur true et, au lieu d'utiliser la syntaxe de substitution de texte simple comme l'exemple ci-dessus, une fonction nommée handle_rewrite(req, captures) doit être définie à la place. Le paramètre req est documenté dans la section module LUA; Le paramètre captures est un tableau contenant toutes les captures, dans l'ordre (c.-à-d. captures[2] est équivalent à %2 dans la syntaxe de substitution de texte simple). Cette fonction renvoie la nouvelle URL vers la redirection vers.
Ce module n'a aucune option en soi. Les options sont spécifiées dans chaque modèle.
| Option | Taper | Défaut | Description |
|---|---|---|---|
rewrite_as | str | NULL | Réécrivez l'URL en suivant ce modèle |
redirect_to | str | NULL | Rediriger vers une nouvelle URL suivant ce modèle |
expand_with_lua | bool | false | Utilisez les scripts LUA pour rediriger vers ou réécrire une demande |
Les options redirect_to et rewrite_as s'excluent mutuellement, et l'une d'elles doit être spécifiée au moins.
Il est également possible de spécifier des conditions pour déclencher une réécriture. Pour en spécifier un, ouvrez un bloc condition , spécifiez le type de condition, puis les paramètres pour que cette condition soit évaluée. Plusieurs conditions peuvent être définies par règle de réécriture tant qu'il y a une condition par type:
| Condition | Peut utiliser subs. syntaxe | Section requise | Paramètres | Description |
|---|---|---|---|---|
cookie | Oui | Oui | Une seule key = value | Vérifie si la demande a key de cookie a une value de valeur |
query | Oui | Oui | Une seule key = value | Vérifie si la demande a key de la variable de requête a une value de valeur |
post | Oui | Oui | Une seule key = value | Vérifie si la demande a key de données postale a une value de valeur |
header | Oui | Oui | Une seule key = value | Vérifie si key de l'en-tête de demande a une value de valeur |
environment | Oui | Oui | Une seule key = value | Vérifie si key de variable d'environnement a une value de valeur |
stat | Oui | Oui | path , is_dir , is_file | Vérifie si path existe dans le système de fichiers et vérifie éventuellement si is_dir ou is_file |
encoding | Non | Oui | deflate , gzip , brotli , zstd , none | Vérifie si le client accepte les réponses dans un codage déterminé (par exemple, deflate = yes pour le codage dégonflé) |
proxied | Non | Non | Booléen | Vérifie si la demande a été procassée via un protocole proxy |
http_1.0 | Non | Non | Booléen | Vérifie si la demande est faite avec un client HTTP / 1.0 |
is_https | Non | Non | Booléen | Vérifie si la demande est faite via HTTPS |
has_query_string | Non | Non | Booléen | Vérifie si la demande a une chaîne de requête (même si vide) |
method | Non | Non | Nom de méthode | Vérifie si la méthode http est celle spécifiée |
lua | Non | Non | Chaîne | Exécute la fonction LUA matches(req) à l'intérieur de la chaîne et vérifie si elle renvoie true ou false |
backref | Non | Oui | Un seul backref index = value | Vérifie si le numéro de rétroviseur correspond à la valeur fournie |
Peut utiliser subs. La syntaxe fait référence à la possibilité de référencer le motif apparié en utilisant la même syntaxe de substitution utilisée pour la rewrite as ou redirect to les actions. Par exemple, condition cookie { some-cookie-name = foo-%1-bar } se remplacera %1 par la première correspondance du modèle auquel cette condition est liée.
Note
Les conditions qui ne nécessitent pas de section doivent être écrites comme une clé; Par exemple, condition has_query_string = yes .
Par exemple, si l'on veut envoyer site-dark-mode.css s'il y a un cookie style avec la valeur dark , et envoyer site-light-mode.css sinon, on peut écrire:
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
Un autre exemple: si l'on veut envoyer des fichiers pré-compressés s'ils existent dans le système de fichiers et que l'utilisateur les a demandé:
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
}
Note
En général, cela n'est pas nécessaire, car le module de service de fichier le fera automatiquement et choisira le plus petit fichier disponible pour l'encodage demandé, mais cela montre qu'il est possible d'avoir une fonctionnalité similaire par configuration seule.
Le module redirect , comme il le dit dans l'étain, générera une 301 Moved permanently (par défaut; le code peut être modifié, voir ci-dessous) la réponse, selon les options spécifiées dans sa configuration. Généralement, le module rewrite doit être utilisé à la place car il emballe plus de fonctionnalités; Cependant, ce module sert également d'exemple de la façon d'écrire des modules LWAN (moins de 100 lignes de code).
Si l'option to n'est pas spécifiée, elle génère toujours une réponse 500 Internal Server Error . La spécification d'un code HTTP non valide, ou d'un code que LWAN ne connaît pas (voir enum lwan_http_status ), produira une réponse 301 Moved Permanently .
| Option | Taper | Défaut | Description |
|---|---|---|---|
to | str | NULL | L'emplacement pour rediriger vers |
code | int | 301 | Le code HTTP pour effectuer une redirection |
Le module response générera une réponse artificielle de tout code HTTP. En plus de servir également d'exemple de la façon d'écrire un module LWAN, il peut être utilisé pour tailler des vides à partir d'autres modules (par exemple, générant une réponse 405 Not Allowed pour les fichiers dans /.git , if / est servi avec le module serve_files ).
Si le code fourni tombe en dehors des codes de réponse connus de LWAN, une erreur 404 Not Found sera envoyée à la place.
| Option | Taper | Défaut | Description |
|---|---|---|---|
code | int | 999 | Un code de réponse HTTP |
Le module fastcgi indique les demandes entre le client HTTP se connectant à LWAN et un serveur FastCGI accessible par LWAN. Ceci est utile, par exemple, pour servir les pages d'un langage de script tel que PHP.
Note
Il s'agit d'une version préliminaire de ce module, et en tant que telle, elle n'est pas bien optimisée, certaines fonctionnalités sont manquantes et certaines valeurs fournies à l'environnement sont codées en dur.
| Option | Taper | Défaut | Description |
|---|---|---|---|
address | str | Adresse pour se connecter à. Peut être un chemin de fichier (pour les sockets de domaine UNIX), l'adresse IPv4 ( aaa.bbb.ccc.ddd:port ) ou l'adresse IPv6 ( [...]:port ). | |
script_path | str | Emplacement où se trouvent les scripts CGI. | |
default_index | str | index.php | Le script par défaut s'exécute s'il est non spécifié dans l'uri de demande. |
Les sections d'autorisation peuvent être déclarées dans n'importe quelle instance ou gestionnaire de module et fournit un moyen d'autoriser la réalisation de cette demande via le mécanisme d'autorisation HTTP standard. Afin d'exiger l'autorisation d'accéder à une certaine instance ou gestionnaire de module, déclarez une section authorization avec un paramètre basic et définissez l'une de ses options.
| Option | Taper | Défaut | Description |
|---|---|---|---|
realm | str | Lwan | Royaume pour l'autorisation. Ceci est généralement affiché dans l'interface utilisateur / mot de passe dans les navigateurs |
password_file | str | NULL | Chemin pour un fichier contenant le nom d'utilisateur et les mots de passe (en texte clair). Le format de fichier est le même que le format de fichier de configuration utilisé par LWAN |
Avertissement
Non seulement les mots de passe sont stockés en texte clair dans un fichier qui devrait être accessible par le serveur, mais ils seront gardés en mémoire pendant quelques secondes. Évitez d'utiliser cette fonctionnalité si possible.
Veuillez lire cette section (et suivre) si vous prévoyez de contribuer à LWAN. Il n'y a rien d'inattendu ici; Cela suit principalement les règles et les attentes de nombreux autres projets FOSS, mais tout le monde s'attend à des choses un peu différentes les unes des autres.
Lwan essaie de suivre un style de codage cohérent tout au long du projet. Si vous envisagez de contribuer un patch au projet, veuillez respecter ce style en essayant de correspondre au style du code environnant. En général:
global_variables_are_named_like_this , même s'ils ont tendance à être rares et doivent être marqués comme static (à de rares exceptions)local_var , i , conntypedef pour les structures sont rarement utilisés dans LWAN#pragma once au lieu de l'inclure habituelle Inclure 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. Donc:
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