Tayang di HN: Hosting situs web dengan server web C
(github.com/cozis)My Blog Technology
Server web ini adalah server web minimal yang dirancang untuk meng-host blog saya. Sejak awal dibuat kokoh agar mampu bertahan di internet publik. Tidak memerlukan reverse proxy. Anda bisa melihatnya berjalan langsung di http://playin.coz.is/index.html. Saya sempat meminta Reddit untuk mencoba meretasnya dan berhasil mengumpulkan log permintaan yang menarik sekaligus berbahaya hingga ukuran gigabita. Sebagiannya disimpan di attempts.txt, dan nanti akan saya telusuri lagi untuk bersenang-senang.
Tapi.. kenapa?
Saya suka membuat alat saya sendiri, dan saya lelah mendengar bahwa semuanya harus "battle-tested". Kalau terjadi crash, lalu bagaimana? Bug bisa diperbaiki.
Spesifikasi
- Khusus Linux
- Implementasi HTTP/1.1, pipelining, dan koneksi keep-alive
- Mendukung HTTPS (menggunakan BearSSL hingga TLS 1.2)
- Dependensi minimal (libc dan BearSSL saat menggunakan HTTPS)
- Timeout yang dapat dikonfigurasi
- Log akses, log crash, rotasi log, pembatasan penggunaan disk
- Tidak ada
Transfer-Encoding: Chunked(merespons dengan411 Length Requireduntuk mendorong klien mengirim ulang denganContent-Length) - Satu core (akan diubah saat mendapatkan VPS yang lebih baik)
- Belum ada caching file statis
Benchmark
Fokus proyek ini adalah ketangguhan, tetapi sama sekali tidak lambat. Perbandingan sederhana dengan nginx (endpoint statis, keduanya single-thread, batas 1K koneksi):
-
(blogtech)
$ wrk -c 500 -d 5s http://127.0.0.1:80/hello- Latensi rata-rata: 6.66ms
- Permintaan/detik: 76974.24
- Transfer/detik: 6.09MB
-
(nginx)
$ wrk -c 500 -d 5s http://127.0.0.1:8080/hello- Latensi rata-rata: 149.11ms
- Permintaan/detik: 44227.78
- Transfer/detik: 8.27MB
Konfigurasi nginx:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location /hello {
add_header Content-Type text/plain;
return 200 "Hello, world!";
}
}
}
Build dan jalankan
Secara default, build server hanya untuk HTTP:
$ make
Perintah ini akan menghasilkan executable serve (build rilis), serve_cov (build cakupan), dan serve_debug (build debug). Build rilis mendengarkan di port 80, dan build debug di port 8080.
Untuk mengaktifkan HTTPS, Anda perlu meng-clone dan membangun BearSSL:
$ mkdir 3p
$ cd 3p
$ git clone https://www.bearssl.org/git/BearSSL
$ cd BearSSL
$ make -j
$ cd ../../
$ make -B HTTPS=1
Executable yang sama akan dihasilkan, tetapi koneksi aman akan tersedia di port 443 (rilis) atau 8081 (debug). Anda harus menempatkan file cert.pem dan key.pem di direktori yang sama dengan executable. Untuk mengubah nama dan lokasinya, modifikasi:
#define HTTPS_KEY_FILE "key.pem"
#define HTTPS_CERT_FILE "cert.pem"
Untuk menguji HTTPS secara lokal, buat sertifikat self-signed (beserta private key):
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -x509 -key key.pem -out cert.pem -days 365
Penggunaan
Server menyajikan konten statis dari folder docroot/. Untuk mengubahnya, modifikasi fungsi respond:
typedef struct {
Method method;
string path;
int major;
int minor;
int nheaders;
Header headers[MAX_HEADERS];
string content;
} Request;
void respond(Request request, ResponseBuilder *b) {
if (request.major != 1 || request.minor > 1) {
status_line(b, 505); // HTTP Version Not Supported
return;
}
if (request.method != M_GET) {
status_line(b, 405); // Method Not Allowed
return;
}
if (string_match_case_insensitive(request.path, LIT("/hello"))) {
status_line(b, 200);
append_content_s(b, LIT("Hello, world!"));
return;
}
if (serve_file_or_dir(b, LIT("/"), LIT("docroot/"), request.path, NULLSTR, false))
return;
status_line(b, 404);
append_content_s(b, LIT("Nothing here :|"));
}
Di sini Anda dapat menambahkan endpoint dengan mencabangkan field request.path. Path hanyalah slice dari buffer permintaan. URI tidak di-parse.
Pengujian
Saya rutin menjalankan server dengan valgrind dan sanitizers (address, undefined), lalu menargetkannya dengan wrk. Saya juga menambahkan pengujian otomatis di tests/test.py untuk memeriksa kepatuhan terhadap spesifikasi HTTP/1.1. Saya menjaga beban tetap ada dengan meng-host situs web saya sendiri dan mempostingnya di berbagai tempat. Semua bot di internet yang memindai situs web rentan menjadi fuzzer yang hebat.
Masalah yang diketahui
- Server merespons klien HTTP/1.0 dengan HTTP/1.1
Kontribusi
Saya terutama bekerja di branch DEV dan sesekali menggabungkannya ke MAIN. Saat membuka pull request, akan lebih mudah jika menargetkan DEV.
Ringkasan GN⁺
- Proyek ini adalah server web yang menargetkan dependensi minimal dan ketangguhan.
- Mendukung HTTP/1.1 dan HTTPS, serta menyediakan berbagai fitur logging dan timeout yang dapat dikonfigurasi.
- Hasil benchmark menunjukkan waktu respons yang lebih cepat daripada nginx.
- Dirancang agar pengembang dapat menikmati proses membuat alat mereka sendiri dan memperbaiki bug.
- Proyek serupa dengan fungsi sebanding mencakup Nginx dan Apache HTTP Server.
1 komentar
Komentar Hacker News
Tidak perlu reverse proxy: Menggunakan Jetty untuk menerapkan aplikasi ke internet tanpa reverse proxy dan tidak mengalami masalah
Server web C buatan sendiri: Membuat server web C yang pernah digunakan untuk menjalankan situs web komersial
Kepuasan membangun layanan: Sangat memuaskan membangun layanan dasar dengan menggunakan API sistem
Perkenalan proyek kecil: Memperkenalkan proyek menarik yang dimulai saat waktu luang
Rekomendasi framework Kore: Jika menulis aplikasi C dan merasa tidak nyaman menulis bagian yang terekspos ke publik, merekomendasikan framework Kore
Berbagi tautan menarik: Instance althttpd milik sqlite.org menangani lebih dari 500 ribu permintaan HTTP per hari
Kesenangan membuat alat sendiri: Lelah dengan pendapat bahwa semua hal harus "battle-tested"
Ceramah Chaos Communication Congress: Mengingat ceramah tentang blog/server web yang ditulis dalam C dengan fitur keamanan bawaan
Situs web yang stabil: Situs web yang tidak crash meskipun tampil di halaman depan
Kembali ke dasar: Menyukai pendekatan kembali ke dasar dengan hanya menggunakan yang diperlukan