nginx、lighttpd、fhttpd、そしてAntiweb3と同じように、Antiweb4は
非同期ないし
イベントベースないし
ノンブロッキングなサーバです。つまり、複数のクライアント接続を単一のスレッドで制御します。Antiwebシステムはunixプロセスの集まりです。接続はプロセス間で
sendmsg()によって転送されます。これが生じると、ソケットから最初に読まれたデータはそのソケット自身を通じて転送されます。ソケットは常に送信側のプロセスにおいて閉じられます。
ひとつのプロセスの中で多重接続を行なうため、Antiwebはsrc/libantiweb.hに定義されている状態機械データ構造を使います。Antiwebは、level-triggeredモードにおいて、kqueue()またはepoll()のいずれかのステートフルなイベントAPIを必要とします。
- 32bit linux/CMUCLシステムにおいて、10000の非アクティヴなkeepalive接続は約3Mのユーザ空間メモリを消費した(2つのlispイメージに加えて)。
- 非アクティヴなkeepalive接続の数は新しい接続において取るに足らない性能影響を持つ。
ファイルの送信には3つのモードがある。すなわち、中、小、大である。
- 中: これらのファイルはファイルのデータをユーザ空間にコピーするのを避けるためにmmap()される(メモリーマップされる)。データはファイルシステムから直接カーネルのソケットバッファにコピーされる。
- 小: これらのファイルはユーザ空間バッファに読まれる。というのは、小さなread()は往々にしてmmap()+munmap()より安価だからである。
- 大: Antiwebは大きなファイルにユーザ空間バッファを使う。これは多数の大きなファイルをクライアントが並列に要求した場合にディスクスラッシングを起こすのを避けるためであり(lighttpdからのアイデアである)、また32bitシステムでアドレス空間を使い切ってしまうのを避けるためでもある。
そいつをスーパーサイズしろ: Antiwebは64bitのoff_t型とlispの桁数無制限の整数をすべてのシステムで使っているので、Antiwebはいかなる容量のファイルでも扱うことができる。また、3つすべての送信モードにおいて
ダウンロード再開をサポートしている。
Antiwebのデータ構造はパイプラインのために設計されている。Antiwebはベクター型I/O(scatter-gather I/Oとしても知られている)をほとんど全面的に使っている。Antiwebの内部メッセージパッシングプロトコルもパイプラインを使っている。たとえば、ひとつのHTTP接続があって、それが小さなファイルについて2つのリクエストがあり、中くらいのファイルのリクエストが1つ続いてパイプラインされているとき、単一のwritev()システムコールで以下のように対応する。
- 最初の2つのファイルのためのHTTPヘッダとファイルコンテント
- 中くらいのファイルのためのHTTPヘッダ
- カーネルバッファを満たすまで中くらいのファイルをメモリにマップする
続いて、生成されたログメッセージをすべてハブプロセスにwritev()で書き込む。ハブはログメッセージをロガープロセスに別のwritev()で転送する。最後に、ロガープロセスがメッセージを
axslogファイルに追記する。
ワーカープロセスの接続統計が見たいなら、
-statsコマンドを使う。
# antiweb -stats /var/aw/example.conf
...
Keepalive Time: 65 seconds
Total Connections: 41 HTTP requests: 72 Avg reqs/conn: 1.8
File descriptor usage (estimate): 17/32767
Current Connections: 11
Keepalives: 7 Sending files to: 2
Proxy: Sources: 0 Sinks: 0 Idle: 0
Timers: 0 Hub: 1 Unix Connections: 1
Lingering: 0 Zombies: 0
...
ふだんは愛しているけれど、ときどきパイプラインは悪い。Antiwebは特定のレスポンスにおいてパーシステントなHTTP接続を切断する。
- 4XXおよび5XX HTTPエラー - niktoのようなウェブ脆弱性スキャナがそのリクエストの95%以上をこれらのエラーとするとき、blindになるのを防ぐ。
- ディレクトリリスト - パイプラインが再帰的にクロールするのを防ぐ。
接続を完了するとき、Antiwebはソケットと、HTTP/1.1で要求されているようにlingerの書き込みディレクションを切断する。AntiwebはHTTP/0.9およびHTTP/1.0のクライアントを常にgracefullyにデグレードする。Antiwebは第一級のIPv6サポートを持っている。もし、4XXおよび5XXエラーをパイプラインしたいなら、2つの選択肢がある。
- Antiwebのrewriteモジュールを使って問題のあるリクエストを実在するファイルへのリクエストに変更する。
- Antiwebのfast-filesモジュールを使う。これはメモリキャッシュであって静的なコンテントの加速化、HTTPヘッダの事前生成、ネガティヴキャッシュ、そして404エラーの永続化/パイプライン化をサポートしている。
Antiwebは最初からセキュリティを意識して設計されている。Antiwebの設計中に下された設計上の決定を以下に列挙する。
- 仮想ホストはプロキシを使わずに権限を分離している。ハブが接続を扱うべきワーカーを決定すると、ハブはワーカープロセスにソケットし、その接続についてそれ以上のことは一切行なわない。ワーカープロセスはハブと異なるUIDsの下で動作する(ワーカープロセスもそれぞれ異なる)。ワーカーはオプションとしてchrootすることがある。
- ワーカーはログファイルにアクセスしない。すべてのログメッセージはUnixソケットを通じてハブに送られる。続いて、ハブはそのメッセージをロガープロセスに送る。つまり、ワーカープロセスはそれまでに生成されたログメッセージを盗むことがないし、他のワーカープロセスによって生成されたログメッセージを盗むこともない。同様にハブはそれまでに生成されたログメッセージを盗むことがない。
- CGIプロセスはリソース制限によって制約できる。
- unicodeをサポートしないLispであっても、Antiwebは内部データならびにファイル名をUTF-8でエンコードする。これにはすべてのコードポイントを最短の表現となるようにし、不正なサロゲートペアがないように検証することが含まれている。(訳者注:本当だろうか? 大言壮語ではないか?)
- Antiwebプロセスは予期せぬ状態のイベントの後始末や回復をけっして試みない。実行できなかったプロセスは失敗する。失敗しなかったプロセスは終了後に後始末される。
Antiwebには
Anti Webpagesと呼ばれるWebページを構築する実験的な技術が含まれている。これはPerlに触発されたプログラムで、意味のある空白類でページレイアウトを表現したり、HTML/CSS/Javascriptを貼り合わせたりといった機能を持つ。