HN'de tanıtıldı: C web sunucusuyla bir web sitesi barındırma
(github.com/cozis)Blog Teknolojim
Bu web sunucusu, blogumu barındırmak için tasarlanmış minimal bir web sunucusudur. Baştan itibaren genel internete dayanacak kadar sağlam şekilde inşa edilmiştir. Reverse proxy gerektirmez. Gerçekte çalışırken http://playin.coz.is/index.html adresinde görülebilir. Eğlenceli ve kötü niyetli istek günlüklerini gigabaytlarca toplamak için Reddit'te hacklenmeyi istedim. Bunların bir kısmını attempts.txt içine kaydettim; daha sonra eğlencesine daha fazlasını inceleyeceğim.
Peki.. neden?
Kendi araçlarımı yapmaktan hoşlanıyorum ve her şeyin "battle-tested" olması gerektiğini duymaktan bıktım. Çökme olursa ne olacak? Hatalar düzeltilebilir.
Özellikler
- Yalnızca Linux
- HTTP/1.1, pipelining ve keep-alive bağlantıları uygulanmış
- HTTPS desteği (BearSSL kullanarak TLS 1.2'ye kadar)
- Minimum bağımlılık (HTTPS kullanıldığında libc ve BearSSL)
- Yapılandırılabilir zaman aşımı
- Erişim günlüğü, çökme günlüğü, log rotasyonu, disk kullanım sınırı
Transfer-Encoding: Chunkedyok (411 Length Requiredyanıtı vererek istemciyiContent-Lengthile yeniden göndermeye yönlendirir)- Tek çekirdekli (daha iyi bir VPS alınca değiştirilecek)
- Statik dosya önbelleklemesi yok (henüz)
Kıyaslama
Bu projenin odağı sağlamlık olsa da kesinlikle yavaş değildir. nginx ile basit bir karşılaştırma (statik endpoint, ikisi de tek iş parçacıklı, 1K bağlantı sınırı):
-
(blogtech)
$ wrk -c 500 -d 5s http://127.0.0.1:80/hello- Ortalama gecikme: 6.66ms
- İstek/sn: 76974.24
- Aktarım/sn: 6.09MB
-
(nginx)
$ wrk -c 500 -d 5s http://127.0.0.1:8080/hello- Ortalama gecikme: 149.11ms
- İstek/sn: 44227.78
- Aktarım/sn: 8.27MB
nginx yapılandırması:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location /hello {
add_header Content-Type text/plain;
return 200 "Hello, world!";
}
}
}
Derleme ve çalıştırma
Varsayılan olarak sunucu derlemesi yalnızca HTTP içindir:
$ make
Bu komut serve (release build), serve_cov (coverage build), serve_debug (debug build) çalıştırılabilirlerini üretir. Release build 80 numaralı portu, debug build ise 8080 numaralı portu dinler.
HTTPS'i etkinleştirmek için BearSSL'i klonlayıp derlemeniz gerekir:
$ mkdir 3p
$ cd 3p
$ git clone https://www.bearssl.org/git/BearSSL
$ cd BearSSL
$ make -j
$ cd ../../
$ make -B HTTPS=1
Aynı çalıştırılabilirler üretilir, ancak 443 (release) veya 8081 (debug) portunda güvenli bağlantılar etkin olur. cert.pem ve key.pem dosyaları çalıştırılabilirlerle aynı dizine yerleştirilmelidir. Adlarını ve konumlarını değiştirmek için şunları düzenleyin:
#define HTTPS_KEY_FILE "key.pem"
#define HTTPS_CERT_FILE "cert.pem"
Yerelde HTTPS ile test etmek için self-signed bir sertifika (ve özel anahtar) oluşturun:
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -x509 -key key.pem -out cert.pem -days 365
Kullanım
Sunucu, docroot/ klasöründeki statik içeriği sunar. Bunu değiştirmek için respond fonksiyonunu düzenleyin:
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 :|"));
}
Burada request.path alanı üzerinden dallanarak endpoint ekleyebilirsiniz. Yol yalnızca istek tamponunun bir dilimidir. URI ayrıştırılmaz.
Testler
Sunucuyu düzenli olarak valgrind ve sanitizer'larla (address, undefined) çalıştırıyor ve wrk ile hedefliyorum. Ayrıca HTTP/1.1 spesifikasyonuna uygunluğu kontrol etmek için tests/test.py içine otomatik testler ekliyorum. Stresi sürdürmek için kendi web sitemi bununla barındırıyor ve çeşitli yerlere gönderiler paylaşıyorum. İnternette zayıf web sitelerini tarayan tüm botlar harika birer fuzzer oluyor.
Bilinen sorunlar
- Sunucu, HTTP/1.0 istemcilerine HTTP/1.1 olarak yanıt veriyor
Katkı
Çoğunlukla DEV dalında çalışıyorum ve ara sıra MAIN'e birleştiriyorum. Pull request açarken DEV'i hedeflerseniz daha kolay olur.
GN⁺ Özeti
- Bu proje, minimum bağımlılık ve sağlamlık hedefleyen bir web sunucusudur.
- HTTP/1.1 ve HTTPS destekler; çeşitli günlük özellikleri ve yapılandırılabilir zaman aşımı sunar.
- Kıyaslama sonuçları, nginx'ten daha hızlı yanıt süreleri gösterir.
- Geliştiricilerin kendi araçlarını yapma ve hataları düzeltme sürecinden keyif alabilmesi için tasarlanmıştır.
- Benzer özelliklere sahip projeler arasında Nginx ve Apache HTTP Server bulunur.
1 yorum
Hacker News yorumları
Ters proxy gerekmiyor: Jetty kullanarak uygulamayı ters proxy olmadan internete dağıtmış ve bir sorun yaşamamış
Kendi yaptığı C web sunucusu: Ticari bir web sitesini çalıştırmış olan bir C web sunucusu geliştirmiş
Servis inşa etmenin tatmini: Sistem API’lerini kullanarak temel bir servis kurmanın çok tatmin edici olduğunu söylüyor
poll()fonksiyonunun yüksek performans göstermesine şaşırmışKüçük proje tanıtımı: Boş zamanlarında başladığı eğlenceli bir projeyi tanıtıyor
Kore framework önerisi: C uygulaması yazarken dışa açık kısımları yazmak zahmetliyse Kore framework’ünü öneriyor
İlginç bağlantı paylaşımı: sqlite.org’daki althttpd örneğinin günde 500 binden fazla HTTP isteğini işlediğini paylaşıyor
Kendi araçlarını yapmanın keyfi: Her şeyin "battle-tested" olması gerektiği yönündeki görüşten yorulmuş
Chaos Communication Congress konuşması: Güvenlik özellikleri içeren, C ile yazılmış bir blog/web sunucusu hakkındaki konuşmayı hatırlatıyor
Kararlı web sitesi: Ana sayfada gösterilse bile çökmeyen bir web sitesi
Temellere dönüş: Yalnızca gerekenleri kullanarak temellere dönme yaklaşımını seviyor