Lwan adalah server web berkinerja tinggi & dapat diskalakan .
Situs Web Proyek berisi lebih banyak detail.
| Os | Lengkungan | Melepaskan | Debug | Analisis statis | Tes |
|---|---|---|---|---|---|
| Linux | x86_64 | Laporan Riwayat | |||
| Freebsd 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
Anda dapat membangun LWAN sendiri, menggunakan gambar kontainer, atau mengambil paket dari distribusi favorit Anda.
Sebelum menginstal LWAN, pastikan semua dependensi diinstal. Semuanya adalah dependensi umum yang ditemukan dalam distribusi GNU/Linux; Nama paket akan berbeda, tetapi seharusnya tidak sulit untuk mencari menggunakan alat manajemen paket apa pun yang digunakan oleh distribusi Anda.
Sistem Build akan mencari perpustakaan ini dan mengaktifkan/Link jika tersedia.
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (default) dilewati:-DUSE_ALTERNATIVE_MALLOC ke cmake dengan nilai -nilai berikut: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
Memilih versi rilis (tidak ada simbol debugging, pesan, mengaktifkan beberapa optimisasi, dll):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
Jika Anda ingin mengaktifkan optimasi tetapi masih menggunakan debugger, gunakan ini sebagai gantinya:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
Untuk menonaktifkan optimasi dan membangun versi yang lebih ramah debugging:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
Ini akan menghasilkan beberapa binari:
src/bin/lwan/lwan : LWAN utama yang dapat dieksekusi. Dapat dieksekusi dengan --help untuk bimbingan.src/bin/testrunner/testrunner : Berisi kode untuk mengeksekusi rangkaian tes ( src/scripts/testsuite.py ).src/samples/freegeoip/freegeoip : Implementasi Sampel FreeGeOIP. Membutuhkan sqlite.src/samples/techempower/techempower : Kode untuk Benchmark Kerangka Teknik Web. Membutuhkan perpustakaan sqlite dan mariadb.src/samples/clock/clock : Sampel Jam. Menghasilkan file GIF yang selalu menunjukkan waktu setempat.src/bin/tools/mimegen : Membangun tabel tipe ekstensi-mime. Digunakan selama proses pembangunan.src/bin/tools/bin2hex : Menghasilkan file C dari file biner, cocok untuk digunakan dengan #include. Digunakan selama proses pembangunan.src/bin/tools/configdump : Membuang file konfigurasi menggunakan API pembaca konfigurasi. Digunakan untuk pengujian.src/bin/tools/weighttp : Tulis ulang alat pembandingan http weighttp .src/bin/tools/statuslookupgen : Menghasilkan tabel hash yang sempurna untuk kode status HTTP dan deskripsinya. Digunakan selama proses pembangunan. Passing -DCMAKE_BUILD_TYPE=Release akan memungkinkan beberapa optimasi kompiler (seperti LTO) dan menyetel kode untuk arsitektur saat ini.
Penting
Harap gunakan rilis Build saat membandingkan . Standarnya adalah debug build, yang tidak hanya mencatat semua permintaan ke output standar, tetapi melakukannya sambil memegang kunci, sangat menahan server.
Bangunan default (yaitu tidak lulus -DCMAKE_BUILD_TYPE=Release ) akan membangun versi yang cocok untuk tujuan debugging. Versi ini dapat digunakan di bawah Valgrind (jika headernya ada) dan termasuk pesan debugging yang dilucuti dalam versi rilis. Pesan debugging dicetak untuk setiap permintaan.
Pada bangunan ini, pembersih dapat diaktifkan. Untuk memilih yang mana yang akan dibangun dengan LWAN, tentukan salah satu opsi berikut ke baris doa CMake:
-DSANITIZER=ubsan memilih pembersih perilaku yang tidak ditentukan.-DSANITIZER=address Memilih Sanitizer Alamat.-DSANITIZER=thread Memilih Sanitizer Thread. Alokasi memori alternatif juga dapat dipilih. LWAN saat ini mendukung TCMalloc, Mimalloc, dan Jemalloc di luar kotak. Untuk menggunakan salah satu dari mereka, lulus -DALTERNATIVE_MALLOC=name ke garis doa cmake, menggunakan nama yang disediakan di bagian "dependensi opsional".
Opsi -DUSE_SYSLOG=ON dapat diteruskan ke CMake untuk juga masuk ke log sistem selain output standar.
Jika Anda membangun LWAN untuk distribusi, mungkin bijaksana untuk menggunakan opsi -DMTUNE_NATIVE=OFF , jika tidak biner yang dihasilkan mungkin gagal berjalan di beberapa komputer.
Dukungan TLS diaktifkan secara otomatis dengan adanya instalasi MBEDTLS yang sesuai pada sistem Linux dengan header yang cukup baru untuk mendukung KTLS, tetapi dapat dinonaktifkan dengan lulus -DENABLE_TLS=NO untuk CMake.
~/lwan/build$ make testsuite
Ini akan mengkompilasi program testrunner dan menjalankan suite tes regresi di src/scripts/testsuite.py .
~/lwan/build$ make benchmark
Ini akan mengkompilasi testrunner dan menjalankan skrip benchmark src/scripts/benchmark.py .
LWAN juga dapat dibangun dengan tipe build cakupan dengan menentukan -DCMAKE_BUILD_TYPE=Coverage . Ini memungkinkan Target Make generate-coverage , yang akan menjalankan testrunner untuk menyiapkan laporan cakupan uji dengan LCOV.
Setiap komit dalam repositori ini memicu generasi laporan ini, dan hasilnya tersedia untuk umum.
Siapkan server dengan mengedit lwan.conf yang disediakan; Format dijelaskan secara rincian di bawah ini.
Catatan
LWAN akan mencoba menemukan file konfigurasi yang berbasis di nama yang dapat dieksekusi di direktori saat ini; testrunner.conf akan digunakan untuk biner testrunner , lwan.conf untuk biner lwan , dan sebagainya.
File konfigurasi dimuat dari direktori saat ini. Jika tidak ada perubahan yang dilakukan pada file ini, menjalankan LWAN akan melayani file statis yang terletak di direktori ./wwwroot . Lwan akan mendengarkan di port 8080 di semua antarmuka.
LWAN akan mendeteksi jumlah CPU, akan meningkatkan jumlah maksimum deskriptor file terbuka dan umumnya mencoba yang terbaik untuk secara otomatis mengatur pengaturan yang masuk akal untuk lingkungan yang sedang berjalan. Banyak dari pengaturan ini dapat di -tweak dalam file konfigurasi, tetapi biasanya ide yang baik untuk tidak mengacaukannya.
Tip
Secara opsional, biner lwan dapat digunakan untuk file statis satu-shot yang melayani tanpa file konfigurasi. Jalankan dengan --help untuk bantuan itu.
LWAN menggunakan key = value Sintaks. Komentar didukung dengan karakter # (mirip dengan skrip shell misalnya, Python, dan Perl). Bagian bersarang dapat dibuat dengan kurung keriting. Bagian bisa kosong; Dalam hal ini, kurung keriting adalah opsional.
some_key_name setara dengan some key name dalam file konfigurasi (sebagai detail implementasi, opsi konfigurasi pembacaan kode hanya akan diberikan versi dengan garis bawah).
Tip
Nilai dapat berisi variabel lingkungan. Gunakan sintaks ${VARIABLE_NAME} . Nilai default dapat ditentukan dengan usus besar (misalnya ${VARIABLE_NAME:foo} , yang mengevaluasi ${VARIABLE_NAME} jika diatur, atau foo sebaliknya).
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
"""
}
Beberapa contoh dapat ditemukan di lwan.conf dan techempower.conf .
Konstanta dapat didefinisikan dan digunakan kembali di seluruh file konfigurasi dengan menentukannya di bagian constants di mana saja di file konfigurasi. Konstanta akan tersedia hanya setelah bagian itu mendefinisikan konstanta tertentu. Konstanta dapat ditentukan ulang. Jika konstanta tidak ditentukan, nilainya akan diperoleh dari variabel lingkungan. Jika tidak didefinisikan di salah satu bagian constants , atau di lingkungan, Lwan akan membatalkan dengan pesan kesalahan yang sesuai.
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
Sintaks yang ${user_name} untuk nilai default yang ditentukan nobody atas adalah valid di sini (misalnya menentukan user_name untuk menjadi $ ${USER} ${USER:nobody}
| Jenis | Keterangan |
|---|---|
str | Segala jenis teks bentuk bebas, biasanya aplikasi spesifik |
int | Nomor integer. Rentang spesifik aplikasi |
time | Interval waktu. Lihat tabel di bawah untuk unit |
bool | Nilai Boolean. Lihat tabel di bawah untuk nilai yang valid |
Bidang waktu dapat ditentukan menggunakan pengganda. Multiple dapat ditentukan, mereka hanya ditambahkan bersama; Misalnya, "1M 1W" menentukan "1 bulan dan 1 minggu" (37 hari). Tabel berikut mencantumkan semua pengganda yang diketahui:
| Pengali | Keterangan |
|---|---|
s | Detik |
m | Menit |
h | Jam |
d | Hari -hari |
w | 7 hari minggu |
M | 30 bulan |
y | 365 hari |
Catatan
Angka dengan pengganda yang tidak dalam tabel ini diabaikan; Peringatan dikeluarkan saat membaca file konfigurasi. Tidak ada spasi yang harus ada antara angka dan pengganda.
| Nilai sejati | Nilai palsu |
|---|---|
| Nomor integer apa pun berbeda dari 0 | 0 |
on | off |
true | false |
yes | no |
Secara umum adalah ide yang baik untuk membiarkan Lwan memutuskan pengaturan terbaik untuk lingkungan Anda. Namun, tidak setiap lingkungan sama, dan tidak semua penggunaan dapat diputuskan secara otomatis, sehingga beberapa opsi konfigurasi disediakan.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
keep_alive_timeout | time | 15 | Batas waktu untuk menjaga koneksi tetap hidup |
quiet | bool | false | Setel ke True untuk tidak mencetak pesan debugging. Hanya efektif dalam pembuatan rilis. |
expires | time | 1M 1w | Nilai header "kedaluwarsa". Default adalah 1 bulan dan 1 minggu |
threads | int | 0 | Jumlah utas I/O. Default (0) adalah jumlah CPU online |
proxy_protocol | bool | false | Mengaktifkan protokol proxy. Versi 1 dan 2 didukung. Hanya mengaktifkan pengaturan ini jika menggunakan LWAN di belakang proxy, dan proxy mendukung protokol ini; Kalau tidak, ini memungkinkan siapa pun untuk memalsukan alamat IP asal |
max_post_data_size | int | 40960 | Menetapkan jumlah maksimum ukuran data untuk permintaan pasca, dalam byte |
max_put_data_size | int | 40960 | Menetapkan jumlah maksimum ukuran data untuk permintaan put, dalam byte |
max_file_descriptors | int | 524288 | Jumlah maksimum deskriptor file. Perlu setidaknya 10x threads |
request_buffer_size | int | 4096 | Panjang ukuran buffer permintaan. Jika lebih besar dari default 4096 , itu akan dialokasikan secara dinamis. |
allow_temp_files | str | "" | Gunakan file sementara; Setel untuk post untuk permintaan posting, put untuk permintaan put, atau all (setara dengan pengaturan ke post put ) untuk keduanya. |
error_template | str | Template kesalahan default | Templat untuk kode kesalahan. Lihat variabel di bawah ini. |
error_template| Variabel | Jenis | Keterangan |
|---|---|---|
short_message | str | Pesan kesalahan pendek (misalnya Not found ) |
long_message | str | Pesan Kesalahan Panjang (misalnya The requested resource could not be found on this server ) |
Lwan dapat memberikan hak istimewa kepada pengguna dalam sistem, dan membatasi tampilan sistem file dengan chroot. Meskipun tidak tahan peluru, ini memberikan lapisan keamanan pertama dalam kasus ini ada bug di Lwan.
Untuk menggunakan fitur ini, nyatakan bagian straitjacket (atau straightjacket ), dan atur beberapa opsi. Ini mengharuskan Lwan dieksekusi sebagai root .
Meskipun bagian ini dapat ditulis di mana saja dalam file (selama itu adalah deklarasi tingkat atas), jika ada direktori yang terbuka, karena misalnya instantiasi modul serve_files , Lwan akan menolak untuk memulai. (Cek ini hanya dilakukan di Linux sebagai perlindungan untuk konfigurasi mal.)
Tip
Deklarasikan Straitjacket tepat sebelum bagian site sedemikian rupa sehingga file konfigurasi dan data pribadi (misalnya kunci TLS) berada di luar jangkauan server setelah inisialisasi telah terjadi.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
user | str | NULL | Jatuhkan hak istimewa ke nama pengguna ini |
chroot | str | NULL | Jalan menuju chroot() |
drop_capabilities | bool | true | Jatuhkan semua kemampuan dengan capset (2) (di bawah linux), atau janji (2) (di bawah OpenBSD). |
Jika ada kebutuhan untuk menentukan header khusus untuk setiap respons, seseorang dapat mendeklarasikan bagian headers dalam ruang lingkup global. Urutan yang bagian ini muncul tidak penting.
Misalnya, deklarasi ini:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Keduanya akan mengganti header Server ( Server: lwan tidak akan dikirim), dan mengatur Some-Custom-Header dengan nilai yang diperoleh dari variabel lingkungan $WITH_THIS_ENVIRONMENT_VARIABLE .
Beberapa header tidak dapat diganti, karena itu akan menyebabkan masalah ketika mengirim nilai -nilai sebenarnya saat melayani permintaan. Ini termasuk tetapi tidak terbatas pada:
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow- Catatan
Nama header juga tidak peka kasus (dan pemeliharaan kasus). SeRVeR utama akan mengganti header Server , tetapi mengirimkannya seperti yang ditulis dalam file konfigurasi.
Hanya dua pendengar yang didukung per proses LWAN: pendengar HTTP (bagian listener ), dan pendengar HTTPS (bagian tls_listener ). Hanya satu pendengar dari masing -masing jenis yang diizinkan.
Peringatan
Dukungan TLS bersifat eksperimental. Meskipun stabil selama pengujian awal, jarak tempuh Anda dapat bervariasi. Hanya TLSV1.2 yang didukung pada titik ini, tetapi TLSV1.3 yang direncanakan.
Catatan
Dukungan TLS Membutuhkan? Linux dengan modul tls.ko bawaan atau dimuat. Dukungan untuk sistem operasi lain dapat ditambahkan di masa depan. FreeBSD tampaknya mungkin, sistem operasi lain tampaknya tidak menawarkan fitur serupa. Untuk sistem operasi yang tidak didukung, menggunakan proxy terminator TLS seperti Hitch adalah pilihan yang baik.
Untuk bagian listener dan tls_listener , satu -satunya parameter adalah alamat antarmuka dan port untuk mendengarkan. Sintaks pendengar adalah ${ADDRESS}:${PORT} , di mana ${ADDRESS} dapat menjadi * (mengikat ke semua antarmuka), alamat IPv6 (jika dikelilingi oleh tanda kurung persegi), alamat IPv4, atau nama host. Misalnya, listener localhost:9876 hanya akan mendengarkan di antarmuka lo , port 9876 .
Sementara bagian listener tidak mengambil kunci, bagian tls_listener membutuhkan dua: cert dan key (masing-masing menunjuk, masing-masing, ke lokasi pada disk di mana sertifikat TLS dan file kunci pribadi berada) dan mengambil kunci hsts boolean opsional, yang mengontrol jika header Strict-Transport-Security akan dikirim pada respons HTTPS.
Tip
Untuk menghasilkan tombol -tombol ini untuk keperluan pengujian, alat baris perintah OpenSSL dapat digunakan seperti berikut: openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
Catatan
Disarankan agar jaket straitt dengan opsi chroot dinyatakan tepat setelah bagian tls_listener , sedemikian rupa sehingga jalur ke sertifikat dan kunci berada di luar jangkauan sejak saat itu.
Jika aktivasi SystemD Socket digunakan, systemd dapat ditentukan sebagai parameter. (Jika beberapa pendengar dari SystemD ditentukan, systemd:FileDescriptorName dapat ditentukan, di mana FileDescriptorName mengikuti konvensi yang ditetapkan dalam dokumentasi systemd.socket .)
Contoh:
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 {
...
}
Bagian site mengelompokkan contoh modul dan penangan yang akan menanggapi permintaan ke awalan URL yang diberikan.
Untuk merutekan URL, LWAN cocok dengan awalan umum terbesar dari permintaan URI dengan satu set awalan yang ditentukan di bagian pendengar. Bagaimana permintaan ke awalan tertentu akan ditangani tergantung pada pawang atau modul mana yang telah dinyatakan di bagian pendengar. Penangan dan modul serupa secara internal; Penangan hanyalah fungsi dan tidak memiliki keadaan, dan modul memegang keadaan (namanya instance). Beberapa contoh modul dapat muncul di bagian pendengar.
Tidak ada sintaks khusus untuk memasang awalan ke pawang atau modul; Semua aturan parser konfigurasi berlaku di sini. Gunakan ${NAME} ${PREFIX} untuk menautkan jalur prefix ${PREFIX} ke salah satu penangan bernama ${NAME} (jika ${NAME} dimulai dengan & , seperti halnya dengan "alamat" dari operator), atau modul bernama ${NAME} . Bagian kosong dapat digunakan di sini.
Setiap modul akan memiliki set opsi spesifik, dan mereka terdaftar di bagian berikutnya. Selain opsi konfigurasi, bagian authorization khusus dapat hadir dalam deklarasi instance modul. Penangan tidak mengambil opsi konfigurasi apa pun, tetapi dapat mencakup bagian authorization .
Tip
Mengeksekusi LWAN dengan argumen baris perintah- --help akan menampilkan daftar modul dan penangan bawaan.
Berikut ini adalah beberapa dokumentasi dasar untuk modul yang dikirim bersama Lwan.
Modul serve_files akan menyajikan file statis, dan secara otomatis membuat indeks direktori atau melayani file yang telah dikompresi. Biasanya akan mencoba yang terbaik untuk melayani file dengan cara tercepat yang mungkin menurut beberapa heuristik.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
path | str | NULL | Jalur ke direktori yang berisi file untuk dilayani |
index_path | str | index.html | Nama file untuk berfungsi sebagai indeks untuk direktori |
serve_precompressed_path | bool | true | Jika $ file.gz ada, lebih kecil dan lebih baru dari $ file, dan klien menerima pengkodean gzip , transfernya |
auto_index | bool | true | Hasilkan daftar direktori secara otomatis jika tidak ada file index_path . Kalau tidak, hasilkan 404 |
auto_index_readme | bool | true | Termasuk isi file readme sebagai bagian dari indeks direktori yang dihasilkan secara otomatis |
directory_list_template | str | NULL | Jalur ke template kumis untuk daftar direktori; Secara default, gunakan template internal |
read_ahead | int | 131702 | Jumlah maksimum byte untuk dibaca ke depan saat caching file terbuka. Nilai 0 menonaktifkan readahead. Readahead dilakukan oleh utas prioritas rendah untuk tidak memblokir utas I/O saat luasan file sedang dibaca dari sistem file. |
cache_for | time | 5s | Waktu untuk menyimpan metadata file (ukuran, konten terkompresi, terbuka deskriptor file, dll.) Dalam cache |
Catatan
File yang lebih kecil dari 16Kib akan dikompresi dalam RAM untuk durasi yang ditentukan dalam pengaturan cache_for . Lwan akan selalu mencoba untuk mengompres dengan mengempa, dan secara opsional akan memampatkan dengan Brotli dan ZSTD (jika LWAN telah dibangun dengan dukungan yang tepat).
Dalam kasus di mana kompresi tidak akan sepadan dengan usaha (misalnya menambahkan header Content-Encoding akan menghasilkan respons yang lebih besar daripada mengirim file yang tidak terkompresi, biasanya kasus untuk file yang sangat kecil), Lwan tidak akan menghabiskan waktu untuk menekan file.
Untuk file yang lebih besar dari 16KIB, LWAN tidak akan mencoba untuk mengompresnya. Dalam versi mendatang, itu mungkin melakukan ini dan mengirim tanggapan menggunakan pengkodean yang dipotong saat file sedang dikompresi (hingga batas tertentu, tentu saja), tetapi untuk saat ini, hanya file yang terkompresi (lihat pengaturan serve_precompressed_path pada tabel di atas) dipertimbangkan.
Untuk semua kasus, LWAN mungkin mencoba menggunakan versi Gzipping jika itu ditemukan di sistem file dan klien meminta pengkodean ini.
directory_list_template| Variabel | Jenis | Keterangan |
|---|---|---|
rel_path | str | Jalur relatif ke jalur real direktori root |
readme | str | Isi file readme pertama ditemukan ( readme , readme.txt , read.me , README.TXT , README ) |
file_list | Iterator | Iterasi dalam daftar file |
file_list.zebra_class | str | odd untuk item aneh, atau even atau bahkan item |
file_list.icon | str | Jalur ke ikon untuk jenis file |
file_list.name | str | Nama file (melarikan diri) |
file_list.type | str | Jenis file (direktori atau file biasa) |
file_list.size | int | Ukuran file |
file_list.unit | str | Unit untuk file_size |
Modul lua akan memungkinkan permintaan untuk dilayani oleh skrip yang ditulis dalam bahasa pemrograman LUA. Meskipun fungsionalitas yang disediakan oleh modul ini cukup spartan, ia dapat menjalankan kerangka kerja seperti Sailor.
Script dapat disajikan dari file atau tertanam dalam file konfigurasi, dan hasil memuatnya, modul LUA standar, dan (secara opsional, jika menggunakan luajit) mengoptimalkan kode akan di -cache untuk sementara waktu.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
default_type | str | text/plain | Tipe mime default untuk tanggapan |
script_file | str | NULL | Jalan ke skrip Lua |
cache_period | time | 15s | Saatnya menjaga negara Lua dimuat dalam memori |
script | str | NULL | Inline skrip lua |
Catatan
Script LUA tidak dapat menggunakan variabel global, karena mereka mungkin tidak hanya dilayani oleh utas yang berbeda, tetapi status akan tersedia hanya untuk jumlah waktu yang ditentukan dalam opsi konfigurasi cache_period . Ini karena setiap utas I/O di LWAN akan membuat contoh Lua VM (yaitu satu lua_State struct untuk setiap utas I/O), dan setiap Lwan coroutine akan memunculkan utas LUA (dengan lua_newthread() ) per permintaan.
Tidak perlu memiliki satu contoh modul LUA untuk setiap titik akhir; satu skrip, tertanam dalam file konfigurasi atau sebaliknya, dapat melayani banyak titik akhir yang berbeda. Skrip seharusnya menerapkan fungsi dengan tanda tangan berikut: handle_${METHOD}_${ENDPOINT}(req) , di mana ${METHOD} dapat menjadi metode http (yaitu get , post , head , dll.), Dan ${ENDPOINT} adalah titik akhir yang diinginkan untuk ditangani oleh fungsi itu. Fungsi handle(req) akan dipanggil jika versi spesifik tidak ada.
Tip
Gunakan titik akhir root untuk catchall. Misalnya, fungsi pawang handle_get_root() akan dipanggil jika tidak ada pawang lain yang dapat ditemukan untuk permintaan itu. Jika tidak ada catchall yang ditentukan, server akan mengembalikan kesalahan 404 Not Found .
Parameter req menunjuk ke metatable yang berisi metode untuk mendapatkan informasi dari permintaan, atau untuk mengatur respons, seperti yang terlihat di bawah ini:
req:query_param(param) Mengembalikan parameter kueri (dari string kueri) dengan param kunci, atau nil jika tidak ditemukanreq:post_param(param) mengembalikan parameter post (hanya untuk ${POST} penangan) dengan param kunci, atau nil jika tidak ditemukanreq:set_response(str) menetapkan respons ke string strreq:say(str) mengirimkan potongan respons (menggunakan pengkodean chunked di http)req:send_event(event, str) mengirim acara (menggunakan acara server-sent)req:cookie(param) mengembalikan cookie bernama param , atau nil tidak ditemukanreq:set_headers(tbl) mengatur header respons dari tabel tbl ; header dapat ditentukan beberapa kali dengan menggunakan tabel, bukan string, dalam nilai tabel ( {'foo'={'bar', 'baz'}} ); Harus dipanggil sebelum mengirim tanggapan dengan say() atau send_event()req:header(name) memperoleh header dari permintaan dengan nama yang diberikan atau nil jika tidak ditemukanreq:sleep(ms) jeda pawang saat ini untuk jumlah milidetik yang ditentukanreq:ws_upgrade() mengembalikan 1 jika koneksi dapat ditingkatkan ke WebSocket; 0 sebaliknyareq:ws_write_text(str) mengirim str melalui koneksi WebSocket-yang ditingkatkan sebagai bingkai teksreq:ws_write_binary(str) mengirimkan str melalui koneksi WebSocket-Tepergraded sebagai bingkai binerreq:ws_write(str) mengirimkan str melalui koneksi WebSocket-yang-dibandingkan sebagai teks atau bingkai biner, tergantung pada konten yang hanya berisi karakter ASCII atau tidakreq:ws_read() Mengembalikan string dengan isi bingkai Websocket terakhir, atau angka yang menunjukkan status (enotconn/107 di Linux jika telah terputus; eAgain/11 di Linux jika tidak ada yang tersedia; enomsg/42 di Linux sebaliknya). Nilai pengembalian di sini mungkin berubah di masa depan untuk sesuatu yang lebih seperti Lua.req:remote_address() Mengembalikan string dengan alamat IP jarak jauh.req:path() mengembalikan string dengan jalur permintaan.req:query_string() Mengembalikan string dengan string kueri (string kosong jika tidak ada string kueri yang ada).req:body() mengembalikan badan permintaan (pasca/put permintaan).req:request_id() Mengembalikan string yang berisi ID permintaan.req:request_date() Mengembalikan tanggal karena akan ditulis di header respons Date .req:is_https() mengembalikan true jika permintaan ini dilayani melalui https, false sebaliknya.req:host() mengembalikan nilai header Host jika ada, jika tidak nil .req:http_version() mengembalikan HTTP/1.0 atau HTTP/1.1 tergantung pada versi permintaan.req:http_method() mengembalikan string, dalam huruf besar, dengan metode http (misalnya "GET" ).req:http_headers() mengembalikan tabel dengan semua header dan nilainya. Fungsi Handler dapat mengembalikan nil (dalam hal ini, respons 200 OK dihasilkan), atau angka yang cocok dengan kode status HTTP. Mencoba mengembalikan kode status HTTP yang tidak valid atau apa pun selain angka atau nil akan menghasilkan respons 500 Internal Server Error yang dilemparkan.
Selain metametes dalam parameter req , orang juga dapat mencatat pesan dengan level logging yang berbeda dengan memanggil metode dari Lwan.log :
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (juga akan membatalkan lwan! Dengan hati -hati)Lwan.log:debug(str) (hanya tersedia di debug builds; no-op sebaliknya) Catatan
Jika LWAN dibangun dengan dukungan syslog, pesan -pesan ini juga akan dikirim ke log sistem, jika tidak mereka akan dicetak ke kesalahan standar.
Modul rewrite akan cocok dengan pola dalam URL dan memberikan opsi untuk mengarahkan kembali ke URL lain, atau menulis ulang permintaan dengan cara yang akan ditangani Lwan seolah -olah dibuat dengan cara itu pada awalnya.
Catatan
Forked dari LUA 5.3.1, mesin ekspresi reguler mungkin tidak dikemas dengan fitur seperti kebanyakan mesin serba guna, tetapi telah dipilih secara khusus karena merupakan otomat terbatas deterministik dalam upaya untuk membuat semacam penyangkalan serangan layanan menjadi tidak mungkin.
URL baru dapat ditentukan menggunakan sintaks substitusi teks sederhana, atau menggunakan skrip LUA.
Tip
Script LUA akan berisi metameto yang sama yang tersedia di req metatable yang disediakan oleh modul LUA, sehingga bisa sangat kuat.
Setiap instance dari modul Penulisan ulang akan membutuhkan pattern dan tindakan untuk dieksekusi ketika pola tersebut dicocokkan. Pola dievaluasi dalam urutan yang muncul dalam file konfigurasi, dan ditentukan menggunakan bagian bersarang dalam file konfigurasi. Misalnya, pertimbangkan contoh berikut, di mana dua pola ditentukan:
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
}
}
Contoh ini mendefinisikan dua pola, satu menyediakan URL yang lebih bagus yang tersembunyi dari pengguna, dan yang lain memberikan cara berbeda untuk mendapatkan tautan langsung ke gambar yang di -host pada layanan hosting gambar yang populer (yaitu meminta /some/base/endpoint/imgur/mp4/4kOZNYX akan mengarahkan langsung ke sumber daya dalam layanan imgur).
Nilai rewrite_as atau redirect_to juga dapat menjadi skrip LUA; Dalam hal ini, opsi expand_with_lua harus diatur ke true , dan, alih -alih menggunakan sintaks substitusi teks sederhana sebagai contoh di atas, fungsi bernama handle_rewrite(req, captures) harus ditentukan sebagai gantinya. Parameter req didokumentasikan di bagian Modul LUA; Parameter captures adalah tabel yang berisi semua penangkapan, secara berurutan (yaitu captures[2] setara dengan %2 dalam sintaks substitisi teks sederhana). Fungsi ini mengembalikan URL baru untuk dialihkan ke.
Modul ini tidak memiliki opsi dengan sendirinya. Opsi ditentukan dalam setiap pola.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
rewrite_as | str | NULL | Tulis ulang URL mengikuti pola ini |
redirect_to | str | NULL | Mengalihkan ke URL baru mengikuti pola ini |
expand_with_lua | bool | false | Gunakan skrip LUA untuk mengarahkan kembali atau menulis ulang permintaan |
Opsi redirect_to dan rewrite_as saling eksklusif, dan salah satunya harus ditentukan setidaknya.
Dimungkinkan juga untuk menentukan kondisi untuk memicu penulisan ulang. Untuk menentukan satu, buka blok condition , tentukan jenis kondisi, dan kemudian parameter untuk kondisi yang akan dievaluasi. Berbagai kondisi dapat diatur per aturan penulisan ulang selama ada satu kondisi per jenis:
| Kondisi | Dapat menggunakan substan. sintaksis | Bagian diperlukan | Parameter | Keterangan |
|---|---|---|---|---|
cookie | Ya | Ya | Satu key = value | Memeriksa apakah permintaan memiliki key cookie memiliki nilai value |
query | Ya | Ya | Satu key = value | Memeriksa apakah permintaan memiliki key variabel kueri memiliki nilai value |
post | Ya | Ya | Satu key = value | Periksa apakah permintaan memiliki key data post memiliki nilai value |
header | Ya | Ya | Satu key = value | Memeriksa apakah key header permintaan memiliki nilai value |
environment | Ya | Ya | Satu key = value | Periksa apakah key variabel lingkungan memiliki nilai value |
stat | Ya | Ya | path , is_dir , is_file | Periksa apakah ada path di sistem file, dan secara opsional memeriksa apakah is_dir atau is_file |
encoding | TIDAK | Ya | deflate , gzip , brotli , zstd , none | Periksa apakah klien menerima respons dalam pengkodean yang ditentukan (misalnya deflate = yes untuk pengkodean pengempuhan) |
proxied | TIDAK | TIDAK | Boolean | Memeriksa apakah permintaan telah diproksi melalui protokol proxy |
http_1.0 | TIDAK | TIDAK | Boolean | Memeriksa apakah permintaan dibuat dengan klien http/1.0 |
is_https | TIDAK | TIDAK | Boolean | Memeriksa apakah permintaan dilakukan melalui https |
has_query_string | TIDAK | TIDAK | Boolean | Periksa apakah permintaan memiliki string kueri (bahkan jika kosong) |
method | TIDAK | TIDAK | Nama metode | Memeriksa apakah metode http adalah yang ditentukan |
lua | TIDAK | TIDAK | Rangkaian | Menjalankan Lua Function matches(req) di dalam string dan memeriksa apakah itu mengembalikan true atau false |
backref | TIDAK | Ya | backref index tunggal = value | Memeriksa apakah nomor backref cocok dengan nilai yang disediakan |
Dapat menggunakan substan. Sintaks mengacu pada kemampuan untuk merujuk pola yang cocok menggunakan sintaks substitusi yang sama yang digunakan untuk rewrite as atau redirect to tindakan. Misalnya, condition cookie { some-cookie-name = foo-%1-bar } akan menggantikan %1 dengan kecocokan pertama dari pola kondisi ini terkait.
Catatan
Kondisi yang tidak memerlukan bagian harus ditulis sebagai kunci; Misalnya, condition has_query_string = yes .
Misalnya, jika seseorang ingin mengirim site-dark-mode.css jika ada cookie style dengan nilai dark , dan mengirim site-light-mode.css sebaliknya, seseorang dapat menulis:
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
Contoh lain: Jika seseorang ingin mengirim file yang telah dikompresi sebelumnya jika ada di sistem file dan pengguna memintanya:
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
}
Catatan
Secara umum, ini tidak perlu, karena modul yang melayani file akan melakukan ini secara otomatis dan memilih file terkecil yang tersedia untuk pengkodean yang diminta, tetapi ini menunjukkan bahwa dimungkinkan untuk memiliki fitur serupa dengan konfigurasi saja.
Modul redirect akan, seperti yang dikatakan dalam timah, menghasilkan 301 Moved permanently (secara default; kode dapat diubah, lihat di bawah) respons, sesuai dengan opsi yang ditentukan dalam konfigurasinya. Secara umum, modul rewrite harus digunakan sebagai gantinya karena mengemas lebih banyak fitur; Namun, modul ini juga berfungsi sebagai contoh cara menulis modul LWAN (kurang dari 100 baris kode).
Jika opsi to tidak ditentukan, itu selalu menghasilkan respons 500 Internal Server Error . Menentukan kode http yang tidak valid, atau kode yang tidak diketahui LWAN (lihat enum lwan_http_status ), akan menghasilkan respons 301 Moved Permanently .
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
to | str | NULL | Lokasi untuk mengarahkan ke |
code | int | 301 | Kode http untuk melakukan pengalihan |
Modul response akan menghasilkan respons buatan dari kode HTTP apa pun. Selain juga berfungsi sebagai contoh bagaimana menulis modul LWAN, ini dapat digunakan untuk mengukir rongga dari modul lain (misalnya menghasilkan respons 405 Not Allowed untuk file di /.git , jika / disajikan dengan modul serve_files ).
Jika code yang disediakan berada di luar kode respons yang diketahui oleh LWAN, kesalahan 404 Not Found akan dikirim sebagai gantinya.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
code | int | 999 | Kode respons http |
Permintaan proksi modul fastcgi antara klien HTTP yang terhubung ke LWAN dan server FastCGI yang dapat diakses oleh LWAN. Ini berguna, misalnya, untuk melayani halaman dari bahasa skrip seperti PHP.
Catatan
Ini adalah versi awal dari modul ini, dan dengan demikian, tidak dioptimalkan dengan baik, beberapa fitur hilang, dan beberapa nilai yang diberikan kepada lingkungan hardcoded.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
address | str | Alamat untuk terhubung ke. Dapat berupa jalur file (untuk soket domain UNIX), alamat IPv4 ( aaa.bbb.ccc.ddd:port ), atau alamat IPv6 ( [...]:port ). | |
script_path | str | Lokasi di mana skrip CGI berada. | |
default_index | str | index.php | Skrip default untuk dieksekusi jika tidak ditentukan dalam permintaan URI. |
Bagian otorisasi dapat dinyatakan dalam contoh atau penangan modul apa pun, dan menyediakan cara untuk mengesahkan pemenuhan permintaan itu melalui mekanisme otorisasi HTTP standar. Untuk meminta otorisasi untuk mengakses instance atau pawang modul tertentu, mendeklarasikan bagian authorization dengan parameter basic , dan mengatur salah satu opsinya.
| Pilihan | Jenis | Bawaan | Keterangan |
|---|---|---|---|
realm | str | Lwan | Ranah untuk otorisasi. Ini biasanya ditampilkan di USER/Kata Sandi UI di browser |
password_file | str | NULL | Jalur untuk file yang berisi nama pengguna dan kata sandi (dalam teks yang jelas). Format file sama dengan format file konfigurasi yang digunakan oleh LWAN |
Peringatan
Tidak hanya kata sandi yang disimpan dalam teks yang jelas dalam file yang harus dapat diakses oleh server, mereka akan disimpan dalam memori selama beberapa detik. Hindari menggunakan fitur ini jika memungkinkan.
Harap baca bagian ini (dan ikuti) jika Anda berencana berkontribusi ke Lwan. Tidak ada yang tidak terduga di sini; Ini sebagian besar mengikuti aturan dan harapan banyak proyek FOSS lainnya, tetapi setiap orang mengharapkan hal -hal yang sedikit berbeda satu sama lain.
Lwan mencoba mengikuti gaya pengkodean yang konsisten di seluruh proyek. Jika Anda mempertimbangkan untuk menyumbangkan tambalan pada proyek, harap hormati gaya ini dengan mencoba mencocokkan gaya kode sekitarnya. Umumnya:
global_variables_are_named_like_this , meskipun mereka cenderung jarang dan harus ditandai sebagai static (dengan pengecualian langka)local_var , i , conntypedef for struct jarang digunakan di LWAN#pragma once , bukan yang biasa termasuk penjaga guardlwan-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. Karena itu:
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