Lwanは、高性能でスケーラブルなWebサーバーです。
プロジェクトWebサイトには詳細が含まれています。
| OS | アーチ | リリース | デバッグ | 静的分析 | テスト |
|---|---|---|---|---|---|
| Linux | x86_64 | 履歴を報告します | |||
| FreeBSD 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
自分でLwanを構築したり、コンテナ画像を使用したり、お気に入りの配布からパッケージをつかむことができます。
Lwanをインストールする前に、すべての依存関係がインストールされていることを確認してください。それらはすべて、GNU/Linux分布に見られる一般的な依存関係です。パッケージ名は異なりますが、配布で使用されているパッケージ管理ツールを使用して検索することは難しくありません。
ビルドシステムは、これらのライブラリを探し、利用可能な場合は有効/リンクします。
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (default)が渡される場合:-DUSE_ALTERNATIVE_MALLOCを渡すことで使用できます。pacman -S cmake zlibpkg install cmake pkgconfapt-get update && apt-get install git cmake zlib1g-dev pkg-configbrew install cmake pacman -S cmake zlib sqlite luajit mariadb-libs gperftools valgrind mbedtlspkg install cmake pkgconf sqlite3 lua51apt-get update && apt-get install git cmake zlib1g-dev pkg-config lua5.1-dev libsqlite3-dev libmariadb-dev libmbedtls-devbrew install cmake mariadb-connector-c sqlite [email protected] pkg-config ~$ git clone git://github.com/lpereira/lwan
~$ cd lwan
~/lwan$ mkdir build
~/lwan$ cd build
リリースバージョンの選択(デバッグシンボル、メッセージ、最適化などを有効にする):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
最適化を有効にしたいが、それでもデバッガーを使用する場合は、代わりにこれを使用してください。
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
最適化を無効にし、よりデバッグに優しいバージョンを構築するには:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
これにより、いくつかのバイナリが生成されます。
src/bin/lwan/lwan :メインのLwan実行可能ファイル。ガイダンスのために--helpで実行される場合があります。src/bin/testrunner/testrunner :テストスイート( src/scripts/testsuite.py )を実行するコードが含まれています。src/samples/freegeoip/freegeoip :FreeGeOIPサンプルの実装。 sqliteが必要です。src/samples/techempower/techempower :Techmpower Webフレームワークベンチマークのコード。 SQLiteおよびMariadBライブラリが必要です。src/samples/clock/clock :クロックサンプル。常に現地時間を表示するGIFファイルを生成します。src/bin/tools/mimegen :拡張ミメ型テーブルを構築します。ビルドプロセス中に使用されます。src/bin/tools/bin2hex :#includeでの使用に適したバイナリファイルからCファイルを生成します。ビルドプロセス中に使用されます。src/bin/tools/configdump :構成リーダーAPIを使用して構成ファイルをダンプします。テストに使用されます。src/bin/tools/weighttp : weighttp HTTPベンチマークツールの書き換え。src/bin/tools/statuslookupgen :HTTPステータスコードとその説明のための完全なハッシュテーブルを生成します。ビルドプロセス中に使用されます。 -DCMAKE_BUILD_TYPE=Releaseを渡すと、コンパイラの最適化(LTOなど)が可能になり、現在のアーキテクチャのコードを調整します。
重要
ベンチマーク時にリリースビルドを使用してください。デフォルトはデバッグビルドです。デバッグビルドは、すべての要求を標準出力にログに記録するだけでなく、ロックを保持しているときにそれを行い、サーバーをひどく押し続けます。
デフォルトのビルド(すなわち、 -DCMAKE_BUILD_TYPE=Releaseを通過しない)は、デバッグの目的に適したバージョンをビルドします。このバージョンは、Valgrind (ヘッダーが存在する場合)で使用でき、リリースバージョンで剥がされたデバッグメッセージが含まれています。デバッグメッセージは、すべてのリクエストに対して印刷されます。
これらのビルドでは、消毒剤を有効にすることができます。 LWANを構築するものを選択するには、次のオプションのいずれかをCmake Invocation Lineに指定します。
-DSANITIZER=ubsan未定義の動作消毒剤を選択します。-DSANITIZER=addressアドレス消毒剤を選択します。-DSANITIZER=threadスレッド消毒剤を選択します。代替メモリアロケーターも選択できます。 Lwanは現在、Tcmalloc、Mimalloc、およびJemallocを箱から出してサポートしています。そのいずれかを使用するには、「オプションの依存関係」セクションに記載されている名前を使用して、 -DALTERNATIVE_MALLOC=nameを渡します。
-DUSE_SYSLOG=ON optionをcmakeに渡すことができ、標準出力に加えてシステムログにログに記録できます。
配布のためにLwanを構築している場合、 -DMTUNE_NATIVE=OFFオプションを使用するのが賢明かもしれません。そうしないと、生成されたバイナリが一部のコンピューターで実行されない場合があります。
TLSサポートは、KTLSをサポートするのに十分な新しいヘッダーを備えたLinux Systemsの適切なMBEDTLSインストールが存在する場合、自動的に有効になりますが、cmakeを通過することで-DENABLE_TLS=NOを実行することで無効にできます。
~/lwan/build$ make testsuite
これにより、 testrunnerプログラムをコンパイルし、 src/scripts/testsuite.pyで回帰テストスイートを実行します。
~/lwan/build$ make benchmark
これにより、 testrunnerをコンパイルし、ベンチマークスクリプトsrc/scripts/benchmark.pyを実行します。
Lwanは-DCMAKE_BUILD_TYPE=Coverageを指定することにより、カバレッジビルドタイプで構築することもできます。これにより、 generate-coverage Make Targetが可能になり、 testrunnerを実行してLCOVを使用したテストカバレッジレポートを準備します。
このリポジトリでのすべてのコミットは、このレポートの生成をトリガーし、結果は公開されています。
提供されたlwan.confを編集してサーバーをセットアップします。形式については、以下の詳細について説明します。
注記
Lwanは、現在のディレクトリの実行可能ファイルに基づいて構成ファイルを見つけようとします。 testrunner.conf 、 testrunnerバイナリ、 lwan.conf for the lwan binaryなどに使用されます。
構成ファイルは現在のディレクトリからロードされます。このファイルに変更が加えられていない場合、Luning Lwanは./wwwrootディレクトリにある静的ファイルを提供します。 Lwanは、すべてのインターフェイスでポート8080で聴きます。
LwanはCPUの数を検出し、オープンファイル記述子の最大数を増やし、通常、実行中の環境の合理的な設定を自動検出するために最善を尽くします。これらの設定の多くは、構成ファイルで微調整できますが、通常、それらを台無しにしないことをお勧めします。
ヒント
オプションで、 lwanバイナリは、構成ファイルなしでワンショット静的ファイルを提供するために使用できます。それについて--helpでそれを実行してください。
Lwanは、馴染みのあるkey = value configurationファイルの構文を使用します。コメントは#文字(Eg Shell Scripts、Python、Perlと同様)でサポートされています。ネストされたセクションは、カーリーブラケットで作成できます。セクションは空にすることができます。この場合、カーリーブラケットはオプションです。
some_key_name構成ファイルのsome key nameと同等です(実装の詳細として、コード読み取り構成オプションには、アンダースコアのバージョンのみが与えられます)。
ヒント
値には環境変数を含めることができます。 Syntax ${VARIABLE_NAME}を使用します。デフォルト値は、コロン( ${VARIABLE_NAME:foo} 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
"""
}
いくつかの例は、 lwan.confおよびtechempower.confにあります。
定数は、構成ファイルのどこにでもconstantsセクションで指定することにより、構成ファイル全体で定義および再利用できます。定数は、そのセクションで特定の定数を定義した後にのみ利用可能になります。定数は再定義できます。定数が定義されていない場合、その値は環境変数から取得されます。 1つのconstantsセクションまたは環境で定義されていない場合、Lwanは適切なエラーメッセージで中止します。
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
上記で指定されたデフォルト値の同じ構文はここで有効です(たとえば、$ { user_name ${USER:nobody}に$ {user_name}を${user_name}を誰にも設定しない場合、 ${USER}をnobody設定しません。)
| タイプ | 説明 |
|---|---|
str | 通常、アプリケーション固有のあらゆる種類の自由形式のテキスト |
int | 整数番号。範囲はアプリケーション固有です |
time | 時間間隔。単位については、以下の表を参照してください |
bool | ブール値。有効な値については、以下の表を参照してください |
時間フィールドは、乗数を使用して指定できます。複数を指定することができ、一緒に追加されます。たとえば、「1M 1W」は「1か月と1週間」(37日間)を指定します。次の表には、既知のすべての乗数を示します。
| 乗数 | 説明 |
|---|---|
s | 秒 |
m | 分 |
h | 時間 |
d | 日 |
w | 7日間の週 |
M | 30日間 |
y | 365日年 |
注記
この表にない乗数がある数字は無視されます。構成ファイルの読み取り中に警告が発行されます。数とその乗数の間にスペースが存在してはなりません。
| 真の値 | 偽の値 |
|---|---|
| 0とは異なる整数数 | 0 |
on | off |
true | false |
yes | no |
一般的に、Lwanにあなたの環境に最適な設定を決定させることをお勧めします。ただし、すべての環境が同じであるわけではなく、すべての用途が自動的に決定できるわけではないため、一部の構成オプションが提供されます。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
keep_alive_timeout | time | 15 | 接続を生かし続けるためのタイムアウト |
quiet | bool | false | デバッグメッセージを印刷しないように設定します。リリースビルドでのみ効果的です。 |
expires | time | 1M 1w | 「期限切れ」ヘッダーの価値。デフォルトは1か月と1週間です |
threads | int | 0 | I/Oスレッドの数。デフォルト(0)はオンラインCPUの数です |
proxy_protocol | bool | false | プロキシプロトコルを有効にします。バージョン1と2がサポートされています。プロキシの背後にLwanを使用する場合にのみこの設定を有効にし、プロキシはこのプロトコルをサポートします。それ以外の場合、これにより、誰でも起源のIPアドレスをスプーフィングできます |
max_post_data_size | int | 40960 | バイト単位で、投稿リクエストのデータサイズの最大数を設定します |
max_put_data_size | int | 40960 | パットリクエストのデータサイズの最大数をバイト単位で設定します |
max_file_descriptors | int | 524288 | ファイル記述子の最大数。少なくとも10倍のthreadsである必要があります |
request_buffer_size | int | 4096 | バッファのサイズの長さを要求します。 4096のデフォルトよりも大きい場合は、動的に割り当てられます。 |
allow_temp_files | str | "" | 一時ファイルを使用します。投稿リクエストのためにpost 、PUTリクエストにput 、またはall ( post putに設定するのと同等)の両方について。 |
error_template | str | デフォルトのエラーテンプレート | エラーコードのテンプレート。以下の変数を参照してください。 |
error_templateの変数| 変数 | タイプ | 説明 |
|---|---|---|
short_message | str | 短いエラーメッセージ(例: Not found ) |
long_message | str | 長いエラーメッセージ(たとえば、 The requested resource could not be found on this server ) |
Lwanは、システム内のユーザーにその特権を削除し、Chrootでファイルシステムビューを制限できます。防弾ではありませんが、これはLwanにバグがある場合にセキュリティの最初の層を提供します。
この機能を使用するには、 straitjacket (またはstraightjacket )セクションを宣言し、いくつかのオプションを設定します。これには、ルワンをrootとして実行する必要があります。
このセクションは、ファイル内のどこにでも書くことができます(トップレベルの宣言である限り)、たとえばserve_filesモジュールのインスタンス化により、ディレクトリが開いている場合、Lwanは開始を拒否します。 (このチェックは、malconfigurationの保護策としてLinuxでのみ実行されます。)
ヒント
構成ファイルとプライベートデータ(TLSキーなど)が初期化が行われた後にサーバーの手の届かないように、 siteセクションの直前にStraitJacketを宣言します。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
user | str | NULL | このユーザー名に特権をドロップします |
chroot | str | NULL | chroot()へのパス |
drop_capabilities | bool | true | すべての機能をCapset(2)(Linuxの下)または誓約(2)(OpenBSDの下)でドロップします。 |
応答ごとにカスタムヘッダーを指定する必要がある場合は、グローバル範囲でheadersセクションを宣言できます。このセクションが表示される順序は重要ではありません。
たとえば、この宣言:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Serverヘッダー( Server: lwan送信されない)をオーバーライドし、環境変数$WITH_THIS_ENVIRONMENT_VARIABLEから取得した値をSome-Custom-Headerに設定します。
一部のヘッダーは、リクエストのサービス中に実際の値を送信するときに問題を引き起こすため、オーバーライドすることはできません。これらには以下が含まれますが、これらに限定されません。
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow-ヘッダー注記
ヘッダー名もケースに依存しない(およびケースプレゼンティング)。オーバーライドSeRVeR Serverヘッダーをオーバーライドしますが、構成ファイルに記述された方法で送信します。
LWANプロセスごとにサポートされているリスナーは2人のみです。HTTPリスナー( listenerセクション)とHTTPSリスナー( tls_listenerセクション)です。各タイプのリスナーは1人だけです。
警告
TLSサポートは実験的です。最初のテスト中は安定していますが、走行距離は異なる場合があります。この時点ではTLSV1.2のみがサポートされていますが、TLSV1.3が計画されています。
注記
TLSサポートが必要ですか? tls.koモジュールが組み込まれているかロードされたLinux。他のオペレーティングシステムのサポートは将来追加される場合があります。 FreeBSDは可能であると思われますが、他のオペレーティングシステムは同様の機能を提供していないようです。サポートされていないオペレーティングシステムの場合、ヒッチなどのTLSターミネータープロキシを使用することは良い選択肢です。
listenerとtls_listenerセクションの両方について、唯一のパラメーターはインターフェイスアドレスとリッスンのポートです。リスナーの構文は${ADDRESS}:${PORT}です。ここで、 ${ADDRESS}は* (すべてのインターフェイスにバインディングされます)、IPv6アドレス(四角いブラケットに囲まれている場合)、IPv4アドレス、またはホスト名です。たとえば、 listener localhost:9876 、ポート9876 loインターフェイスでのみ聞きます。
listenerセクションにはキーがありませんが、 tls_listenerセクションには2つが必要です。 certとkey (それぞれ、TLS証明書とプライベートキーファイルが配置されているディスク上の場所を指す)を必要とし、 Strict-Transport-SecurityヘッダーをHTTPS応答に送信するかどうかを制御するオプションのブールhstsキーを取得します。
ヒント
これらのキーをテスト目的で生成するために、OpenSSLコマンドラインツールを次のように使用できます。OpenSSLREQ openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
注記
証明書とキーへのパスがその時点から手の届かないように、 tls_listenerセクションの直後に、 chrootオプションを備えたStraitJacketが宣言されることをお勧めします。
SystemDソケットのアクティブ化が使用される場合、 systemdパラメーターとして指定できます。 (SystemDの複数のリスナーが指定されている場合、 systemd:FileDescriptorName FileDescriptorName指定できますsystemd.socketで設定された条約に従うことができます。)
例:
listener *:8080 # Listen on all interfaces, port 8080, HTTP
tls_listener *:8081 { # Listen on all interfaces, port 8081, HTTPS
cert = /path/to/cert.pem
key = /path/to/key.pem
}
# Use named systemd socket activation for HTTP listener
listener systemd:my-service-http.socket
# Use named systemd socket activation for HTTPS listener
tls_listener systemd:my-service-https.socket {
...
}
siteセクションは、特定のURLプレフィックスへのリクエストに応答するモジュールとハンドラーのインスタンスをグループ化します。
URLをルーティングするために、LwanはリクエストURIの最大の共通プレフィックスを、リスナーセクションで指定されたプレフィックスのセットと一致させます。特定のプレフィックスへのリクエストの処理方法は、リスナーセクションでどのハンドラーまたはモジュールが宣言されているかによって異なります。ハンドラーとモジュールは内部的に似ています。ハンドラーは単なる機能であり、状態を保持しておらず、モジュールは状態を保持します(インスタンスと呼ばれます)。モジュールの複数のインスタンスは、リスナーセクションに表示できます。
ハンドラーまたはモジュールに接頭辞を接続する特別な構文はありません。すべての構成パーサールールはここに適用されます。 ${NAME} ${PREFIX}を使用して、$ ${NAME} ${NAME} ${PREFIX}プレフィックスパスを& ${NAME}ます。ここでは、空のセクションを使用できます。
各モジュールには特定のオプションがあり、次のセクションにリストされています。構成オプションに加えて、モジュールインスタンスの宣言に特別なauthorizationセクションが存在する可能性があります。ハンドラーは構成オプションを取得しませんが、 authorizationセクションを含めることができます。
ヒント
--helpコマンドライン引数でLwanを実行すると、組み込みモジュールとハンドラーのリストが表示されます。
以下は、Lwanに出荷されたモジュールの基本的なドキュメントです。
serve_filesモジュールは静的ファイルを提供し、ディレクトリインデックスを自動的に作成するか、事前に圧縮されたファイルを提供します。一部のヒューリスティックに従って、可能な限り速い方法でファイルを提供するために最善を尽くします。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
path | str | NULL | 提供されるファイルを含むディレクトリへのパス |
index_path | str | index.html | ディレクトリのインデックスとして機能するファイル名 |
serve_precompressed_path | bool | true | $ file.gzが存在する場合、$ fileよりも小さくて新しい場合、クライアントはgzipエンコードを受け入れ、それを転送します |
auto_index | bool | true | index_pathファイルが表示されない場合は、ディレクトリリストを自動的に生成します。それ以外の場合は、404を生成します |
auto_index_readme | bool | true | 自動化されたディレクトリインデックスの一部として、readmeファイルの内容を含める |
directory_list_template | str | NULL | ディレクトリリストの口ひげテンプレートへのパス。デフォルトでは、内部テンプレートを使用します |
read_ahead | int | 131702 | オープンファイルをキャッシュするときに先に読み取る最大バイトの量。 0の値はreadaheadを無効にします。 Readaheadは、ファイルエクステントがファイルシステムから読み取られている間に、I/Oスレッドをブロックしないように、優先度の低いスレッドによって実行されます。 |
cache_for | time | 5s | キャッシュにファイルメタデータ(サイズ、圧縮コンテンツ、開いたファイル記述子など)を保持する時間 |
注記
16KIBより小さいファイルは、 cache_for設定で指定された期間、RAMで圧縮されます。 Lwanは常にDeflateで圧縮しようとし、オプションでBrotliとZSTDで圧縮します(Lwanが適切なサポートで構築されている場合)。
圧縮が努力する価値がない場合(たとえば、 Content-Encodingヘッダーを追加すると、非圧縮ファイル、通常は非常に小さなファイルの場合がある場合よりも大きな応答が発生します)、Lwanはファイルの圧縮に時間を費やすことはありません。
16kibを超えるファイルの場合、Lwanはそれらを圧縮しようとしません。将来のバージョンでは、これを実行し、ファイルが圧縮されている間にチャンクエンコードを使用して応答を送信する場合があります(もちろん、一定の制限まで)が、今のところ、プレプレッシングファイルのみ(上記の表のserve_precompressed_path設定を参照)を考慮してください。
すべての場合、Lwanはファイルシステムで見つかった場合、GZIPTバージョンを使用してみて、クライアントがこのエンコードを要求します。
directory_list_templateの変数| 変数 | タイプ | 説明 |
|---|---|---|
rel_path | str | ルートディレクトリのリアルパスに対するパス |
readme | str | 見つかった最初のreadmeファイルの内容( readme 、 readme.txt 、 read.me 、 README.TXT 、 README ) |
file_list | イテレーター | ファイルリストで繰り返します |
file_list.zebra_class | str | 奇妙なアイテム、 evenアイテムの場合もodd |
file_list.icon | str | ファイルタイプのアイコンへのパス |
file_list.name | str | ファイル名(脱出) |
file_list.type | str | ファイルタイプ(ディレクトリまたは通常のファイル) |
file_list.size | int | ファイルサイズ |
file_list.unit | str | file_sizeのユニット |
luaモジュールでは、LUAプログラミング言語で書かれたスクリプトによってリクエストを提供することができます。このモジュールによって提供される機能は非常にスパルタンですが、セーラーなどのフレームワークを実行できます。
スクリプトはファイルから、または構成ファイルに埋め込まれ、それらをロードする結果、標準のLUAモジュール、および(オプションでは、luajitを使用する場合)コードを最適化する結果はしばらくキャッシュされます。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
default_type | str | text/plain | 応答のデフォルトのMIMEタイプ |
script_file | str | NULL | LUAスクリプトへのパス |
cache_period | time | 15s | LUA状態をメモリにロードする時間 |
script | str | NULL | インラインLUAスクリプト |
注記
LUAスクリプトは、異なるスレッドでサービスされるだけでなく、 cache_period構成オプションで指定された時間の間のみ利用可能になるため、グローバル変数を使用することはできません。これは、LWANの各I/OスレッドがLUA VM(つまり、すべてのI/Oスレッドの1つのlua_State構造体)のインスタンスを作成し、各Lwan CoroutineがリクエストごとにLUAスレッド( lua_newthread()を使用)を生成するためです。
各エンドポイントにLUAモジュールのインスタンスが1つある必要はありません。構成ファイルなどに埋め込まれた単一のスクリプトは、多くの異なるエンドポイントにサービスを提供できます。スクリプトはhead次の署名で関数を実装するgetになっています:ハンドル_ $ {method ${METHOD} handle_${METHOD}_${ENDPOINT}(req) ${ENDPOINT} post特定のバージョンが存在しない場合、一般的なhandle(req)関数が呼び出されます。
ヒント
キャッチオールにrootエンドポイントを使用します。たとえば、そのリクエストのために他のハンドラーが見つからなかった場合、ハンドラー関数handle_get_root()が呼び出されます。 CatchAllが指定されていない場合、サーバーは404 Not Foundエラーを返します。
reqパラメーターは、以下に示すように、リクエストから情報を取得する方法、または応答を設定するメソッドを含むメタテーブルを指します。
req:query_param(param) queryパラメーター(query文字列から)をキーparam 、またはnilが見つかった場合はnilを返しますreq:post_param(param) postパラメーター( ${POST}ハンドラーのみ)をキーparamで返しますnilreq:set_response(str) string strへの応答を設定しますreq:say(str)応答チャンクを送信します(httpでチャンクエンコードを使用)req:send_event(event, str)イベントを送信します(サーバーセントイベントを使用)req:cookie(param) paramという名前のクッキーを返します、またはnilが見つかりませんreq:set_headers(tbl)テーブルtblから応答ヘッダーを設定します。ヘッダーは、テーブル値( {'foo'={'bar', 'baz'}} )で、文字列ではなくテーブルを使用して複数回指定できます。 say()またはsend_event()で応答を送信する前に呼び出す必要がありますreq:header(name)指定されたnilでリクエストからヘッダーを取得します。req:sleep(ms)指定された量のミリ秒のために現在のハンドラーを一時停止しますreq:ws_upgrade()接続をWebSocketにアップグレードできる場合に1返します。 0それ以外の場合req:ws_write_text(str)テキストフレームとしてWebSocketアップグレードされた接続を介してstrを送信しますreq:ws_write_binary(str) websocket-upgraged接続をバイナリフレームとしてstrしますreq:ws_write(str) ASCII文字のみを含むコンテンツに応じて、テキストまたはバイナリフレームとしてWebSocketアップグレードされた接続を介してstrを送信しますreq:ws_read()最後のwebsocketフレームの内容を持つ文字列、またはステータスを示す数字(Linuxでenotconn/107が切断されている場合は、Linuxでeagain/11が利用できない場合は、enomsg/42 linuxでは)。ここでの返品値は、よりルアのようなもののために将来変化する可能性があります。req:remote_address()リモートIPアドレスで文字列を返します。req:path()要求パスで文字列を返します。req:query_string() query string(query stringが表示されない場合は空の文字列)で文字列を返します。req:body() request body(post/put requests)を返します。req:request_id()要求IDを含む文字列を返します。req:request_date()日付はDate応答ヘッダーに記述されるため、日付を返します。req:is_https()このリクエストがhttpsを介して保護falseている場合にtrueを返します。req:host() 、存在する場合はHostヘッダーの値をnilます。req:http_version()リクエストバージョンに応じてHTTP/1.0またはHTTP/1.1を返します。req:http_method() httpメソッド( "GET" )を使用して、大文字で文字列を返します。req:http_headers()すべてのヘッダーとその値でテーブルを返します。ハンドラー関数は、 nil (この場合、 200 OK応答が生成されます)、またはHTTPステータスコードに一致する数値を返す場合があります。無効なHTTPステータスコードまたは数値またはnil以外のものを返しようとすると、 500 Internal Server Error応答がスローされます。
reqパラメーターのメタムソッドに加えて、 Lwan.logからメソッドを呼び出すことにより、異なるロギングレベルでメッセージをログに記録することもできます。
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (lwanも中止します!慎重に使用します)Lwan.log:debug(str) (デバッグビルドでのみ使用できます。 注記
LwanがSyslogサポートで構築されている場合、これらのメッセージはシステムログにも送信されます。それ以外の場合は、標準誤差に印刷されます。
rewriteモジュールは、URLのパターンと一致し、別のURLにリダイレクトするオプションを提供するか、Lwanがリクエストをそのように元に作成したかのようにリクエストを書き直すオプションを提供します。
注記
LUA 5.3.1からフォークされた通常のExpresionエンジンは、ほとんどの汎用エンジンほど機能が満たされていない可能性がありますが、一部の種類のサービス攻撃を不可能にするために決定論的な有限オートマトンであるため、特に選択されています。
新しいURLは、単純なテキスト代替構文を使用して指定するか、LUAスクリプトを使用できます。
ヒント
LUAスクリプトには、LUAモジュールが提供するreq Metatableで利用可能な同じメタメソッドが含まれているため、非常に強力です。
書き換えモジュールの各インスタンスには、そのようなパターンが一致するときにpatternと実行するアクションが必要です。パターンは、構成ファイルに表示される順序で評価され、構成ファイルのネストされたセクションを使用して指定されます。たとえば、2つのパターンが指定されている次の例を考えてみましょう。
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
}
}
この例は2つのパターンを定義します。1つはユーザーから隠されたより良いURLを提供し、もう1つは人気のある画像ホスティングサービス(つまり、 /some/base/endpoint/imgur/mp4/4kOZNYXを要求する画像への直接リンクを取得する別の方法を提供します。
rewrite_asまたはredirect_toの値もLUAスクリプトにすることができます。その場合、オプションexpand_with_lua trueに設定する必要があり、上記の例として単純なテキスト置換構文を使用する代わりに、 handle_rewrite(req, captures)という名前の関数を代わりに定義する必要があります。 reqパラメーターは、LUAモジュールセクションに文書化されています。 capturesパラメーターは、すべてのキャプチャを含むテーブルであり、順番に(つまり、 captures[2]単純なテキスト置換構文の%2に相当します)。この関数は、新しいURLを返してリダイレクトします。
このモジュールには、単独でオプションがありません。オプションは、すべてのパターンで指定されています。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
rewrite_as | str | NULL | このパターンに従ってURLを書き直します |
redirect_to | str | NULL | このパターンに従って新しいURLにリダイレクトします |
expand_with_lua | bool | false | LUAスクリプトを使用してリクエストにリダイレクトまたは書き換えます |
redirect_toとrewrite_asオプションは相互に排他的であり、そのうちの1つを少なくとも指定する必要があります。
書き換えをトリガーする条件を指定することも可能です。 1つを指定するには、 conditionブロックを開き、条件タイプを指定し、その条件が評価されるパラメーターを指定します。タイプごとに1つの条件がある限り、書き換えルールごとに複数の条件を設定できます。
| 状態 | サッツを使用できます。構文 | セクションが必要です | パラメーター | 説明 |
|---|---|---|---|---|
cookie | はい | はい | 単一のkey = value | リクエストにCookie keyに値valueがあるかどうかを確認します |
query | はい | はい | 単一のkey = value | リクエストにクエリ変数keyのvalueがあるかどうかをチェックします |
post | はい | はい | 単一のkey = value | リクエストが投稿されているかどうかをチェックしてくださいデータkeyには値のvalueがあります |
header | はい | はい | 単一のkey = value | リクエストヘッダーkeyに値valueがあるかどうかを確認します |
environment | はい | はい | 単一のkey = value | 環境変数keyに値valueがあるかどうかを確認します |
stat | はい | はい | path 、 is_dir 、 is_file | ファイルシステムにpathが存在するかどうかを確認し、オプションでis_dirまたはis_fileのかどうかをチェックします |
encoding | いいえ | はい | deflate 、 gzip 、 brotli 、 zstd 、 none | クライアントが決定されたエンコードで応答を受け入れるかどうかを確認します(例: deflate = yes for deflateエンコード) |
proxied | いいえ | いいえ | ブール | プロキシプロトコルを通じてリクエストがプロキシされているかどうかを確認します |
http_1.0 | いいえ | いいえ | ブール | HTTP/1.0クライアントでリクエストが行われているかどうかを確認します |
is_https | いいえ | いいえ | ブール | httpsを介して要求が行われるかどうかを確認します |
has_query_string | いいえ | いいえ | ブール | 要求にクエリ文字列があるかどうかを確認します(たとえ空であっても) |
method | いいえ | いいえ | メソッド名 | HTTPメソッドが指定された方法であるかどうかを確認します |
lua | いいえ | いいえ | 弦 | 文字列内でlua関数matches(req)を実行し、 trueまたはfalseを返すかどうかを確認します |
backref | いいえ | はい | 単一のbackref index = value | Backref番号が提供された値と一致するかどうかを確認します |
サッツを使用できます。構文とは、アクションrewrite asまたはリダイレクトredirect to使用される同じ置換構文を使用して、一致したパターンを参照する機能を指します。たとえば、 condition cookie { some-cookie-name = foo-%1-bar }この条件が関連しているパターンの最初の一致に%1を置き換えます。
注記
セクションを必要としない条件は、キーとして記述する必要があります。たとえば、 condition has_query_string = yes 。
たとえば、Value darkのstyle Cookieがある場合、 site-dark-mode.cssを送信したい場合、 site-light-mode.cssを送信する場合は、以下を書くことができます。
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
別の例:ファイルシステムに存在し、ユーザーがそれらを要求した場合、事前に圧縮されたファイルを送信したい場合:
pattern (%g+) {
condition encoding { brotli = yes }
condition stat { path = %1.brotli }
rewrite as = %1.brotli
}
pattern (%g+) {
condition encoding { gzip = yes }
condition stat { path = %1.gzip }
rewrite as = %1.gzip
}
pattern (%g+) {
condition encoding { zstd = yes }
condition stat { path = %1.zstd }
rewrite as = %1.zstd
}
pattern (%g+) {
condition encoding { deflate = yes }
condition stat { path = %1.deflate }
rewrite as = %1.deflate
}
注記
一般に、これは必要ありません。ファイルサービングモジュールはこれを自動的に実行し、要求されたエンコードで使用可能な最小のファイルを選択しますが、これは構成だけで同様の機能を持つことができることを示しています。
redirectモジュールは、TINで示されているように、構成で指定されたオプションに従って、ティンに記載されているように301 Moved permanentlyます(デフォルトで、コードを変更できます。以下を参照)応答を生成します。一般に、より多くの機能を梱包するため、代わりにrewriteモジュールを使用する必要があります。ただし、このモジュールは、Lwanモジュールの書き方の例としても機能します(100行未満のコード)。
toオプションが指定されていない場合、常に500 Internal Server Error応答を生成します。無効なHTTPコード、またはLwanが知らないコードを指定すると( enum lwan_http_statusを参照)、 301 Moved Permanently応答が生成されます。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
to | str | NULL | リダイレクトする場所 |
code | int | 301 | リダイレクトを実行するHTTPコード |
responseモジュールは、HTTPコードの人為的な応答を生成します。 Lwanモジュールの書き方の例としても機能することに加えて、他のモジュールからボイドを切り開くために使用できます(たとえば、 / /.gitのファイルの405の許可serve_files 405 Not Allowed応答を生成します。
付属のcode Lwanが知っている応答コードの外側にある場合、代わりに404 Not Foundエラーが送信されます。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
code | int | 999 | HTTP応答コード |
fastcgiモジュールプロキシは、Lwanに接続するHTTPクライアントとLwanがアクセスできるFastCGIサーバーの間のリクエストをプロキシします。これは、たとえば、PHPなどのスクリプト言語のページを提供するのに役立ちます。
注記
これはこのモジュールの予備バージョンであるため、最適化されておらず、いくつかの機能が欠落しており、環境に提供されるいくつかの値がハードコードされています。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
address | str | 接続するアドレス。ファイルパス(UNIXドメインソケット用)、IPv4アドレス( aaa.bbb.ccc.ddd:port )、またはIPv6アドレス( [...]:port )にすることができます。 | |
script_path | str | CGIスクリプトが配置されている場所。 | |
default_index | str | index.php | リクエストURIで不特定の場合に実行するデフォルトスクリプト。 |
承認セクションは、任意のモジュールインスタンスまたはハンドラーで宣言でき、標準のHTTP認証メカニズムを介してその要求の履行を承認する方法を提供します。特定のモジュールインスタンスまたはハンドラーにアクセスするための許可を要求するために、 basicパラメーターを使用したauthorizationセクションを宣言し、そのオプションのいずれかを設定します。
| オプション | タイプ | デフォルト | 説明 |
|---|---|---|---|
realm | str | Lwan | 許可のための領域。これは通常、ブラウザのユーザー/パスワードUIに表示されます |
password_file | str | NULL | ユーザー名とパスワードを含むファイルのパス(クリアテキスト)。ファイル形式は、lwanが使用する構成ファイル形式と同じです |
警告
パスワードがサーバーがアクセスできるはずのファイルにクリアテキストに保存されるだけでなく、数秒間メモリに保持されます。可能であれば、この機能を使用しないでください。
Lwanへの貢献を計画している場合は、このセクションをお読みください(そしてそれに従ってください)。ここには予想外のことは何もありません。これは主に他の多くのFOSSプロジェクトのルールと期待に従っていますが、誰もが互いに少し異なることを期待しています。
Lwanは、プロジェクト全体で一貫したコーディングスタイルに従うことを試みています。プロジェクトにパッチを提供することを検討している場合は、周囲のコードのスタイルを一致させようとして、このスタイルを尊重してください。一般的に:
global_variables_are_named_like_this staticlocal_var 、 i 、 conntypedef 、Lwanではめったに使用されません#pragma once使用する必要がありますlwan-private.hに追加する必要があります。lwan_が付いている必要がありますlwan_でプレフィックスする必要がありますlwan_プレフィックスなしで名前を付けることができます/* Old C-style comments are preferred */clang-format使用して、ソースコードを許容できる方法でフォーマットできます。 a .clang-formatファイルが提供されますIf modifying well-tested areas of the code (eg the event loop, HTTP parser, etc.), please add a new integration test and make sure that, before you send a pull request, all tests (including the new ones you've sent) are working. Tests can be added by modifying src/scripts/testsuite.py , and executed by either invoking that script directly from the source root, or executing the testsuite build target.
Some tests will only work on Linux, and won't be executed on other platforms.
Lwan is automatically fuzz-tested by OSS-Fuzz. To fuzz-test locally, though, one can follow the instructions to test locally.
Currently, there are fuzzing drivers for the request parsing code, the configuration file parser, the template parser, and the Lua string pattern matching library used in the rewrite module.
Adding new fuzzers is trivial:
src/bin/fuzz .${FUZZER_NAME}_fuzzer.cc . Look at the OSS-Fuzz documentation and other fuzzers on information about how to write these.src/fuzz/corpus . Files have to be named corpus-${FUZZER_NAME}-${UNIQUE_ID} . The shared object version of liblwan on ELF targets (eg Linux) will use a symbol filter script to hide symbols that are considered private to the library. Please edit src/lib/liblwan.sym to add new symbols that should be exported to liblwan.so .
Lwan tries to maintain a source history that's as flat as possible, devoid of merge commits. This means that pull requests should be rebased on top of the current master before they can be merged; sometimes this can be done automatically by the GitHub interface, sometimes they need some manual work to fix conflicts. It is appreciated if the contributor fixes these conflicts when asked.
It is advisable to push your changes to your fork on a branch-per-pull request, rather than pushing to the master branch; the reason is explained below.
Please ensure that Git is configured properly with your name (it doesn't really matter if it is your legal name or a nickname, but it should be enough to credit you) and a valid email address. There's no need to add Signed-off-by lines, even though it's fine to send commits with them.
If a change is requested in a pull request, you have two choices:
It is not enforced, but it is recommended to create smaller commits. How commits are split in Lwan is pretty much arbitrary, so please take a look at the commit history to get an idea on how the division should be made. Git offers a plethora of commands to achieve this result: the already mentioned interactive rebase, the -p option to git add , and git commit --amend are good examples.
Commit messages should have one line of summary (~72 chars), followed by an empty line, followed by paragraphs of 80-char lines explaining the change. The paragraphs explaining the changes are usually not necessary if the summary is good enough. Try to write good commit messages.
Lwan is licensed under the GNU General Public License, version 2, or (at your option), any later version.したがって:
While Lwan was written originally for Linux, it has been ported to BSD systems as well. The build system will detect the supported features and build support library functions as appropriate.
For instance, epoll has been implemented on top of kqueue, and Linux-only syscalls and GNU extensions have been implemented for the supported systems. This blog post explains the details and how #include_next is used.
It can achieve good performance, yielding about 320000 requests/second on a Core i7 laptop for requests without disk access, and without pipelining.
When disk I/O is required, for files up to 16KiB, it yields about 290000 requests/second ; for larger files, this drops to 185000 requests/second , which isn't too shabby either.
These results, of course, with keep-alive connections, and with weighttp running on the same machine (and thus using resources that could be used for the webserver itself).
Without keep-alive, these numbers drop around 6-fold.
There is an IRC channel ( #lwan ) on Libera. A standard IRC client can be used.
Here's a non-definitive list of third-party stuff that uses Lwan and have been seen in the wild. If you see mentions of Lwan in the media or academia, however small it might be, please contact the author! It'll make her day!
Some other distribution channels were made available as well:
Dockerfile is maintained by @jaxgeller, and is available from the Docker registry.Lwan has been also used as a benchmark:
Mentions in academic journals:
Mentions in magazines:
Mentions in books:
Some talks mentioning Lwan:
Not really third-party, but alas:
Lwan container images are available at ghcr.io/lpereira/lwan. Container runtimes like Docker or Podman may be used to build and run Lwan in a container.
Container images are tagged with release version numbers, so a specific version of Lwan can be pulled.
# latest version
docker pull ghcr.io/lpereira/lwan:latest
# pull a specific version
docker pull ghcr.io/lpereira/lwan:v0.3
Clone the repository and use Containerfile (Dockerfile) to build Lwan with all optional dependencies enabled.
podman build -t lwan .
The image expects to find static content at /wwwroot , so a volume containing your content can be mounted.
docker run --rm -p 8080:8080 -v ./www:/wwwroot lwan
To bring your own lwan.conf , simply mount it at /lwan.conf .
podman run --rm -p 8080:8080 -v ./lwan.conf:/lwan.conf lwan
Podman supports socket activation of containers. This example shows how to run lwan with socket activation and Podman on a Linux host.
Requirements: Podman version 4.5.0 or higher.
sudo useradd test
sudo machinectl shell test@
podman build -t lwan ~/lwan
mkdir -p ~/.config/containers/systemd
mkdir -p ~/.config/systemd/user
listener systemd:my.socket
site {
serve_files / {
path = /web
}
}
[Socket]
ListenStream=8080
[Unit]
After=my.socket
Requires=my.socket
[Container]
Network=none
Image=localhost/lwan
Volume=/home/test/lwan.conf:/lwan.conf:Z
Volume=/home/test/web:/web:Z
:Z is needed on SELinux systems. As lwan only needs to communicate over the socket-activated socket, it's possible to use Network=none . See the article How to limit container privilege with socket activation. mkdir ~/web
echo hello > ~/web/file.txt
systemctl --user daemon-reload
systemctl --user start my.socket
$ curl localhost:8080/file.txt
hello
These are some of the quotes found in the wild about Lwan. They're presented in no particular order. Contributions are appreciated:
"Lwan is like a classic, according to the definition given by Italian -- writer Italo Calvino: you can read it again and again" Antonio Piccolboni
"I read lwan's source code. Especially, the part of using coroutine was very impressive and it was more interesting than a good novel. Thank you for that." -- @patagonia
"For the server side, we're using Lwan, which can handle 100k+ reqs/s. It's supposed to be super robust and it's working well for us." -- @fawadkhaliq
"Insane C thing" -- Michael Sproul
"The best performer is LWAN, a newcomer" -- InfoQ
"I've never had a chance to thank you for Lwan. It inspired me a lot to develop Zewo" -- @paulofariarl
"Let me say that lwan is a thing of beauty. I got sucked into reading the source code for pure entertainment, it's so good. high five " -- @kwilczynski
"mad science" -- jwz
"Nice work with Lwan! I haven't looked that carefully yet but so far I like what I saw. You definitely have the right ideas." -- @thinkingfish
"Lwan is a work of art. Every time I read through it, I am almost always awe-struck." -- @neurodrone
"For Round 10, Lwan has taken the crown" -- TechEmpower
"Jeez this is amazing. Just end to end, rock solid engineering. (...) But that sells this work short." kjeetgill
"I am only a spare time C coder myself and was surprised that I can follow the code. Nice!" cntlzw
"Impressive all and all, even more for being written in (grokkable!) C. Nice work." tpaschalis
"LWAN was a complete failure" dermetfan