KABAFORCEGeliştirici KabaOS'u edinin
Önizleme · 0.6.0 — dil 1.0 öncesidir; sözdizimi ve kütüphane değişebilir. Buradaki her örnek derlenerek sınanır.

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

KabaLang
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:

Uçbirim
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

KabaLang
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:

KabaLang
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

KabaLang
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:

KabaLang
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.

Sonraki adım

13. Proje, modül ve şablonlar