12. Web uygulaması
kaba.web bir HTTP sunucusudur: tarayıcıya ve curla cevap veren
programlar yazarsınız. Sunucu ek kütüphane gerektirmez, bu yüzden
ikili küçük ve bağımlılığı azdır.
Hazır iskelet: kabalang yeni <ad> --web
(13. sayfa).
Bu sayfadaki sunucular belge testinde derlenir; şablon testinde ayrıca gerçekten başlatılıp HTTP isteğiyle yanıtı ölçülür.
En küçük sunucu
kullan kaba.web
fonksiyon ana_sayfa(_istek):
don web.html("<h1>Merhaba KabaOS</h1>")
sunucu = web.sunucu(8080)
sunucu.al("/", ana_sayfa)
yazdir("http://127.0.0.1:8080 dinleniyor")
sunucu.basla()Denemek için:
curl http://127.0.0.1:8080/⚠️ Sunucu varsayılan olarak yalnız 127.0.0.1i dinler. Ağa açmak
açık bir karardır: web.sunucu(8080, "0.0.0.0"). Böylece
web.sunucu(80) yazan kimse farkında olmadan sunucusunu ağa açmaz.
Yollar, parametreler, sorgu, gövde
kullan kaba.web
fonksiyon selamla(istek):
# Yol parametresi: /kisi/{ad}
don web.yanit("Merhaba " + istek.parametre("ad"))
fonksiyon arama(istek):
# ⚠️ Kullanıcı verisi HTML'e girerken KAÇIRILIR — yoksa yansımalı XSS olur.
aranan = web.kacir(istek.sorgu("q"))
don web.html("<p>Aranan: " + aranan + "</p>")
fonksiyon yukleme(istek):
don web.json("{\"alinan_bayt\": " + metin(istek.govde_boyutu()) + "}")
sunucu = web.sunucu(8080)
sunucu.al("/kisi/{ad}", selamla)
sunucu.al("/ara", arama)
sunucu.gonder("/yukle", yukleme)
# Tek satırlık işleyici de olur:
sunucu.al("/saglik", (istek) => web.yanit("tamam"))
sunucu.basla()| Yöntem | HTTP | Yazımı |
|---|---|---|
| GET | oku | sunucu.al(yol, isleyici) |
| POST | gönder | sunucu.gonder(yol, isleyici) |
| PUT | güncelle | sunucu.guncelle(yol, isleyici) |
| DELETE | sil | sunucu.sil(yol, isleyici) |
| 404 | bulunamadı | sunucu.bulunamadi(isleyici) |
| statik dosya | — | sunucu.dizin("/statik", "/var/www") |
İstek nesnesi
| Çağrı | Verir |
|---|---|
istek.yol() |
istenen yol |
istek.metot() |
GET, POST, … |
istek.parametre("ad") |
yol parametresi (/kisi/{ad}) |
istek.sorgu("q") · istek.sorgu_var_mi("q") |
sorgu dizgesi alanı |
istek.baslik("User-Agent") |
HTTP başlığı |
istek.govde() · istek.govde_boyutu() |
istek gövdesi |
istek.adres() |
istemci adresi |
Yanıt üretmek
| Çağrı | Üretir |
|---|---|
web.yanit(metin) |
düz metin |
web.yanit(metin, durum) |
düz metin + durum kodu |
web.html(govde) |
text/html |
web.json(govde) |
application/json |
web.bulunamadi() |
404 |
web.hata(ileti, durum) |
istenen durum kodu |
web.dosya(yol) |
dosyanın kendisi (tip uzantıdan; yoksa 404) |
web.kacir(ham) |
HTML'e gömmeden önce metni kaçırır |
Güvenlik: web.kacir ne zaman yetmez
Yanıta başlık ve durum zincirle eklenir:
web.dosya(yol).baslik("Cache-Control", "no-cache"),
web.yanit("", 301).baslik("Location", "/yeni/").
⚠️ web.dosya(yol) yolu doğrulamaz. Yolu kullanıcı girdisinden
kuruyorsanız kökten kaçışı (.., gizli dosya) siz engelleyin; bir dizini
olduğu gibi sunmak için sunucu.dizin(...) bunu zaten yapar.
web.kacir metin gövdesi içindir: &, <, >, ", ' kaçırılır.
⚠️ Öznitelik içi (href="..."), <script> içi ya da URL bağlamları için
yetmez — oraya kullanıcı verisi koymayın. JSON üretirken kacir değil
kaba.json kullanılır.
Statik dosya sunumu yol geçişine (../) kapalıdır ve gizli dosyaları
sunmaz.
Binlerce bağlantı: olay döngüsü
Sunucu bağlantı başına bir iş parçacığı açmaz. Her iş parçacığı bir olay döngüsü çalıştırır ve binlerce bağlantıyı aynı anda taşır: bekleyen (boşta duran, yavaş gönderen) bir bağlantı kimseyi bekletmez. Döngü sayısı üçüncü argümandır; verilmezse işlemci çekirdeği kadardır. Statik dosyalar belleğe okunmadan doğrudan çekirdekten gönderilir.
İşleyiciniz sıradan bir fonksiyondur — async, await yoktur. İki kural:
⚠️ İşleyici kısa olmalı. İşleyicinin içinde uzun süren bir iş (uzun bir
komut, uyu(...)) o döngüdeki bütün bağlantıları bekletir. Uzun işi bir
göreve verin, sonucu kanaldan alın; işleyici bekleyen() ile
beklemeden bakar:
kullan kaba.web
kullan kaba.sistem
isler: kanal<sayi> = kanal(1)
sonuclar: kanal<metin> = kanal(1)
son = "henüz yok"
fonksiyon hesapci(gelen, giden):
iken dogru:
v = gelen.al()
eger v.bos_mu():
kir
uyu(2000) # uzun iş — işleyici değil, görev yapar
giden.gonder("hesaplandı")
fonksiyon durum(istek):
kuresel son
eger sonuclar.bekleyen() > 0: # beklemeden bak
son = sonuclar.al().deger()
don web.yanit("son: " + son)
fonksiyon baslat_istegi(istek):
eger isler.bekleyen() == 0:
isler.gonder(1)
don web.yanit("başladı", 202)
fonksiyon sun(s):
s.basla()
sunucu = web.sunucu(8080, "127.0.0.1", 1)
sunucu.al("/", durum)
sunucu.gonder("/hesapla", baslat_istegi)
birlikte:
baslat hesapci(isler, sonuclar)
baslat sun(sunucu)⚠️ Paylaşılan durum. Birden çok döngü varken işleyiciler aynı anda
koşar; işleyicinin içinden paylaşılan bir değişkeni yazmak yarış koşuludur.
Durum tutan sunucuyu üçüncü argümanla tek döngüde çalıştırın (yukarıdaki
gibi 1 — tek döngü de binlerce bağlantıyı taşır) ya da durumu kanalla bir
göreve verin.
⚠️ İşleyici, kendisinden ÖNCE tanımlanmış değişkenleri görür. sunucu
aşağıda tanımlandığı için bir işleyicinin içinden sunucu.istek_sayisi()
çağıramazsınız (ölçüldü: 'sunucu' adında bir değişken tanımlanmamış).
Sayaç gerekiyorsa işleyicinin dışında okuyun.
Sınırlar ve ölçüm
kullan kaba.web
sunucu = web.sunucu(8080)
sunucu.govde_siniri(1048576) # 1 MB'tan büyük gövdeyi reddet
sunucu.zaman_asimi(15) # soket zaman aşımı (saniye)
sunucu.baglanti_siniri(10000) # aynı anda en çok bağlantı (aşımda 503)
sunucu.al("/", (istek) => web.yanit("tamam"))
sunucu.al("/olcum", (istek) => web.json("{\"tamam\": true}"))
sunucu.basla()Sunucu çalışırken sunucu.istek_sayisi() ve sunucu.reddedilen_sayisi()
sayaçlarını okuyabilirsiniz.
Portu ayardan almak
Şablonun yaptığı şey — sabit port yerine ortamdan okumak:
kullan kaba.web
p = cevre.al("PORT")
eger p == "":
p = "8080"
sunucu = web.sunucu(sayi(p))
sunucu.al("/", (istek) => web.html("<h1>Merhaba</h1>"))
yazdir(f"http://127.0.0.1:{p} dinleniyor")
sunucu.basla()Engellemeden başlatmak
sunucu.basla() çağıran akışı bloklar. Sunucuyu arka planda başlatıp başka
iş yapmak isterseniz sunucu.arkada_basla() ve sunucu.durdur() vardır.
⚠️ Arka planda başlatıp kapsamdan çıkarsanız yakalanan değişkenler ölür;
arkada_basla() sonrası programı canlı tutmayı unutmayın.